Merge pull request #11757 from bluetech/rm-removed-in-8

Remove pytest 8 deprecations
This commit is contained in:
Ran Benita 2024-01-03 16:53:10 +02:00 committed by GitHub
commit f017df443a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 410 additions and 1835 deletions

View File

@ -97,8 +97,8 @@ Features
- `Modular fixtures <https://docs.pytest.org/en/stable/explanation/fixtures.html>`_ for
managing small or parametrized long-lived test resources
- Can run `unittest <https://docs.pytest.org/en/stable/how-to/unittest.html>`_ (or trial),
`nose <https://docs.pytest.org/en/stable/how-to/nose.html>`_ test suites out of the box
- Can run `unittest <https://docs.pytest.org/en/stable/how-to/unittest.html>`_ (or trial)
test suites out of the box
- Python 3.8+ or PyPy3

View File

@ -44,7 +44,7 @@ Partner projects, sign up here! (by 22 March)
What does it mean to "adopt pytest"?
-----------------------------------------
There can be many different definitions of "success". Pytest can run many nose_ and unittest_ tests by default, so using pytest as your testrunner may be possible from day 1. Job done, right?
There can be many different definitions of "success". Pytest can run many unittest_ tests by default, so using pytest as your testrunner may be possible from day 1. Job done, right?
Progressive success might look like:
@ -62,7 +62,6 @@ Progressive success might look like:
It may be after the month is up, the partner project decides that pytest is not right for it. That's okay - hopefully the pytest team will also learn something about its weaknesses or deficiencies.
.. _nose: nose.html
.. _unittest: unittest.html
.. _assert: assert.html
.. _pycmd: https://bitbucket.org/hpk42/pycmd/overview

View File

@ -227,7 +227,7 @@ These are breaking changes where deprecation was not possible.
Deprecations
------------
- `#10465 <https://github.com/pytest-dev/pytest/issues/10465>`_: Test functions returning a value other than ``None`` will now issue a :class:`pytest.PytestWarning` instead of :class:`pytest.PytestRemovedIn8Warning`, meaning this will stay a warning instead of becoming an error in the future.
- `#10465 <https://github.com/pytest-dev/pytest/issues/10465>`_: Test functions returning a value other than ``None`` will now issue a :class:`pytest.PytestWarning` instead of ``pytest.PytestRemovedIn8Warning``, meaning this will stay a warning instead of becoming an error in the future.
- `#3664 <https://github.com/pytest-dev/pytest/issues/3664>`_: Applying a mark to a fixture function now issues a warning: marks in fixtures never had any effect, but it is a common user error to apply a mark to a fixture (for example ``usefixtures``) and expect it to work.
@ -1257,7 +1257,7 @@ Deprecations
See :ref:`the deprecation note <diamond-inheritance-deprecated>` for full details.
- `#8592 <https://github.com/pytest-dev/pytest/issues/8592>`_: :hook:`pytest_cmdline_preparse` has been officially deprecated. It will be removed in a future release. Use :hook:`pytest_load_initial_conftests` instead.
- `#8592 <https://github.com/pytest-dev/pytest/issues/8592>`_: ``pytest_cmdline_preparse`` has been officially deprecated. It will be removed in a future release. Use :hook:`pytest_load_initial_conftests` instead.
See :ref:`the deprecation note <cmdline-preparse-deprecated>` for full details.

View File

