-
-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7625 from seberg/test-warning-filters
TST: add a test for "ignore" warning filters
- Loading branch information
Showing
3 changed files
with
122 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
""" | ||
Tests which scan for certain occurrences in the code, they may not find | ||
all of these occurrences but should catch almost all. This file was adapted | ||
from numpy. | ||
""" | ||
|
||
|
||
from __future__ import division, absolute_import, print_function | ||
|
||
import sys | ||
import scipy | ||
|
||
import pytest | ||
|
||
|
||
if sys.version_info >= (3, 4): | ||
from pathlib import Path | ||
import ast | ||
import tokenize | ||
|
||
class ParseCall(ast.NodeVisitor): | ||
def __init__(self): | ||
self.ls = [] | ||
|
||
def visit_Attribute(self, node): | ||
ast.NodeVisitor.generic_visit(self, node) | ||
self.ls.append(node.attr) | ||
|
||
def visit_Name(self, node): | ||
self.ls.append(node.id) | ||
|
||
class FindFuncs(ast.NodeVisitor): | ||
def __init__(self, filename): | ||
super().__init__() | ||
self.__filename = filename | ||
self.bad_filters = [] | ||
self.bad_stacklevels = [] | ||
|
||
def visit_Call(self, node): | ||
p = ParseCall() | ||
p.visit(node.func) | ||
ast.NodeVisitor.generic_visit(self, node) | ||
|
||
if p.ls[-1] == 'simplefilter' or p.ls[-1] == 'filterwarnings': | ||
if node.args[0].s == "ignore": | ||
self.bad_filters.append( | ||
"{}:{}".format(self.__filename, node.lineno)) | ||
|
||
if p.ls[-1] == 'warn' and ( | ||
len(p.ls) == 1 or p.ls[-2] == 'warnings'): | ||
|
||
if self.__filename == "_lib/tests/test_warnings.py": | ||
# This file | ||
return | ||
|
||
# See if stacklevel exists: | ||
if len(node.args) == 3: | ||
return | ||
args = {kw.arg for kw in node.keywords} | ||
if "stacklevel" not in args: | ||
self.bad_stacklevels.append( | ||
"{}:{}".format(self.__filename, node.lineno)) | ||
|
||
|
||
@pytest.fixture(scope="session") | ||
def warning_calls(): | ||
# combined "ignore" and stacklevel error | ||
base = Path(scipy.__file__).parent | ||
|
||
bad_filters = [] | ||
bad_stacklevels = [] | ||
|
||
for path in base.rglob("*.py"): | ||
# use tokenize to auto-detect encoding on systems where no | ||
# default encoding is defined (e.g. LANG='C') | ||
with tokenize.open(str(path)) as file: | ||
tree = ast.parse(file.read()) | ||
finder = FindFuncs(path.relative_to(base)) | ||
finder.visit(tree) | ||
bad_filters.extend(finder.bad_filters) | ||
bad_stacklevels.extend(finder.bad_stacklevels) | ||
|
||
return bad_filters, bad_stacklevels | ||
|
||
|
||
@pytest.mark.slow | ||
@pytest.mark.skipif(sys.version_info < (3, 4), reason="needs Python >= 3.4") | ||
def test_warning_calls_filters(warning_calls): | ||
bad_filters, bad_stacklevels = warning_calls | ||
|
||
# There is still one missing occurance in optimize.py, | ||
# this is one that should be fixed and this removed then. | ||
bad_filters = [item for item in bad_filters | ||
if 'optimize.py' not in item] | ||
|
||
if bad_filters: | ||
raise AssertionError( | ||
"ignore filter should not be used; found in:\n {}".format( | ||
"\n ".join(bad_filters))) | ||
|
||
|
||
@pytest.mark.slow | ||
@pytest.mark.skipif(sys.version_info < (3, 4), reason="needs Python >= 3.4") | ||
@pytest.mark.xfail(reason="stacklevels currently missing") | ||
def test_warning_calls_stacklevels(warning_calls): | ||
bad_filters, bad_stacklevels = warning_calls | ||
|
||
msg = "" | ||
|
||
if bad_filters: | ||
msg += "ignore filter should not be used:\n {}".format( | ||
"\n ".join(bad_filters)) | ||
msg += "\n\n" | ||
|
||
if bad_stacklevels: | ||
msg += "warnings should have an appropriate stacklevel:\n {}".format( | ||
"\n ".join(bad_stacklevels)) | ||
|
||
if msg: | ||
raise AssertionError(msg) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters