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
|
2.8.3
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
|
|
@ -29,29 +29,39 @@ def pytest_namespace():
|
||||||
|
|
||||||
|
|
||||||
def deprecated_call(func, *args, **kwargs):
|
def deprecated_call(func, *args, **kwargs):
|
||||||
""" assert that calling ``func(*args, **kwargs)``
|
""" assert that calling ``func(*args, **kwargs)`` triggers a
|
||||||
triggers a DeprecationWarning.
|
``DeprecationWarning`` or ``PendingDeprecationWarning``.
|
||||||
"""
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
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_explicit = warn_explicit
|
||||||
warnings.warn = warn
|
warnings.warn = warn
|
||||||
try:
|
try:
|
||||||
ret = func(*args, **kwargs)
|
ret = func(*args, **kwargs)
|
||||||
finally:
|
finally:
|
||||||
warnings.warn_explicit = oldwarn_explicit
|
warnings.warn_explicit = old_warn_explicit
|
||||||
warnings.warn = oldwarn
|
warnings.warn = old_warn
|
||||||
if not l:
|
deprecation_categories = (DeprecationWarning, PendingDeprecationWarning)
|
||||||
|
if not any(issubclass(c, deprecation_categories) for c in categories):
|
||||||
__tracebackhide__ = True
|
__tracebackhide__ = True
|
||||||
raise AssertionError("%r did not produce DeprecationWarning" %(func,))
|
raise AssertionError("%r did not produce DeprecationWarning" % (func,))
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -63,32 +63,30 @@ class TestWarningsRecorderChecker(object):
|
||||||
with rec:
|
with rec:
|
||||||
pass # can't enter twice
|
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):
|
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):
|
def test_deprecated_call_raises(self):
|
||||||
excinfo = pytest.raises(AssertionError,
|
with pytest.raises(AssertionError) as excinfo:
|
||||||
"pytest.deprecated_call(dep, 3)")
|
pytest.deprecated_call(self.dep, 3)
|
||||||
assert str(excinfo).find("did not produce") != -1
|
assert str(excinfo).find("did not produce") != -1
|
||||||
|
|
||||||
def test_deprecated_call(self):
|
def test_deprecated_call(self):
|
||||||
pytest.deprecated_call(dep, 0)
|
pytest.deprecated_call(self.dep, 0)
|
||||||
|
|
||||||
def test_deprecated_call_ret(self):
|
def test_deprecated_call_ret(self):
|
||||||
ret = pytest.deprecated_call(dep, 0)
|
ret = pytest.deprecated_call(self.dep, 0)
|
||||||
assert ret == 42
|
assert ret == 42
|
||||||
|
|
||||||
def test_deprecated_call_preserves(self):
|
def test_deprecated_call_preserves(self):
|
||||||
|
@ -104,25 +102,48 @@ class TestDeprecatedCall(object):
|
||||||
assert warn_explicit is py.std.warnings.warn_explicit
|
assert warn_explicit is py.std.warnings.warn_explicit
|
||||||
|
|
||||||
def test_deprecated_explicit_call_raises(self):
|
def test_deprecated_explicit_call_raises(self):
|
||||||
pytest.raises(AssertionError,
|
with pytest.raises(AssertionError):
|
||||||
"pytest.deprecated_call(dep_explicit, 3)")
|
pytest.deprecated_call(self.dep_explicit, 3)
|
||||||
|
|
||||||
def test_deprecated_explicit_call(self):
|
def test_deprecated_explicit_call(self):
|
||||||
pytest.deprecated_call(dep_explicit, 0)
|
pytest.deprecated_call(self.dep_explicit, 0)
|
||||||
pytest.deprecated_call(dep_explicit, 0)
|
pytest.deprecated_call(self.dep_explicit, 0)
|
||||||
|
|
||||||
def test_deprecated_call_pending(self):
|
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)
|
pytest.deprecated_call(f)
|
||||||
|
|
||||||
def test_deprecated_call_specificity(self):
|
def test_deprecated_call_specificity(self):
|
||||||
other_warnings = [Warning, UserWarning, SyntaxWarning, RuntimeWarning,
|
other_warnings = [Warning, UserWarning, SyntaxWarning, RuntimeWarning,
|
||||||
FutureWarning, ImportWarning, UnicodeWarning]
|
FutureWarning, ImportWarning, UnicodeWarning]
|
||||||
for warning in other_warnings:
|
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):
|
with pytest.raises(AssertionError):
|
||||||
pytest.deprecated_call(f)
|
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):
|
class TestWarns(object):
|
||||||
def test_strings(self):
|
def test_strings(self):
|
||||||
|
|
Loading…
Reference in New Issue