@ -199,7 +199,6 @@ nitpick_ignore = [
("py:class", "_tracing.TagTracerSub"),
("py:class", "warnings.WarningMessage"),
# Undocumented type aliases
("py:class", "LEGACY_PATH"),
("py:class", "_PluggyPlugin"),
# TypeVars
("py:class", "_pytest._code.code.E"),

View File

@ -44,7 +44,6 @@ How-to guides
how-to/existingtestsuite
how-to/unittest
how-to/nose
how-to/xunit_setup
how-to/bash-completion

View File

@ -19,12 +19,273 @@ Below is a complete list of all pytest features which are considered deprecated.
:class:`~pytest.PytestWarning` or subclasses, which can be filtered using :ref:`standard warning filters <warnings>`.
.. _legacy-path-hooks-deprecated:
Configuring hook specs/impls using markers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Before pluggy, pytest's plugin library, was its own package and had a clear API,
pytest just used ``pytest.mark`` to configure hooks.
The :py:func:`pytest.hookimpl` and :py:func:`pytest.hookspec` decorators
have been available since years and should be used instead.
.. code-block:: python
@pytest.mark.tryfirst
def pytest_runtest_call():
...
# or
def pytest_runtest_call():
...
pytest_runtest_call.tryfirst = True
should be changed to:
.. code-block:: python
@pytest.hookimpl(tryfirst=True)
def pytest_runtest_call():
...
Changed ``hookimpl`` attributes:
* ``tryfirst``
* ``trylast``
* ``optionalhook``
* ``hookwrapper``
Changed ``hookwrapper`` attributes:
* ``firstresult``
* ``historic``
Directly constructing internal classes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 7.0
Directly constructing the following classes is now deprecated:
- ``_pytest.mark.structures.Mark``
- ``_pytest.mark.structures.MarkDecorator``
- ``_pytest.mark.structures.MarkGenerator``
- ``_pytest.python.Metafunc``
- ``_pytest.runner.CallInfo``
- ``_pytest._code.ExceptionInfo``
- ``_pytest.config.argparsing.Parser``
- ``_pytest.config.argparsing.OptionGroup``
- ``_pytest.pytester.HookRecorder``
These constructors have always been considered private, but now issue a deprecation warning, which may become a hard error in pytest 8.
.. _diamond-inheritance-deprecated:
Diamond inheritance between :class:`pytest.Collector` and :class:`pytest.Item`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 7.0
Defining a custom pytest node type which is both an :class:`~pytest.Item` and a :class:`~pytest.Collector` (e.g. :class:`~pytest.File`) now issues a warning.
It was never sanely supported and triggers hard to debug errors.
Some plugins providing linting/code analysis have been using this as a hack.
Instead, a separate collector node should be used, which collects the item. See
:ref:`non-python tests` for an example, as well as an `example pr fixing inheritance`_.
.. _example pr fixing inheritance: https://github.com/asmeurer/pytest-flakes/pull/40/files
.. _uncooperative-constructors-deprecated:
Constructors of custom :class:`~_pytest.nodes.Node` subclasses should take ``**kwargs``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 7.0
If custom subclasses of nodes like :class:`pytest.Item` override the
``__init__`` method, they should take ``**kwargs``. Thus,
.. code-block:: python
class CustomItem(pytest.Item):
def __init__(self, name, parent, additional_arg):
super().__init__(name, parent)
self.additional_arg = additional_arg
should be turned into:
.. code-block:: python
class CustomItem(pytest.Item):
def __init__(self, *, additional_arg, **kwargs):
super().__init__(**kwargs)
self.additional_arg = additional_arg
to avoid hard-coding the arguments pytest can pass to the superclass.
See :ref:`non-python tests` for a full example.
For cases without conflicts, no deprecation warning is emitted. For cases with
conflicts (such as :class:`pytest.File` now taking ``path`` instead of
``fspath``, as :ref:`outlined above <node-ctor-fspath-deprecation>`), a
deprecation warning is now raised.
Applying a mark to a fixture function
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 7.4
Applying a mark to a fixture function never had any effect, but it is a common user error.
.. code-block:: python
@pytest.mark.usefixtures("clean_database")
@pytest.fixture
def user() -> User:
...
Users expected in this case that the ``usefixtures`` mark would have its intended effect of using the ``clean_database`` fixture when ``user`` was invoked, when in fact it has no effect at all.
Now pytest will issue a warning when it encounters this problem, and will raise an error in the future versions.
Returning non-None value in test functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 7.2
A :class:`pytest.PytestReturnNotNoneWarning` is now emitted if a test function returns something other than `None`.
This prevents a common mistake among beginners that expect that returning a `bool` would cause a test to pass or fail, for example:
.. code-block:: python
@pytest.mark.parametrize(
["a", "b", "result"],
[
[1, 2, 5],
[2, 3, 8],
[5, 3, 18],
],
)
def test_foo(a, b, result):
return foo(a, b) == result
Given that pytest ignores the return value, this might be surprising that it will never fail.
The proper fix is to change the `return` to an `assert`:
.. code-block:: python
@pytest.mark.parametrize(
["a", "b", "result"],
[
[1, 2, 5],
[2, 3, 8],
[5, 3, 18],
],
)
def test_foo(a, b, result):
assert foo(a, b) == result
The ``yield_fixture`` function/decorator
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 6.2
``pytest.yield_fixture`` is a deprecated alias for :func:`pytest.fixture`.
It has been so for a very long time, so can be search/replaced safely.
Removed Features and Breaking Changes
-------------------------------------
As stated in our :ref:`backwards-compatibility` policy, deprecated features are removed only in major releases after
an appropriate period of deprecation has passed.
Some breaking changes which could not be deprecated are also listed.
.. _node-ctor-fspath-deprecation:
``fspath`` argument for Node constructors replaced with ``pathlib.Path``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 7.0
In order to support the transition from ``py.path.local`` to :mod:`pathlib`,
the ``fspath`` argument to :class:`~_pytest.nodes.Node` constructors like
:func:`pytest.Function.from_parent()` and :func:`pytest.Class.from_parent()`
is now deprecated.
Plugins which construct nodes should pass the ``path`` argument, of type
:class:`pathlib.Path`, instead of the ``fspath`` argument.
Plugins which implement custom items and collectors are encouraged to replace
``fspath`` parameters (``py.path.local``) with ``path`` parameters
(``pathlib.Path``), and drop any other usage of the ``py`` library if possible.
If possible, plugins with custom items should use :ref:`cooperative
constructors <uncooperative-constructors-deprecated>` to avoid hardcoding
arguments they only pass on to the superclass.
.. note::
The name of the :class:`~_pytest.nodes.Node` arguments and attributes (the
new attribute being ``path``) is **the opposite** of the situation for
hooks, :ref:`outlined below <legacy-path-hooks-deprecated>` (the old
argument being ``path``).
This is an unfortunate artifact due to historical reasons, which should be
resolved in future versions as we slowly get rid of the :pypi:`py`
dependency (see :issue:`9283` for a longer discussion).
Due to the ongoing migration of methods like :meth:`~pytest.Item.reportinfo`
which still is expected to return a ``py.path.local`` object, nodes still have
both ``fspath`` (``py.path.local``) and ``path`` (``pathlib.Path``) attributes,
no matter what argument was used in the constructor. We expect to deprecate the
``fspath`` attribute in a future release.
``py.path.local`` arguments for hooks replaced with ``pathlib.Path``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 7.0
.. versionremoved:: 8.0
In order to support the transition from ``py.path.local`` to :mod:`pathlib`, the following hooks now receive additional arguments:
* :hook:`pytest_ignore_collect(collection_path: pathlib.Path) <pytest_ignore_collect>` as equivalent to ``path``
* :hook:`pytest_collect_file(file_path: pathlib.Path) <pytest_collect_file>` as equivalent to ``path``
* :hook:`pytest_pycollect_makemodule(module_path: pathlib.Path) <pytest_pycollect_makemodule>` as equivalent to ``path``
* :hook:`pytest_report_header(start_path: pathlib.Path) <pytest_report_header>` as equivalent to ``startdir``
* :hook:`pytest_report_collectionfinish(start_path: pathlib.Path) <pytest_report_collectionfinish>` as equivalent to ``startdir``
The accompanying ``py.path.local`` based paths have been deprecated: plugins which manually invoke those hooks should only pass the new ``pathlib.Path`` arguments, and users should change their hook implementations to use the new ``pathlib.Path`` arguments.
.. note::
The name of the :class:`~_pytest.nodes.Node` arguments and attributes,
:ref:`outlined above <node-ctor-fspath-deprecation>` (the new attribute
being ``path``) is **the opposite** of the situation for hooks (the old
argument being ``path``).
This is an unfortunate artifact due to historical reasons, which should be
resolved in future versions as we slowly get rid of the :pypi:`py`
dependency (see :issue:`9283` for a longer discussion).
.. _nose-deprecation:
Support for tests written for nose
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 7.2
.. versionremoved:: 8.0
Support for running tests written for `nose <https://nose.readthedocs.io/en/latest/>`__ is now deprecated.
@ -125,160 +386,13 @@ Will also need to be ported to a supported pytest style. One way to do it is usi
.. _`with-setup-nose`: https://nose.readthedocs.io/en/latest/testing_tools.html?highlight=with_setup#nose.tools.with_setup
.. _instance-collector-deprecation:
The ``pytest.Instance`` collector
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. versionremoved:: 7.0
The ``pytest.Instance`` collector type has been removed.
Previously, Python test methods were collected as :class:`~pytest.Class` -> ``Instance`` -> :class:`~pytest.Function`.
Now :class:`~pytest.Class` collects the test methods directly.
Most plugins which reference ``Instance`` do so in order to ignore or skip it,
using a check such as ``if isinstance(node, Instance): return``.
Such plugins should simply remove consideration of ``Instance`` on pytest>=7.
However, to keep such uses working, a dummy type has been instanted in ``pytest.Instance`` and ``_pytest.python.Instance``,
and importing it emits a deprecation warning. This will be removed in pytest 8.
.. _node-ctor-fspath-deprecation:
``fspath`` argument for Node constructors replaced with ``pathlib.Path``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 7.0
In order to support the transition from ``py.path.local`` to :mod:`pathlib`,
the ``fspath`` argument to :class:`~_pytest.nodes.Node` constructors like
:func:`pytest.Function.from_parent()` and :func:`pytest.Class.from_parent()`
is now deprecated.
Plugins which construct nodes should pass the ``path`` argument, of type
:class:`pathlib.Path`, instead of the ``fspath`` argument.
Plugins which implement custom items and collectors are encouraged to replace
``fspath`` parameters (``py.path.local``) with ``path`` parameters
(``pathlib.Path``), and drop any other usage of the ``py`` library if possible.
If possible, plugins with custom items should use :ref:`cooperative
constructors <uncooperative-constructors-deprecated>` to avoid hardcoding
arguments they only pass on to the superclass.
.. note::
The name of the :class:`~_pytest.nodes.Node` arguments and attributes (the
new attribute being ``path``) is **the opposite** of the situation for
hooks, :ref:`outlined below <legacy-path-hooks-deprecated>` (the old
argument being ``path``).
This is an unfortunate artifact due to historical reasons, which should be
resolved in future versions as we slowly get rid of the :pypi:`py`
dependency (see :issue:`9283` for a longer discussion).
Due to the ongoing migration of methods like :meth:`~pytest.Item.reportinfo`
which still is expected to return a ``py.path.local`` object, nodes still have
both ``fspath`` (``py.path.local``) and ``path`` (``pathlib.Path``) attributes,
no matter what argument was used in the constructor. We expect to deprecate the
``fspath`` attribute in a future release.
.. _legacy-path-hooks-deprecated:
Configuring hook specs/impls using markers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Before pluggy, pytest's plugin library, was its own package and had a clear API,
pytest just used ``pytest.mark`` to configure hooks.
The :py:func:`pytest.hookimpl` and :py:func:`pytest.hookspec` decorators
have been available since years and should be used instead.
.. code-block:: python
@pytest.mark.tryfirst
def pytest_runtest_call():
...
# or
def pytest_runtest_call():
...
pytest_runtest_call.tryfirst = True
should be changed to:
.. code-block:: python
@pytest.hookimpl(tryfirst=True)
def pytest_runtest_call():
...
Changed ``hookimpl`` attributes:
* ``tryfirst``
* ``trylast``
* ``optionalhook``
* ``hookwrapper``
Changed ``hookwrapper`` attributes:
* ``firstresult``
* ``historic``
``py.path.local`` arguments for hooks replaced with ``pathlib.Path``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 7.0
In order to support the transition from ``py.path.local`` to :mod:`pathlib`, the following hooks now receive additional arguments:
* :hook:`pytest_ignore_collect(collection_path: pathlib.Path) <pytest_ignore_collect>` as equivalent to ``path``
* :hook:`pytest_collect_file(file_path: pathlib.Path) <pytest_collect_file>` as equivalent to ``path``
* :hook:`pytest_pycollect_makemodule(module_path: pathlib.Path) <pytest_pycollect_makemodule>` as equivalent to ``path``
* :hook:`pytest_report_header(start_path: pathlib.Path) <pytest_report_header>` as equivalent to ``startdir``
* :hook:`pytest_report_collectionfinish(start_path: pathlib.Path) <pytest_report_collectionfinish>` as equivalent to ``startdir``
The accompanying ``py.path.local`` based paths have been deprecated: plugins which manually invoke those hooks should only pass the new ``pathlib.Path`` arguments, and users should change their hook implementations to use the new ``pathlib.Path`` arguments.
.. note::
The name of the :class:`~_pytest.nodes.Node` arguments and attributes,
:ref:`outlined above <node-ctor-fspath-deprecation>` (the new attribute
being ``path``) is **the opposite** of the situation for hooks (the old
argument being ``path``).
This is an unfortunate artifact due to historical reasons, which should be
resolved in future versions as we slowly get rid of the :pypi:`py`
dependency (see :issue:`9283` for a longer discussion).
Directly constructing internal classes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 7.0
Directly constructing the following classes is now deprecated:
- ``_pytest.mark.structures.Mark``
- ``_pytest.mark.structures.MarkDecorator``
- ``_pytest.mark.structures.MarkGenerator``
- ``_pytest.python.Metafunc``
- ``_pytest.runner.CallInfo``
- ``_pytest._code.ExceptionInfo``
- ``_pytest.config.argparsing.Parser``
- ``_pytest.config.argparsing.OptionGroup``
- ``_pytest.pytester.HookRecorder``
These constructors have always been considered private, but now issue a deprecation warning, which may become a hard error in pytest 8.
.. _cmdline-preparse-deprecated:
Passing ``msg=`` to ``pytest.skip``, ``pytest.fail`` or ``pytest.exit``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 7.0
.. versionremoved:: 8.0
Passing the keyword argument ``msg`` to :func:`pytest.skip`, :func:`pytest.fail` or :func:`pytest.exit`
is now deprecated and ``reason`` should be used instead. This change is to bring consistency between these
@ -307,12 +421,74 @@ functions and the ``@pytest.mark.skip`` and ``@pytest.mark.xfail`` markers which
pytest.exit(reason="bar")
.. _instance-collector-deprecation:
The ``pytest.Instance`` collector
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. versionremoved:: 7.0
The ``pytest.Instance`` collector type has been removed.
Previously, Python test methods were collected as :class:`~pytest.Class` -> ``Instance`` -> :class:`~pytest.Function`.
Now :class:`~pytest.Class` collects the test methods directly.
Most plugins which reference ``Instance`` do so in order to ignore or skip it,
using a check such as ``if isinstance(node, Instance): return``.
Such plugins should simply remove consideration of ``Instance`` on pytest>=7.
However, to keep such uses working, a dummy type has been instanted in ``pytest.Instance`` and ``_pytest.python.Instance``,
and importing it emits a deprecation warning. This was removed in pytest 8.
Using ``pytest.warns(None)``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 7.0
.. versionremoved:: 8.0
:func:`pytest.warns(None) <pytest.warns>` is now deprecated because it was frequently misused.
Its correct usage was checking that the code emits at least one warning of any type - like ``pytest.warns()``
or ``pytest.warns(Warning)``.
See :ref:`warns use cases` for examples.
Backward compatibilities in ``Parser.addoption``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 2.4
.. versionremoved:: 8.0
Several behaviors of :meth:`Parser.addoption <pytest.Parser.addoption>` are now
removed in pytest 8 (deprecated since pytest 2.4.0):
- ``parser.addoption(..., help=".. %default ..")`` - use ``%(default)s`` instead.
- ``parser.addoption(..., type="int/string/float/complex")`` - use ``type=int`` etc. instead.
The ``--strict`` command-line option
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 6.2
.. versionremoved:: 8.0
The ``--strict`` command-line option has been deprecated in favor of ``--strict-markers``, which
better conveys what the option does.
We have plans to maybe in the future to reintroduce ``--strict`` and make it an encompassing
flag for all strictness related options (``--strict-markers`` and ``--strict-config``
at the moment, more might be introduced in the future).
.. _cmdline-preparse-deprecated:
Implementing the ``pytest_cmdline_preparse`` hook
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 7.0
.. versionremoved:: 8.0
Implementing the :hook:`pytest_cmdline_preparse` hook has been officially deprecated.
Implementing the ``pytest_cmdline_preparse`` hook has been officially deprecated.
Implement the :hook:`pytest_load_initial_conftests` hook instead.
.. code-block:: python
@ -329,171 +505,6 @@ Implement the :hook:`pytest_load_initial_conftests` hook instead.
) -> None:
...
.. _diamond-inheritance-deprecated:
Diamond inheritance between :class:`pytest.Collector` and :class:`pytest.Item`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 7.0
Defining a custom pytest node type which is both an :class:`~pytest.Item` and a :class:`~pytest.Collector` (e.g. :class:`~pytest.File`) now issues a warning.
It was never sanely supported and triggers hard to debug errors.
Some plugins providing linting/code analysis have been using this as a hack.
Instead, a separate collector node should be used, which collects the item. See
:ref:`non-python tests` for an example, as well as an `example pr fixing inheritance`_.
.. _example pr fixing inheritance: https://github.com/asmeurer/pytest-flakes/pull/40/files
.. _uncooperative-constructors-deprecated:
Constructors of custom :class:`~_pytest.nodes.Node` subclasses should take ``**kwargs``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 7.0
If custom subclasses of nodes like :class:`pytest.Item` override the
``__init__`` method, they should take ``**kwargs``. Thus,
.. code-block:: python
class CustomItem(pytest.Item):
def __init__(self, name, parent, additional_arg):
super().__init__(name, parent)
self.additional_arg = additional_arg
should be turned into:
.. code-block:: python
class CustomItem(pytest.Item):
def __init__(self, *, additional_arg, **kwargs):
super().__init__(**kwargs)
self.additional_arg = additional_arg
to avoid hard-coding the arguments pytest can pass to the superclass.
See :ref:`non-python tests` for a full example.
For cases without conflicts, no deprecation warning is emitted. For cases with
conflicts (such as :class:`pytest.File` now taking ``path`` instead of
``fspath``, as :ref:`outlined above <node-ctor-fspath-deprecation>`), a
deprecation warning is now raised.
Applying a mark to a fixture function
-------------------------------------
.. deprecated:: 7.4
Applying a mark to a fixture function never had any effect, but it is a common user error.
.. code-block:: python
@pytest.mark.usefixtures("clean_database")
@pytest.fixture
def user() -> User:
...
Users expected in this case that the ``usefixtures`` mark would have its intended effect of using the ``clean_database`` fixture when ``user`` was invoked, when in fact it has no effect at all.
Now pytest will issue a warning when it encounters this problem, and will raise an error in the future versions.
Backward compatibilities in ``Parser.addoption``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 2.4
Several behaviors of :meth:`Parser.addoption <pytest.Parser.addoption>` are now
scheduled for removal in pytest 8 (deprecated since pytest 2.4.0):
- ``parser.addoption(..., help=".. %default ..")`` - use ``%(default)s`` instead.
- ``parser.addoption(..., type="int/string/float/complex")`` - use ``type=int`` etc. instead.
Using ``pytest.warns(None)``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 7.0
:func:`pytest.warns(None) <pytest.warns>` is now deprecated because it was frequently misused.
Its correct usage was checking that the code emits at least one warning of any type - like ``pytest.warns()``
or ``pytest.warns(Warning)``.
See :ref:`warns use cases` for examples.
Returning non-None value in test functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 7.2
A :class:`pytest.PytestReturnNotNoneWarning` is now emitted if a test function returns something other than `None`.
This prevents a common mistake among beginners that expect that returning a `bool` would cause a test to pass or fail, for example:
.. code-block:: python
@pytest.mark.parametrize(
["a", "b", "result"],
[
[1, 2, 5],
[2, 3, 8],
[5, 3, 18],
],
)
def test_foo(a, b, result):
return foo(a, b) == result
Given that pytest ignores the return value, this might be surprising that it will never fail.
The proper fix is to change the `return` to an `assert`:
.. code-block:: python
@pytest.mark.parametrize(
["a", "b", "result"],
[
[1, 2, 5],
[2, 3, 8],
[5, 3, 18],
],
)
def test_foo(a, b, result):
assert foo(a, b) == result
The ``--strict`` command-line option
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 6.2
The ``--strict`` command-line option has been deprecated in favor of ``--strict-markers``, which
better conveys what the option does.
We have plans to maybe in the future to reintroduce ``--strict`` and make it an encompassing
flag for all strictness related options (``--strict-markers`` and ``--strict-config``
at the moment, more might be introduced in the future).
The ``yield_fixture`` function/decorator
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 6.2
``pytest.yield_fixture`` is a deprecated alias for :func:`pytest.fixture`.
It has been so for a very long time, so can be search/replaced safely.
Removed Features and Breaking Changes
-------------------------------------
As stated in our :ref:`backwards-compatibility` policy, deprecated features are removed only in major releases after
an appropriate period of deprecation has passed.
Some breaking changes which could not be deprecated are also listed.
Collection changes in pytest 8
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -18,7 +18,6 @@ For basic examples, see
- :ref:`Fixtures <fixtures>` for basic fixture/setup examples
- :ref:`parametrize` for basic test function parametrization
- :ref:`unittest` for basic unittest integration
- :ref:`noseintegration` for basic nosetests integration
The following examples aim at various use cases you might encounter.

View File

@ -85,7 +85,7 @@ style of setup/teardown functions:
In addition, pytest continues to support :ref:`xunitsetup`. You can mix
both styles, moving incrementally from classic to new style, as you
prefer. You can also start out from existing :ref:`unittest.TestCase
style <unittest.TestCase>` or :ref:`nose based <nosestyle>` projects.
style <unittest.TestCase>`.

View File

@ -4,8 +4,8 @@ How to use pytest with an existing test suite
==============================================
Pytest can be used with most existing test suites, but its
behavior differs from other test runners such as :ref:`nose <noseintegration>` or
Python's default unittest framework.
behavior differs from other test runners such as Python's
default unittest framework.
Before using this section you will want to :ref:`install pytest <getstarted>`.

View File

@ -52,7 +52,6 @@ pytest and other test systems
existingtestsuite
unittest
nose
xunit_setup
pytest development environment

View File

@ -1,99 +0,0 @@
.. _`noseintegration`:
How to run tests written for nose
=======================================
``pytest`` has basic support for running tests written for nose_.
.. warning::
This functionality has been deprecated and is likely to be removed in ``pytest 8.x``.
.. _nosestyle:
Usage
-------------
After :ref:`installation` type:
.. code-block:: bash
python setup.py develop # make sure tests can import our package
pytest # instead of 'nosetests'
and you should be able to run your nose style tests and
make use of pytest's capabilities.
Supported nose Idioms
----------------------
* ``setup()`` and ``teardown()`` at module/class/method level: any function or method called ``setup`` will be called during the setup phase for each test, same for ``teardown``.
* ``SkipTest`` exceptions and markers
* setup/teardown decorators
* ``__test__`` attribute on modules/classes/functions
* general usage of nose utilities
Unsupported idioms / known issues
----------------------------------
- unittest-style ``setUp, tearDown, setUpClass, tearDownClass``
are recognized only on ``unittest.TestCase`` classes but not
on plain classes. ``nose`` supports these methods also on plain
classes but pytest deliberately does not. As nose and pytest already
both support ``setup_class, teardown_class, setup_method, teardown_method``
it doesn't seem useful to duplicate the unittest-API like nose does.
If you however rather think pytest should support the unittest-spelling on
plain classes please post to :issue:`377`.
- nose imports test modules with the same import path (e.g.
``tests.test_mode``) but different file system paths
(e.g. ``tests/test_mode.py`` and ``other/tests/test_mode.py``)
by extending sys.path/import semantics. pytest does not do that. Note that
`nose2 choose to avoid this sys.path/import hackery <https://nose2.readthedocs.io/en/latest/differences.html#test-discovery-and-loading>`_.
If you place a conftest.py file in the root directory of your project
(as determined by pytest) pytest will run tests "nose style" against
the code below that directory by adding it to your ``sys.path`` instead of
running against your installed code.
You may find yourself wanting to do this if you ran ``python setup.py install``
to set up your project, as opposed to ``python setup.py develop`` or any of
the package manager equivalents. Installing with develop in a
virtual environment like tox is recommended over this pattern.
- nose-style doctests are not collected and executed correctly,
also doctest fixtures don't work.
- no nose-configuration is recognized.
- ``yield``-based methods are
fundamentally incompatible with pytest because they don't support fixtures
properly since collection and test execution are separated.
Here is a table comparing the default supported naming conventions for both
nose and pytest.
========= ========================== ======= =====
what default naming convention pytest nose
========= ========================== ======= =====
module ``test*.py``
module ``test_*.py`` ✅ ✅
module ``*_test.py``
module ``*_tests.py``
class ``*(unittest.TestCase)`` ✅ ✅
method ``test_*`` ✅ ✅
class ``Test*``
method ``test_*``
function ``test_*``
========= ========================== ======= =====
Migrating from nose to pytest
------------------------------
`nose2pytest <https://github.com/pytest-dev/nose2pytest>`_ is a Python script
and pytest plugin to help convert Nose-based tests into pytest-based tests.
Specifically, the script transforms ``nose.tools.assert_*`` function calls into
raw assert statements, while preserving format of original arguments
as much as possible.
.. _nose: https://nose.readthedocs.io/en/latest/

View File

@ -74,7 +74,7 @@ Features
- :ref:`Modular fixtures <fixture>` for managing small or parametrized long-lived test resources
- Can run :ref:`unittest <unittest>` (including trial) and :ref:`nose <noseintegration>` test suites out of the box
- Can run :ref:`unittest <unittest>` (including trial) test suites out of the box
- Python 3.8+ or PyPy 3

View File

