Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No longer override existing warning filters during warnings capture #2445

Merged
merged 1 commit into from
May 30, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion _pytest/warnings.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ def catch_warnings_for_item(item):
args = item.config.getoption('pythonwarnings') or []
inifilters = item.config.getini("filterwarnings")
with warnings.catch_warnings(record=True) as log:
warnings.simplefilter('once')
for arg in args:
warnings._setoption(arg)

Expand Down
2 changes: 1 addition & 1 deletion changelog/2390.doc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
initial addition of towncrier
Addition of towncrier for changelog management.
4 changes: 4 additions & 0 deletions changelog/2430.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pytest warning capture no longer overrides existing warning filters. The previous
behaviour would override all filters and caused regressions in test suites which configure warning
filters to match their needs. Note that as a side-effect of this is that ``DeprecationWarning``
and ``PendingDeprecationWarning`` are no longer shown by default.
74 changes: 44 additions & 30 deletions doc/en/warnings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,18 @@ Warnings Capture

.. versionadded:: 3.1

.. warning::
pytest captures all warnings between tests, which prevents custom warning
filters in existing test suites from working. If this causes problems to your test suite,
this plugin can be disabled in your ``pytest.ini`` file with:

.. code-block:: ini

[pytest]
addopts = -p no:warnings

There's an ongoing discussion about this on `#2430
<https://github.com/pytest-dev/pytest/issues/2430>`_.


Starting from version ``3.1``, pytest now automatically catches all warnings during test execution
Starting from version ``3.1``, pytest now automatically catches warnings during test execution
and displays them at the end of the session::

# content of test_show_warnings.py
import warnings

def deprecated_function():
warnings.warn("this function is deprecated, use another_function()", DeprecationWarning)
def api_v1():
warnings.warn(UserWarning("api v1, should use functions from v2"))
return 1

def test_one():
assert deprecated_function() == 1
assert api_v1() == 1

Running pytest now produces this output::

Expand All @@ -39,48 +25,50 @@ Running pytest now produces this output::
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items

test_show_warnings.py .

======= warnings summary ========
test_show_warnings.py::test_one
$REGENDOC_TMPDIR/test_show_warnings.py:4: DeprecationWarning: this function is deprecated, use another_function()
warnings.warn("this function is deprecated, use another_function()", DeprecationWarning)

-- Docs: http://doc.pytest.org/en/latest/warnings.html
======= 1 passed, 1 warnings in 0.12 seconds ========

Pytest by default catches all warnings except for ``DeprecationWarning`` and ``PendingDeprecationWarning``.

The ``-W`` flag can be passed to control which warnings will be displayed or even turn
them into errors::

$ pytest -q test_show_warnings.py -W error::DeprecationWarning
$ pytest -q test_show_warnings.py -W error::UserWarning
F
======= FAILURES ========
_______ test_one ________

def test_one():
> assert deprecated_function() == 1
test_show_warnings.py:8:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

test_show_warnings.py:8:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

def deprecated_function():
> warnings.warn("this function is deprecated, use another_function()", DeprecationWarning)
E DeprecationWarning: this function is deprecated, use another_function()

test_show_warnings.py:4: DeprecationWarning
1 failed in 0.12 seconds

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

.. code-block:: ini

[pytest]
filterwarnings =
error
ignore::DeprecationWarning
ignore::UserWarning


When a warning matches more than one option in the list, the action for the last matching option
Expand All @@ -90,13 +78,39 @@ Both ``-W`` command-line option and ``filterwarnings`` ini option are based on P
`-W option`_ and `warnings.simplefilter`_, so please refer to those sections in the Python
documentation for other examples and advanced usage.

.. note::

``DeprecationWarning`` and ``PendingDeprecationWarning`` are hidden by the standard library
by default so you have to explicitly configure them to be displayed in your ``pytest.ini``:

.. code-block:: ini

[pytest]
filterwarnings =
once::DeprecationWarning
once::PendingDeprecationWarning


*Credits go to Florian Schulze for the reference implementation in the* `pytest-warnings`_
*plugin.*

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


Disabling warning capture
-------------------------

This feature is enabled by default but can be disabled entirely in your ``pytest.ini`` file with:

.. code-block:: ini

[pytest]
addopts = -p no:warnings

Or passing ``-p no:warnings`` in the command-line.

