Skip to content

Commit 1dee443

Browse files
authored
Merge pull request #2445 from nicoddemus/warnings-remove-filter
No longer override existing warning filters during warnings capture
2 parents 454426c + 32e2642 commit 1dee443

File tree

7 files changed

+92
-48
lines changed

7 files changed

+92
-48
lines changed

_pytest/warnings.py

-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ def catch_warnings_for_item(item):
5353
args = item.config.getoption('pythonwarnings') or []
5454
inifilters = item.config.getini("filterwarnings")
5555
with warnings.catch_warnings(record=True) as log:
56-
warnings.simplefilter('once')
5756
for arg in args:
5857
warnings._setoption(arg)
5958

changelog/2390.doc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
initial addition of towncrier
1+
Addition of towncrier for changelog management.

changelog/2430.bugfix

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pytest warning capture no longer overrides existing warning filters. The previous
2+
behaviour would override all filters and caused regressions in test suites which configure warning
3+
filters to match their needs. Note that as a side-effect of this is that ``DeprecationWarning``
4+
and ``PendingDeprecationWarning`` are no longer shown by default.

doc/en/warnings.rst

+44-30
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,18 @@ Warnings Capture
55

66
.. versionadded:: 3.1
77

8-
.. warning::
9-
pytest captures all warnings between tests, which prevents custom warning
10-
filters in existing test suites from working. If this causes problems to your test suite,
11-
this plugin can be disabled in your ``pytest.ini`` file with:
12-
13-
.. code-block:: ini
14-
15-
[pytest]
16-
addopts = -p no:warnings
17-
18-
There's an ongoing discussion about this on `#2430
19-
<https://github.com/pytest-dev/pytest/issues/2430>`_.
20-
21-
22-
Starting from version ``3.1``, pytest now automatically catches all warnings during test execution
8+
Starting from version ``3.1``, pytest now automatically catches warnings during test execution
239
and displays them at the end of the session::
2410

2511
# content of test_show_warnings.py
2612
import warnings
2713

28-
def deprecated_function():
29-
warnings.warn("this function is deprecated, use another_function()", DeprecationWarning)
14+
def api_v1():
15+
warnings.warn(UserWarning("api v1, should use functions from v2"))
3016
return 1
3117

3218
def test_one():
33-
assert deprecated_function() == 1
19+
assert api_v1() == 1
3420

3521
Running pytest now produces this output::
3622

@@ -39,48 +25,50 @@ Running pytest now produces this output::
3925
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
4026
rootdir: $REGENDOC_TMPDIR, inifile:
4127
collected 1 items
42-
28+
4329
test_show_warnings.py .
44-
30+
4531
======= warnings summary ========
4632
test_show_warnings.py::test_one
4733
$REGENDOC_TMPDIR/test_show_warnings.py:4: DeprecationWarning: this function is deprecated, use another_function()
4834
warnings.warn("this function is deprecated, use another_function()", DeprecationWarning)
49-
35+
5036
-- Docs: http://doc.pytest.org/en/latest/warnings.html
5137
======= 1 passed, 1 warnings in 0.12 seconds ========
5238

39+
Pytest by default catches all warnings except for ``DeprecationWarning`` and ``PendingDeprecationWarning``.
40+
5341
The ``-W`` flag can be passed to control which warnings will be displayed or even turn
5442
them into errors::
5543

56-
$ pytest -q test_show_warnings.py -W error::DeprecationWarning
44+
$ pytest -q test_show_warnings.py -W error::UserWarning
5745
F
5846
======= FAILURES ========
5947
_______ test_one ________
60-
48+
6149
def test_one():
6250
> assert deprecated_function() == 1
63-
64-
test_show_warnings.py:8:
65-
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
66-
51+
52+
test_show_warnings.py:8:
53+
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
54+
6755
def deprecated_function():
6856
> warnings.warn("this function is deprecated, use another_function()", DeprecationWarning)
6957
E DeprecationWarning: this function is deprecated, use another_function()
70-
58+
7159
test_show_warnings.py:4: DeprecationWarning
7260
1 failed in 0.12 seconds
7361

7462
The same option can be set in the ``pytest.ini`` file using the ``filterwarnings`` ini option.
75-
For example, the configuration below will ignore all deprecation warnings, but will transform
63+
For example, the configuration below will ignore all user warnings, but will transform
7664
all other warnings into errors.
7765

7866
.. code-block:: ini
7967
8068
[pytest]
8169
filterwarnings =
8270
error
83-
ignore::DeprecationWarning
71+
ignore::UserWarning
8472
8573
8674
When a warning matches more than one option in the list, the action for the last matching option
@@ -90,13 +78,39 @@ Both ``-W`` command-line option and ``filterwarnings`` ini option are based on P
9078
`-W option`_ and `warnings.simplefilter`_, so please refer to those sections in the Python
9179
documentation for other examples and advanced usage.
9280

81+
.. note::
82+
83+
``DeprecationWarning`` and ``PendingDeprecationWarning`` are hidden by the standard library
84+
by default so you have to explicitly configure them to be displayed in your ``pytest.ini``:
85+
86+
.. code-block:: ini
87+
88+
[pytest]
89+
filterwarnings =
90+
once::DeprecationWarning
91+
once::PendingDeprecationWarning
92+
93+
9394
*Credits go to Florian Schulze for the reference implementation in the* `pytest-warnings`_
9495
*plugin.*
9596

9697
.. _`-W option`: https://docs.python.org/3/using/cmdline.html?highlight=#cmdoption-W
9798
.. _warnings.simplefilter: https://docs.python.org/3/library/warnings.html#warnings.simplefilter
9899
.. _`pytest-warnings`: https://github.com/fschulze/pytest-warnings
99100

