deprecated_call now uses monkey patching strategy to capture warnings
similar to what we had in 2.7, with a few enhancements Fix #1190
This commit is contained in:
parent
6378cdf7a9
commit
603d81ef2f
|
@ -1,3 +1,11 @@
|
|||
2.8.4.dev
|
||||
---------
|
||||
|
||||
- fix #1190: ``deprecated_call()`` now works when the deprecated
|
||||
function has been already called by another test in the same
|
||||
module. Thanks Mikhail Chernykh for the report and Bruno Oliveira for the
|
||||
PR.
|
||||
|
||||
2.8.3
|
||||
-----
|
||||
|
||||
|
|
|
@ -29,29 +29,39 @@ def pytest_namespace():
|
|||
|
||||
|
||||
def deprecated_call(func, *args, **kwargs):
|
||||
""" assert that calling ``func(*args, **kwargs)``
|
||||
triggers a DeprecationWarning.
|
||||
"""
|
||||
l = []
|
||||
oldwarn_explicit = getattr(warnings, 'warn_explicit')
|
||||
def warn_explicit(*args, **kwargs):
|
||||
l.append(args)
|
||||
oldwarn_explicit(*args, **kwargs)
|
||||
oldwarn = getattr(warnings, 'warn')
|
||||
def warn(*args, **kwargs):
|
||||
l.append(args)
|
||||
oldwarn(*args, **kwargs)
|
||||
""" assert that calling ``func(*args, **kwargs)`` triggers a
|
||||
``DeprecationWarning`` or ``PendingDeprecationWarning``.
|
||||
|
||||
Note: we cannot use WarningsRecorder here because it is still subject
|
||||
to the mechanism that prevents warnings of the same type from being
|
||||
triggered twice for the same module. See #1190.
|
||||
"""
|
||||
categories = []
|
||||
|
||||
def warn_explicit(message, category, *args, **kwargs):
|
||||
categories.append(category)
|
||||
old_warn_explicit(message, category, *args, **kwargs)
|
||||
|
||||
def warn(message, category=None, **kwargs):
|
||||
if isinstance(message, Warning):
|
||||
categories.append(message.__class__)
|
||||
else:
|
||||
categories.append(category)
|
||||
old_warn(message, category, *args, **kwargs)
|
||||
|
||||
old_warn = warnings.warn
|
||||
old_warn_explicit = warnings.warn_explicit
|
||||
warnings.warn_explicit = warn_explicit
|
||||
warnings.warn = warn
|
||||
try:
|
||||
ret = func(*args, **kwargs)
|
||||
finally:
|
||||
warnings.warn_explicit = oldwarn_explicit
|
||||
warnings.warn = oldwarn
|
||||
if not l:
|
||||
warnings.warn_explicit = old_warn_explicit
|
||||
warnings.warn = old_warn
|
||||
deprecation_categories = (DeprecationWarning, PendingDeprecationWarning)
|
||||
if not any(issubclass(c, deprecation_categories) for c in categories):
|
||||
__tracebackhide__ = True
|
||||
raise AssertionError("%r did not produce DeprecationWarning" %(func,))
|
||||
raise AssertionError("%r did not produce DeprecationWarning" % (func,))
|
||||
return ret
|
||||
|
||||
|
||||
|
|
|
@ -63,32 +63,30 @@ class TestWarningsRecorderChecker(object):
|
|||
with rec:
|
||||
pass # can't enter twice
|
||||
|
||||
#
|
||||
# ============ test pytest.deprecated_call() ==============
|
||||
#
|
||||
|
||||
def dep(i):
|
||||
if i == 0:
|
||||
py.std.warnings.warn("is deprecated", DeprecationWarning)
|
||||
return 42
|
||||
|
||||
reg = {}
|
||||
def dep_explicit(i):
|
||||
if i == 0:
|
||||
py.std.warnings.warn_explicit("dep_explicit", category=DeprecationWarning,
|
||||
filename="hello", lineno=3)
|
||||
|
||||
class TestDeprecatedCall(object):
|
||||
"""test pytest.deprecated_call()"""
|
||||
|
||||
def dep(self, i):
|
||||
if i == 0:
|
||||
py.std.warnings.warn("is deprecated", DeprecationWarning)
|
||||
return 42
|
||||
|
||||
def dep_explicit(self, i):
|
||||
if i == 0:
|
||||
py.std.warnings.warn_explicit("dep_explicit", category=DeprecationWarning,
|
||||
filename="hello", lineno=3)
|
||||
|
||||
def test_deprecated_call_raises(self):
|
||||
excinfo = pytest.raises(AssertionError,
|
||||
"pytest.deprecated_call(dep, 3)")
|
||||
with pytest.raises(AssertionError) as excinfo:
|
||||
pytest.deprecated_call(self.dep, 3)
|
||||
assert str(excinfo).find("did not produce") != -1
|
||||
|
||||
def test_deprecated_call(self):
|
||||
pytest.deprecated_call(dep, 0)
|
||||
pytest.deprecated_call(self.dep, 0)
|
||||
|
||||
def test_deprecated_call_ret(self):
|
||||
ret = pytest.deprecated_call(dep, 0)
|
||||
ret = pytest.deprecated_call(self.dep, 0)
|
||||
assert ret == 42
|
||||
|
||||
def test_deprecated_call_preserves(self):
|
||||
|
@ -104,25 +102,48 @@ class TestDeprecatedCall(object):
|
|||
assert warn_explicit is py.std.warnings.warn_explicit
|
||||
|
||||
def test_deprecated_explicit_call_raises(self):
|
||||
pytest.raises(AssertionError,
|
||||
"pytest.deprecated_call(dep_explicit, 3)")
|
||||
with pytest.raises(AssertionError):
|
||||
pytest.deprecated_call(self.dep_explicit, 3)
|
||||
|
||||
def test_deprecated_explicit_call(self):
|
||||
pytest.deprecated_call(dep_explicit, 0)
|
||||
pytest.deprecated_call(dep_explicit, 0)
|
||||
pytest.deprecated_call(self.dep_explicit, 0)
|
||||
pytest.deprecated_call(self.dep_explicit, 0)
|
||||
|
||||
def test_deprecated_call_pending(self):
|
||||
f = lambda: py.std.warnings.warn(PendingDeprecationWarning("hi"))
|
||||
def f():
|
||||
py.std.warnings.warn(PendingDeprecationWarning("hi"))
|
||||
pytest.deprecated_call(f)
|
||||
|
||||
def test_deprecated_call_specificity(self):
|
||||
other_warnings = [Warning, UserWarning, SyntaxWarning, RuntimeWarning,
|
||||
FutureWarning, ImportWarning, UnicodeWarning]
|
||||
for warning in other_warnings:
|
||||
f = lambda: py.std.warnings.warn(warning("hi"))
|
||||
def f():
|
||||
py.std.warnings.warn(warning("hi"))
|
||||
with pytest.raises(AssertionError):
|
||||
pytest.deprecated_call(f)
|
||||
|
||||
def test_deprecated_function_already_called(self, testdir):
|
||||
"""deprecated_call should be able to catch a call to a deprecated
|
||||
function even if that function has already been called in the same
|
||||
module. See #1190.
|
||||
"""
|
||||
testdir.makepyfile("""
|
||||
import warnings
|
||||
import pytest
|
||||
|
||||
def deprecated_function():
|
||||
warnings.warn("deprecated", DeprecationWarning)
|
||||
|
||||
def test_one():
|
||||
deprecated_function()
|
||||
|
||||
def test_two():
|
||||
pytest.deprecated_call(deprecated_function)
|
||||
""")
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines('*=== 2 passed in *===')
|
||||
|
||||
|
||||
class TestWarns(object):
|
||||
def test_strings(self):
|
||||
|
|
Loading…
Reference in New Issue