Merge pull request #2303 from nicoddemus/recwarn-refactor

Refactor recwarn to use warnings.catch_warnings instead of custom code
This commit is contained in:
Bruno Oliveira 2017-03-15 12:27:31 -03:00 committed by GitHub
commit 5482dfe0f3
2 changed files with 11 additions and 47 deletions

View File

@ -6,11 +6,10 @@ import py
import sys import sys
import warnings import warnings
import pytest import pytest
from collections import namedtuple
@pytest.yield_fixture @pytest.yield_fixture
def recwarn(request): def recwarn():
"""Return a WarningsRecorder instance that provides these methods: """Return a WarningsRecorder instance that provides these methods:
* ``pop(category=None)``: return last warning matching the category. * ``pop(category=None)``: return last warning matching the category.
@ -115,19 +114,14 @@ def warns(expected_warning, *args, **kwargs):
return func(*args[1:], **kwargs) return func(*args[1:], **kwargs)
RecordedWarning = namedtuple('RecordedWarning', ( class WarningsRecorder(warnings.catch_warnings):
'message', 'category', 'filename', 'lineno', 'file', 'line',
))
class WarningsRecorder(object):
"""A context manager to record raised warnings. """A context manager to record raised warnings.
Adapted from `warnings.catch_warnings`. Adapted from `warnings.catch_warnings`.
""" """
def __init__(self, module=None): def __init__(self):
self._module = sys.modules['warnings'] if module is None else module super(WarningsRecorder, self).__init__(record=True)
self._entered = False self._entered = False
self._list = [] self._list = []
@ -164,38 +158,20 @@ class WarningsRecorder(object):
if self._entered: if self._entered:
__tracebackhide__ = True __tracebackhide__ = True
raise RuntimeError("Cannot enter %r twice" % self) raise RuntimeError("Cannot enter %r twice" % self)
self._entered = True self._list = super(WarningsRecorder, self).__enter__()
self._filters = self._module.filters warnings.simplefilter('always')
self._module.filters = self._filters[:]
self._showwarning = self._module.showwarning
def showwarning(message, category, filename, lineno,
file=None, line=None):
self._list.append(RecordedWarning(
message, category, filename, lineno, file, line))
# still perform old showwarning functionality
self._showwarning(
message, category, filename, lineno, file=file, line=line)
self._module.showwarning = showwarning
# allow the same warning to be raised more than once
self._module.simplefilter('always')
return self return self
def __exit__(self, *exc_info): def __exit__(self, *exc_info):
if not self._entered: if not self._entered:
__tracebackhide__ = True __tracebackhide__ = True
raise RuntimeError("Cannot exit %r without entering first" % self) raise RuntimeError("Cannot exit %r without entering first" % self)
self._module.filters = self._filters super(WarningsRecorder, self).__exit__(*exc_info)
self._module.showwarning = self._showwarning
class WarningsChecker(WarningsRecorder): class WarningsChecker(WarningsRecorder):
def __init__(self, expected_warning=None, module=None): def __init__(self, expected_warning=None):
super(WarningsChecker, self).__init__(module=module) super(WarningsChecker, self).__init__()
msg = ("exceptions must be old-style classes or " msg = ("exceptions must be old-style classes or "
"derived from Warning, not %s") "derived from Warning, not %s")

View File

@ -8,25 +8,19 @@ from _pytest.recwarn import WarningsRecorder
def test_recwarn_functional(testdir): def test_recwarn_functional(testdir):
reprec = testdir.inline_runsource(""" reprec = testdir.inline_runsource("""
import warnings import warnings
oldwarn = warnings.showwarning
def test_method(recwarn): def test_method(recwarn):
assert warnings.showwarning != oldwarn
warnings.warn("hello") warnings.warn("hello")
warn = recwarn.pop() warn = recwarn.pop()
assert isinstance(warn.message, UserWarning) assert isinstance(warn.message, UserWarning)
def test_finalized():
assert warnings.showwarning == oldwarn
""") """)
res = reprec.countoutcomes() res = reprec.countoutcomes()
assert tuple(res) == (2, 0, 0), res assert tuple(res) == (1, 0, 0), res
class TestWarningsRecorderChecker(object): class TestWarningsRecorderChecker(object):
def test_recording(self, recwarn): def test_recording(self):
showwarning = py.std.warnings.showwarning
rec = WarningsRecorder() rec = WarningsRecorder()
with rec: with rec:
assert py.std.warnings.showwarning != showwarning
assert not rec.list assert not rec.list
py.std.warnings.warn_explicit("hello", UserWarning, "xyz", 13) py.std.warnings.warn_explicit("hello", UserWarning, "xyz", 13)
assert len(rec.list) == 1 assert len(rec.list) == 1
@ -40,8 +34,6 @@ class TestWarningsRecorderChecker(object):
assert l is rec.list assert l is rec.list
pytest.raises(AssertionError, "rec.pop()") pytest.raises(AssertionError, "rec.pop()")
assert showwarning == py.std.warnings.showwarning
def test_typechecking(self): def test_typechecking(self):
from _pytest.recwarn import WarningsChecker from _pytest.recwarn import WarningsChecker
with pytest.raises(TypeError): with pytest.raises(TypeError):
@ -217,7 +209,6 @@ class TestWarns(object):
excinfo.match(re.escape(message_template.format(warning_classes, excinfo.match(re.escape(message_template.format(warning_classes,
[each.message for each in warninfo]))) [each.message for each in warninfo])))
def test_record(self): def test_record(self):
with pytest.warns(UserWarning) as record: with pytest.warns(UserWarning) as record:
warnings.warn("user", UserWarning) warnings.warn("user", UserWarning)
@ -225,9 +216,6 @@ class TestWarns(object):
assert len(record) == 1 assert len(record) == 1
assert str(record[0].message) == "user" assert str(record[0].message) == "user"
print(repr(record[0]))
assert str(record[0].message) in repr(record[0])
def test_record_only(self): def test_record_only(self):
with pytest.warns(None) as record: with pytest.warns(None) as record:
warnings.warn("user", UserWarning) warnings.warn("user", UserWarning)