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

Use standard warnings for internal pytest warnings #3931

Merged
merged 44 commits into from
Sep 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
10f21b4
Remove assert for "reprec" because this is no longer set on the plugi…
nicoddemus Aug 25, 2018
ffd47ce
Implement new pytest_warning_captured hook
nicoddemus Aug 25, 2018
3fcc4cd
Make terminal capture pytest_warning_capture
nicoddemus Aug 26, 2018
51e32cf
Remove Python 2.6 specific warning
nicoddemus Aug 26, 2018
1a9d913
Capture and display warnings during collection
nicoddemus Aug 26, 2018
0100f61
Start the laywork to capture standard warnings
nicoddemus Aug 29, 2018
8e4501e
Use std_warn for warning about applying marks directly to parameters
nicoddemus Aug 31, 2018
0c8dbdc
Fix existing tests now that we are using standard warnings
nicoddemus Sep 1, 2018
78ac7d9
Deprecate Config.warn and Node.warn, replaced by standard warnings
nicoddemus Sep 2, 2018
19a01c9
Make PytestWarning and RemovedInPytest4Warning part of the public API
nicoddemus Sep 3, 2018
208dd3a
Add docs for internal warnings and introduce PytestDeprecationWarning
nicoddemus Sep 3, 2018
7e13593
Add CHANGELOG entries for #2452
nicoddemus Sep 3, 2018
9965ed8
Show deprecation warnings by default if no other filters are configured
nicoddemus Sep 3, 2018
60499d2
Add test to ensure that users can suppress internal warnings
nicoddemus Sep 3, 2018
0fffa6b
Implement hack to issue warnings during config
nicoddemus Sep 3, 2018
56d4141
Remove nodeid from messages for warnings generated by standard warnings
nicoddemus Sep 3, 2018
b818314
Improve docs for warnings capture and PEP-0506 remarks
nicoddemus Sep 4, 2018
8ce3aea
Move PytestExerimentalApiWarning to warning_types
nicoddemus Sep 4, 2018
415a62e
Fix typo in PytestExperimentalApiWarning
nicoddemus Sep 4, 2018
c304998
Remove commented out code
nicoddemus Sep 4, 2018
e9417be
Add comment about deprecation warnings being shown by default
nicoddemus Sep 4, 2018
016f8f1
Improve get_fslocation_from_item's docstring
nicoddemus Sep 4, 2018
615c671
Connect string literals
nicoddemus Sep 4, 2018
9ae0a3c
Do not trigger warning about tuples being always True if the tuple ha…
nicoddemus Sep 4, 2018
284a2d1
Move warnings import to top level
nicoddemus Sep 4, 2018
b42518a
Change std_warn to receive a single warning instance, addressed revie…
nicoddemus Sep 4, 2018
022c58b
Revert pytest_terminal_summary(tryfirst) in warnings module as this b…
nicoddemus Sep 4, 2018
d3f72ca
Fix linting for warnings.rst
nicoddemus Sep 4, 2018
f1cfd10
Handle cache warnings in tests
nicoddemus Sep 4, 2018
a054aa4
Issue assert rewrite warning if tuple >=1 as suggested in review
nicoddemus Sep 4, 2018
5ef5126
Fix reference to PytestWarning in warningsfilter mark
nicoddemus Sep 4, 2018
47bf58d
Make Node.warn support two forms, new and deprecated
nicoddemus Sep 4, 2018
438f7a1
Add "setup", "call" and "teardown" values to "when" parameter of pyte…
nicoddemus Sep 4, 2018
3db76cc
Fix Cache.warn function to issue a "config" warning
nicoddemus Sep 4, 2018
d3ca739
Use explicit instances when calling warnings.warn_explicit
nicoddemus Sep 4, 2018
b7560a8
Keep backward compatibility for code as kw in Node.warn
nicoddemus Sep 4, 2018
6d497f2
Fix stacklevel for warning about Metafunc.addcall
nicoddemus Sep 4, 2018
5a52aca
Make config no longer optional in parametrize id functions
nicoddemus Sep 4, 2018
2e0a7cf
Revert to having just "runtest" as "when" parameter of the pytest_war…
nicoddemus Sep 4, 2018
4592def
Improve test_rewarn_functional
nicoddemus Sep 4, 2018
adc9ed8
Fix test_idval_hypothesis
nicoddemus Sep 4, 2018
f42b501
Make code_or_warning parameter private for backward-compatibility
nicoddemus Sep 4, 2018
ddb3084
Make sure warn is called in test_parameterset_extractfrom
nicoddemus Sep 5, 2018
f63c683
No longer escape regex in pytest.mark.filterwarnings
nicoddemus Sep 5, 2018
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
5 changes: 5 additions & 0 deletions changelog/2452.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Internal pytest warnings are now issued using the standard ``warnings`` module, making it possible to use
the standard warnings filters to manage those warnings. This introduces ``PytestWarning``,
``PytestDeprecationWarning`` and ``RemovedInPytest4Warning`` warning types as part of the public API.