.. _`asserting warnings`:

.. _assertwarnings:
Expand Down
11 changes: 8 additions & 3 deletions testing/test_recwarn.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import warnings
import re
import py
import sys

import pytest
from _pytest.recwarn import WarningsRecorder

Expand Down Expand Up @@ -146,9 +148,12 @@ def test_two():
pytest.deprecated_call(deprecated_function)
""")
result = testdir.runpytest()
# the 2 tests must pass, but the call to test_one() will generate a warning
# in pytest's summary
result.stdout.fnmatch_lines('*=== 2 passed, 1 warnings in *===')
# for some reason in py26 catch_warnings manages to catch the deprecation warning
# from deprecated_function(), even with default filters active (which ignore deprecation
# warnings)
py26 = sys.version_info[:2] == (2, 6)
expected = '*=== 2 passed in *===' if not py26 else '*=== 2 passed, 1 warnings in *==='
result.stdout.fnmatch_lines(expected)


class TestWarns(object):
Expand Down
46 changes: 34 additions & 12 deletions testing/test_warnings.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ def pyfile_with_warnings(testdir, request):
module_name: '''
import warnings
def foo():
warnings.warn(PendingDeprecationWarning("functionality is pending deprecation"))
warnings.warn(DeprecationWarning("functionality is deprecated"))
warnings.warn(UserWarning("user warning"))
warnings.warn(RuntimeWarning("runtime warning"))
return 1
''',
test_name: '''
Expand All @@ -43,11 +43,11 @@ def test_normal_flow(testdir, pyfile_with_warnings):

'*test_normal_flow.py::test_func',

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

'*normal_flow_module.py:4: DeprecationWarning: functionality is deprecated',
'* warnings.warn(DeprecationWarning("functionality is deprecated"))',
'*normal_flow_module.py:4: RuntimeWarning: runtime warning',
'* warnings.warn(RuntimeWarning("runtime warning"))',
'* 1 passed, 2 warnings*',
])
assert result.stdout.str().count('test_normal_flow.py::test_func') == 1
Expand Down Expand Up @@ -90,8 +90,8 @@ def test_as_errors(testdir, pyfile_with_warnings, method):
''')
result = testdir.runpytest(*args)
result.stdout.fnmatch_lines([
'E PendingDeprecationWarning: functionality is pending deprecation',
'as_errors_module.py:3: PendingDeprecationWarning',
'E UserWarning: user warning',
'as_errors_module.py:3: UserWarning',
'* 1 failed in *',
])

Expand Down Expand Up @@ -133,9 +133,7 @@ def test_func(fix):
result = testdir.runpytest()
result.stdout.fnmatch_lines([
'*== %s ==*' % WARNINGS_SUMMARY_HEADER,

'*test_unicode.py:8: UserWarning: \u6d4b\u8bd5',
'*warnings.warn(u"\u6d4b\u8bd5")',
'*test_unicode.py:8: UserWarning: \u6d4b\u8bd5*',
'* 1 passed, 1 warnings*',
])

Expand Down Expand Up @@ -163,6 +161,30 @@ def test_func(fix):

'*test_py2_unicode.py:8: UserWarning: \u6d4b\u8bd5',
'*warnings.warn(u"\u6d4b\u8bd5")',
'*warnings.py:82: UnicodeWarning: This warning*\u6d4b\u8bd5',
'*warnings.py:*: UnicodeWarning: This warning*\u6d4b\u8bd5',
'* 1 passed, 2 warnings*',
])


def test_works_with_filterwarnings(testdir):
"""Ensure our warnings capture does not mess with pre-installed filters (#2430)."""
testdir.makepyfile('''
import warnings

class MyWarning(Warning):
pass

warnings.filterwarnings("error", category=MyWarning)

class TestWarnings(object):
def test_my_warning(self):
try:
warnings.warn(MyWarning("warn!"))
assert False
except MyWarning:
assert True
''')
result = testdir.runpytest()
result.stdout.fnmatch_lines([
'*== 1 passed in *',
])
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ python_files=test_*.py *_test.py testing/*/*.py
python_classes=Test Acceptance
python_functions=test
norecursedirs = .tox ja .hg cx_freeze_source
filterwarnings= error
filterwarnings=
# produced by path.local
ignore:bad escape.*:DeprecationWarning:re
# produced by path.readlines
Expand Down