101+
102+
Disabling warning capture
103+
-------------------------
104+
105+
This feature is enabled by default but can be disabled entirely in your ``pytest.ini`` file with:
106+
107+
.. code-block:: ini
108+
109+
[pytest]
110+
addopts = -p no:warnings
111+
112+
Or passing ``-p no:warnings`` in the command-line.
113+
100114
.. _`asserting warnings`:
101115

102116
.. _assertwarnings:

testing/test_recwarn.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import warnings
33
import re
44
import py
5+
import sys
6+
57
import pytest
68
from _pytest.recwarn import WarningsRecorder
79

@@ -149,9 +151,12 @@ def test_two():
149151
pytest.deprecated_call(deprecated_function)
150152
""")
151153
result = testdir.runpytest()
152-
# the 2 tests must pass, but the call to test_one() will generate a warning
153-
# in pytest's summary
154-
result.stdout.fnmatch_lines('*=== 2 passed, 1 warnings in *===')
154+
# for some reason in py26 catch_warnings manages to catch the deprecation warning
155+
# from deprecated_function(), even with default filters active (which ignore deprecation
156+
# warnings)
157+
py26 = sys.version_info[:2] == (2, 6)
158+
expected = '*=== 2 passed in *===' if not py26 else '*=== 2 passed, 1 warnings in *==='
159+
result.stdout.fnmatch_lines(expected)
155160

156161

157162
class TestWarns(object):

testing/test_warnings.py

+34-12
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ def pyfile_with_warnings(testdir, request):
2020
module_name: '''
2121
import warnings
2222
def foo():
23-
warnings.warn(PendingDeprecationWarning("functionality is pending deprecation"))
24-
warnings.warn(DeprecationWarning("functionality is deprecated"))
23+
warnings.warn(UserWarning("user warning"))
24+
warnings.warn(RuntimeWarning("runtime warning"))
2525
return 1
2626
''',
2727
test_name: '''
@@ -43,11 +43,11 @@ def test_normal_flow(testdir, pyfile_with_warnings):
4343

4444
'*test_normal_flow.py::test_func',
4545

46-
'*normal_flow_module.py:3: PendingDeprecationWarning: functionality is pending deprecation',
47-
'* warnings.warn(PendingDeprecationWarning("functionality is pending deprecation"))',
46+
'*normal_flow_module.py:3: UserWarning: user warning',
47+
'* warnings.warn(UserWarning("user warning"))',
4848

49-
'*normal_flow_module.py:4: DeprecationWarning: functionality is deprecated',
50-
'* warnings.warn(DeprecationWarning("functionality is deprecated"))',
49+
'*normal_flow_module.py:4: RuntimeWarning: runtime warning',
50+
'* warnings.warn(RuntimeWarning("runtime warning"))',
5151
'* 1 passed, 2 warnings*',
5252
])
5353
assert result.stdout.str().count('test_normal_flow.py::test_func') == 1
@@ -90,8 +90,8 @@ def test_as_errors(testdir, pyfile_with_warnings, method):
9090
''')
9191
result = testdir.runpytest(*args)
9292
result.stdout.fnmatch_lines([
93-
'E PendingDeprecationWarning: functionality is pending deprecation',
94-
'as_errors_module.py:3: PendingDeprecationWarning',
93+
'E UserWarning: user warning',
94+
'as_errors_module.py:3: UserWarning',
9595
'* 1 failed in *',
9696
])
9797

@@ -133,9 +133,7 @@ def test_func(fix):
133133
result = testdir.runpytest()
134134
result.stdout.fnmatch_lines([
135135
'*== %s ==*' % WARNINGS_SUMMARY_HEADER,
136-
137-
'*test_unicode.py:8: UserWarning: \u6d4b\u8bd5',
138-
'*warnings.warn(u"\u6d4b\u8bd5")',
136+
'*test_unicode.py:8: UserWarning: \u6d4b\u8bd5*',
139137
'* 1 passed, 1 warnings*',
140138
])
141139

@@ -163,6 +161,30 @@ def test_func(fix):
163161

164162
'*test_py2_unicode.py:8: UserWarning: \u6d4b\u8bd5',
165163
'*warnings.warn(u"\u6d4b\u8bd5")',
166-
'*warnings.py:82: UnicodeWarning: This warning*\u6d4b\u8bd5',
164+
'*warnings.py:*: UnicodeWarning: This warning*\u6d4b\u8bd5',
167165
'* 1 passed, 2 warnings*',
168166
])
167+
168+
169+
def test_works_with_filterwarnings(testdir):
170+
"""Ensure our warnings capture does not mess with pre-installed filters (#2430)."""
171+
testdir.makepyfile('''
172+
import warnings
173+
174+
class MyWarning(Warning):
175+
pass
176+
177+
warnings.filterwarnings("error", category=MyWarning)
178+
179+
class TestWarnings(object):
180+
def test_my_warning(self):
181+
try:
182+
warnings.warn(MyWarning("warn!"))
183+
assert False
184+
except MyWarning:
185+
assert True
186+
''')
187+
result = testdir.runpytest()
188+
result.stdout.fnmatch_lines([
189+
'*== 1 passed in *',
190+
])

tox.ini

+1-1
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ python_files=test_*.py *_test.py testing/*/*.py
184184
python_classes=Test Acceptance
185185
python_functions=test
186186
norecursedirs = .tox ja .hg cx_freeze_source
187-
filterwarnings= error
187+
filterwarnings=
188188
# produced by path.local
189189
ignore:bad escape.*:DeprecationWarning:re
190190
# produced by path.readlines

0 commit comments

Comments
 (0)