Consult `the documentation <https://docs.pytest.org/en/latest/warnings.html#internal-pytest-warnings>`_ for more info.
12 changes: 12 additions & 0 deletions changelog/2452.removal.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
``Config.warn`` has been deprecated, it should be replaced by calls to the standard ``warnings.warn``.

``Node.warn`` now supports two signatures:

* ``node.warn(PytestWarning("some message"))``: is now the recommended way to call this function. The warning
instance must be a ``PytestWarning`` or subclass instance.

* ``node.warn("CI", "some message")``: this code/message form is now deprecated and should be converted to
the warning instance form above.

``RemovedInPytest4Warning`` and ``PytestExperimentalApiWarning`` are now part of the public API and should be accessed
using ``pytest.RemovedInPytest4Warning`` and ``pytest.PytestExperimentalApiWarning``.
5 changes: 5 additions & 0 deletions changelog/2908.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
``DeprecationWarning`` and ``PendingDeprecationWarning`` are now shown by default if no other warning filter is
configured. This makes pytest more compliant with
`PEP-0506 <https://www.python.org/dev/peps/pep-0565/#recommended-filter-settings-for-test-runners>`_. See
`the docs <https://docs.pytest.org/en/latest/warnings.html#deprecationwarning-and-pendingdeprecationwarning>`_ for
more info.
1 change: 1 addition & 0 deletions changelog/3251.feture.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Warnings are now captured and displayed during test collection.
5 changes: 5 additions & 0 deletions changelog/3936.removal.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
``@pytest.mark.filterwarnings`` second parameter is no longer regex-escaped,
making it possible to actually use regular expressions to check the warning message.

**Note**: regex-escaping the match string was an implementation oversight that might break test suites which depend
on the old behavior.
2 changes: 2 additions & 0 deletions doc/en/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,8 @@ Session related reporting hooks:
.. autofunction:: pytest_terminal_summary
.. autofunction:: pytest_fixture_setup
.. autofunction:: pytest_fixture_post_finalizer
.. autofunction:: pytest_logwarning
.. autofunction:: pytest_warning_captured

And here is the central hook for reporting about
test execution:
Expand Down
111 changes: 97 additions & 14 deletions doc/en/warnings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ Running pytest now produces this output::
-- Docs: https://docs.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::

Expand Down Expand Up @@ -78,6 +76,53 @@ 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.

Disabling warning summary
-------------------------

Although not recommended, you can use the ``--disable-warnings`` command-line option to suppress the
warning summary entirely from the test run output.

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

This plugin 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. This might be useful if your test suites handles warnings
using an external system.


.. _`deprecation-warnings`:

DeprecationWarning and PendingDeprecationWarning
------------------------------------------------

.. versionadded:: 3.8

