Merge pull request #11757 from bluetech/rm-removed-in-8
Remove pytest 8 deprecations
This commit is contained in:
commit
f017df443a
|
@ -97,8 +97,8 @@ Features
|
||||||
- `Modular fixtures <https://docs.pytest.org/en/stable/explanation/fixtures.html>`_ for
|
- `Modular fixtures <https://docs.pytest.org/en/stable/explanation/fixtures.html>`_ for
|
||||||
managing small or parametrized long-lived test resources
|
managing small or parametrized long-lived test resources
|
||||||
|
|
||||||
- Can run `unittest <https://docs.pytest.org/en/stable/how-to/unittest.html>`_ (or trial),
|
- 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
|
test suites out of the box
|
||||||
|
|
||||||
- Python 3.8+ or PyPy3
|
- Python 3.8+ or PyPy3
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ Partner projects, sign up here! (by 22 March)
|
||||||
What does it mean to "adopt pytest"?
|
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:
|
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.
|
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
|
.. _unittest: unittest.html
|
||||||
.. _assert: assert.html
|
.. _assert: assert.html
|
||||||
.. _pycmd: https://bitbucket.org/hpk42/pycmd/overview
|
.. _pycmd: https://bitbucket.org/hpk42/pycmd/overview
|
||||||
|
|
|
@ -227,7 +227,7 @@ These are breaking changes where deprecation was not possible.
|
||||||
Deprecations
|
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.
|
- `#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.
|
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.
|
See :ref:`the deprecation note <cmdline-preparse-deprecated>` for full details.
|
||||||
|
|
||||||
|
|
|
@ -199,7 +199,6 @@ nitpick_ignore = [
|
||||||
("py:class", "_tracing.TagTracerSub"),
|
("py:class", "_tracing.TagTracerSub"),
|
||||||
("py:class", "warnings.WarningMessage"),
|
("py:class", "warnings.WarningMessage"),
|
||||||
# Undocumented type aliases
|
# Undocumented type aliases
|
||||||
("py:class", "LEGACY_PATH"),
|
|
||||||
("py:class", "_PluggyPlugin"),
|
("py:class", "_PluggyPlugin"),
|
||||||
# TypeVars
|
# TypeVars
|
||||||
("py:class", "_pytest._code.code.E"),
|
("py:class", "_pytest._code.code.E"),
|
||||||
|
|
|
@ -44,7 +44,6 @@ How-to guides
|
||||||
|
|
||||||
how-to/existingtestsuite
|
how-to/existingtestsuite
|
||||||
how-to/unittest
|
how-to/unittest
|
||||||
how-to/nose
|
|
||||||
how-to/xunit_setup
|
how-to/xunit_setup
|
||||||
|
|
||||||
how-to/bash-completion
|
how-to/bash-completion
|
||||||
|
|
|
@ -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>`.
|
: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:
|
.. _nose-deprecation:
|
||||||
|
|
||||||
Support for tests written for nose
|
Support for tests written for nose
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. deprecated:: 7.2
|
.. deprecated:: 7.2
|
||||||
|
.. versionremoved:: 8.0
|
||||||
|
|
||||||
Support for running tests written for `nose <https://nose.readthedocs.io/en/latest/>`__ is now deprecated.
|
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
|
.. _`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``
|
Passing ``msg=`` to ``pytest.skip``, ``pytest.fail`` or ``pytest.exit``
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. deprecated:: 7.0
|
.. deprecated:: 7.0
|
||||||
|
.. versionremoved:: 8.0
|
||||||
|
|
||||||
Passing the keyword argument ``msg`` to :func:`pytest.skip`, :func:`pytest.fail` or :func:`pytest.exit`
|
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
|
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")
|
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
|
Implementing the ``pytest_cmdline_preparse`` hook
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. deprecated:: 7.0
|
.. 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.
|
Implement the :hook:`pytest_load_initial_conftests` hook instead.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
@ -329,171 +505,6 @@ Implement the :hook:`pytest_load_initial_conftests` hook instead.
|
||||||
) -> None:
|
) -> 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
|
Collection changes in pytest 8
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
|
@ -18,7 +18,6 @@ For basic examples, see
|
||||||
- :ref:`Fixtures <fixtures>` for basic fixture/setup examples
|
- :ref:`Fixtures <fixtures>` for basic fixture/setup examples
|
||||||
- :ref:`parametrize` for basic test function parametrization
|
- :ref:`parametrize` for basic test function parametrization
|
||||||
- :ref:`unittest` for basic unittest integration
|
- :ref:`unittest` for basic unittest integration
|
||||||
- :ref:`noseintegration` for basic nosetests integration
|
|
||||||
|
|
||||||
The following examples aim at various use cases you might encounter.
|
The following examples aim at various use cases you might encounter.
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,7 @@ style of setup/teardown functions:
|
||||||
In addition, pytest continues to support :ref:`xunitsetup`. You can mix
|
In addition, pytest continues to support :ref:`xunitsetup`. You can mix
|
||||||
both styles, moving incrementally from classic to new style, as you
|
both styles, moving incrementally from classic to new style, as you
|
||||||
prefer. You can also start out from existing :ref:`unittest.TestCase
|
prefer. You can also start out from existing :ref:`unittest.TestCase
|
||||||
style <unittest.TestCase>` or :ref:`nose based <nosestyle>` projects.
|
style <unittest.TestCase>`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,8 @@ How to use pytest with an existing test suite
|
||||||
==============================================
|
==============================================
|
||||||
|
|
||||||
Pytest can be used with most existing test suites, but its
|
Pytest can be used with most existing test suites, but its
|
||||||
behavior differs from other test runners such as :ref:`nose <noseintegration>` or
|
behavior differs from other test runners such as Python's
|
||||||
Python's default unittest framework.
|
default unittest framework.
|
||||||
|
|
||||||
Before using this section you will want to :ref:`install pytest <getstarted>`.
|
Before using this section you will want to :ref:`install pytest <getstarted>`.
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,6 @@ pytest and other test systems
|
||||||
|
|
||||||
existingtestsuite
|
existingtestsuite
|
||||||
unittest
|
unittest
|
||||||
nose
|
|
||||||
xunit_setup
|
xunit_setup
|
||||||
|
|
||||||
pytest development environment
|
pytest development environment
|
||||||
|
|
|
@ -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/
|
|
|
@ -74,7 +74,7 @@ Features
|
||||||
|
|
||||||
- :ref:`Modular fixtures <fixture>` for managing small or parametrized long-lived test resources
|
- :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
|
- Python 3.8+ or PyPy 3
|
||||||
|
|
||||||
|
|
|
@ -643,8 +643,6 @@ Bootstrapping hooks called for plugins registered early enough (internal and set
|
||||||
|
|
||||||
.. hook:: pytest_load_initial_conftests
|
.. hook:: pytest_load_initial_conftests
|
||||||
.. autofunction:: pytest_load_initial_conftests
|
.. autofunction:: pytest_load_initial_conftests
|
||||||
.. hook:: pytest_cmdline_preparse
|
|
||||||
.. autofunction:: pytest_cmdline_preparse
|
|
||||||
.. hook:: pytest_cmdline_parse
|
.. hook:: pytest_cmdline_parse
|
||||||
.. autofunction:: pytest_cmdline_parse
|
.. autofunction:: pytest_cmdline_parse
|
||||||
.. hook:: pytest_cmdline_main
|
.. hook:: pytest_cmdline_main
|
||||||
|
@ -1209,9 +1207,6 @@ Custom warnings generated in some situations such as improper usage or deprecate
|
||||||
.. autoclass:: pytest.PytestReturnNotNoneWarning
|
.. autoclass:: pytest.PytestReturnNotNoneWarning
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
.. autoclass:: pytest.PytestRemovedIn8Warning
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
.. autoclass:: pytest.PytestRemovedIn9Warning
|
.. autoclass:: pytest.PytestRemovedIn9Warning
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,6 @@ testing =
|
||||||
attrs>=19.2.0
|
attrs>=19.2.0
|
||||||
hypothesis>=3.56
|
hypothesis>=3.56
|
||||||
mock
|
mock
|
||||||
nose
|
|
||||||
pygments>=2.7.2
|
pygments>=2.7.2
|
||||||
requests
|
requests
|
||||||
setuptools
|
setuptools
|
||||||
|
|
|
@ -16,25 +16,10 @@ from typing import Final
|
||||||
from typing import NoReturn
|
from typing import NoReturn
|
||||||
from typing import TypeVar
|
from typing import TypeVar
|
||||||
|
|
||||||
import py
|
|
||||||
|
|
||||||
|
|
||||||
_T = TypeVar("_T")
|
_T = TypeVar("_T")
|
||||||
_S = TypeVar("_S")
|
_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
|
# fmt: off
|
||||||
# Singleton type for NOTSET, as described in:
|
# Singleton type for NOTSET, as described in:
|
||||||
|
|
|
@ -38,7 +38,6 @@ from typing import Type
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import pluggy
|
|
||||||
from pluggy import HookimplMarker
|
from pluggy import HookimplMarker
|
||||||
from pluggy import HookimplOpts
|
from pluggy import HookimplOpts
|
||||||
from pluggy import HookspecMarker
|
from pluggy import HookspecMarker
|
||||||
|
@ -48,7 +47,6 @@ from pluggy import PluginManager
|
||||||
import _pytest._code
|
import _pytest._code
|
||||||
import _pytest.deprecated
|
import _pytest.deprecated
|
||||||
import _pytest.hookspec
|
import _pytest.hookspec
|
||||||
from .compat import PathAwareHookProxy
|
|
||||||
from .exceptions import PrintHelp as PrintHelp
|
from .exceptions import PrintHelp as PrintHelp
|
||||||
from .exceptions import UsageError as UsageError
|
from .exceptions import UsageError as UsageError
|
||||||
from .findpaths import determine_setup
|
from .findpaths import determine_setup
|
||||||
|
@ -252,7 +250,6 @@ default_plugins = essential_plugins + (
|
||||||
"monkeypatch",
|
"monkeypatch",
|
||||||
"recwarn",
|
"recwarn",
|
||||||
"pastebin",
|
"pastebin",
|
||||||
"nose",
|
|
||||||
"assertion",
|
"assertion",
|
||||||
"junitxml",
|
"junitxml",
|
||||||
"doctest",
|
"doctest",
|
||||||
|
@ -1009,7 +1006,7 @@ class Config:
|
||||||
self._store = self.stash
|
self._store = self.stash
|
||||||
|
|
||||||
self.trace = self.pluginmanager.trace.root.get("config")
|
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._inicache: Dict[str, Any] = {}
|
||||||
self._override_ini: Sequence[str] = ()
|
self._override_ini: Sequence[str] = ()
|
||||||
self._opt2dest: Dict[str, str] = {}
|
self._opt2dest: Dict[str, str] = {}
|
||||||
|
@ -1319,11 +1316,6 @@ class Config:
|
||||||
self._validate_plugins()
|
self._validate_plugins()
|
||||||
self._warn_about_skipped_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.known_args_namespace.confcutdir is None:
|
||||||
if self.inipath is not None:
|
if self.inipath is not None:
|
||||||
confcutdir = str(self.inipath.parent)
|
confcutdir = str(self.inipath.parent)
|
||||||
|
@ -1432,8 +1424,6 @@ class Config:
|
||||||
kwargs=dict(pluginmanager=self.pluginmanager)
|
kwargs=dict(pluginmanager=self.pluginmanager)
|
||||||
)
|
)
|
||||||
self._preparse(args, addopts=addopts)
|
self._preparse(args, addopts=addopts)
|
||||||
# XXX deprecated hook:
|
|
||||||
self.hook.pytest_cmdline_preparse(config=self, args=args)
|
|
||||||
self._parser.after_preparse = True # type: ignore
|
self._parser.after_preparse = True # type: ignore
|
||||||
try:
|
try:
|
||||||
args = self._parser.parse_setoption(
|
args = self._parser.parse_setoption(
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
|
||||||
from gettext import gettext
|
from gettext import gettext
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
@ -19,9 +18,6 @@ from typing import Union
|
||||||
|
|
||||||
import _pytest._io
|
import _pytest._io
|
||||||
from _pytest.config.exceptions import UsageError
|
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
|
from _pytest.deprecated import check_ispytest
|
||||||
|
|
||||||
FILE_OR_DIR = "file_or_dir"
|
FILE_OR_DIR = "file_or_dir"
|
||||||
|
@ -259,39 +255,15 @@ class Argument:
|
||||||
https://docs.python.org/3/library/optparse.html#optparse-standard-option-types
|
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:
|
def __init__(self, *names: str, **attrs: Any) -> None:
|
||||||
"""Store params in private vars for use in add_argument."""
|
"""Store params in private vars for use in add_argument."""
|
||||||
self._attrs = attrs
|
self._attrs = attrs
|
||||||
self._short_opts: List[str] = []
|
self._short_opts: List[str] = []
|
||||||
self._long_opts: List[str] = []
|
self._long_opts: List[str] = []
|
||||||
if "%default" in (attrs.get("help") or ""):
|
|
||||||
warnings.warn(ARGUMENT_PERCENT_DEFAULT, stacklevel=3)
|
|
||||||
try:
|
try:
|
||||||
typ = attrs["type"]
|
self.type = attrs["type"]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
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:
|
try:
|
||||||
# Attribute existence is tested in Config._processopt.
|
# Attribute existence is tested in Config._processopt.
|
||||||
self.default = attrs["default"]
|
self.default = attrs["default"]
|
||||||
|
@ -322,11 +294,6 @@ class Argument:
|
||||||
self._attrs[attr] = getattr(self, attr)
|
self._attrs[attr] = getattr(self, attr)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
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
|
return self._attrs
|
||||||
|
|
||||||
def _set_opt_strings(self, opts: Sequence[str]) -> None:
|
def _set_opt_strings(self, opts: Sequence[str]) -> None:
|
||||||
|
|
|
@ -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]
|
|
|
@ -11,7 +11,6 @@ in case of warnings which need to format their messages.
|
||||||
from warnings import warn
|
from warnings import warn
|
||||||
|
|
||||||
from _pytest.warning_types import PytestDeprecationWarning
|
from _pytest.warning_types import PytestDeprecationWarning
|
||||||
from _pytest.warning_types import PytestRemovedIn8Warning
|
|
||||||
from _pytest.warning_types import PytestRemovedIn9Warning
|
from _pytest.warning_types import PytestRemovedIn9Warning
|
||||||
from _pytest.warning_types import UnformattedWarning
|
from _pytest.warning_types import UnformattedWarning
|
||||||
|
|
||||||
|
@ -23,21 +22,6 @@ DEPRECATED_EXTERNAL_PLUGINS = {
|
||||||
"pytest_faulthandler",
|
"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.
|
# 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".
|
# * If you're in the future: "could have been".
|
||||||
|
@ -46,74 +30,10 @@ YIELD_FIXTURE = PytestDeprecationWarning(
|
||||||
"Use @pytest.fixture instead; they are the same."
|
"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.
|
# This deprecation is never really meant to be removed.
|
||||||
PRIVATE = PytestDeprecationWarning("A private pytest class or function was used.")
|
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(
|
HOOK_LEGACY_MARKING = UnformattedWarning(
|
||||||
PytestDeprecationWarning,
|
PytestDeprecationWarning,
|
||||||
"The hook{type} {fullname} uses old-style configuration options (marks or attributes).\n"
|
"The hook{type} {fullname} uses old-style configuration options (marks or attributes).\n"
|
||||||
|
|
|
@ -13,8 +13,6 @@ from typing import Union
|
||||||
|
|
||||||
from pluggy import HookspecMarker
|
from pluggy import HookspecMarker
|
||||||
|
|
||||||
from _pytest.deprecated import WARNING_CMDLINE_PREPARSE_HOOK
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
import pdb
|
import pdb
|
||||||
import warnings
|
import warnings
|
||||||
|
@ -42,7 +40,6 @@ if TYPE_CHECKING:
|
||||||
from _pytest.runner import CallInfo
|
from _pytest.runner import CallInfo
|
||||||
from _pytest.terminal import TerminalReporter
|
from _pytest.terminal import TerminalReporter
|
||||||
from _pytest.terminal import TestShortLogReport
|
from _pytest.terminal import TestShortLogReport
|
||||||
from _pytest.compat import LEGACY_PATH
|
|
||||||
|
|
||||||
|
|
||||||
hookspec = HookspecMarker("pytest")
|
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)
|
@hookspec(firstresult=True)
|
||||||
def pytest_cmdline_main(config: "Config") -> Optional[Union["ExitCode", int]]:
|
def pytest_cmdline_main(config: "Config") -> Optional[Union["ExitCode", int]]:
|
||||||
"""Called for performing the main command line action. The default
|
"""Called for performing the main command line action. The default
|
||||||
|
@ -263,9 +245,7 @@ def pytest_collection_finish(session: "Session") -> None:
|
||||||
|
|
||||||
|
|
||||||
@hookspec(firstresult=True)
|
@hookspec(firstresult=True)
|
||||||
def pytest_ignore_collect(
|
def pytest_ignore_collect(collection_path: Path, config: "Config") -> Optional[bool]:
|
||||||
collection_path: Path, path: "LEGACY_PATH", config: "Config"
|
|
||||||
) -> Optional[bool]:
|
|
||||||
"""Return True to prevent considering this path for collection.
|
"""Return True to prevent considering this path for collection.
|
||||||
|
|
||||||
This hook is consulted for all files and directories prior to calling
|
This hook is consulted for all files and directories prior to calling
|
||||||
|
@ -279,8 +259,10 @@ def pytest_ignore_collect(
|
||||||
|
|
||||||
.. versionchanged:: 7.0.0
|
.. versionchanged:: 7.0.0
|
||||||
The ``collection_path`` parameter was added as a :class:`pathlib.Path`
|
The ``collection_path`` parameter was added as a :class:`pathlib.Path`
|
||||||
equivalent of the ``path`` parameter. The ``path`` parameter
|
equivalent of the ``path`` parameter.
|
||||||
has been deprecated.
|
|
||||||
|
.. 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(
|
def pytest_collect_file(file_path: Path, parent: "Collector") -> "Optional[Collector]":
|
||||||
file_path: Path, path: "LEGACY_PATH", parent: "Collector"
|
|
||||||
) -> "Optional[Collector]":
|
|
||||||
"""Create a :class:`~pytest.Collector` for the given path, or None if not relevant.
|
"""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
|
For best results, the returned collector should be a subclass of
|
||||||
|
@ -320,8 +300,10 @@ def pytest_collect_file(
|
||||||
|
|
||||||
.. versionchanged:: 7.0.0
|
.. versionchanged:: 7.0.0
|
||||||
The ``file_path`` parameter was added as a :class:`pathlib.Path`
|
The ``file_path`` parameter was added as a :class:`pathlib.Path`
|
||||||
equivalent of the ``path`` parameter. The ``path`` parameter
|
equivalent of the ``path`` parameter.
|
||||||
has been deprecated.
|
|
||||||
|
.. 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)
|
@hookspec(firstresult=True)
|
||||||
def pytest_pycollect_makemodule(
|
def pytest_pycollect_makemodule(module_path: Path, parent) -> Optional["Module"]:
|
||||||
module_path: Path, path: "LEGACY_PATH", parent
|
|
||||||
) -> Optional["Module"]:
|
|
||||||
"""Return a :class:`pytest.Module` collector or None for the given path.
|
"""Return a :class:`pytest.Module` collector or None for the given path.
|
||||||
|
|
||||||
This hook will be called for each matching test module 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`
|
The ``module_path`` parameter was added as a :class:`pathlib.Path`
|
||||||
equivalent of the ``path`` parameter.
|
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]
|
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]]:
|
) -> Union[str, List[str]]:
|
||||||
"""Return a string or list of strings to be displayed as header info for terminal reporting.
|
"""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
|
.. versionchanged:: 7.0.0
|
||||||
The ``start_path`` parameter was added as a :class:`pathlib.Path`
|
The ``start_path`` parameter was added as a :class:`pathlib.Path`
|
||||||
equivalent of the ``startdir`` parameter. The ``startdir`` parameter
|
equivalent of the ``startdir`` parameter.
|
||||||
has been deprecated.
|
|
||||||
|
.. versionchanged:: 8.0.0
|
||||||
|
The ``startdir`` parameter has been removed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def pytest_report_collectionfinish( # type:ignore[empty-body]
|
def pytest_report_collectionfinish( # type:ignore[empty-body]
|
||||||
config: "Config",
|
config: "Config",
|
||||||
start_path: Path,
|
start_path: Path,
|
||||||
startdir: "LEGACY_PATH",
|
|
||||||
items: Sequence["Item"],
|
items: Sequence["Item"],
|
||||||
) -> Union[str, List[str]]:
|
) -> Union[str, List[str]]:
|
||||||
"""Return a string or list of strings to be displayed after collection
|
"""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
|
.. versionchanged:: 7.0.0
|
||||||
The ``start_path`` parameter was added as a :class:`pathlib.Path`
|
The ``start_path`` parameter was added as a :class:`pathlib.Path`
|
||||||
equivalent of the ``startdir`` parameter. The ``startdir`` parameter
|
equivalent of the ``startdir`` parameter.
|
||||||
has been deprecated.
|
|
||||||
|
.. versionchanged:: 8.0.0
|
||||||
|
The ``startdir`` parameter has been removed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
"""Add backward compatibility support for the legacy py path type."""
|
"""Add backward compatibility support for the legacy py path type."""
|
||||||
import dataclasses
|
import dataclasses
|
||||||
|
import os
|
||||||
import shlex
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
@ -12,9 +13,8 @@ from typing import Union
|
||||||
|
|
||||||
from iniconfig import SectionWrapper
|
from iniconfig import SectionWrapper
|
||||||
|
|
||||||
|
import py
|
||||||
from _pytest.cacheprovider import Cache
|
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 Config
|
||||||
from _pytest.config import hookimpl
|
from _pytest.config import hookimpl
|
||||||
from _pytest.config import PytestPluginManager
|
from _pytest.config import PytestPluginManager
|
||||||
|
@ -36,6 +36,20 @@ if TYPE_CHECKING:
|
||||||
import pexpect
|
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
|
@final
|
||||||
class Testdir:
|
class Testdir:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -33,7 +33,6 @@ from _pytest.config import hookimpl
|
||||||
from _pytest.config import PytestPluginManager
|
from _pytest.config import PytestPluginManager
|
||||||
from _pytest.config import UsageError
|
from _pytest.config import UsageError
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
from _pytest.config.compat import PathAwareHookProxy
|
|
||||||
from _pytest.fixtures import FixtureManager
|
from _pytest.fixtures import FixtureManager
|
||||||
from _pytest.outcomes import exit
|
from _pytest.outcomes import exit
|
||||||
from _pytest.pathlib import absolutepath
|
from _pytest.pathlib import absolutepath
|
||||||
|
@ -542,7 +541,6 @@ class Session(nodes.Collector):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
name="",
|
name="",
|
||||||
path=config.rootpath,
|
path=config.rootpath,
|
||||||
fspath=None,
|
|
||||||
parent=None,
|
parent=None,
|
||||||
config=config,
|
config=config,
|
||||||
session=self,
|
session=self,
|
||||||
|
@ -644,7 +642,7 @@ class Session(nodes.Collector):
|
||||||
proxy: pluggy.HookRelay
|
proxy: pluggy.HookRelay
|
||||||
if remove_mods:
|
if remove_mods:
|
||||||
# One or more conftests are not in use at this path.
|
# 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:
|
else:
|
||||||
# All plugins are active for this fspath.
|
# All plugins are active for this fspath.
|
||||||
proxy = self.config.hook
|
proxy = self.config.hook
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import abc
|
import abc
|
||||||
import os
|
import os
|
||||||
import pathlib
|
|
||||||
import warnings
|
import warnings
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
from inspect import signature
|
from inspect import signature
|
||||||
|
@ -28,12 +27,8 @@ from _pytest._code import getfslineno
|
||||||
from _pytest._code.code import ExceptionInfo
|
from _pytest._code.code import ExceptionInfo
|
||||||
from _pytest._code.code import TerminalRepr
|
from _pytest._code.code import TerminalRepr
|
||||||
from _pytest._code.code import Traceback
|
from _pytest._code.code import Traceback
|
||||||
from _pytest.compat import LEGACY_PATH
|
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
from _pytest.config import ConftestImportFailure
|
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 Mark
|
||||||
from _pytest.mark.structures import MarkDecorator
|
from _pytest.mark.structures import MarkDecorator
|
||||||
from _pytest.mark.structures import NodeKeywords
|
from _pytest.mark.structures import NodeKeywords
|
||||||
|
@ -99,27 +94,6 @@ def iterparentnodeids(nodeid: str) -> Iterator[str]:
|
||||||
yield nodeid
|
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")
|
_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
|
``Collector``\'s are the internal nodes of the tree, and ``Item``\'s are the
|
||||||
leaf nodes.
|
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.
|
# Use __slots__ to make attribute access faster.
|
||||||
# Note that __dict__ is still available.
|
# Note that __dict__ is still available.
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
|
@ -201,7 +167,6 @@ class Node(abc.ABC, metaclass=NodeMeta):
|
||||||
parent: "Optional[Node]" = None,
|
parent: "Optional[Node]" = None,
|
||||||
config: Optional[Config] = None,
|
config: Optional[Config] = None,
|
||||||
session: "Optional[Session]" = None,
|
session: "Optional[Session]" = None,
|
||||||
fspath: Optional[LEGACY_PATH] = None,
|
|
||||||
path: Optional[Path] = None,
|
path: Optional[Path] = None,
|
||||||
nodeid: Optional[str] = None,
|
nodeid: Optional[str] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -227,10 +192,11 @@ class Node(abc.ABC, metaclass=NodeMeta):
|
||||||
raise TypeError("session or parent must be provided")
|
raise TypeError("session or parent must be provided")
|
||||||
self.session = parent.session
|
self.session = parent.session
|
||||||
|
|
||||||
if path is None and fspath is None:
|
if path is None:
|
||||||
path = getattr(parent, "path", None)
|
path = getattr(parent, "path", None)
|
||||||
|
assert path is not None
|
||||||
#: Filesystem path where this node was collected from (can be 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.
|
# The explicit annotation is to avoid publicly exposing NodeKeywords.
|
||||||
#: Keywords/markers collected from all scopes.
|
#: Keywords/markers collected from all scopes.
|
||||||
|
@ -596,7 +562,6 @@ class FSCollector(Collector, abc.ABC):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
fspath: Optional[LEGACY_PATH] = None,
|
|
||||||
path_or_parent: Optional[Union[Path, Node]] = None,
|
path_or_parent: Optional[Union[Path, Node]] = None,
|
||||||
path: Optional[Path] = None,
|
path: Optional[Path] = None,
|
||||||
name: Optional[str] = None,
|
name: Optional[str] = None,
|
||||||
|
@ -612,8 +577,8 @@ class FSCollector(Collector, abc.ABC):
|
||||||
elif isinstance(path_or_parent, Path):
|
elif isinstance(path_or_parent, Path):
|
||||||
assert path is None
|
assert path is None
|
||||||
path = path_or_parent
|
path = path_or_parent
|
||||||
|
assert path is not None
|
||||||
|
|
||||||
path = _imply_path(type(self), path, fspath=fspath)
|
|
||||||
if name is None:
|
if name is None:
|
||||||
name = path.name
|
name = path.name
|
||||||
if parent is not None and parent.path != path:
|
if parent is not None and parent.path != path:
|
||||||
|
@ -653,20 +618,11 @@ class FSCollector(Collector, abc.ABC):
|
||||||
cls,
|
cls,
|
||||||
parent,
|
parent,
|
||||||
*,
|
*,
|
||||||
fspath: Optional[LEGACY_PATH] = None,
|
|
||||||
path: Optional[Path] = None,
|
path: Optional[Path] = None,
|
||||||
**kw,
|
**kw,
|
||||||
):
|
):
|
||||||
"""The public constructor."""
|
"""The public constructor."""
|
||||||
return super().from_parent(parent=parent, fspath=fspath, path=path, **kw)
|
return super().from_parent(parent=parent, 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)
|
|
||||||
|
|
||||||
|
|
||||||
class File(FSCollector, abc.ABC):
|
class File(FSCollector, abc.ABC):
|
||||||
|
|
|
@ -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
|
|
|
@ -1,7 +1,6 @@
|
||||||
"""Exception classes and constants handling test outcomes as well as
|
"""Exception classes and constants handling test outcomes as well as
|
||||||
functions creating them."""
|
functions creating them."""
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
from typing import cast
|
from typing import cast
|
||||||
|
@ -11,8 +10,6 @@ from typing import Protocol
|
||||||
from typing import Type
|
from typing import Type
|
||||||
from typing import TypeVar
|
from typing import TypeVar
|
||||||
|
|
||||||
from _pytest.deprecated import KEYWORD_MSG_ARG
|
|
||||||
|
|
||||||
|
|
||||||
class OutcomeException(BaseException):
|
class OutcomeException(BaseException):
|
||||||
"""OutcomeException and its subclass instances indicate and contain info
|
"""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)
|
@_with_exception(Exit)
|
||||||
def exit(
|
def exit(
|
||||||
reason: str = "", returncode: Optional[int] = None, *, msg: Optional[str] = None
|
reason: str = "",
|
||||||
|
returncode: Optional[int] = None,
|
||||||
) -> NoReturn:
|
) -> NoReturn:
|
||||||
"""Exit testing process.
|
"""Exit testing process.
|
||||||
|
|
||||||
|
@ -113,28 +111,16 @@ def exit(
|
||||||
|
|
||||||
:param returncode:
|
:param returncode:
|
||||||
Return code to be used when exiting pytest. None means the same as ``0`` (no error), same as :func:`sys.exit`.
|
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
|
__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)
|
raise Exit(reason, returncode)
|
||||||
|
|
||||||
|
|
||||||
@_with_exception(Skipped)
|
@_with_exception(Skipped)
|
||||||
def skip(
|
def skip(
|
||||||
reason: str = "", *, allow_module_level: bool = False, msg: Optional[str] = None
|
reason: str = "",
|
||||||
|
*,
|
||||||
|
allow_module_level: bool = False,
|
||||||
) -> NoReturn:
|
) -> NoReturn:
|
||||||
"""Skip an executing test with the given message.
|
"""Skip an executing test with the given message.
|
||||||
|
|
||||||
|
@ -153,9 +139,6 @@ def skip(
|
||||||
|
|
||||||
Defaults to False.
|
Defaults to False.
|
||||||
|
|
||||||
:param msg:
|
|
||||||
Same as ``reason``, but deprecated. Will be removed in a future version, use ``reason`` instead.
|
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
It is better to use the :ref:`pytest.mark.skipif ref` marker when
|
It is better to use the :ref:`pytest.mark.skipif ref` marker when
|
||||||
possible to declare a test to be skipped under certain conditions
|
possible to declare a test to be skipped under certain conditions
|
||||||
|
@ -164,12 +147,11 @@ def skip(
|
||||||
to skip a doctest statically.
|
to skip a doctest statically.
|
||||||
"""
|
"""
|
||||||
__tracebackhide__ = True
|
__tracebackhide__ = True
|
||||||
reason = _resolve_msg_to_reason("skip", reason, msg)
|
|
||||||
raise Skipped(msg=reason, allow_module_level=allow_module_level)
|
raise Skipped(msg=reason, allow_module_level=allow_module_level)
|
||||||
|
|
||||||
|
|
||||||
@_with_exception(Failed)
|
@_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.
|
"""Explicitly fail an executing test with the given message.
|
||||||
|
|
||||||
:param reason:
|
:param reason:
|
||||||
|
@ -178,51 +160,11 @@ def fail(reason: str = "", pytrace: bool = True, msg: Optional[str] = None) -> N
|
||||||
:param pytrace:
|
:param pytrace:
|
||||||
If False, msg represents the full failure information and no
|
If False, msg represents the full failure information and no
|
||||||
python traceback will be reported.
|
python traceback will be reported.
|
||||||
|
|
||||||
:param msg:
|
|
||||||
Same as ``reason``, but deprecated. Will be removed in a future version, use ``reason`` instead.
|
|
||||||
"""
|
"""
|
||||||
__tracebackhide__ = True
|
__tracebackhide__ = True
|
||||||
reason = _resolve_msg_to_reason("fail", reason, msg)
|
|
||||||
raise Failed(msg=reason, pytrace=pytrace)
|
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):
|
class XFailed(Failed):
|
||||||
"""Raised from an explicit call to pytest.xfail()."""
|
"""Raised from an explicit call to pytest.xfail()."""
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,6 @@ from _pytest.compat import getimfunc
|
||||||
from _pytest.compat import getlocation
|
from _pytest.compat import getlocation
|
||||||
from _pytest.compat import is_async_function
|
from _pytest.compat import is_async_function
|
||||||
from _pytest.compat import is_generator
|
from _pytest.compat import is_generator
|
||||||
from _pytest.compat import LEGACY_PATH
|
|
||||||
from _pytest.compat import NOTSET
|
from _pytest.compat import NOTSET
|
||||||
from _pytest.compat import safe_getattr
|
from _pytest.compat import safe_getattr
|
||||||
from _pytest.compat import safe_isclass
|
from _pytest.compat import safe_isclass
|
||||||
|
@ -57,8 +56,6 @@ from _pytest.config import ExitCode
|
||||||
from _pytest.config import hookimpl
|
from _pytest.config import hookimpl
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
from _pytest.deprecated import check_ispytest
|
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 FixtureDef
|
||||||
from _pytest.fixtures import FixtureRequest
|
from _pytest.fixtures import FixtureRequest
|
||||||
from _pytest.fixtures import FuncFixtureInfo
|
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
|
Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with
|
||||||
other fixtures (#517).
|
other fixtures (#517).
|
||||||
"""
|
"""
|
||||||
has_nose = self.config.pluginmanager.has_plugin("nose")
|
|
||||||
setup_module = _get_first_non_fixture_func(
|
setup_module = _get_first_non_fixture_func(
|
||||||
self.obj, ("setUpModule", "setup_module")
|
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(
|
teardown_module = _get_first_non_fixture_func(
|
||||||
self.obj, ("tearDownModule", "teardown_module")
|
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:
|
if setup_module is None and teardown_module is None:
|
||||||
return
|
return
|
||||||
|
@ -685,7 +671,6 @@ class Package(nodes.Directory):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
fspath: Optional[LEGACY_PATH],
|
|
||||||
parent: nodes.Collector,
|
parent: nodes.Collector,
|
||||||
# NOTE: following args are unused:
|
# NOTE: following args are unused:
|
||||||
config=None,
|
config=None,
|
||||||
|
@ -697,7 +682,6 @@ class Package(nodes.Directory):
|
||||||
# super().__init__(self, fspath, parent=parent)
|
# super().__init__(self, fspath, parent=parent)
|
||||||
session = parent.session
|
session = parent.session
|
||||||
super().__init__(
|
super().__init__(
|
||||||
fspath=fspath,
|
|
||||||
path=path,
|
path=path,
|
||||||
parent=parent,
|
parent=parent,
|
||||||
config=config,
|
config=config,
|
||||||
|
@ -854,21 +838,10 @@ class Class(PyCollector):
|
||||||
Using a fixture to invoke these methods ensures we play nicely and unsurprisingly with
|
Using a fixture to invoke these methods ensures we play nicely and unsurprisingly with
|
||||||
other fixtures (#517).
|
other fixtures (#517).
|
||||||
"""
|
"""
|
||||||
has_nose = self.config.pluginmanager.has_plugin("nose")
|
|
||||||
setup_name = "setup_method"
|
setup_name = "setup_method"
|
||||||
setup_method = _get_first_non_fixture_func(self.obj, (setup_name,))
|
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_name = "teardown_method"
|
||||||
teardown_method = _get_first_non_fixture_func(self.obj, (teardown_name,))
|
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:
|
if setup_method is None and teardown_method is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -883,42 +856,14 @@ class Class(PyCollector):
|
||||||
if setup_method is not None:
|
if setup_method is not None:
|
||||||
func = getattr(self, setup_name)
|
func = getattr(self, setup_name)
|
||||||
_call_with_optional_argument(func, method)
|
_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
|
yield
|
||||||
if teardown_method is not None:
|
if teardown_method is not None:
|
||||||
func = getattr(self, teardown_name)
|
func = getattr(self, teardown_name)
|
||||||
_call_with_optional_argument(func, method)
|
_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
|
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:
|
def hasinit(obj: object) -> bool:
|
||||||
init: object = getattr(obj, "__init__", None)
|
init: object = getattr(obj, "__init__", None)
|
||||||
if init:
|
if init:
|
||||||
|
|
|
@ -18,7 +18,6 @@ from typing import TypeVar
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
from _pytest.deprecated import check_ispytest
|
from _pytest.deprecated import check_ispytest
|
||||||
from _pytest.deprecated import WARNS_NONE_ARG
|
|
||||||
from _pytest.fixtures import fixture
|
from _pytest.fixtures import fixture
|
||||||
from _pytest.outcomes import fail
|
from _pytest.outcomes import fail
|
||||||
|
|
||||||
|
@ -264,9 +263,7 @@ class WarningsRecorder(warnings.catch_warnings): # type:ignore[type-arg]
|
||||||
class WarningsChecker(WarningsRecorder):
|
class WarningsChecker(WarningsRecorder):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
expected_warning: Optional[
|
expected_warning: Union[Type[Warning], Tuple[Type[Warning], ...]] = Warning,
|
||||||
Union[Type[Warning], Tuple[Type[Warning], ...]]
|
|
||||||
] = Warning,
|
|
||||||
match_expr: Optional[Union[str, Pattern[str]]] = None,
|
match_expr: Optional[Union[str, Pattern[str]]] = None,
|
||||||
*,
|
*,
|
||||||
_ispytest: bool = False,
|
_ispytest: bool = False,
|
||||||
|
@ -275,15 +272,14 @@ class WarningsChecker(WarningsRecorder):
|
||||||
super().__init__(_ispytest=True)
|
super().__init__(_ispytest=True)
|
||||||
|
|
||||||
msg = "exceptions must be derived from Warning, not %s"
|
msg = "exceptions must be derived from Warning, not %s"
|
||||||
if expected_warning is None:
|
if isinstance(expected_warning, tuple):
|
||||||
warnings.warn(WARNS_NONE_ARG, stacklevel=4)
|
|
||||||
expected_warning_tup = None
|
|
||||||
elif isinstance(expected_warning, tuple):
|
|
||||||
for exc in expected_warning:
|
for exc in expected_warning:
|
||||||
if not issubclass(exc, Warning):
|
if not issubclass(exc, Warning):
|
||||||
raise TypeError(msg % type(exc))
|
raise TypeError(msg % type(exc))
|
||||||
expected_warning_tup = expected_warning
|
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,)
|
expected_warning_tup = (expected_warning,)
|
||||||
else:
|
else:
|
||||||
raise TypeError(msg % type(expected_warning))
|
raise TypeError(msg % type(expected_warning))
|
||||||
|
@ -307,10 +303,6 @@ class WarningsChecker(WarningsRecorder):
|
||||||
|
|
||||||
__tracebackhide__ = True
|
__tracebackhide__ = True
|
||||||
|
|
||||||
if self.expected_warning is None:
|
|
||||||
# nothing to do in this deprecated case, see WARNS_NONE_ARG above
|
|
||||||
return
|
|
||||||
|
|
||||||
def found_str():
|
def found_str():
|
||||||
return pformat([record.message for record in self], indent=2)
|
return pformat([record.message for record in self], indent=2)
|
||||||
|
|
||||||
|
|
|
@ -49,12 +49,6 @@ class PytestDeprecationWarning(PytestWarning, DeprecationWarning):
|
||||||
__module__ = "pytest"
|
__module__ = "pytest"
|
||||||
|
|
||||||
|
|
||||||
class PytestRemovedIn8Warning(PytestDeprecationWarning):
|
|
||||||
"""Warning class for features that will be removed in pytest 8."""
|
|
||||||
|
|
||||||
__module__ = "pytest"
|
|
||||||
|
|
||||||
|
|
||||||
class PytestRemovedIn9Warning(PytestDeprecationWarning):
|
class PytestRemovedIn9Warning(PytestDeprecationWarning):
|
||||||
"""Warning class for features that will be removed in pytest 9."""
|
"""Warning class for features that will be removed in pytest 9."""
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,8 @@ def catch_warnings_for_item(
|
||||||
warnings.filterwarnings("always", category=DeprecationWarning)
|
warnings.filterwarnings("always", category=DeprecationWarning)
|
||||||
warnings.filterwarnings("always", category=PendingDeprecationWarning)
|
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)
|
apply_warning_filters(config_filters, cmdline_filters)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
# PYTHON_ARGCOMPLETE_OK
|
# PYTHON_ARGCOMPLETE_OK
|
||||||
"""pytest: unit and functional testing with Python."""
|
"""pytest: unit and functional testing with Python."""
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
from _pytest import __version__
|
from _pytest import __version__
|
||||||
from _pytest import version_tuple
|
from _pytest import version_tuple
|
||||||
from _pytest._code import ExceptionInfo
|
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 PytestConfigWarning
|
||||||
from _pytest.warning_types import PytestDeprecationWarning
|
from _pytest.warning_types import PytestDeprecationWarning
|
||||||
from _pytest.warning_types import PytestExperimentalApiWarning
|
from _pytest.warning_types import PytestExperimentalApiWarning
|
||||||
from _pytest.warning_types import PytestRemovedIn8Warning
|
|
||||||
from _pytest.warning_types import PytestRemovedIn9Warning
|
from _pytest.warning_types import PytestRemovedIn9Warning
|
||||||
from _pytest.warning_types import PytestReturnNotNoneWarning
|
from _pytest.warning_types import PytestReturnNotNoneWarning
|
||||||
from _pytest.warning_types import PytestUnhandledCoroutineWarning
|
from _pytest.warning_types import PytestUnhandledCoroutineWarning
|
||||||
|
@ -139,7 +136,6 @@ __all__ = [
|
||||||
"PytestConfigWarning",
|
"PytestConfigWarning",
|
||||||
"PytestDeprecationWarning",
|
"PytestDeprecationWarning",
|
||||||
"PytestExperimentalApiWarning",
|
"PytestExperimentalApiWarning",
|
||||||
"PytestRemovedIn8Warning",
|
|
||||||
"PytestRemovedIn9Warning",
|
"PytestRemovedIn9Warning",
|
||||||
"PytestReturnNotNoneWarning",
|
"PytestReturnNotNoneWarning",
|
||||||
"Pytester",
|
"Pytester",
|
||||||
|
@ -170,13 +166,3 @@ __all__ = [
|
||||||
"xfail",
|
"xfail",
|
||||||
"yield_fixture",
|
"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}")
|
|
||||||
|
|
|
@ -1,11 +1,5 @@
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
import warnings
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest import deprecated
|
from _pytest import deprecated
|
||||||
from _pytest.compat import legacy_path
|
|
||||||
from _pytest.pytester import Pytester
|
from _pytest.pytester import Pytester
|
||||||
from pytest import PytestDeprecationWarning
|
from pytest import PytestDeprecationWarning
|
||||||
|
|
||||||
|
@ -68,50 +62,6 @@ def test_hookimpl_via_function_attributes_are_deprecated():
|
||||||
assert record.filename == __file__
|
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:
|
def test_yield_fixture_is_deprecated() -> None:
|
||||||
with pytest.warns(DeprecationWarning, match=r"yield_fixture is deprecated"):
|
with pytest.warns(DeprecationWarning, match=r"yield_fixture is deprecated"):
|
||||||
|
|
||||||
|
@ -134,159 +84,6 @@ def test_private_is_deprecated() -> None:
|
||||||
PrivateInit(10, _ispytest=True)
|
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():
|
def test_fixture_disallow_on_marked_functions():
|
||||||
"""Test that applying @pytest.fixture to a marked function warns (#3364)."""
|
"""Test that applying @pytest.fixture to a marked function warns (#3364)."""
|
||||||
with pytest.warns(
|
with pytest.warns(
|
||||||
|
@ -336,62 +133,3 @@ def test_fixture_disallowed_between_marks():
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
assert len(record) == 2 # one for each mark decorator
|
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)
|
|
||||||
|
|
|
@ -1253,17 +1253,6 @@ def test_plugin_loading_order(pytester: Pytester) -> None:
|
||||||
assert result.ret == 0
|
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:
|
def test_invalid_options_show_extra_information(pytester: Pytester) -> None:
|
||||||
"""Display extra information when pytest exits due to unrecognized
|
"""Display extra information when pytest exits due to unrecognized
|
||||||
options in the command-line."""
|
options in the command-line."""
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest.compat import LEGACY_PATH
|
|
||||||
from _pytest.fixtures import TopRequest
|
from _pytest.fixtures import TopRequest
|
||||||
|
from _pytest.legacypath import LEGACY_PATH
|
||||||
from _pytest.legacypath import TempdirFactory
|
from _pytest.legacypath import TempdirFactory
|
||||||
from _pytest.legacypath import Testdir
|
from _pytest.legacypath import Testdir
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ def test_item_fspath(pytester: pytest.Pytester) -> None:
|
||||||
items2, hookrec = pytester.inline_genitems(item.nodeid)
|
items2, hookrec = pytester.inline_genitems(item.nodeid)
|
||||||
(item2,) = items2
|
(item2,) = items2
|
||||||
assert item2.name == item.name
|
assert item2.name == item.name
|
||||||
assert item2.fspath == item.fspath
|
assert item2.fspath == item.fspath # type: ignore[attr-defined]
|
||||||
assert item2.path == item.path
|
assert item2.path == item.path
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ from typing import Type
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest import nodes
|
from _pytest import nodes
|
||||||
from _pytest.compat import legacy_path
|
|
||||||
from _pytest.outcomes import OutcomeException
|
from _pytest.outcomes import OutcomeException
|
||||||
from _pytest.pytester import Pytester
|
from _pytest.pytester import Pytester
|
||||||
from _pytest.warning_types import PytestWarning
|
from _pytest.warning_types import PytestWarning
|
||||||
|
@ -69,9 +68,9 @@ def test_subclassing_both_item_and_collector_deprecated(
|
||||||
warnings.simplefilter("error")
|
warnings.simplefilter("error")
|
||||||
|
|
||||||
class SoWrong(nodes.Item, nodes.File):
|
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"""
|
"""Legacy ctor with legacy call # don't wana see"""
|
||||||
super().__init__(fspath, parent)
|
super().__init__(parent, path)
|
||||||
|
|
||||||
def collect(self):
|
def collect(self):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
@ -80,9 +79,7 @@ def test_subclassing_both_item_and_collector_deprecated(
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
with pytest.warns(PytestWarning) as rec:
|
with pytest.warns(PytestWarning) as rec:
|
||||||
SoWrong.from_parent(
|
SoWrong.from_parent(request.session, path=tmp_path / "broken.txt", wrong=10)
|
||||||
request.session, fspath=legacy_path(tmp_path / "broken.txt")
|
|
||||||
)
|
|
||||||
messages = [str(x.message) for x in rec]
|
messages = [str(x.message) for x in rec]
|
||||||
assert any(
|
assert any(
|
||||||
re.search(".*SoWrong.* not using a cooperative constructor.*", x)
|
re.search(".*SoWrong.* not using a cooperative constructor.*", x)
|
||||||
|
|
|
@ -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
|
|
|
@ -54,9 +54,6 @@ class TestParser:
|
||||||
assert argument.type is str
|
assert argument.type is str
|
||||||
argument = parseopt.Argument("-t", dest="abc", type=float)
|
argument = parseopt.Argument("-t", dest="abc", type=float)
|
||||||
assert argument.type is 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(
|
argument = parseopt.Argument(
|
||||||
"-t", dest="abc", type=str, choices=["red", "blue"]
|
"-t", dest="abc", type=str, choices=["red", "blue"]
|
||||||
)
|
)
|
||||||
|
|
|
@ -345,17 +345,9 @@ class TestWarns:
|
||||||
assert str(record[0].message) == "user"
|
assert str(record[0].message) == "user"
|
||||||
assert str(record[1].message) == "runtime"
|
assert str(record[1].message) == "runtime"
|
||||||
|
|
||||||
def test_record_only_none_deprecated_warn(self) -> None:
|
def test_record_only_none_type_error(self) -> None:
|
||||||
# This should become an error when WARNS_NONE_ARG is removed in Pytest 8.0
|
with pytest.raises(TypeError):
|
||||||
with warnings.catch_warnings():
|
pytest.warns(None) # type: ignore[call-overload]
|
||||||
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_by_subclass(self) -> None:
|
def test_record_by_subclass(self) -> None:
|
||||||
with pytest.warns(Warning) as record:
|
with pytest.warns(Warning) as record:
|
||||||
|
|
|
@ -1494,54 +1494,6 @@ def test_fail_using_reason_works_ok(pytester: Pytester) -> None:
|
||||||
result.assert_outcomes(failed=1)
|
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:
|
def test_exit_with_reason_works_ok(pytester: Pytester) -> None:
|
||||||
p = pytester.makepyfile(
|
p = pytester.makepyfile(
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -530,13 +530,11 @@ class TestRmRf:
|
||||||
assert fn.is_file()
|
assert fn.is_file()
|
||||||
|
|
||||||
# ignored function
|
# ignored function
|
||||||
with warnings.catch_warnings():
|
with warnings.catch_warnings(record=True) as w:
|
||||||
warnings.simplefilter("ignore")
|
|
||||||
with pytest.warns(None) as warninfo: # type: ignore[call-overload]
|
|
||||||
exc_info4 = PermissionError()
|
exc_info4 = PermissionError()
|
||||||
on_rm_rf_error(os.open, str(fn), exc_info4, start_path=tmp_path)
|
on_rm_rf_error(os.open, str(fn), exc_info4, start_path=tmp_path)
|
||||||
assert fn.is_file()
|
assert fn.is_file()
|
||||||
assert not [x.message for x in warninfo]
|
assert not [x.message for x in w]
|
||||||
|
|
||||||
exc_info5 = PermissionError()
|
exc_info5 = PermissionError()
|
||||||
on_rm_rf_error(os.unlink, str(fn), exc_info5, start_path=tmp_path)
|
on_rm_rf_error(os.unlink, str(fn), exc_info5, start_path=tmp_path)
|
||||||
|
|
|
@ -518,8 +518,7 @@ class TestDeprecationWarningsByDefault:
|
||||||
assert WARNINGS_SUMMARY_HEADER not in result.stdout.str()
|
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"])
|
@pytest.mark.parametrize("change_default", [None, "ini", "cmdline"])
|
||||||
def test_removed_in_x_warning_as_error(pytester: Pytester, change_default) -> None:
|
def test_removed_in_x_warning_as_error(pytester: Pytester, change_default) -> None:
|
||||||
"""This ensures that PytestRemovedInXWarnings raised by pytest are turned into errors.
|
"""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
|
import warnings, pytest
|
||||||
def test():
|
def test():
|
||||||
warnings.warn(pytest.PytestRemovedIn8Warning("some warning"))
|
warnings.warn(pytest.PytestRemovedIn9Warning("some warning"))
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
if change_default == "ini":
|
if change_default == "ini":
|
||||||
|
@ -539,12 +538,12 @@ def test_removed_in_x_warning_as_error(pytester: Pytester, change_default) -> No
|
||||||
"""
|
"""
|
||||||
[pytest]
|
[pytest]
|
||||||
filterwarnings =
|
filterwarnings =
|
||||||
ignore::pytest.PytestRemovedIn8Warning
|
ignore::pytest.PytestRemovedIn9Warning
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
args = (
|
args = (
|
||||||
("-Wignore::pytest.PytestRemovedIn8Warning",)
|
("-Wignore::pytest.PytestRemovedIn9Warning",)
|
||||||
if change_default == "cmdline"
|
if change_default == "cmdline"
|
||||||
else ()
|
else ()
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue