Use warnings.catch_warnings instead of WarningsRecorder

This has the benefical side-effect of not calling the original
warnings.showwarnings function, which in its original form
only writes the formatted warning to sys.stdout.

Calling the original warnings.showwarnings has the effect that nested WarningsRecorder all catch the warnings:

with WarningsRecorder() as rec1:
    with WarningsRecorder() as rec2:
        warnings.warn(UserWarning, 'some warning')

(both rec1 and rec2 sees the warning)

When running tests with `testdir`, the main pytest session would then see the warnings created by
the internal code being tested (if any), and the main pytest session would end up with warnings as well.
This commit is contained in:
Bruno Oliveira 2017-02-18 12:57:26 -02:00
parent a7643a5fbe
commit 82785fcd40
2 changed files with 8 additions and 26 deletions

View File

@ -1,5 +1,3 @@
from _pytest.recwarn import RecordedWarning, WarningsRecorder
import inspect
import os import os
import pytest import pytest
import warnings import warnings
@ -42,35 +40,19 @@ def pytest_addoption(parser):
@pytest.hookimpl(hookwrapper=True) @pytest.hookimpl(hookwrapper=True)
def pytest_runtest_call(item): def pytest_runtest_call(item):
wrec = WarningsRecorder()
def showwarning(message, category, filename, lineno, file=None, line=None):
frame = inspect.currentframe()
if '/_pytest/recwarn' in frame.f_back.f_code.co_filename:
# we are in test recorder, so this warning is already handled
return
wrec._list.append(RecordedWarning(
message, category, filename, lineno, file, line))
# still perform old showwarning functionality
wrec._showwarning(
message, category, filename, lineno, file=file, line=line)
args = item.config.getoption('pythonwarnings') or [] args = item.config.getoption('pythonwarnings') or []
inifilters = item.config.getini("filterwarnings") inifilters = item.config.getini("filterwarnings")
with wrec: with warnings.catch_warnings(record=True) as log:
_showwarning = wrec._showwarning warnings.simplefilter('once')
warnings.showwarning = showwarning
wrec._module.simplefilter('once')
for arg in args: for arg in args:
wrec._module._setoption(arg) warnings._setoption(arg)
for arg in inifilters: for arg in inifilters:
_setoption(wrec._module, arg) _setoption(warnings, arg)
yield yield
wrec._showwarning = _showwarning
for warning in wrec.list: for warning in log:
msg = warnings.formatwarning( msg = warnings.formatwarning(
warning.message, warning.category, warning.message, warning.category,
os.path.relpath(warning.filename), warning.lineno, warning.line) os.path.relpath(warning.filename), warning.lineno, warning.line)

View File

@ -12,7 +12,7 @@ def pyfile_with_warnings(testdir):
def test_normal_flow(testdir, pyfile_with_warnings): def test_normal_flow(testdir, pyfile_with_warnings):
result = testdir.runpytest_subprocess() result = testdir.runpytest()
result.stdout.fnmatch_lines([ result.stdout.fnmatch_lines([
'*== pytest-warning summary ==*', '*== pytest-warning summary ==*',
@ -35,7 +35,7 @@ def test_as_errors(testdir, pyfile_with_warnings, method):
[pytest] [pytest]
filterwarnings= error filterwarnings= error
''') ''')
result = testdir.runpytest_subprocess(*args) result = testdir.runpytest(*args)
result.stdout.fnmatch_lines([ result.stdout.fnmatch_lines([
'E PendingDeprecationWarning: functionality is pending deprecation', 'E PendingDeprecationWarning: functionality is pending deprecation',
'test_as_errors.py:3: PendingDeprecationWarning', 'test_as_errors.py:3: PendingDeprecationWarning',
@ -52,7 +52,7 @@ def test_ignore(testdir, pyfile_with_warnings, method):
filterwarnings= ignore filterwarnings= ignore
''') ''')
result = testdir.runpytest_subprocess(*args) result = testdir.runpytest(*args)
result.stdout.fnmatch_lines([ result.stdout.fnmatch_lines([
'* 1 passed in *', '* 1 passed in *',
]) ])