By default pytest will display ``DeprecationWarning`` and ``PendingDeprecationWarning`` if no other warning filters
are configured.

To disable showing ``DeprecationWarning`` and ``PendingDeprecationWarning`` warnings, you might define any warnings
filter either in the command-line or in the ini file, or you can use:

.. code-block:: ini

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

.. note::
This makes pytest more compliant with `PEP-0506 <https://www.python.org/dev/peps/pep-0565/#recommended-filter-settings-for-test-runners>`_ which suggests that those warnings should
be shown by default by test runners, but pytest doesn't follow ``PEP-0506`` completely because resetting all
warning filters like suggested in the PEP will break existing test suites that configure warning filters themselves
by calling ``warnings.simplefilter`` (see issue `#2430 <https://github.com/pytest-dev/pytest/issues/2430>`_
for an example of that).


.. _`filterwarnings`:

Expand Down Expand Up @@ -144,18 +189,6 @@ decorator or to all tests in a module by setting the ``pytestmark`` variable:
.. _`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 Expand Up @@ -296,3 +329,53 @@ You can also use it as a contextmanager::
def test_global():
with pytest.deprecated_call():
myobject.deprecated_method()


Internal pytest warnings
------------------------

.. versionadded:: 3.8

pytest may generate its own warnings in some situations, such as improper usage or deprecated features.

For example, pytest will emit a warning if it encounters a class that matches :confval:`python_classes` but also
defines an ``__init__`` constructor, as this prevents the class from being instantiated:

.. code-block:: python

# content of test_pytest_warnings.py
class Test:
def __init__(self):
pass

def test_foo(self):
assert 1 == 1

::

$ pytest test_pytest_warnings.py -q
======================================== warnings summary =========================================
test_pytest_warnings.py:1
$REGENDOC_TMPDIR/test_pytest_warnings.py:1: PytestWarning: cannot collect test class 'Test' because it has a __init__ constructor
class Test:

-- Docs: http://doc.pytest.org/en/latest/warnings.html
1 warnings in 0.01 seconds



These warnings might be filtered using the same builtin mechanisms used to filter other types of warnings.

Following our :ref:`backwards-compatibility`, deprecated features will be kept *at least* two minor releases. After that,
they will changed so they by default raise errors instead of just warnings, so users can adapt to it on their own time
if not having done so until now. In a later release the deprecated feature will be removed completely.

The following warning types ares used by pytest and are part of the public API:

.. autoclass:: pytest.PytestWarning

.. autoclass:: pytest.PytestDeprecationWarning

.. autoclass:: pytest.RemovedInPytest4Warning

.. autoclass:: pytest.PytestExperimentalApiWarning
2 changes: 1 addition & 1 deletion doc/en/writing_plugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ additionally it is possible to copy examples for a example folder before running

============================= warnings summary =============================
test_example.py::test_plugin
$REGENDOC_TMPDIR/test_example.py:4: PytestExerimentalApiWarning: testdir.copy_example is an experimental api that may change over time
$REGENDOC_TMPDIR/test_example.py:4: PytestExperimentalApiWarning: testdir.copy_example is an experimental api that may change over time
testdir.copy_example("test_example.py")

-- Docs: https://docs.pytest.org/en/latest/warnings.html
Expand Down
24 changes: 16 additions & 8 deletions src/_pytest/assertion/rewrite.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,12 @@ def mark_rewrite(self, *names):
self._must_rewrite.update(names)

def _warn_already_imported(self, name):
self.config.warn(
"P1", "Module already imported so cannot be rewritten: %s" % name
from _pytest.warning_types import PytestWarning
from _pytest.warnings import _issue_config_warning

_issue_config_warning(
PytestWarning("Module already imported so cannot be rewritten: %s" % name),
self.config,
)

def load_module(self, name):
Expand Down Expand Up @@ -746,13 +750,17 @@ def visit_Assert(self, assert_):
the expression is false.

"""
if isinstance(assert_.test, ast.Tuple) and self.config is not None:
fslocation = (self.module_path, assert_.lineno)
self.config.warn(
"R1",
"assertion is always true, perhaps " "remove parentheses?",
fslocation=fslocation,
if isinstance(assert_.test, ast.Tuple) and len(assert_.test.elts) >= 1:
from _pytest.warning_types import PytestWarning
import warnings

warnings.warn_explicit(
PytestWarning("assertion is always true, perhaps remove parentheses?"),
category=None,
filename=str(self.module_path),
lineno=assert_.lineno,
)

self.statements = []
self.variables = []
self.variable_counter = itertools.count()
Expand Down
11 changes: 8 additions & 3 deletions src/_pytest/cacheprovider.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,27 @@
@attr.s
class Cache(object):
_cachedir = attr.ib(repr=False)
_warn = attr.ib(repr=False)
_config = attr.ib(repr=False)

@classmethod
def for_config(cls, config):
cachedir = cls.cache_dir_from_config(config)
if config.getoption("cacheclear") and cachedir.exists():
shutil.rmtree(str(cachedir))
cachedir.mkdir()
return cls(cachedir, config.warn)
return cls(cachedir, config)

@staticmethod
def cache_dir_from_config(config):
return paths.resolve_from_str(config.getini("cache_dir"), config.rootdir)

def warn(self, fmt, **args):
self._warn(code="I9", message=fmt.format(**args) if args else fmt)
from _pytest.warnings import _issue_config_warning
from _pytest.warning_types import PytestWarning

_issue_config_warning(
PytestWarning(fmt.format(**args) if args else fmt), self._config
)

def makedir(self, name):
""" return a directory path object with the given name. If the
Expand Down
37 changes: 33 additions & 4 deletions src/_pytest/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,9 @@ def _prepareconfig(args=None, plugins=None):
else:
pluginmanager.register(plugin)
if warning:
config.warn("C1", warning)
from _pytest.warnings import _issue_config_warning

_issue_config_warning(warning, config=config)
return pluginmanager.hook.pytest_cmdline_parse(
pluginmanager=pluginmanager, args=args
)
Expand Down Expand Up @@ -417,7 +419,12 @@ def _importconftest(self, conftestpath):
PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST
)

warnings.warn(PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST)
warnings.warn_explicit(
PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST,
category=None,
filename=str(conftestpath),
lineno=0,
)
except Exception:
raise ConftestImportFailure(conftestpath, sys.exc_info())

Expand Down Expand Up @@ -602,7 +609,29 @@ def _ensure_unconfigure(self):
fin()

def warn(self, code, message, fslocation=None, nodeid=None):
""" generate a warning for this test session. """
"""
.. deprecated:: 3.8

Use :py:func:`warnings.warn` or :py:func:`warnings.warn_explicit` directly instead.

Generate a warning for this test session.
"""
from _pytest.warning_types import RemovedInPytest4Warning

if isinstance(fslocation, (tuple, list)) and len(fslocation) > 2:
filename, lineno = fslocation[:2]
else:
filename = "unknown file"
lineno = 0
msg = "config.warn has been deprecated, use warnings.warn instead"
if nodeid:
msg = "{}: {}".format(nodeid, msg)
warnings.warn_explicit(
RemovedInPytest4Warning(msg),
category=None,
filename=filename,
lineno=lineno,
)
self.hook.pytest_logwarning.call_historic(
kwargs=dict(
code=code, message=message, fslocation=fslocation, nodeid=nodeid
Expand Down Expand Up @@ -667,8 +696,8 @@ def _initini(self, args):
r = determine_setup(
ns.inifilename,
ns.file_or_dir + unknown_args,
warnfunc=self.warn,
rootdir_cmd_arg=ns.rootdir or None,
config=self,
)
self.rootdir, self.inifile, self.inicfg = r
self._parser.extra_info["rootdir"] = self.rootdir
Expand Down
Loading