200 lines
5.9 KiB
Python
200 lines
5.9 KiB
Python
"""
|
|
helpers for asserting deprecation and other warnings.
|
|
|
|
Example usage
|
|
---------------------
|
|
|
|
You can use the ``recwarn`` funcarg to track
|
|
warnings within a test function:
|
|
|
|
.. sourcecode:: python
|
|
|
|
def test_hello(recwarn):
|
|
from warnings import warn
|
|
warn("hello", DeprecationWarning)
|
|
w = recwarn.pop(DeprecationWarning)
|
|
assert issubclass(w.category, DeprecationWarning)
|
|
assert 'hello' in str(w.message)
|
|
assert w.filename
|
|
assert w.lineno
|
|
|
|
You can also call a global helper for checking
|
|
taht a certain function call yields a Deprecation
|
|
warning:
|
|
|
|
.. sourcecode:: python
|
|
|
|
import py
|
|
|
|
def test_global():
|
|
py.test.deprecated_call(myfunction, 17)
|
|
|
|
|
|
"""
|
|
|
|
import py
|
|
import os
|
|
|
|
def pytest_funcarg__recwarn(request):
|
|
"""Return a WarningsRecorder instance that provides these methods:
|
|
|
|
* ``pop(category=None)``: return last warning matching the category.
|
|
* ``clear()``: clear list of warnings
|
|
"""
|
|
warnings = WarningsRecorder()
|
|
request.addfinalizer(warnings.finalize)
|
|
return warnings
|
|
|
|
def pytest_namespace():
|
|
return {'deprecated_call': deprecated_call}
|
|
|
|
def deprecated_call(func, *args, **kwargs):
|
|
""" assert that calling func(*args, **kwargs)
|
|
triggers a DeprecationWarning.
|
|
"""
|
|
warningmodule = py.std.warnings
|
|
l = []
|
|
oldwarn_explicit = getattr(warningmodule, 'warn_explicit')
|
|
def warn_explicit(*args, **kwargs):
|
|
l.append(args)
|
|
oldwarn_explicit(*args, **kwargs)
|
|
oldwarn = getattr(warningmodule, 'warn')
|
|
def warn(*args, **kwargs):
|
|
l.append(args)
|
|
oldwarn(*args, **kwargs)
|
|
|
|
warningmodule.warn_explicit = warn_explicit
|
|
warningmodule.warn = warn
|
|
try:
|
|
ret = func(*args, **kwargs)
|
|
finally:
|
|
warningmodule.warn_explicit = warn_explicit
|
|
warningmodule.warn = warn
|
|
if not l:
|
|
#print warningmodule
|
|
raise AssertionError("%r did not produce DeprecationWarning" %(func,))
|
|
return ret
|
|
|
|
|
|
class RecordedWarning:
|
|
def __init__(self, message, category, filename, lineno, line):
|
|
self.message = message
|
|
self.category = category
|
|
self.filename = filename
|
|
self.lineno = lineno
|
|
self.line = line
|
|
|
|
class WarningsRecorder:
|
|
def __init__(self):
|
|
warningmodule = py.std.warnings
|
|
self.list = []
|
|
def showwarning(message, category, filename, lineno, line=0):
|
|
self.list.append(RecordedWarning(
|
|
message, category, filename, lineno, line))
|
|
try:
|
|
self.old_showwarning(message, category,
|
|
filename, lineno, line=line)
|
|
except TypeError:
|
|
# < python2.6
|
|
self.old_showwarning(message, category, filename, lineno)
|
|
self.old_showwarning = warningmodule.showwarning
|
|
warningmodule.showwarning = showwarning
|
|
|
|
def pop(self, cls=Warning):
|
|
""" pop the first recorded warning, raise exception if not exists."""
|
|
for i, w in enumerate(self.list):
|
|
if issubclass(w.category, cls):
|
|
return self.list.pop(i)
|
|
__tracebackhide__ = True
|
|
assert 0, "%r not found in %r" %(cls, self.list)
|
|
|
|
#def resetregistry(self):
|
|
# import warnings
|
|
# warnings.onceregistry.clear()
|
|
# warnings.__warningregistry__.clear()
|
|
|
|
def clear(self):
|
|
self.list[:] = []
|
|
|
|
def finalize(self):
|
|
py.std.warnings.showwarning = self.old_showwarning
|
|
|
|
def test_WarningRecorder():
|
|
showwarning = py.std.warnings.showwarning
|
|
rec = WarningsRecorder()
|
|
assert py.std.warnings.showwarning != showwarning
|
|
assert not rec.list
|
|
py.std.warnings.warn_explicit("hello", UserWarning, "xyz", 13)
|
|
assert len(rec.list) == 1
|
|
py.std.warnings.warn(DeprecationWarning("hello"))
|
|
assert len(rec.list) == 2
|
|
warn = rec.pop()
|
|
assert str(warn.message) == "hello"
|
|
l = rec.list
|
|
rec.clear()
|
|
assert len(rec.list) == 0
|
|
assert l is rec.list
|
|
py.test.raises(AssertionError, "rec.pop()")
|
|
rec.finalize()
|
|
assert showwarning == py.std.warnings.showwarning
|
|
|
|
def test_recwarn_functional(testdir):
|
|
reprec = testdir.inline_runsource("""
|
|
pytest_plugins = 'pytest_recwarn',
|
|
import warnings
|
|
oldwarn = warnings.showwarning
|
|
def test_method(recwarn):
|
|
assert warnings.showwarning != oldwarn
|
|
warnings.warn("hello")
|
|
warn = recwarn.pop()
|
|
assert isinstance(warn.message, UserWarning)
|
|
def test_finalized():
|
|
assert warnings.showwarning == oldwarn
|
|
""")
|
|
res = reprec.countoutcomes()
|
|
assert tuple(res) == (2, 0, 0), res
|
|
|
|
#
|
|
# ============ test py.test.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)
|
|
|
|
def test_deprecated_call_raises():
|
|
excinfo = py.test.raises(AssertionError,
|
|
"py.test.deprecated_call(dep, 3)")
|
|
assert str(excinfo).find("did not produce") != -1
|
|
|
|
def test_deprecated_call():
|
|
py.test.deprecated_call(dep, 0)
|
|
|
|
def test_deprecated_call_ret():
|
|
ret = py.test.deprecated_call(dep, 0)
|
|
assert ret == 42
|
|
|
|
def test_deprecated_call_preserves():
|
|
r = py.std.warnings.onceregistry.copy()
|
|
f = py.std.warnings.filters[:]
|
|
test_deprecated_call_raises()
|
|
test_deprecated_call()
|
|
assert r == py.std.warnings.onceregistry
|
|
assert f == py.std.warnings.filters
|
|
|
|
def test_deprecated_explicit_call_raises():
|
|
py.test.raises(AssertionError,
|
|
"py.test.deprecated_call(dep_explicit, 3)")
|
|
|
|
def test_deprecated_explicit_call():
|
|
py.test.deprecated_call(dep_explicit, 0)
|
|
py.test.deprecated_call(dep_explicit, 0)
|
|
|