@ -643,8 +643,6 @@ Bootstrapping hooks called for plugins registered early enough (internal and set
.. hook:: pytest_load_initial_conftests
.. autofunction:: pytest_load_initial_conftests
.. hook:: pytest_cmdline_preparse
.. autofunction:: pytest_cmdline_preparse
.. hook:: pytest_cmdline_parse
.. autofunction:: pytest_cmdline_parse
.. hook:: pytest_cmdline_main
@ -1209,9 +1207,6 @@ Custom warnings generated in some situations such as improper usage or deprecate
.. autoclass:: pytest.PytestReturnNotNoneWarning
:show-inheritance:
.. autoclass:: pytest.PytestRemovedIn8Warning
:show-inheritance:
.. autoclass:: pytest.PytestRemovedIn9Warning
:show-inheritance:

View File

@ -69,7 +69,6 @@ testing =
attrs>=19.2.0
hypothesis>=3.56
mock
nose
pygments>=2.7.2
requests
setuptools

View File

@ -16,25 +16,10 @@ from typing import Final
from typing import NoReturn
from typing import TypeVar
import py
_T = TypeVar("_T")
_S = TypeVar("_S")
#: constant to prepare valuing pylib path replacements/lazy proxies later on
# intended for removal in pytest 8.0 or 9.0
# fmt: off
# intentional space to create a fake difference for the verification
LEGACY_PATH = py.path. local
# fmt: on
def legacy_path(path: str | os.PathLike[str]) -> LEGACY_PATH:
"""Internal wrapper to prepare lazy proxies for legacy_path instances"""
return LEGACY_PATH(path)
# fmt: off
# Singleton type for NOTSET, as described in:

View File

@ -38,7 +38,6 @@ from typing import Type
from typing import TYPE_CHECKING
from typing import Union
import pluggy
from pluggy import HookimplMarker
from pluggy import HookimplOpts
from pluggy import HookspecMarker
@ -48,7 +47,6 @@ from pluggy import PluginManager
import _pytest._code
import _pytest.deprecated
import _pytest.hookspec
from .compat import PathAwareHookProxy
from .exceptions import PrintHelp as PrintHelp
from .exceptions import UsageError as UsageError
from .findpaths import determine_setup
@ -252,7 +250,6 @@ default_plugins = essential_plugins + (
"monkeypatch",
"recwarn",
"pastebin",
"nose",
"assertion",
"junitxml",
"doctest",
@ -1009,7 +1006,7 @@ class Config:
self._store = self.stash
self.trace = self.pluginmanager.trace.root.get("config")
self.hook: pluggy.HookRelay = PathAwareHookProxy(self.pluginmanager.hook) # type: ignore[assignment]
self.hook = self.pluginmanager.hook # type: ignore[assignment]
self._inicache: Dict[str, Any] = {}
self._override_ini: Sequence[str] = ()
self._opt2dest: Dict[str, str] = {}
@ -1319,11 +1316,6 @@ class Config:
self._validate_plugins()
self._warn_about_skipped_plugins()
if self.known_args_namespace.strict:
self.issue_config_time_warning(
_pytest.deprecated.STRICT_OPTION, stacklevel=2
)
if self.known_args_namespace.confcutdir is None:
if self.inipath is not None:
confcutdir = str(self.inipath.parent)
@ -1432,8 +1424,6 @@ class Config:
kwargs=dict(pluginmanager=self.pluginmanager)
)
self._preparse(args, addopts=addopts)
# XXX deprecated hook:
self.hook.pytest_cmdline_preparse(config=self, args=args)
self._parser.after_preparse = True # type: ignore
try:
args = self._parser.parse_setoption(

View File

@ -1,7 +1,6 @@
import argparse
import os
import sys
import warnings
from gettext import gettext
from typing import Any
from typing import Callable
@ -19,9 +18,6 @@ from typing import Union
import _pytest._io
from _pytest.config.exceptions import UsageError
from _pytest.deprecated import ARGUMENT_PERCENT_DEFAULT
from _pytest.deprecated import ARGUMENT_TYPE_STR
from _pytest.deprecated import ARGUMENT_TYPE_STR_CHOICE
from _pytest.deprecated import check_ispytest
FILE_OR_DIR = "file_or_dir"
@ -259,39 +255,15 @@ class Argument:
https://docs.python.org/3/library/optparse.html#optparse-standard-option-types
"""
_typ_map = {"int": int, "string": str, "float": float, "complex": complex}
def __init__(self, *names: str, **attrs: Any) -> None:
"""Store params in private vars for use in add_argument."""
self._attrs = attrs
self._short_opts: List[str] = []
self._long_opts: List[str] = []
if "%default" in (attrs.get("help") or ""):
warnings.warn(ARGUMENT_PERCENT_DEFAULT, stacklevel=3)
try:
typ = attrs["type"]
self.type = attrs["type"]
except KeyError:
pass
else:
# This might raise a keyerror as well, don't want to catch that.
if isinstance(typ, str):
if typ == "choice":
warnings.warn(
ARGUMENT_TYPE_STR_CHOICE.format(typ=typ, names=names),
stacklevel=4,
)
# argparse expects a type here take it from
# the type of the first element
attrs["type"] = type(attrs["choices"][0])
else:
warnings.warn(
ARGUMENT_TYPE_STR.format(typ=typ, names=names), stacklevel=4
)
attrs["type"] = Argument._typ_map[typ]
# Used in test_parseopt -> test_parse_defaultgetter.
self.type = attrs["type"]
else:
self.type = typ
try:
# Attribute existence is tested in Config._processopt.
self.default = attrs["default"]
@ -322,11 +294,6 @@ class Argument:
self._attrs[attr] = getattr(self, attr)
except AttributeError:
pass
if self._attrs.get("help"):
a = self._attrs["help"]
a = a.replace("%default", "%(default)s")
# a = a.replace('%prog', '%(prog)s')
self._attrs["help"] = a
return self._attrs
def _set_opt_strings(self, opts: Sequence[str]) -> None:

View File

@ -1,83 +0,0 @@
from __future__ import annotations
import functools
import warnings
from pathlib import Path
from typing import Mapping
import pluggy
from ..compat import LEGACY_PATH
from ..compat import legacy_path
from ..deprecated import HOOK_LEGACY_PATH_ARG
# hookname: (Path, LEGACY_PATH)
imply_paths_hooks: Mapping[str, tuple[str, str]] = {
"pytest_ignore_collect": ("collection_path", "path"),
"pytest_collect_file": ("file_path", "path"),
"pytest_pycollect_makemodule": ("module_path", "path"),
"pytest_report_header": ("start_path", "startdir"),
"pytest_report_collectionfinish": ("start_path", "startdir"),
}
def _check_path(path: Path, fspath: LEGACY_PATH) -> None:
if Path(fspath) != path:
raise ValueError(
f"Path({fspath!r}) != {path!r}\n"
"if both path and fspath are given they need to be equal"
)
class PathAwareHookProxy:
"""
this helper wraps around hook callers
until pluggy supports fixingcalls, this one will do
it currently doesn't return full hook caller proxies for fixed hooks,
this may have to be changed later depending on bugs
"""
def __init__(self, hook_relay: pluggy.HookRelay) -> None:
self._hook_relay = hook_relay
def __dir__(self) -> list[str]:
return dir(self._hook_relay)
def __getattr__(self, key: str) -> pluggy.HookCaller:
hook: pluggy.HookCaller = getattr(self._hook_relay, key)
if key not in imply_paths_hooks:
self.__dict__[key] = hook
return hook
else:
path_var, fspath_var = imply_paths_hooks[key]
@functools.wraps(hook)
def fixed_hook(**kw):
path_value: Path | None = kw.pop(path_var, None)
fspath_value: LEGACY_PATH | None = kw.pop(fspath_var, None)
if fspath_value is not None:
warnings.warn(
HOOK_LEGACY_PATH_ARG.format(
pylib_path_arg=fspath_var, pathlib_path_arg=path_var
),
stacklevel=2,
)
if path_value is not None:
if fspath_value is not None:
_check_path(path_value, fspath_value)
else:
fspath_value = legacy_path(path_value)
else:
assert fspath_value is not None
path_value = Path(fspath_value)
kw[path_var] = path_value
kw[fspath_var] = fspath_value
return hook(**kw)
fixed_hook.name = hook.name # type: ignore[attr-defined]
fixed_hook.spec = hook.spec # type: ignore[attr-defined]
fixed_hook.__name__ = key
self.__dict__[key] = fixed_hook
return fixed_hook # type: ignore[return-value]

View File

@ -11,7 +11,6 @@ in case of warnings which need to format their messages.
from warnings import warn
from _pytest.warning_types import PytestDeprecationWarning
from _pytest.warning_types import PytestRemovedIn8Warning
from _pytest.warning_types import PytestRemovedIn9Warning
from _pytest.warning_types import UnformattedWarning
@ -23,21 +22,6 @@ DEPRECATED_EXTERNAL_PLUGINS = {
"pytest_faulthandler",
}
NOSE_SUPPORT = UnformattedWarning(
PytestRemovedIn8Warning,
"Support for nose tests is deprecated and will be removed in a future release.\n"
"{nodeid} is using nose method: `{method}` ({stage})\n"
"See docs: https://docs.pytest.org/en/stable/deprecations.html#support-for-tests-written-for-nose",
)
NOSE_SUPPORT_METHOD = UnformattedWarning(
PytestRemovedIn8Warning,
"Support for nose tests is deprecated and will be removed in a future release.\n"
"{nodeid} is using nose-specific method: `{method}(self)`\n"
"To remove this warning, rename it to `{method}_method(self)`\n"
"See docs: https://docs.pytest.org/en/stable/deprecations.html#support-for-tests-written-for-nose",
)
# This can be* removed pytest 8, but it's harmless and common, so no rush to remove.
# * If you're in the future: "could have been".
@ -46,74 +30,10 @@ YIELD_FIXTURE = PytestDeprecationWarning(
"Use @pytest.fixture instead; they are the same."
)
WARNING_CMDLINE_PREPARSE_HOOK = PytestRemovedIn8Warning(
"The pytest_cmdline_preparse hook is deprecated and will be removed in a future release. \n"
"Please use pytest_load_initial_conftests hook instead."
)
FSCOLLECTOR_GETHOOKPROXY_ISINITPATH = PytestRemovedIn8Warning(
"The gethookproxy() and isinitpath() methods of FSCollector and Package are deprecated; "
"use self.session.gethookproxy() and self.session.isinitpath() instead. "
)
STRICT_OPTION = PytestRemovedIn8Warning(
"The --strict option is deprecated, use --strict-markers instead."
)
# This deprecation is never really meant to be removed.
PRIVATE = PytestDeprecationWarning("A private pytest class or function was used.")
ARGUMENT_PERCENT_DEFAULT = PytestRemovedIn8Warning(
'pytest now uses argparse. "%default" should be changed to "%(default)s"',
)
ARGUMENT_TYPE_STR_CHOICE = UnformattedWarning(
PytestRemovedIn8Warning,
"`type` argument to addoption() is the string {typ!r}."
" For choices this is optional and can be omitted, "
" but when supplied should be a type (for example `str` or `int`)."
" (options: {names})",
)
ARGUMENT_TYPE_STR = UnformattedWarning(
PytestRemovedIn8Warning,
"`type` argument to addoption() is the string {typ!r}, "
" but when supplied should be a type (for example `str` or `int`)."
" (options: {names})",
)
HOOK_LEGACY_PATH_ARG = UnformattedWarning(
PytestRemovedIn8Warning,
"The ({pylib_path_arg}: py.path.local) argument is deprecated, please use ({pathlib_path_arg}: pathlib.Path)\n"
"see https://docs.pytest.org/en/latest/deprecations.html"
"#py-path-local-arguments-for-hooks-replaced-with-pathlib-path",
)
NODE_CTOR_FSPATH_ARG = UnformattedWarning(
PytestRemovedIn8Warning,
"The (fspath: py.path.local) argument to {node_type_name} is deprecated. "
"Please use the (path: pathlib.Path) argument instead.\n"
"See https://docs.pytest.org/en/latest/deprecations.html"
"#fspath-argument-for-node-constructors-replaced-with-pathlib-path",
)
WARNS_NONE_ARG = PytestRemovedIn8Warning(
"Passing None has been deprecated.\n"
"See https://docs.pytest.org/en/latest/how-to/capture-warnings.html"
"#additional-use-cases-of-warnings-in-tests"
" for alternatives in common use cases."
)
KEYWORD_MSG_ARG = UnformattedWarning(
PytestRemovedIn8Warning,
"pytest.{func}(msg=...) is now deprecated, use pytest.{func}(reason=...) instead",
)
INSTANCE_COLLECTOR = PytestRemovedIn8Warning(
"The pytest.Instance collector type is deprecated and is no longer used. "
"See https://docs.pytest.org/en/latest/deprecations.html#the-pytest-instance-collector",
)
HOOK_LEGACY_MARKING = UnformattedWarning(
PytestDeprecationWarning,
"The hook{type} {fullname} uses old-style configuration options (marks or attributes).\n"

View File

@ -13,8 +13,6 @@ from typing import Union
from pluggy import HookspecMarker
from _pytest.deprecated import WARNING_CMDLINE_PREPARSE_HOOK
if TYPE_CHECKING:
import pdb
import warnings
@ -42,7 +40,6 @@ if TYPE_CHECKING:
from _pytest.runner import CallInfo
from _pytest.terminal import TerminalReporter
from _pytest.terminal import TestShortLogReport
from _pytest.compat import LEGACY_PATH
hookspec = HookspecMarker("pytest")
@ -159,21 +156,6 @@ def pytest_cmdline_parse(
"""
@hookspec(warn_on_impl=WARNING_CMDLINE_PREPARSE_HOOK)
def pytest_cmdline_preparse(config: "Config", args: List[str]) -> None:
"""(**Deprecated**) modify command line arguments before option parsing.
This hook is considered deprecated and will be removed in a future pytest version. Consider
using :hook:`pytest_load_initial_conftests` instead.
.. note::
This hook will not be called for ``conftest.py`` files, only for setuptools plugins.
:param config: The pytest config object.
:param args: Arguments passed on the command line.
"""
@hookspec(firstresult=True)
def pytest_cmdline_main(config: "Config") -> Optional[Union["ExitCode", int]]:
"""Called for performing the main command line action. The default
@ -263,9 +245,7 @@ def pytest_collection_finish(session: "Session") -> None:
@hookspec(firstresult=True)
def pytest_ignore_collect(
collection_path: Path, path: "LEGACY_PATH", config: "Config"
) -> Optional[bool]:
def pytest_ignore_collect(collection_path: Path, config: "Config") -> Optional[bool]:
"""Return True to prevent considering this path for collection.
This hook is consulted for all files and directories prior to calling
@ -279,8 +259,10 @@ def pytest_ignore_collect(
.. versionchanged:: 7.0.0
The ``collection_path`` parameter was added as a :class:`pathlib.Path`
equivalent of the ``path`` parameter. The ``path`` parameter
has been deprecated.
equivalent of the ``path`` parameter.
.. versionchanged:: 8.0.0
The ``path`` parameter has been removed.
"""
@ -305,9 +287,7 @@ def pytest_collect_directory(path: Path, parent: "Collector") -> "Optional[Colle
"""
def pytest_collect_file(
file_path: Path, path: "LEGACY_PATH", parent: "Collector"
) -> "Optional[Collector]":
def pytest_collect_file(file_path: Path, parent: "Collector") -> "Optional[Collector]":
"""Create a :class:`~pytest.Collector` for the given path, or None if not relevant.
For best results, the returned collector should be a subclass of
@ -320,8 +300,10 @@ def pytest_collect_file(
.. versionchanged:: 7.0.0
The ``file_path`` parameter was added as a :class:`pathlib.Path`
equivalent of the ``path`` parameter. The ``path`` parameter
has been deprecated.
equivalent of the ``path`` parameter.
.. versionchanged:: 8.0.0
The ``path`` parameter was removed.
"""
@ -380,9 +362,7 @@ def pytest_make_collect_report(collector: "Collector") -> "Optional[CollectRepor
@hookspec(firstresult=True)
def pytest_pycollect_makemodule(
module_path: Path, path: "LEGACY_PATH", parent
) -> Optional["Module"]:
def pytest_pycollect_makemodule(module_path: Path, parent) -> Optional["Module"]:
"""Return a :class:`pytest.Module` collector or None for the given path.
This hook will be called for each matching test module path.
@ -398,7 +378,8 @@ def pytest_pycollect_makemodule(
The ``module_path`` parameter was added as a :class:`pathlib.Path`
equivalent of the ``path`` parameter.
The ``path`` parameter has been deprecated in favor of ``fspath``.
.. versionchanged:: 8.0.0
The ``path`` parameter has been removed in favor of ``module_path``.
"""
@ -768,7 +749,7 @@ def pytest_assertion_pass(item: "Item", lineno: int, orig: str, expl: str) -> No
def pytest_report_header( # type:ignore[empty-body]
config: "Config", start_path: Path, startdir: "LEGACY_PATH"
config: "Config", start_path: Path
) -> Union[str, List[str]]:
"""Return a string or list of strings to be displayed as header info for terminal reporting.
@ -791,15 +772,16 @@ def pytest_report_header( # type:ignore[empty-body]
.. versionchanged:: 7.0.0
The ``start_path`` parameter was added as a :class:`pathlib.Path`
equivalent of the ``startdir`` parameter. The ``startdir`` parameter
has been deprecated.
equivalent of the ``startdir`` parameter.
.. versionchanged:: 8.0.0
The ``startdir`` parameter has been removed.
"""
def pytest_report_collectionfinish( # type:ignore[empty-body]
config: "Config",
start_path: Path,
startdir: "LEGACY_PATH",
items: Sequence["Item"],
) -> Union[str, List[str]]:
"""Return a string or list of strings to be displayed after collection
@ -823,8 +805,10 @@ def pytest_report_collectionfinish( # type:ignore[empty-body]
.. versionchanged:: 7.0.0
The ``start_path`` parameter was added as a :class:`pathlib.Path`
equivalent of the ``startdir`` parameter. The ``startdir`` parameter
has been deprecated.
equivalent of the ``startdir`` parameter.
.. versionchanged:: 8.0.0
The ``startdir`` parameter has been removed.
"""

View File

@ -1,5 +1,6 @@
"""Add backward compatibility support for the legacy py path type."""
import dataclasses
import os
import shlex
import subprocess
from pathlib import Path
@ -12,9 +13,8 @@ from typing import Union
from iniconfig import SectionWrapper
import py
from _pytest.cacheprovider import Cache
from _pytest.compat import LEGACY_PATH
from _pytest.compat import legacy_path
from _pytest.config import Config
from _pytest.config import hookimpl
from _pytest.config import PytestPluginManager
@ -36,6 +36,20 @@ if TYPE_CHECKING:
import pexpect
#: constant to prepare valuing pylib path replacements/lazy proxies later on
# intended for removal in pytest 8.0 or 9.0
# fmt: off
# intentional space to create a fake difference for the verification
LEGACY_PATH = py.path. local
# fmt: on
def legacy_path(path: Union[str, "os.PathLike[str]"]) -> LEGACY_PATH:
"""Internal wrapper to prepare lazy proxies for legacy_path instances"""
return LEGACY_PATH(path)
@final
class Testdir:
"""

View File

@ -33,7 +33,6 @@ from _pytest.config import hookimpl
from _pytest.config import PytestPluginManager
from _pytest.config import UsageError
from _pytest.config.argparsing import Parser
from _pytest.config.compat import PathAwareHookProxy
from _pytest.fixtures import FixtureManager
from _pytest.outcomes import exit
from _pytest.pathlib import absolutepath
@ -542,7 +541,6 @@ class Session(nodes.Collector):
super().__init__(
name="",
path=config.rootpath,
fspath=None,
parent=None,
config=config,
session=self,
@ -644,7 +642,7 @@ class Session(nodes.Collector):
proxy: pluggy.HookRelay
if remove_mods:
# One or more conftests are not in use at this path.
proxy = PathAwareHookProxy(FSHookProxy(pm, remove_mods)) # type: ignore[arg-type,assignment]
proxy = FSHookProxy(pm, remove_mods) # type: ignore[arg-type,assignment]
else:
# All plugins are active for this fspath.
proxy = self.config.hook

View File

@ -1,6 +1,5 @@
import abc
import os
import pathlib
import warnings
from functools import cached_property
from inspect import signature
@ -28,12 +27,8 @@ from _pytest._code import getfslineno
from _pytest._code.code import ExceptionInfo
from _pytest._code.code import TerminalRepr
from _pytest._code.code import Traceback
from _pytest.compat import LEGACY_PATH
from _pytest.config import Config
from _pytest.config import ConftestImportFailure
from _pytest.config.compat import _check_path
from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH
from _pytest.deprecated import NODE_CTOR_FSPATH_ARG
from _pytest.mark.structures import Mark
from _pytest.mark.structures import MarkDecorator
from _pytest.mark.structures import NodeKeywords
@ -99,27 +94,6 @@ def iterparentnodeids(nodeid: str) -> Iterator[str]:
yield nodeid
def _imply_path(
node_type: Type["Node"],
path: Optional[Path],
fspath: Optional[LEGACY_PATH],
) -> Path:
if fspath is not None:
warnings.warn(
NODE_CTOR_FSPATH_ARG.format(
node_type_name=node_type.__name__,
),
stacklevel=6,
)
if path is not None:
if fspath is not None:
_check_path(path, fspath)
return path
else:
assert fspath is not None
return Path(fspath)
_NodeType = TypeVar("_NodeType", bound="Node")
@ -174,14 +148,6 @@ class Node(abc.ABC, metaclass=NodeMeta):
``Collector``\'s are the internal nodes of the tree, and ``Item``\'s are the
leaf nodes.
"""
# Implemented in the legacypath plugin.
#: A ``LEGACY_PATH`` copy of the :attr:`path` attribute. Intended for usage
#: for methods not migrated to ``pathlib.Path`` yet, such as
#: :meth:`Item.reportinfo <pytest.Item.reportinfo>`. Will be deprecated in
#: a future release, prefer using :attr:`path` instead.
fspath: LEGACY_PATH
# Use __slots__ to make attribute access faster.
# Note that __dict__ is still available.
__slots__ = (
@ -201,7 +167,6 @@ class Node(abc.ABC, metaclass=NodeMeta):
parent: "Optional[Node]" = None,
config: Optional[Config] = None,
session: "Optional[Session]" = None,
fspath: Optional[LEGACY_PATH] = None,
path: Optional[Path] = None,
nodeid: Optional[str] = None,
) -> None:
@ -227,10 +192,11 @@ class Node(abc.ABC, metaclass=NodeMeta):
raise TypeError("session or parent must be provided")
self.session = parent.session
if path is None and fspath is None:
if path is None:
path = getattr(parent, "path", None)
assert path is not None
#: Filesystem path where this node was collected from (can be None).
self.path: pathlib.Path = _imply_path(type(self), path, fspath=fspath)
self.path = path
# The explicit annotation is to avoid publicly exposing NodeKeywords.
#: Keywords/markers collected from all scopes.
@ -596,7 +562,6 @@ class FSCollector(Collector, abc.ABC):
def __init__(
self,
fspath: Optional[LEGACY_PATH] = None,
path_or_parent: Optional[Union[Path, Node]] = None,
path: Optional[Path] = None,
name: Optional[str] = None,
@ -612,8 +577,8 @@ class FSCollector(Collector, abc.ABC):
elif isinstance(path_or_parent, Path):
assert path is None
path = path_or_parent
assert path is not None
path = _imply_path(type(self), path, fspath=fspath)
if name is None:
name = path.name
if parent is not None and parent.path != path:
@ -653,20 +618,11 @@ class FSCollector(Collector, abc.ABC):
cls,
parent,
*,
fspath: Optional[LEGACY_PATH] = None,
path: Optional[Path] = None,
**kw,
):
"""The public constructor."""
return super().from_parent(parent=parent, fspath=fspath, path=path, **kw)
def gethookproxy(self, fspath: "os.PathLike[str]"):
warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2)
return self.session.gethookproxy(fspath)
def isinitpath(self, path: Union[str, "os.PathLike[str]"]) -> bool:
warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2)
return self.session.isinitpath(path)
return super().from_parent(parent=parent, path=path, **kw)
class File(FSCollector, abc.ABC):

View File

@ -1,50 +0,0 @@
"""Run testsuites written for nose."""
import warnings
from _pytest.config import hookimpl
from _pytest.deprecated import NOSE_SUPPORT
from _pytest.fixtures import getfixturemarker
from _pytest.nodes import Item
from _pytest.python import Function
from _pytest.unittest import TestCaseFunction
@hookimpl(trylast=True)
def pytest_runtest_setup(item: Item) -> None:
if not isinstance(item, Function):
return
# Don't do nose style setup/teardown on direct unittest style classes.
if isinstance(item, TestCaseFunction):
return
# Capture the narrowed type of item for the teardown closure,
# see https://github.com/python/mypy/issues/2608
func = item
call_optional(func.obj, "setup", func.nodeid)
func.addfinalizer(lambda: call_optional(func.obj, "teardown", func.nodeid))
# NOTE: Module- and class-level fixtures are handled in python.py
# with `pluginmanager.has_plugin("nose")` checks.
# It would have been nicer to implement them outside of core, but
# it's not straightforward.
def call_optional(obj: object, name: str, nodeid: str) -> bool:
method = getattr(obj, name, None)
if method is None:
return False
is_fixture = getfixturemarker(method) is not None
if is_fixture:
return False
if not callable(method):
return False
# Warn about deprecation of this plugin.
method_name = getattr(method, "__name__", str(method))
warnings.warn(
NOSE_SUPPORT.format(nodeid=nodeid, method=method_name, stage=name), stacklevel=2
)
# If there are any problems allow the exception to raise rather than
# silently ignoring it.
method()
return True

View File

@ -1,7 +1,6 @@
"""Exception classes and constants handling test outcomes as well as
functions creating them."""
import sys
import warnings
from typing import Any
from typing import Callable
from typing import cast
@ -11,8 +10,6 @@ from typing import Protocol
from typing import Type
from typing import TypeVar
from _pytest.deprecated import KEYWORD_MSG_ARG
class OutcomeException(BaseException):
"""OutcomeException and its subclass instances indicate and contain info
@ -103,7 +100,8 @@ def _with_exception(exception_type: _ET) -> Callable[[_F], _WithException[_F, _E
@_with_exception(Exit)
def exit(
reason: str = "", returncode: Optional[int] = None, *, msg: Optional[str] = None
reason: str = "",
returncode: Optional[int] = None,
) -> NoReturn:
"""Exit testing process.
@ -113,28 +111,16 @@ def exit(
:param returncode:
Return code to be used when exiting pytest. None means the same as ``0`` (no error), same as :func:`sys.exit`.
:param msg:
Same as ``reason``, but deprecated. Will be removed in a future version, use ``reason`` instead.
"""
__tracebackhide__ = True
from _pytest.config import UsageError
if reason and msg:
raise UsageError(
"cannot pass reason and msg to exit(), `msg` is deprecated, use `reason`."
)
if not reason:
if msg is None:
raise UsageError("exit() requires a reason argument")
warnings.warn(KEYWORD_MSG_ARG.format(func="exit"), stacklevel=2)
reason = msg
raise Exit(reason, returncode)
@_with_exception(Skipped)
def skip(
reason: str = "", *, allow_module_level: bool = False, msg: Optional[str] = None
reason: str = "",
*,
allow_module_level: bool = False,
) -> NoReturn:
"""Skip an executing test with the given message.
@ -153,9 +139,6 @@ def skip(
Defaults to False.
:param msg:
Same as ``reason``, but deprecated. Will be removed in a future version, use ``reason`` instead.
.. note::
It is better to use the :ref:`pytest.mark.skipif ref` marker when
possible to declare a test to be skipped under certain conditions
@ -164,12 +147,11 @@ def skip(
to skip a doctest statically.
"""
__tracebackhide__ = True
reason = _resolve_msg_to_reason("skip", reason, msg)
raise Skipped(msg=reason, allow_module_level=allow_module_level)
@_with_exception(Failed)
def fail(reason: str = "", pytrace: bool = True, msg: Optional[str] = None) -> NoReturn:
def fail(reason: str = "", pytrace: bool = True) -> NoReturn:
"""Explicitly fail an executing test with the given message.
:param reason:
@ -178,51 +160,11 @@ def fail(reason: str = "", pytrace: bool = True, msg: Optional[str] = None) -> N
:param pytrace:
If False, msg represents the full failure information and no
python traceback will be reported.
:param msg:
Same as ``reason``, but deprecated. Will be removed in a future version, use ``reason`` instead.
"""
__tracebackhide__ = True
reason = _resolve_msg_to_reason("fail", reason, msg)
raise Failed(msg=reason, pytrace=pytrace)
def _resolve_msg_to_reason(
func_name: str, reason: str, msg: Optional[str] = None
) -> str:
"""
Handles converting the deprecated msg parameter if provided into
reason, raising a deprecation warning. This function will be removed
when the optional msg argument is removed from here in future.
:param str func_name:
The name of the offending function, this is formatted into the deprecation message.
:param str reason:
The reason= passed into either pytest.fail() or pytest.skip()
:param str msg:
The msg= passed into either pytest.fail() or pytest.skip(). This will
be converted into reason if it is provided to allow pytest.skip(msg=) or
pytest.fail(msg=) to continue working in the interim period.
:returns:
The value to use as reason.
"""
__tracebackhide__ = True
if msg is not None:
if reason:
from pytest import UsageError
raise UsageError(
f"Passing both ``reason`` and ``msg`` to pytest.{func_name}(...) is not permitted."
)
warnings.warn(KEYWORD_MSG_ARG.format(func=func_name), stacklevel=3)
reason = msg
return reason
class XFailed(Failed):
"""Raised from an explicit call to pytest.xfail()."""

View File

@ -47,7 +47,6 @@ from _pytest.compat import getimfunc
from _pytest.compat import getlocation
from _pytest.compat import is_async_function
from _pytest.compat import is_generator
from _pytest.compat import LEGACY_PATH
from _pytest.compat import NOTSET
from _pytest.compat import safe_getattr
from _pytest.compat import safe_isclass
@ -57,8 +56,6 @@ from _pytest.config import ExitCode
from _pytest.config import hookimpl
from _pytest.config.argparsing import Parser
from _pytest.deprecated import check_ispytest
from _pytest.deprecated import INSTANCE_COLLECTOR
from _pytest.deprecated import NOSE_SUPPORT_METHOD
from _pytest.fixtures import FixtureDef
from _pytest.fixtures import FixtureRequest
from _pytest.fixtures import FuncFixtureInfo
@ -597,23 +594,12 @@ class Module(nodes.File, PyCollector):
Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with
other fixtures (#517).
"""
has_nose = self.config.pluginmanager.has_plugin("nose")
setup_module = _get_first_non_fixture_func(
self.obj, ("setUpModule", "setup_module")
)
if setup_module is None and has_nose:
# The name "setup" is too common - only treat as fixture if callable.
setup_module = _get_first_non_fixture_func(self.obj, ("setup",))
if not callable(setup_module):
setup_module = None
teardown_module = _get_first_non_fixture_func(
self.obj, ("tearDownModule", "teardown_module")
)
if teardown_module is None and has_nose:
teardown_module = _get_first_non_fixture_func(self.obj, ("teardown",))
# Same as "setup" above - only treat as fixture if callable.
if not callable(teardown_module):
teardown_module = None
if setup_module is None and teardown_module is None:
return
@ -685,7 +671,6 @@ class Package(nodes.Directory):
def __init__(
self,
fspath: Optional[LEGACY_PATH],
parent: nodes.Collector,
# NOTE: following args are unused:
config=None,
@ -697,7 +682,6 @@ class Package(nodes.Directory):
# super().__init__(self, fspath, parent=parent)
session = parent.session
super().__init__(
fspath=fspath,
path=path,
parent=parent,
config=config,
@ -854,21 +838,10 @@ class Class(PyCollector):
Using a fixture to invoke these methods ensures we play nicely and unsurprisingly with
other fixtures (#517).
"""
has_nose = self.config.pluginmanager.has_plugin("nose")
setup_name = "setup_method"
setup_method = _get_first_non_fixture_func(self.obj, (setup_name,))
emit_nose_setup_warning = False
if setup_method is None and has_nose:
setup_name = "setup"
emit_nose_setup_warning = True
setup_method = _get_first_non_fixture_func(self.obj, (setup_name,))
teardown_name = "teardown_method"
teardown_method = _get_first_non_fixture_func(self.obj, (teardown_name,))
emit_nose_teardown_warning = False
if teardown_method is None and has_nose:
teardown_name = "teardown"
emit_nose_teardown_warning = True
teardown_method = _get_first_non_fixture_func(self.obj, (teardown_name,))
if setup_method is None and teardown_method is None:
return
@ -883,42 +856,14 @@ class Class(PyCollector):
if setup_method is not None:
func = getattr(self, setup_name)
_call_with_optional_argument(func, method)
if emit_nose_setup_warning:
warnings.warn(
NOSE_SUPPORT_METHOD.format(
nodeid=request.node.nodeid, method="setup"
),
stacklevel=2,
)
yield
if teardown_method is not None:
func = getattr(self, teardown_name)
_call_with_optional_argument(func, method)
if emit_nose_teardown_warning:
warnings.warn(
NOSE_SUPPORT_METHOD.format(
nodeid=request.node.nodeid, method="teardown"
),
stacklevel=2,
)
self.obj.__pytest_setup_method = xunit_setup_method_fixture
class InstanceDummy:
"""Instance used to be a node type between Class and Function. It has been
removed in pytest 7.0. Some plugins exist which reference `pytest.Instance`
only to ignore it; this dummy class keeps them working. This will be removed
in pytest 8."""
def __getattr__(name: str) -> object:
if name == "Instance":
warnings.warn(INSTANCE_COLLECTOR, 2)
return InstanceDummy
raise AttributeError(f"module {__name__} has no attribute {name}")
def hasinit(obj: object) -> bool:
init: object = getattr(obj, "__init__", None)
if init:

View File

@ -18,7 +18,6 @@ from typing import TypeVar
from typing import Union
from _pytest.deprecated import check_ispytest
from _pytest.deprecated import WARNS_NONE_ARG
from _pytest.fixtures import fixture
from _pytest.outcomes import fail
@ -264,9 +263,7 @@ class WarningsRecorder(warnings.catch_warnings): # type:ignore[type-arg]
class WarningsChecker(WarningsRecorder):
def __init__(
self,
expected_warning: Optional[
Union[Type[Warning], Tuple[Type[Warning], ...]]
] = Warning,
expected_warning: Union[Type[Warning], Tuple[Type[Warning], ...]] = Warning,
match_expr: Optional[Union[str, Pattern[str]]] = None,
*,
_ispytest: bool = False,
@ -275,15 +272,14 @@ class WarningsChecker(WarningsRecorder):
super().__init__(_ispytest=True)
msg = "exceptions must be derived from Warning, not %s"
if expected_warning is None:
warnings.warn(WARNS_NONE_ARG, stacklevel=4)
expected_warning_tup = None
elif isinstance(expected_warning, tuple):
if isinstance(expected_warning, tuple):
for exc in expected_warning:
if not issubclass(exc, Warning):
raise TypeError(msg % type(exc))
expected_warning_tup = expected_warning
elif issubclass(expected_warning, Warning):
elif isinstance(expected_warning, type) and issubclass(
expected_warning, Warning
):
expected_warning_tup = (expected_warning,)
else:
raise TypeError(msg % type(expected_warning))
@ -307,10 +303,6 @@ class WarningsChecker(WarningsRecorder):
__tracebackhide__ = True
if self.expected_warning is None:
# nothing to do in this deprecated case, see WARNS_NONE_ARG above
return
def found_str():
return pformat([record.message for record in self], indent=2)

View File

@ -49,12 +49,6 @@ class PytestDeprecationWarning(PytestWarning, DeprecationWarning):
__module__ = "pytest"
class PytestRemovedIn8Warning(PytestDeprecationWarning):
"""Warning class for features that will be removed in pytest 8."""
__module__ = "pytest"
class PytestRemovedIn9Warning(PytestDeprecationWarning):
"""Warning class for features that will be removed in pytest 9."""

View File

@ -46,7 +46,8 @@ def catch_warnings_for_item(
warnings.filterwarnings("always", category=DeprecationWarning)
warnings.filterwarnings("always", category=PendingDeprecationWarning)
warnings.filterwarnings("error", category=pytest.PytestRemovedIn8Warning)
# To be enabled in pytest 9.0.0.
# warnings.filterwarnings("error", category=pytest.PytestRemovedIn9Warning)
apply_warning_filters(config_filters, cmdline_filters)

View File

@ -1,7 +1,5 @@
# PYTHON_ARGCOMPLETE_OK
"""pytest: unit and functional testing with Python."""
from typing import TYPE_CHECKING
from _pytest import __version__
from _pytest import version_tuple
from _pytest._code import ExceptionInfo
@ -75,7 +73,6 @@ from _pytest.warning_types import PytestCollectionWarning
from _pytest.warning_types import PytestConfigWarning
from _pytest.warning_types import PytestDeprecationWarning
from _pytest.warning_types import PytestExperimentalApiWarning
from _pytest.warning_types import PytestRemovedIn8Warning
from _pytest.warning_types import PytestRemovedIn9Warning
from _pytest.warning_types import PytestReturnNotNoneWarning
from _pytest.warning_types import PytestUnhandledCoroutineWarning
@ -139,7 +136,6 @@ __all__ = [
"PytestConfigWarning",
"PytestDeprecationWarning",
"PytestExperimentalApiWarning",
"PytestRemovedIn8Warning",
"PytestRemovedIn9Warning",
"PytestReturnNotNoneWarning",
"Pytester",
@ -170,13 +166,3 @@ __all__ = [
"xfail",
"yield_fixture",
]
if not TYPE_CHECKING:
def __getattr__(name: str) -> object:
if name == "Instance":
# The import emits a deprecation warning.
from _pytest.python import Instance
return Instance
raise AttributeError(f"module {__name__} has no attribute {name}")

View File

@ -1,11 +1,5 @@
import re
import sys
import warnings
from pathlib import Path
import pytest
from _pytest import deprecated
from _pytest.compat import legacy_path
from _pytest.pytester import Pytester
from pytest import PytestDeprecationWarning
@ -68,50 +62,6 @@ def test_hookimpl_via_function_attributes_are_deprecated():
assert record.filename == __file__
def test_fscollector_gethookproxy_isinitpath(pytester: Pytester) -> None:
module = pytester.getmodulecol(
"""
def test_foo(): pass
""",
withinit=True,
)
assert isinstance(module, pytest.Module)
package = module.parent
assert isinstance(package, pytest.Package)
with pytest.warns(pytest.PytestDeprecationWarning, match="gethookproxy"):
package.gethookproxy(pytester.path)
with pytest.warns(pytest.PytestDeprecationWarning, match="isinitpath"):
package.isinitpath(pytester.path)
# The methods on Session are *not* deprecated.
session = module.session
with warnings.catch_warnings(record=True) as rec:
session.gethookproxy(pytester.path)
session.isinitpath(pytester.path)
assert len(rec) == 0
def test_strict_option_is_deprecated(pytester: Pytester) -> None:
"""--strict is a deprecated alias to --strict-markers (#7530)."""
pytester.makepyfile(
"""
import pytest
@pytest.mark.unknown
def test_foo(): pass
"""
)
result = pytester.runpytest("--strict", "-Wdefault::pytest.PytestRemovedIn8Warning")
result.stdout.fnmatch_lines(
[
"'unknown' not found in `markers` configuration option",
"*PytestRemovedIn8Warning: The --strict option is deprecated, use --strict-markers instead.",
]
)
def test_yield_fixture_is_deprecated() -> None:
with pytest.warns(DeprecationWarning, match=r"yield_fixture is deprecated"):
@ -134,159 +84,6 @@ def test_private_is_deprecated() -> None:
PrivateInit(10, _ispytest=True)
@pytest.mark.parametrize("hooktype", ["hook", "ihook"])
def test_hookproxy_warnings_for_pathlib(tmp_path, hooktype, request):
path = legacy_path(tmp_path)
PATH_WARN_MATCH = r".*path: py\.path\.local\) argument is deprecated, please use \(collection_path: pathlib\.Path.*"
if hooktype == "ihook":
hooks = request.node.ihook
else:
hooks = request.config.hook
with pytest.warns(PytestDeprecationWarning, match=PATH_WARN_MATCH) as r:
l1 = sys._getframe().f_lineno
hooks.pytest_ignore_collect(
config=request.config, path=path, collection_path=tmp_path
)
l2 = sys._getframe().f_lineno
(record,) = r
assert record.filename == __file__
assert l1 < record.lineno < l2
hooks.pytest_ignore_collect(config=request.config, collection_path=tmp_path)
# Passing entirely *different* paths is an outright error.
with pytest.raises(ValueError, match=r"path.*fspath.*need to be equal"):
with pytest.warns(PytestDeprecationWarning, match=PATH_WARN_MATCH) as r:
hooks.pytest_ignore_collect(
config=request.config, path=path, collection_path=Path("/bla/bla")
)
def test_warns_none_is_deprecated():
with pytest.warns(
PytestDeprecationWarning,
match=re.escape(
"Passing None has been deprecated.\n"
"See https://docs.pytest.org/en/latest/how-to/capture-warnings.html"
"#additional-use-cases-of-warnings-in-tests"
" for alternatives in common use cases."
),
):
with pytest.warns(None): # type: ignore[call-overload]
pass
class TestSkipMsgArgumentDeprecated:
def test_skip_with_msg_is_deprecated(self, pytester: Pytester) -> None:
p = pytester.makepyfile(
"""
import pytest
def test_skipping_msg():
pytest.skip(msg="skippedmsg")
"""
)
result = pytester.runpytest(p, "-Wdefault::pytest.PytestRemovedIn8Warning")
result.stdout.fnmatch_lines(
[
"*PytestRemovedIn8Warning: pytest.skip(msg=...) is now deprecated, "
"use pytest.skip(reason=...) instead",
'*pytest.skip(msg="skippedmsg")*',
]
)
result.assert_outcomes(skipped=1, warnings=1)
def test_fail_with_msg_is_deprecated(self, pytester: Pytester) -> None:
p = pytester.makepyfile(
"""
import pytest
def test_failing_msg():
pytest.fail(msg="failedmsg")
"""
)
result = pytester.runpytest(p, "-Wdefault::pytest.PytestRemovedIn8Warning")
result.stdout.fnmatch_lines(
[
"*PytestRemovedIn8Warning: pytest.fail(msg=...) is now deprecated, "
"use pytest.fail(reason=...) instead",
'*pytest.fail(msg="failedmsg")',
]
)
result.assert_outcomes(failed=1, warnings=1)
def test_exit_with_msg_is_deprecated(self, pytester: Pytester) -> None:
p = pytester.makepyfile(
"""
import pytest
def test_exit_msg():
pytest.exit(msg="exitmsg")
"""
)
result = pytester.runpytest(p, "-Wdefault::pytest.PytestRemovedIn8Warning")
result.stdout.fnmatch_lines(
[
"*PytestRemovedIn8Warning: pytest.exit(msg=...) is now deprecated, "
"use pytest.exit(reason=...) instead",
]
)
result.assert_outcomes(warnings=1)
def test_deprecation_of_cmdline_preparse(pytester: Pytester) -> None:
pytester.makeconftest(
"""
def pytest_cmdline_preparse(config, args):
...
"""
)
result = pytester.runpytest("-Wdefault::pytest.PytestRemovedIn8Warning")
result.stdout.fnmatch_lines(
[
"*PytestRemovedIn8Warning: The pytest_cmdline_preparse hook is deprecated*",
"*Please use pytest_load_initial_conftests hook instead.*",
]
)
def test_node_ctor_fspath_argument_is_deprecated(pytester: Pytester) -> None:
mod = pytester.getmodulecol("")
class MyFile(pytest.File):
def collect(self):
raise NotImplementedError()
with pytest.warns(
pytest.PytestDeprecationWarning,
match=re.escape(
"The (fspath: py.path.local) argument to MyFile is deprecated."
),
):
MyFile.from_parent(
parent=mod.parent,
fspath=legacy_path("bla"),
)
def test_importing_instance_is_deprecated(pytester: Pytester) -> None:
with pytest.warns(
pytest.PytestDeprecationWarning,
match=re.escape("The pytest.Instance collector type is deprecated"),
):
pytest.Instance # type:ignore[attr-defined]
with pytest.warns(
pytest.PytestDeprecationWarning,
match=re.escape("The pytest.Instance collector type is deprecated"),
):
from _pytest.python import Instance # noqa: F401
def test_fixture_disallow_on_marked_functions():
"""Test that applying @pytest.fixture to a marked function warns (#3364)."""
with pytest.warns(
@ -336,62 +133,3 @@ def test_fixture_disallowed_between_marks():
raise NotImplementedError()
assert len(record) == 2 # one for each mark decorator
@pytest.mark.filterwarnings("default")
def test_nose_deprecated_with_setup(pytester: Pytester) -> None:
pytest.importorskip("nose")
pytester.makepyfile(
"""
from nose.tools import with_setup
def setup_fn_no_op():
...
def teardown_fn_no_op():
...
@with_setup(setup_fn_no_op, teardown_fn_no_op)
def test_omits_warnings():
...
"""
)
output = pytester.runpytest("-Wdefault::pytest.PytestRemovedIn8Warning")
message = [
"*PytestRemovedIn8Warning: Support for nose tests is deprecated and will be removed in a future release.",
"*test_nose_deprecated_with_setup.py::test_omits_warnings is using nose method: `setup_fn_no_op` (setup)",
"*PytestRemovedIn8Warning: Support for nose tests is deprecated and will be removed in a future release.",
"*test_nose_deprecated_with_setup.py::test_omits_warnings is using nose method: `teardown_fn_no_op` (teardown)",
]
output.stdout.fnmatch_lines(message)
output.assert_outcomes(passed=1)
@pytest.mark.filterwarnings("default")
def test_nose_deprecated_setup_teardown(pytester: Pytester) -> None:
pytest.importorskip("nose")
pytester.makepyfile(
"""
class Test:
def setup(self):
...
def teardown(self):
...
def test(self):
...
"""
)
output = pytester.runpytest("-Wdefault::pytest.PytestRemovedIn8Warning")
message = [
"*PytestRemovedIn8Warning: Support for nose tests is deprecated and will be removed in a future release.",
"*test_nose_deprecated_setup_teardown.py::Test::test is using nose-specific method: `setup(self)`",
"*To remove this warning, rename it to `setup_method(self)`",
"*PytestRemovedIn8Warning: Support for nose tests is deprecated and will be removed in a future release.",
"*test_nose_deprecated_setup_teardown.py::Test::test is using nose-specific method: `teardown(self)`",
"*To remove this warning, rename it to `teardown_method(self)`",
]
output.stdout.fnmatch_lines(message)
output.assert_outcomes(passed=1)

View File

@ -1253,17 +1253,6 @@ def test_plugin_loading_order(pytester: Pytester) -> None:
assert result.ret == 0
def test_cmdline_processargs_simple(pytester: Pytester) -> None:
pytester.makeconftest(
"""
def pytest_cmdline_preparse(args):
args.append("-h")
"""
)
result = pytester.runpytest("-Wignore::pytest.PytestRemovedIn8Warning")
result.stdout.fnmatch_lines(["*pytest*", "*-h*"])
def test_invalid_options_show_extra_information(pytester: Pytester) -> None:
"""Display extra information when pytest exits due to unrecognized
options in the command-line."""

View File

@ -1,8 +1,8 @@
from pathlib import Path
import pytest
from _pytest.compat import LEGACY_PATH
from _pytest.fixtures import TopRequest
from _pytest.legacypath import LEGACY_PATH
from _pytest.legacypath import TempdirFactory
from _pytest.legacypath import Testdir
@ -15,7 +15,7 @@ def test_item_fspath(pytester: pytest.Pytester) -> None:
items2, hookrec = pytester.inline_genitems(item.nodeid)
(item2,) = items2
assert item2.name == item.name
assert item2.fspath == item.fspath
assert item2.fspath == item.fspath # type: ignore[attr-defined]
assert item2.path == item.path

View File

@ -7,7 +7,6 @@ from typing import Type
import pytest
from _pytest import nodes
from _pytest.compat import legacy_path
from _pytest.outcomes import OutcomeException
from _pytest.pytester import Pytester
from _pytest.warning_types import PytestWarning
@ -69,9 +68,9 @@ def test_subclassing_both_item_and_collector_deprecated(
warnings.simplefilter("error")
class SoWrong(nodes.Item, nodes.File):
def __init__(self, fspath, parent):
def __init__(self, path, parent):
"""Legacy ctor with legacy call # don't wana see"""
super().__init__(fspath, parent)
super().__init__(parent, path)
def collect(self):
raise NotImplementedError()
@ -80,9 +79,7 @@ def test_subclassing_both_item_and_collector_deprecated(
raise NotImplementedError()
with pytest.warns(PytestWarning) as rec:
SoWrong.from_parent(
request.session, fspath=legacy_path(tmp_path / "broken.txt")
)
SoWrong.from_parent(request.session, path=tmp_path / "broken.txt", wrong=10)
messages = [str(x.message) for x in rec]
assert any(
re.search(".*SoWrong.* not using a cooperative constructor.*", x)

View File

@ -1,529 +0,0 @@
import pytest
from _pytest.pytester import Pytester
def setup_module(mod):
mod.nose = pytest.importorskip("nose")
def test_nose_setup(pytester: Pytester) -> None:
p = pytester.makepyfile(
"""
values = []
from nose.tools import with_setup
@with_setup(lambda: values.append(1), lambda: values.append(2))
def test_hello():
assert values == [1]
def test_world():
assert values == [1,2]
test_hello.setup = lambda: values.append(1)
test_hello.teardown = lambda: values.append(2)
"""
)
result = pytester.runpytest(
p, "-p", "nose", "-Wignore::pytest.PytestRemovedIn8Warning"
)
result.assert_outcomes(passed=2)
def test_setup_func_with_setup_decorator() -> None:
from _pytest.nose import call_optional
values = []
class A:
@pytest.fixture(autouse=True)
def f(self):
values.append(1)
call_optional(A(), "f", "A.f")
assert not values
def test_setup_func_not_callable() -> None:
from _pytest.nose import call_optional
class A:
f = 1
call_optional(A(), "f", "A.f")
def test_nose_setup_func(pytester: Pytester) -> None:
p = pytester.makepyfile(
"""
from nose.tools import with_setup
values = []
def my_setup():
a = 1
values.append(a)
def my_teardown():
b = 2
values.append(b)
@with_setup(my_setup, my_teardown)
def test_hello():
print(values)
assert values == [1]
def test_world():
print(values)
assert values == [1,2]
"""
)
result = pytester.runpytest(
p, "-p", "nose", "-Wignore::pytest.PytestRemovedIn8Warning"
)
result.assert_outcomes(passed=2)
def test_nose_setup_func_failure(pytester: Pytester) -> None:
p = pytester.makepyfile(
"""
from nose.tools import with_setup
values = []
my_setup = lambda x: 1
my_teardown = lambda x: 2
@with_setup(my_setup, my_teardown)
def test_hello():
print(values)
assert values == [1]
def test_world():
print(values)
assert values == [1,2]
"""
)
result = pytester.runpytest(
p, "-p", "nose", "-Wignore::pytest.PytestRemovedIn8Warning"
)
result.stdout.fnmatch_lines(["*TypeError: <lambda>()*"])
def test_nose_setup_func_failure_2(pytester: Pytester) -> None:
pytester.makepyfile(
"""
values = []
my_setup = 1
my_teardown = 2
def test_hello():
assert values == []
test_hello.setup = my_setup
test_hello.teardown = my_teardown
"""
)
reprec = pytester.inline_run()
reprec.assertoutcome(passed=1)
def test_nose_setup_partial(pytester: Pytester) -> None:
pytest.importorskip("functools")
p = pytester.makepyfile(
"""
from functools import partial
values = []
def my_setup(x):
a = x
values.append(a)
def my_teardown(x):
b = x
values.append(b)
my_setup_partial = partial(my_setup, 1)
my_teardown_partial = partial(my_teardown, 2)
def test_hello():
print(values)
assert values == [1]
def test_world():
print(values)
assert values == [1,2]
test_hello.setup = my_setup_partial
test_hello.teardown = my_teardown_partial
"""
)
result = pytester.runpytest(
p, "-p", "nose", "-Wignore::pytest.PytestRemovedIn8Warning"
)
result.stdout.fnmatch_lines(["*2 passed*"])
def test_module_level_setup(pytester: Pytester) -> None:
pytester.makepyfile(
"""
from nose.tools import with_setup
items = {}
def setup():
items.setdefault("setup", []).append("up")
def teardown():
items.setdefault("setup", []).append("down")
def setup2():
items.setdefault("setup2", []).append("up")
def teardown2():
items.setdefault("setup2", []).append("down")
def test_setup_module_setup():
assert items["setup"] == ["up"]
def test_setup_module_setup_again():
assert items["setup"] == ["up"]
@with_setup(setup2, teardown2)
def test_local_setup():
assert items["setup"] == ["up"]
assert items["setup2"] == ["up"]
@with_setup(setup2, teardown2)
def test_local_setup_again():
assert items["setup"] == ["up"]
assert items["setup2"] == ["up", "down", "up"]
"""
)
result = pytester.runpytest(
"-p", "nose", "-Wignore::pytest.PytestRemovedIn8Warning"
)
result.stdout.fnmatch_lines(["*4 passed*"])
def test_nose_style_setup_teardown(pytester: Pytester) -> None:
pytester.makepyfile(
"""
values = []
def setup_module():
values.append(1)
def teardown_module():
del values[0]
def test_hello():
assert values == [1]
def test_world():
assert values == [1]
"""
)
result = pytester.runpytest("-p", "nose")
result.stdout.fnmatch_lines(["*2 passed*"])
def test_fixtures_nose_setup_issue8394(pytester: Pytester) -> None:
pytester.makepyfile(
"""
def setup_module():
pass
def teardown_module():
pass
def setup_function(func):
pass
def teardown_function(func):
pass
def test_world():
pass
class Test(object):
def setup_class(cls):
pass
def teardown_class(cls):
pass
def setup_method(self, meth):
pass
def teardown_method(self, meth):
pass
def test_method(self): pass
"""
)
match = "*no docstring available*"
result = pytester.runpytest("--fixtures")
assert result.ret == 0
result.stdout.no_fnmatch_line(match)
result = pytester.runpytest("--fixtures", "-v")
assert result.ret == 0
result.stdout.fnmatch_lines([match, match, match, match])
def test_nose_setup_ordering(pytester: Pytester) -> None:
pytester.makepyfile(
"""
def setup_module(mod):
mod.visited = True
class TestClass(object):
def setup(self):
assert visited
self.visited_cls = True
def test_first(self):
assert visited
assert self.visited_cls
"""
)
result = pytester.runpytest("-Wignore::pytest.PytestRemovedIn8Warning")
result.stdout.fnmatch_lines(["*1 passed*"])
def test_apiwrapper_problem_issue260(pytester: Pytester) -> None:
# this would end up trying a call an optional teardown on the class
# for plain unittests we don't want nose behaviour
pytester.makepyfile(
"""
import unittest
class TestCase(unittest.TestCase):
def setup(self):
#should not be called in unittest testcases
assert 0, 'setup'
def teardown(self):
#should not be called in unittest testcases
assert 0, 'teardown'
def setUp(self):
print('setup')
def tearDown(self):
print('teardown')
def test_fun(self):
pass
"""
)
result = pytester.runpytest()
result.assert_outcomes(passed=1)
def test_setup_teardown_linking_issue265(pytester: Pytester) -> None:
# we accidentally didn't integrate nose setupstate with normal setupstate
# this test ensures that won't happen again
pytester.makepyfile(
'''
import pytest
class TestGeneric(object):
def test_nothing(self):
"""Tests the API of the implementation (for generic and specialized)."""
@pytest.mark.skipif("True", reason=
"Skip tests to check if teardown is skipped as well.")
class TestSkipTeardown(TestGeneric):
def setup(self):
"""Sets up my specialized implementation for $COOL_PLATFORM."""
raise Exception("should not call setup for skipped tests")
def teardown(self):
"""Undoes the setup."""
raise Exception("should not call teardown for skipped tests")
'''
)
reprec = pytester.runpytest()
reprec.assert_outcomes(passed=1, skipped=1)
def test_SkipTest_during_collection(pytester: Pytester) -> None:
p = pytester.makepyfile(
"""
import nose
raise nose.SkipTest("during collection")
def test_failing():
assert False
"""
)
result = pytester.runpytest(p)
result.assert_outcomes(skipped=1, warnings=0)
def test_SkipTest_in_test(pytester: Pytester) -> None:
pytester.makepyfile(
"""
import nose
def test_skipping():
raise nose.SkipTest("in test")
"""
)
reprec = pytester.inline_run()
reprec.assertoutcome(skipped=1)
def test_istest_function_decorator(pytester: Pytester) -> None:
p = pytester.makepyfile(
"""
import nose.tools
@nose.tools.istest
def not_test_prefix():
pass
"""
)
result = pytester.runpytest(p)
result.assert_outcomes(passed=1)
def test_nottest_function_decorator(pytester: Pytester) -> None:
pytester.makepyfile(
"""
import nose.tools
@nose.tools.nottest
def test_prefix():
pass
"""
)
reprec = pytester.inline_run()
assert not reprec.getfailedcollections()
calls = reprec.getreports("pytest_runtest_logreport")
assert not calls
def test_istest_class_decorator(pytester: Pytester) -> None:
p = pytester.makepyfile(
"""
import nose.tools
@nose.tools.istest
class NotTestPrefix(object):
def test_method(self):
pass
"""
)
result = pytester.runpytest(p)
result.assert_outcomes(passed=1)
def test_nottest_class_decorator(pytester: Pytester) -> None:
pytester.makepyfile(
"""
import nose.tools
@nose.tools.nottest
class TestPrefix(object):
def test_method(self):
pass
"""
)
reprec = pytester.inline_run()
assert not reprec.getfailedcollections()
calls = reprec.getreports("pytest_runtest_logreport")
assert not calls
def test_skip_test_with_unicode(pytester: Pytester) -> None:
pytester.makepyfile(
"""\
import unittest
class TestClass():
def test_io(self):
raise unittest.SkipTest('😊')
"""
)
result = pytester.runpytest()
result.stdout.fnmatch_lines(["* 1 skipped *"])
def test_raises(pytester: Pytester) -> None:
pytester.makepyfile(
"""
from nose.tools import raises
@raises(RuntimeError)
def test_raises_runtimeerror():
raise RuntimeError
@raises(Exception)
def test_raises_baseexception_not_caught():
raise BaseException
@raises(BaseException)
def test_raises_baseexception_caught():
raise BaseException
"""
)
result = pytester.runpytest("-vv")
result.stdout.fnmatch_lines(
[
"test_raises.py::test_raises_runtimeerror PASSED*",
"test_raises.py::test_raises_baseexception_not_caught FAILED*",
"test_raises.py::test_raises_baseexception_caught PASSED*",
"*= FAILURES =*",
"*_ test_raises_baseexception_not_caught _*",
"",
"arg = (), kw = {}",
"",
" def newfunc(*arg, **kw):",
" try:",
"> func(*arg, **kw)",
"",
"*/nose/*: ",
"_ _ *",
"",
" @raises(Exception)",
" def test_raises_baseexception_not_caught():",
"> raise BaseException",
"E BaseException",
"",
"test_raises.py:9: BaseException",
"* 1 failed, 2 passed *",
]
)
def test_nose_setup_skipped_if_non_callable(pytester: Pytester) -> None:
"""Regression test for #9391."""
p = pytester.makepyfile(
__init__="",
setup="""
""",
teardown="""
""",
test_it="""
from . import setup, teardown
def test_it():
pass
""",
)
result = pytester.runpytest(p.parent, "-p", "nose")
assert result.ret == 0
@pytest.mark.parametrize("fixture_name", ("teardown", "teardown_class"))
def test_teardown_fixture_not_called_directly(fixture_name, pytester: Pytester) -> None:
"""Regression test for #10597."""
p = pytester.makepyfile(
f"""
import pytest
class TestHello:
@pytest.fixture
def {fixture_name}(self):
yield
def test_hello(self, {fixture_name}):
assert True
"""
)
result = pytester.runpytest(p, "-p", "nose")
assert result.ret == 0

View File

@ -54,9 +54,6 @@ class TestParser:
assert argument.type is str
argument = parseopt.Argument("-t", dest="abc", type=float)
assert argument.type is float
with pytest.warns(DeprecationWarning):
with pytest.raises(KeyError):
argument = parseopt.Argument("-t", dest="abc", type="choice")
argument = parseopt.Argument(
"-t", dest="abc", type=str, choices=["red", "blue"]
)

View File

@ -345,17 +345,9 @@ class TestWarns:
assert str(record[0].message) == "user"
assert str(record[1].message) == "runtime"
def test_record_only_none_deprecated_warn(self) -> None:
# This should become an error when WARNS_NONE_ARG is removed in Pytest 8.0
with warnings.catch_warnings():
warnings.simplefilter("ignore")
with pytest.warns(None) as record: # type: ignore[call-overload]
warnings.warn("user", UserWarning)
warnings.warn("runtime", RuntimeWarning)
assert len(record) == 2
assert str(record[0].message) == "user"
assert str(record[1].message) == "runtime"
def test_record_only_none_type_error(self) -> None:
with pytest.raises(TypeError):
pytest.warns(None) # type: ignore[call-overload]
def test_record_by_subclass(self) -> None:
with pytest.warns(Warning) as record:

View File

@ -1494,54 +1494,6 @@ def test_fail_using_reason_works_ok(pytester: Pytester) -> None:
result.assert_outcomes(failed=1)
def test_fail_fails_with_msg_and_reason(pytester: Pytester) -> None:
p = pytester.makepyfile(
"""
import pytest
def test_fail_both_arguments():
pytest.fail(reason="foo", msg="bar")
"""
)
result = pytester.runpytest(p)
result.stdout.fnmatch_lines(
"*UsageError: Passing both ``reason`` and ``msg`` to pytest.fail(...) is not permitted.*"
)
result.assert_outcomes(failed=1)
def test_skip_fails_with_msg_and_reason(pytester: Pytester) -> None:
p = pytester.makepyfile(
"""
import pytest
def test_skip_both_arguments():
pytest.skip(reason="foo", msg="bar")
"""
)
result = pytester.runpytest(p)
result.stdout.fnmatch_lines(
"*UsageError: Passing both ``reason`` and ``msg`` to pytest.skip(...) is not permitted.*"
)
result.assert_outcomes(failed=1)
def test_exit_with_msg_and_reason_fails(pytester: Pytester) -> None:
p = pytester.makepyfile(
"""
import pytest
def test_exit_both_arguments():
pytest.exit(reason="foo", msg="bar")
"""
)
result = pytester.runpytest(p)
result.stdout.fnmatch_lines(
"*UsageError: cannot pass reason and msg to exit(), `msg` is deprecated, use `reason`.*"
)
result.assert_outcomes(failed=1)
def test_exit_with_reason_works_ok(pytester: Pytester) -> None:
p = pytester.makepyfile(
"""

View File

@ -530,13 +530,11 @@ class TestRmRf:
assert fn.is_file()
# ignored function
with warnings.catch_warnings():
warnings.simplefilter("ignore")
with pytest.warns(None) as warninfo: # type: ignore[call-overload]
with warnings.catch_warnings(record=True) as w:
exc_info4 = PermissionError()
on_rm_rf_error(os.open, str(fn), exc_info4, start_path=tmp_path)
assert fn.is_file()
assert not [x.message for x in warninfo]
assert not [x.message for x in w]
exc_info5 = PermissionError()
on_rm_rf_error(os.unlink, str(fn), exc_info5, start_path=tmp_path)

View File

@ -518,8 +518,7 @@ class TestDeprecationWarningsByDefault:
assert WARNINGS_SUMMARY_HEADER not in result.stdout.str()
# In 8.1, uncomment below and change RemovedIn8 -> RemovedIn9.
# @pytest.mark.skip("not relevant until pytest 9.0")
@pytest.mark.skip("not relevant until pytest 9.0")
@pytest.mark.parametrize("change_default", [None, "ini", "cmdline"])
def test_removed_in_x_warning_as_error(pytester: Pytester, change_default) -> None:
"""This ensures that PytestRemovedInXWarnings raised by pytest are turned into errors.
@ -531,7 +530,7 @@ def test_removed_in_x_warning_as_error(pytester: Pytester, change_default) -> No
"""
import warnings, pytest
def test():
warnings.warn(pytest.PytestRemovedIn8Warning("some warning"))
warnings.warn(pytest.PytestRemovedIn9Warning("some warning"))
"""
)
if change_default == "ini":
@ -539,12 +538,12 @@ def test_removed_in_x_warning_as_error(pytester: Pytester, change_default) -> No
"""
[pytest]
filterwarnings =
ignore::pytest.PytestRemovedIn8Warning
ignore::pytest.PytestRemovedIn9Warning
"""
)
args = (
("-Wignore::pytest.PytestRemovedIn8Warning",)
("-Wignore::pytest.PytestRemovedIn9Warning",)
if change_default == "cmdline"
else ()
)