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
|
||||
managing small or parametrized long-lived test resources
|
||||
|
||||
- Can run `unittest <https://docs.pytest.org/en/stable/how-to/unittest.html>`_ (or trial),
|
||||
`nose <https://docs.pytest.org/en/stable/how-to/nose.html>`_ test suites out of the box
|
||||
- Can run `unittest <https://docs.pytest.org/en/stable/how-to/unittest.html>`_ (or trial)
|
||||
test suites out of the box
|
||||
|
||||
- Python 3.8+ or PyPy3
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ Partner projects, sign up here! (by 22 March)
|
|||
What does it mean to "adopt pytest"?
|
||||
-----------------------------------------
|
||||
|
||||
There can be many different definitions of "success". Pytest can run many nose_ and unittest_ tests by default, so using pytest as your testrunner may be possible from day 1. Job done, right?
|
||||
There can be many different definitions of "success". Pytest can run many unittest_ tests by default, so using pytest as your testrunner may be possible from day 1. Job done, right?
|
||||
|
||||
Progressive success might look like:
|
||||
|
||||
|
@ -62,7 +62,6 @@ Progressive success might look like:
|
|||
|
||||
It may be after the month is up, the partner project decides that pytest is not right for it. That's okay - hopefully the pytest team will also learn something about its weaknesses or deficiencies.
|
||||
|
||||
.. _nose: nose.html
|
||||
.. _unittest: unittest.html
|
||||
.. _assert: assert.html
|
||||
.. _pycmd: https://bitbucket.org/hpk42/pycmd/overview
|
||||
|
|
|
@ -227,7 +227,7 @@ These are breaking changes where deprecation was not possible.
|
|||
Deprecations
|
||||
------------
|
||||
|
||||
- `#10465 <https://github.com/pytest-dev/pytest/issues/10465>`_: Test functions returning a value other than ``None`` will now issue a :class:`pytest.PytestWarning` instead of :class:`pytest.PytestRemovedIn8Warning`, meaning this will stay a warning instead of becoming an error in the future.
|
||||
- `#10465 <https://github.com/pytest-dev/pytest/issues/10465>`_: Test functions returning a value other than ``None`` will now issue a :class:`pytest.PytestWarning` instead of ``pytest.PytestRemovedIn8Warning``, meaning this will stay a warning instead of becoming an error in the future.
|
||||
|
||||
|
||||
- `#3664 <https://github.com/pytest-dev/pytest/issues/3664>`_: Applying a mark to a fixture function now issues a warning: marks in fixtures never had any effect, but it is a common user error to apply a mark to a fixture (for example ``usefixtures``) and expect it to work.
|
||||
|
@ -1257,7 +1257,7 @@ Deprecations
|
|||
See :ref:`the deprecation note <diamond-inheritance-deprecated>` for full details.
|
||||
|
||||
|
||||
- `#8592 <https://github.com/pytest-dev/pytest/issues/8592>`_: :hook:`pytest_cmdline_preparse` has been officially deprecated. It will be removed in a future release. Use :hook:`pytest_load_initial_conftests` instead.
|
||||
- `#8592 <https://github.com/pytest-dev/pytest/issues/8592>`_: ``pytest_cmdline_preparse`` has been officially deprecated. It will be removed in a future release. Use :hook:`pytest_load_initial_conftests` instead.
|
||||
|
||||
See :ref:`the deprecation note <cmdline-preparse-deprecated>` for full details.
|
||||
|
||||
|
|
|
@ -199,7 +199,6 @@ nitpick_ignore = [
|
|||
("py:class", "_tracing.TagTracerSub"),
|
||||
("py:class", "warnings.WarningMessage"),
|
||||
# Undocumented type aliases
|
||||
("py:class", "LEGACY_PATH"),
|
||||
("py:class", "_PluggyPlugin"),
|
||||
# TypeVars
|
||||
("py:class", "_pytest._code.code.E"),
|
||||
|
|
|
@ -44,7 +44,6 @@ How-to guides
|
|||
|
||||
how-to/existingtestsuite
|
||||
how-to/unittest
|
||||
how-to/nose
|
||||
how-to/xunit_setup
|
||||
|
||||
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>`.
|
||||
|
||||
|
||||
.. _legacy-path-hooks-deprecated:
|
||||
|
||||
Configuring hook specs/impls using markers
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Before pluggy, pytest's plugin library, was its own package and had a clear API,
|
||||
pytest just used ``pytest.mark`` to configure hooks.
|
||||
|
||||
The :py:func:`pytest.hookimpl` and :py:func:`pytest.hookspec` decorators
|
||||
have been available since years and should be used instead.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.mark.tryfirst
|
||||
def pytest_runtest_call():
|
||||
...
|
||||
|
||||
|
||||
# or
|
||||
def pytest_runtest_call():
|
||||
...
|
||||
|
||||
|
||||
pytest_runtest_call.tryfirst = True
|
||||
|
||||
should be changed to:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.hookimpl(tryfirst=True)
|
||||
def pytest_runtest_call():
|
||||
...
|
||||
|
||||
Changed ``hookimpl`` attributes:
|
||||
|
||||
* ``tryfirst``
|
||||
* ``trylast``
|
||||
* ``optionalhook``
|
||||
* ``hookwrapper``
|
||||
|
||||
Changed ``hookwrapper`` attributes:
|
||||
|
||||
* ``firstresult``
|
||||
* ``historic``
|
||||
|
||||
|
||||
Directly constructing internal classes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 7.0
|
||||
|
||||
Directly constructing the following classes is now deprecated:
|
||||
|
||||
- ``_pytest.mark.structures.Mark``
|
||||
- ``_pytest.mark.structures.MarkDecorator``
|
||||
- ``_pytest.mark.structures.MarkGenerator``
|
||||
- ``_pytest.python.Metafunc``
|
||||
- ``_pytest.runner.CallInfo``
|
||||
- ``_pytest._code.ExceptionInfo``
|
||||
- ``_pytest.config.argparsing.Parser``
|
||||
- ``_pytest.config.argparsing.OptionGroup``
|
||||
- ``_pytest.pytester.HookRecorder``
|
||||
|
||||
These constructors have always been considered private, but now issue a deprecation warning, which may become a hard error in pytest 8.
|
||||
|
||||
.. _diamond-inheritance-deprecated:
|
||||
|
||||
Diamond inheritance between :class:`pytest.Collector` and :class:`pytest.Item`
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 7.0
|
||||
|
||||
Defining a custom pytest node type which is both an :class:`~pytest.Item` and a :class:`~pytest.Collector` (e.g. :class:`~pytest.File`) now issues a warning.
|
||||
It was never sanely supported and triggers hard to debug errors.
|
||||
|
||||
Some plugins providing linting/code analysis have been using this as a hack.
|
||||
Instead, a separate collector node should be used, which collects the item. See
|
||||
:ref:`non-python tests` for an example, as well as an `example pr fixing inheritance`_.
|
||||
|
||||
.. _example pr fixing inheritance: https://github.com/asmeurer/pytest-flakes/pull/40/files
|
||||
|
||||
|
||||
.. _uncooperative-constructors-deprecated:
|
||||
|
||||
Constructors of custom :class:`~_pytest.nodes.Node` subclasses should take ``**kwargs``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 7.0
|
||||
|
||||
If custom subclasses of nodes like :class:`pytest.Item` override the
|
||||
``__init__`` method, they should take ``**kwargs``. Thus,
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class CustomItem(pytest.Item):
|
||||
def __init__(self, name, parent, additional_arg):
|
||||
super().__init__(name, parent)
|
||||
self.additional_arg = additional_arg
|
||||
|
||||
should be turned into:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class CustomItem(pytest.Item):
|
||||
def __init__(self, *, additional_arg, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.additional_arg = additional_arg
|
||||
|
||||
to avoid hard-coding the arguments pytest can pass to the superclass.
|
||||
See :ref:`non-python tests` for a full example.
|
||||
|
||||
For cases without conflicts, no deprecation warning is emitted. For cases with
|
||||
conflicts (such as :class:`pytest.File` now taking ``path`` instead of
|
||||
``fspath``, as :ref:`outlined above <node-ctor-fspath-deprecation>`), a
|
||||
deprecation warning is now raised.
|
||||
|
||||
Applying a mark to a fixture function
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 7.4
|
||||
|
||||
Applying a mark to a fixture function never had any effect, but it is a common user error.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.mark.usefixtures("clean_database")
|
||||
@pytest.fixture
|
||||
def user() -> User:
|
||||
...
|
||||
|
||||
Users expected in this case that the ``usefixtures`` mark would have its intended effect of using the ``clean_database`` fixture when ``user`` was invoked, when in fact it has no effect at all.
|
||||
|
||||
Now pytest will issue a warning when it encounters this problem, and will raise an error in the future versions.
|
||||
|
||||
|
||||
Returning non-None value in test functions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 7.2
|
||||
|
||||
A :class:`pytest.PytestReturnNotNoneWarning` is now emitted if a test function returns something other than `None`.
|
||||
|
||||
This prevents a common mistake among beginners that expect that returning a `bool` would cause a test to pass or fail, for example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
["a", "b", "result"],
|
||||
[
|
||||
[1, 2, 5],
|
||||
[2, 3, 8],
|
||||
[5, 3, 18],
|
||||
],
|
||||
)
|
||||
def test_foo(a, b, result):
|
||||
return foo(a, b) == result
|
||||
|
||||
Given that pytest ignores the return value, this might be surprising that it will never fail.
|
||||
|
||||
The proper fix is to change the `return` to an `assert`:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
["a", "b", "result"],
|
||||
[
|
||||
[1, 2, 5],
|
||||
[2, 3, 8],
|
||||
[5, 3, 18],
|
||||
],
|
||||
)
|
||||
def test_foo(a, b, result):
|
||||
assert foo(a, b) == result
|
||||
|
||||
|
||||
The ``yield_fixture`` function/decorator
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 6.2
|
||||
|
||||
``pytest.yield_fixture`` is a deprecated alias for :func:`pytest.fixture`.
|
||||
|
||||
It has been so for a very long time, so can be search/replaced safely.
|
||||
|
||||
|
||||
Removed Features and Breaking Changes
|
||||
-------------------------------------
|
||||
|
||||
As stated in our :ref:`backwards-compatibility` policy, deprecated features are removed only in major releases after
|
||||
an appropriate period of deprecation has passed.
|
||||
|
||||
Some breaking changes which could not be deprecated are also listed.
|
||||
|
||||
.. _node-ctor-fspath-deprecation:
|
||||
|
||||
``fspath`` argument for Node constructors replaced with ``pathlib.Path``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 7.0
|
||||
|
||||
In order to support the transition from ``py.path.local`` to :mod:`pathlib`,
|
||||
the ``fspath`` argument to :class:`~_pytest.nodes.Node` constructors like
|
||||
:func:`pytest.Function.from_parent()` and :func:`pytest.Class.from_parent()`
|
||||
is now deprecated.
|
||||
|
||||
Plugins which construct nodes should pass the ``path`` argument, of type
|
||||
:class:`pathlib.Path`, instead of the ``fspath`` argument.
|
||||
|
||||
Plugins which implement custom items and collectors are encouraged to replace
|
||||
``fspath`` parameters (``py.path.local``) with ``path`` parameters
|
||||
(``pathlib.Path``), and drop any other usage of the ``py`` library if possible.
|
||||
|
||||
If possible, plugins with custom items should use :ref:`cooperative
|
||||
constructors <uncooperative-constructors-deprecated>` to avoid hardcoding
|
||||
arguments they only pass on to the superclass.
|
||||
|
||||
.. note::
|
||||
The name of the :class:`~_pytest.nodes.Node` arguments and attributes (the
|
||||
new attribute being ``path``) is **the opposite** of the situation for
|
||||
hooks, :ref:`outlined below <legacy-path-hooks-deprecated>` (the old
|
||||
argument being ``path``).
|
||||
|
||||
This is an unfortunate artifact due to historical reasons, which should be
|
||||
resolved in future versions as we slowly get rid of the :pypi:`py`
|
||||
dependency (see :issue:`9283` for a longer discussion).
|
||||
|
||||
Due to the ongoing migration of methods like :meth:`~pytest.Item.reportinfo`
|
||||
which still is expected to return a ``py.path.local`` object, nodes still have
|
||||
both ``fspath`` (``py.path.local``) and ``path`` (``pathlib.Path``) attributes,
|
||||
no matter what argument was used in the constructor. We expect to deprecate the
|
||||
``fspath`` attribute in a future release.
|
||||
|
||||
|
||||
``py.path.local`` arguments for hooks replaced with ``pathlib.Path``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 7.0
|
||||
.. versionremoved:: 8.0
|
||||
|
||||
In order to support the transition from ``py.path.local`` to :mod:`pathlib`, the following hooks now receive additional arguments:
|
||||
|
||||
* :hook:`pytest_ignore_collect(collection_path: pathlib.Path) <pytest_ignore_collect>` as equivalent to ``path``
|
||||
* :hook:`pytest_collect_file(file_path: pathlib.Path) <pytest_collect_file>` as equivalent to ``path``
|
||||
* :hook:`pytest_pycollect_makemodule(module_path: pathlib.Path) <pytest_pycollect_makemodule>` as equivalent to ``path``
|
||||
* :hook:`pytest_report_header(start_path: pathlib.Path) <pytest_report_header>` as equivalent to ``startdir``
|
||||
* :hook:`pytest_report_collectionfinish(start_path: pathlib.Path) <pytest_report_collectionfinish>` as equivalent to ``startdir``
|
||||
|
||||
The accompanying ``py.path.local`` based paths have been deprecated: plugins which manually invoke those hooks should only pass the new ``pathlib.Path`` arguments, and users should change their hook implementations to use the new ``pathlib.Path`` arguments.
|
||||
|
||||
.. note::
|
||||
The name of the :class:`~_pytest.nodes.Node` arguments and attributes,
|
||||
:ref:`outlined above <node-ctor-fspath-deprecation>` (the new attribute
|
||||
being ``path``) is **the opposite** of the situation for hooks (the old
|
||||
argument being ``path``).
|
||||
|
||||
This is an unfortunate artifact due to historical reasons, which should be
|
||||
resolved in future versions as we slowly get rid of the :pypi:`py`
|
||||
dependency (see :issue:`9283` for a longer discussion).
|
||||
|
||||
|
||||
.. _nose-deprecation:
|
||||
|
||||
Support for tests written for nose
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 7.2
|
||||
.. versionremoved:: 8.0
|
||||
|
||||
Support for running tests written for `nose <https://nose.readthedocs.io/en/latest/>`__ is now deprecated.
|
||||
|
||||
|
@ -125,160 +386,13 @@ Will also need to be ported to a supported pytest style. One way to do it is usi
|
|||
|
||||
.. _`with-setup-nose`: https://nose.readthedocs.io/en/latest/testing_tools.html?highlight=with_setup#nose.tools.with_setup
|
||||
|
||||
.. _instance-collector-deprecation:
|
||||
|
||||
The ``pytest.Instance`` collector
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionremoved:: 7.0
|
||||
|
||||
The ``pytest.Instance`` collector type has been removed.
|
||||
|
||||
Previously, Python test methods were collected as :class:`~pytest.Class` -> ``Instance`` -> :class:`~pytest.Function`.
|
||||
Now :class:`~pytest.Class` collects the test methods directly.
|
||||
|
||||
Most plugins which reference ``Instance`` do so in order to ignore or skip it,
|
||||
using a check such as ``if isinstance(node, Instance): return``.
|
||||
Such plugins should simply remove consideration of ``Instance`` on pytest>=7.
|
||||
However, to keep such uses working, a dummy type has been instanted in ``pytest.Instance`` and ``_pytest.python.Instance``,
|
||||
and importing it emits a deprecation warning. This will be removed in pytest 8.
|
||||
|
||||
|
||||
.. _node-ctor-fspath-deprecation:
|
||||
|
||||
``fspath`` argument for Node constructors replaced with ``pathlib.Path``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 7.0
|
||||
|
||||
In order to support the transition from ``py.path.local`` to :mod:`pathlib`,
|
||||
the ``fspath`` argument to :class:`~_pytest.nodes.Node` constructors like
|
||||
:func:`pytest.Function.from_parent()` and :func:`pytest.Class.from_parent()`
|
||||
is now deprecated.
|
||||
|
||||
Plugins which construct nodes should pass the ``path`` argument, of type
|
||||
:class:`pathlib.Path`, instead of the ``fspath`` argument.
|
||||
|
||||
Plugins which implement custom items and collectors are encouraged to replace
|
||||
``fspath`` parameters (``py.path.local``) with ``path`` parameters
|
||||
(``pathlib.Path``), and drop any other usage of the ``py`` library if possible.
|
||||
|
||||
If possible, plugins with custom items should use :ref:`cooperative
|
||||
constructors <uncooperative-constructors-deprecated>` to avoid hardcoding
|
||||
arguments they only pass on to the superclass.
|
||||
|
||||
.. note::
|
||||
The name of the :class:`~_pytest.nodes.Node` arguments and attributes (the
|
||||
new attribute being ``path``) is **the opposite** of the situation for
|
||||
hooks, :ref:`outlined below <legacy-path-hooks-deprecated>` (the old
|
||||
argument being ``path``).
|
||||
|
||||
This is an unfortunate artifact due to historical reasons, which should be
|
||||
resolved in future versions as we slowly get rid of the :pypi:`py`
|
||||
dependency (see :issue:`9283` for a longer discussion).
|
||||
|
||||
Due to the ongoing migration of methods like :meth:`~pytest.Item.reportinfo`
|
||||
which still is expected to return a ``py.path.local`` object, nodes still have
|
||||
both ``fspath`` (``py.path.local``) and ``path`` (``pathlib.Path``) attributes,
|
||||
no matter what argument was used in the constructor. We expect to deprecate the
|
||||
``fspath`` attribute in a future release.
|
||||
|
||||
.. _legacy-path-hooks-deprecated:
|
||||
|
||||
Configuring hook specs/impls using markers
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Before pluggy, pytest's plugin library, was its own package and had a clear API,
|
||||
pytest just used ``pytest.mark`` to configure hooks.
|
||||
|
||||
The :py:func:`pytest.hookimpl` and :py:func:`pytest.hookspec` decorators
|
||||
have been available since years and should be used instead.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.mark.tryfirst
|
||||
def pytest_runtest_call():
|
||||
...
|
||||
|
||||
|
||||
# or
|
||||
def pytest_runtest_call():
|
||||
...
|
||||
|
||||
|
||||
pytest_runtest_call.tryfirst = True
|
||||
|
||||
should be changed to:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.hookimpl(tryfirst=True)
|
||||
def pytest_runtest_call():
|
||||
...
|
||||
|
||||
Changed ``hookimpl`` attributes:
|
||||
|
||||
* ``tryfirst``
|
||||
* ``trylast``
|
||||
* ``optionalhook``
|
||||
* ``hookwrapper``
|
||||
|
||||
Changed ``hookwrapper`` attributes:
|
||||
|
||||
* ``firstresult``
|
||||
* ``historic``
|
||||
|
||||
|
||||
``py.path.local`` arguments for hooks replaced with ``pathlib.Path``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 7.0
|
||||
|
||||
In order to support the transition from ``py.path.local`` to :mod:`pathlib`, the following hooks now receive additional arguments:
|
||||
|
||||
* :hook:`pytest_ignore_collect(collection_path: pathlib.Path) <pytest_ignore_collect>` as equivalent to ``path``
|
||||
* :hook:`pytest_collect_file(file_path: pathlib.Path) <pytest_collect_file>` as equivalent to ``path``
|
||||
* :hook:`pytest_pycollect_makemodule(module_path: pathlib.Path) <pytest_pycollect_makemodule>` as equivalent to ``path``
|
||||
* :hook:`pytest_report_header(start_path: pathlib.Path) <pytest_report_header>` as equivalent to ``startdir``
|
||||
* :hook:`pytest_report_collectionfinish(start_path: pathlib.Path) <pytest_report_collectionfinish>` as equivalent to ``startdir``
|
||||
|
||||
The accompanying ``py.path.local`` based paths have been deprecated: plugins which manually invoke those hooks should only pass the new ``pathlib.Path`` arguments, and users should change their hook implementations to use the new ``pathlib.Path`` arguments.
|
||||
|
||||
.. note::
|
||||
The name of the :class:`~_pytest.nodes.Node` arguments and attributes,
|
||||
:ref:`outlined above <node-ctor-fspath-deprecation>` (the new attribute
|
||||
being ``path``) is **the opposite** of the situation for hooks (the old
|
||||
argument being ``path``).
|
||||
|
||||
This is an unfortunate artifact due to historical reasons, which should be
|
||||
resolved in future versions as we slowly get rid of the :pypi:`py`
|
||||
dependency (see :issue:`9283` for a longer discussion).
|
||||
|
||||
Directly constructing internal classes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 7.0
|
||||
|
||||
Directly constructing the following classes is now deprecated:
|
||||
|
||||
- ``_pytest.mark.structures.Mark``
|
||||
- ``_pytest.mark.structures.MarkDecorator``
|
||||
- ``_pytest.mark.structures.MarkGenerator``
|
||||
- ``_pytest.python.Metafunc``
|
||||
- ``_pytest.runner.CallInfo``
|
||||
- ``_pytest._code.ExceptionInfo``
|
||||
- ``_pytest.config.argparsing.Parser``
|
||||
- ``_pytest.config.argparsing.OptionGroup``
|
||||
- ``_pytest.pytester.HookRecorder``
|
||||
|
||||
These constructors have always been considered private, but now issue a deprecation warning, which may become a hard error in pytest 8.
|
||||
|
||||
.. _cmdline-preparse-deprecated:
|
||||
|
||||
Passing ``msg=`` to ``pytest.skip``, ``pytest.fail`` or ``pytest.exit``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 7.0
|
||||
.. versionremoved:: 8.0
|
||||
|
||||
Passing the keyword argument ``msg`` to :func:`pytest.skip`, :func:`pytest.fail` or :func:`pytest.exit`
|
||||
is now deprecated and ``reason`` should be used instead. This change is to bring consistency between these
|
||||
|
@ -307,12 +421,74 @@ functions and the ``@pytest.mark.skip`` and ``@pytest.mark.xfail`` markers which
|
|||
pytest.exit(reason="bar")
|
||||
|
||||
|
||||
.. _instance-collector-deprecation:
|
||||
|
||||
The ``pytest.Instance`` collector
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionremoved:: 7.0
|
||||
|
||||
The ``pytest.Instance`` collector type has been removed.
|
||||
|
||||
Previously, Python test methods were collected as :class:`~pytest.Class` -> ``Instance`` -> :class:`~pytest.Function`.
|
||||
Now :class:`~pytest.Class` collects the test methods directly.
|
||||
|
||||
Most plugins which reference ``Instance`` do so in order to ignore or skip it,
|
||||
using a check such as ``if isinstance(node, Instance): return``.
|
||||
Such plugins should simply remove consideration of ``Instance`` on pytest>=7.
|
||||
However, to keep such uses working, a dummy type has been instanted in ``pytest.Instance`` and ``_pytest.python.Instance``,
|
||||
and importing it emits a deprecation warning. This was removed in pytest 8.
|
||||
|
||||
|
||||
Using ``pytest.warns(None)``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 7.0
|
||||
.. versionremoved:: 8.0
|
||||
|
||||
:func:`pytest.warns(None) <pytest.warns>` is now deprecated because it was frequently misused.
|
||||
Its correct usage was checking that the code emits at least one warning of any type - like ``pytest.warns()``
|
||||
or ``pytest.warns(Warning)``.
|
||||
|
||||
See :ref:`warns use cases` for examples.
|
||||
|
||||
|
||||
Backward compatibilities in ``Parser.addoption``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 2.4
|
||||
.. versionremoved:: 8.0
|
||||
|
||||
Several behaviors of :meth:`Parser.addoption <pytest.Parser.addoption>` are now
|
||||
removed in pytest 8 (deprecated since pytest 2.4.0):
|
||||
|
||||
- ``parser.addoption(..., help=".. %default ..")`` - use ``%(default)s`` instead.
|
||||
- ``parser.addoption(..., type="int/string/float/complex")`` - use ``type=int`` etc. instead.
|
||||
|
||||
|
||||
The ``--strict`` command-line option
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 6.2
|
||||
.. versionremoved:: 8.0
|
||||
|
||||
The ``--strict`` command-line option has been deprecated in favor of ``--strict-markers``, which
|
||||
better conveys what the option does.
|
||||
|
||||
We have plans to maybe in the future to reintroduce ``--strict`` and make it an encompassing
|
||||
flag for all strictness related options (``--strict-markers`` and ``--strict-config``
|
||||
at the moment, more might be introduced in the future).
|
||||
|
||||
|
||||
.. _cmdline-preparse-deprecated:
|
||||
|
||||
Implementing the ``pytest_cmdline_preparse`` hook
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 7.0
|
||||
.. versionremoved:: 8.0
|
||||
|
||||
Implementing the :hook:`pytest_cmdline_preparse` hook has been officially deprecated.
|
||||
Implementing the ``pytest_cmdline_preparse`` hook has been officially deprecated.
|
||||
Implement the :hook:`pytest_load_initial_conftests` hook instead.
|
||||
|
||||
.. code-block:: python
|
||||
|
@ -329,171 +505,6 @@ Implement the :hook:`pytest_load_initial_conftests` hook instead.
|
|||
) -> None:
|
||||
...
|
||||
|
||||
.. _diamond-inheritance-deprecated:
|
||||
|
||||
Diamond inheritance between :class:`pytest.Collector` and :class:`pytest.Item`
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 7.0
|
||||
|
||||
Defining a custom pytest node type which is both an :class:`~pytest.Item` and a :class:`~pytest.Collector` (e.g. :class:`~pytest.File`) now issues a warning.
|
||||
It was never sanely supported and triggers hard to debug errors.
|
||||
|
||||
Some plugins providing linting/code analysis have been using this as a hack.
|
||||
Instead, a separate collector node should be used, which collects the item. See
|
||||
:ref:`non-python tests` for an example, as well as an `example pr fixing inheritance`_.
|
||||
|
||||
.. _example pr fixing inheritance: https://github.com/asmeurer/pytest-flakes/pull/40/files
|
||||
|
||||
|
||||
.. _uncooperative-constructors-deprecated:
|
||||
|
||||
Constructors of custom :class:`~_pytest.nodes.Node` subclasses should take ``**kwargs``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 7.0
|
||||
|
||||
If custom subclasses of nodes like :class:`pytest.Item` override the
|
||||
``__init__`` method, they should take ``**kwargs``. Thus,
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class CustomItem(pytest.Item):
|
||||
def __init__(self, name, parent, additional_arg):
|
||||
super().__init__(name, parent)
|
||||
self.additional_arg = additional_arg
|
||||
|
||||
should be turned into:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class CustomItem(pytest.Item):
|
||||
def __init__(self, *, additional_arg, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.additional_arg = additional_arg
|
||||
|
||||
to avoid hard-coding the arguments pytest can pass to the superclass.
|
||||
See :ref:`non-python tests` for a full example.
|
||||
|
||||
For cases without conflicts, no deprecation warning is emitted. For cases with
|
||||
conflicts (such as :class:`pytest.File` now taking ``path`` instead of
|
||||
``fspath``, as :ref:`outlined above <node-ctor-fspath-deprecation>`), a
|
||||
deprecation warning is now raised.
|
||||
|
||||
Applying a mark to a fixture function
|
||||
-------------------------------------
|
||||
|
||||
.. deprecated:: 7.4
|
||||
|
||||
Applying a mark to a fixture function never had any effect, but it is a common user error.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.mark.usefixtures("clean_database")
|
||||
@pytest.fixture
|
||||
def user() -> User:
|
||||
...
|
||||
|
||||
Users expected in this case that the ``usefixtures`` mark would have its intended effect of using the ``clean_database`` fixture when ``user`` was invoked, when in fact it has no effect at all.
|
||||
|
||||
Now pytest will issue a warning when it encounters this problem, and will raise an error in the future versions.
|
||||
|
||||
|
||||
Backward compatibilities in ``Parser.addoption``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 2.4
|
||||
|
||||
Several behaviors of :meth:`Parser.addoption <pytest.Parser.addoption>` are now
|
||||
scheduled for removal in pytest 8 (deprecated since pytest 2.4.0):
|
||||
|
||||
- ``parser.addoption(..., help=".. %default ..")`` - use ``%(default)s`` instead.
|
||||
- ``parser.addoption(..., type="int/string/float/complex")`` - use ``type=int`` etc. instead.
|
||||
|
||||
|
||||
Using ``pytest.warns(None)``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 7.0
|
||||
|
||||
:func:`pytest.warns(None) <pytest.warns>` is now deprecated because it was frequently misused.
|
||||
Its correct usage was checking that the code emits at least one warning of any type - like ``pytest.warns()``
|
||||
or ``pytest.warns(Warning)``.
|
||||
|
||||
See :ref:`warns use cases` for examples.
|
||||
|
||||
|
||||
Returning non-None value in test functions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 7.2
|
||||
|
||||
A :class:`pytest.PytestReturnNotNoneWarning` is now emitted if a test function returns something other than `None`.
|
||||
|
||||
This prevents a common mistake among beginners that expect that returning a `bool` would cause a test to pass or fail, for example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
["a", "b", "result"],
|
||||
[
|
||||
[1, 2, 5],
|
||||
[2, 3, 8],
|
||||
[5, 3, 18],
|
||||
],
|
||||
)
|
||||
def test_foo(a, b, result):
|
||||
return foo(a, b) == result
|
||||
|
||||
Given that pytest ignores the return value, this might be surprising that it will never fail.
|
||||
|
||||
The proper fix is to change the `return` to an `assert`:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
["a", "b", "result"],
|
||||
[
|
||||
[1, 2, 5],
|
||||
[2, 3, 8],
|
||||
[5, 3, 18],
|
||||
],
|
||||
)
|
||||
def test_foo(a, b, result):
|
||||
assert foo(a, b) == result
|
||||
|
||||
|
||||
The ``--strict`` command-line option
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 6.2
|
||||
|
||||
The ``--strict`` command-line option has been deprecated in favor of ``--strict-markers``, which
|
||||
better conveys what the option does.
|
||||
|
||||
We have plans to maybe in the future to reintroduce ``--strict`` and make it an encompassing
|
||||
flag for all strictness related options (``--strict-markers`` and ``--strict-config``
|
||||
at the moment, more might be introduced in the future).
|
||||
|
||||
|
||||
The ``yield_fixture`` function/decorator
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 6.2
|
||||
|
||||
``pytest.yield_fixture`` is a deprecated alias for :func:`pytest.fixture`.
|
||||
|
||||
It has been so for a very long time, so can be search/replaced safely.
|
||||
|
||||
|
||||
Removed Features and Breaking Changes
|
||||
-------------------------------------
|
||||
|
||||
As stated in our :ref:`backwards-compatibility` policy, deprecated features are removed only in major releases after
|
||||
an appropriate period of deprecation has passed.
|
||||
|
||||
Some breaking changes which could not be deprecated are also listed.
|
||||
|
||||
|
||||
Collection changes in pytest 8
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
|
@ -18,7 +18,6 @@ For basic examples, see
|
|||
- :ref:`Fixtures <fixtures>` for basic fixture/setup examples
|
||||
- :ref:`parametrize` for basic test function parametrization
|
||||
- :ref:`unittest` for basic unittest integration
|
||||
- :ref:`noseintegration` for basic nosetests integration
|
||||
|
||||
The following examples aim at various use cases you might encounter.
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ style of setup/teardown functions:
|
|||
In addition, pytest continues to support :ref:`xunitsetup`. You can mix
|
||||
both styles, moving incrementally from classic to new style, as you
|
||||
prefer. You can also start out from existing :ref:`unittest.TestCase
|
||||
style <unittest.TestCase>` or :ref:`nose based <nosestyle>` projects.
|
||||
style <unittest.TestCase>`.
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@ How to use pytest with an existing test suite
|
|||
==============================================
|
||||
|
||||
Pytest can be used with most existing test suites, but its
|
||||
behavior differs from other test runners such as :ref:`nose <noseintegration>` or
|
||||
Python's default unittest framework.
|
||||
behavior differs from other test runners such as Python's
|
||||
default unittest framework.
|
||||
|
||||
Before using this section you will want to :ref:`install pytest <getstarted>`.
|
||||
|
||||
|
|
|
@ -52,7 +52,6 @@ pytest and other test systems
|
|||
|
||||
existingtestsuite
|
||||
unittest
|
||||
nose
|
||||
xunit_setup
|
||||
|
||||
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
|
||||
|
||||
- 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
|
||||
|
||||
|
|
|
@ -643,8 +643,6 @@ Bootstrapping hooks called for plugins registered early enough (internal and set
|
|||
|
||||
.. hook:: pytest_load_initial_conftests
|
||||
.. autofunction:: pytest_load_initial_conftests
|
||||
.. hook:: pytest_cmdline_preparse
|
||||
.. autofunction:: pytest_cmdline_preparse
|
||||
.. hook:: pytest_cmdline_parse
|
||||
.. autofunction:: pytest_cmdline_parse
|
||||
.. hook:: pytest_cmdline_main
|
||||
|
@ -1209,9 +1207,6 @@ Custom warnings generated in some situations such as improper usage or deprecate
|
|||
.. autoclass:: pytest.PytestReturnNotNoneWarning
|
||||
:show-inheritance:
|
||||
|
||||
.. autoclass:: pytest.PytestRemovedIn8Warning
|
||||
:show-inheritance:
|
||||
|
||||
.. autoclass:: pytest.PytestRemovedIn9Warning
|
||||
:show-inheritance:
|
||||
|
||||
|
|
|
@ -69,7 +69,6 @@ testing =
|
|||
attrs>=19.2.0
|
||||
hypothesis>=3.56
|
||||
mock
|
||||
nose
|
||||
pygments>=2.7.2
|
||||
requests
|
||||
setuptools
|
||||
|
|
|
@ -16,25 +16,10 @@ from typing import Final
|
|||
from typing import NoReturn
|
||||
from typing import TypeVar
|
||||
|
||||
import py
|
||||
|
||||
|
||||
_T = TypeVar("_T")
|
||||
_S = TypeVar("_S")
|
||||
|
||||
#: constant to prepare valuing pylib path replacements/lazy proxies later on
|
||||
# intended for removal in pytest 8.0 or 9.0
|
||||
|
||||
# fmt: off
|
||||
# intentional space to create a fake difference for the verification
|
||||
LEGACY_PATH = py.path. local
|
||||
# fmt: on
|
||||
|
||||
|
||||
def legacy_path(path: str | os.PathLike[str]) -> LEGACY_PATH:
|
||||
"""Internal wrapper to prepare lazy proxies for legacy_path instances"""
|
||||
return LEGACY_PATH(path)
|
||||
|
||||
|
||||
# fmt: off
|
||||
# Singleton type for NOTSET, as described in:
|
||||
|
|
|
@ -38,7 +38,6 @@ from typing import Type
|
|||
from typing import TYPE_CHECKING
|
||||
from typing import Union
|
||||
|
||||
import pluggy
|
||||
from pluggy import HookimplMarker
|
||||
from pluggy import HookimplOpts
|
||||
from pluggy import HookspecMarker
|
||||
|
@ -48,7 +47,6 @@ from pluggy import PluginManager
|
|||
import _pytest._code
|
||||
import _pytest.deprecated
|
||||
import _pytest.hookspec
|
||||
from .compat import PathAwareHookProxy
|
||||
from .exceptions import PrintHelp as PrintHelp
|
||||
from .exceptions import UsageError as UsageError
|
||||
from .findpaths import determine_setup
|
||||
|
@ -252,7 +250,6 @@ default_plugins = essential_plugins + (
|
|||
"monkeypatch",
|
||||
"recwarn",
|
||||
"pastebin",
|
||||
"nose",
|
||||
"assertion",
|
||||
"junitxml",
|
||||
"doctest",
|
||||
|
@ -1009,7 +1006,7 @@ class Config:
|
|||
self._store = self.stash
|
||||
|
||||
self.trace = self.pluginmanager.trace.root.get("config")
|
||||
self.hook: pluggy.HookRelay = PathAwareHookProxy(self.pluginmanager.hook) # type: ignore[assignment]
|
||||
self.hook = self.pluginmanager.hook # type: ignore[assignment]
|
||||
self._inicache: Dict[str, Any] = {}
|
||||
self._override_ini: Sequence[str] = ()
|
||||
self._opt2dest: Dict[str, str] = {}
|
||||
|
@ -1319,11 +1316,6 @@ class Config:
|
|||
self._validate_plugins()
|
||||
self._warn_about_skipped_plugins()
|
||||
|
||||
if self.known_args_namespace.strict:
|
||||
self.issue_config_time_warning(
|
||||
_pytest.deprecated.STRICT_OPTION, stacklevel=2
|
||||
)
|
||||
|
||||
if self.known_args_namespace.confcutdir is None:
|
||||
if self.inipath is not None:
|
||||
confcutdir = str(self.inipath.parent)
|
||||
|
@ -1432,8 +1424,6 @@ class Config:
|
|||
kwargs=dict(pluginmanager=self.pluginmanager)
|
||||
)
|
||||
self._preparse(args, addopts=addopts)
|
||||
# XXX deprecated hook:
|
||||
self.hook.pytest_cmdline_preparse(config=self, args=args)
|
||||
self._parser.after_preparse = True # type: ignore
|
||||
try:
|
||||
args = self._parser.parse_setoption(
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
from gettext import gettext
|
||||
from typing import Any
|
||||
from typing import Callable
|
||||
|
@ -19,9 +18,6 @@ from typing import Union
|
|||
|
||||
import _pytest._io
|
||||
from _pytest.config.exceptions import UsageError
|
||||
from _pytest.deprecated import ARGUMENT_PERCENT_DEFAULT
|
||||
from _pytest.deprecated import ARGUMENT_TYPE_STR
|
||||
from _pytest.deprecated import ARGUMENT_TYPE_STR_CHOICE
|
||||
from _pytest.deprecated import check_ispytest
|
||||
|
||||
FILE_OR_DIR = "file_or_dir"
|
||||
|
@ -259,39 +255,15 @@ class Argument:
|
|||
https://docs.python.org/3/library/optparse.html#optparse-standard-option-types
|
||||
"""
|
||||
|
||||
_typ_map = {"int": int, "string": str, "float": float, "complex": complex}
|
||||
|
||||
def __init__(self, *names: str, **attrs: Any) -> None:
|
||||
"""Store params in private vars for use in add_argument."""
|
||||
self._attrs = attrs
|
||||
self._short_opts: List[str] = []
|
||||
self._long_opts: List[str] = []
|
||||
if "%default" in (attrs.get("help") or ""):
|
||||
warnings.warn(ARGUMENT_PERCENT_DEFAULT, stacklevel=3)
|
||||
try:
|
||||
typ = attrs["type"]
|
||||
self.type = attrs["type"]
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
# This might raise a keyerror as well, don't want to catch that.
|
||||
if isinstance(typ, str):
|
||||
if typ == "choice":
|
||||
warnings.warn(
|
||||
ARGUMENT_TYPE_STR_CHOICE.format(typ=typ, names=names),
|
||||
stacklevel=4,
|
||||
)
|
||||
# argparse expects a type here take it from
|
||||
# the type of the first element
|
||||
attrs["type"] = type(attrs["choices"][0])
|
||||
else:
|
||||
warnings.warn(
|
||||
ARGUMENT_TYPE_STR.format(typ=typ, names=names), stacklevel=4
|
||||
)
|
||||
attrs["type"] = Argument._typ_map[typ]
|
||||
# Used in test_parseopt -> test_parse_defaultgetter.
|
||||
self.type = attrs["type"]
|
||||
else:
|
||||
self.type = typ
|
||||
try:
|
||||
# Attribute existence is tested in Config._processopt.
|
||||
self.default = attrs["default"]
|
||||
|
@ -322,11 +294,6 @@ class Argument:
|
|||
self._attrs[attr] = getattr(self, attr)
|
||||
except AttributeError:
|
||||
pass
|
||||
if self._attrs.get("help"):
|
||||
a = self._attrs["help"]
|
||||
a = a.replace("%default", "%(default)s")
|
||||
# a = a.replace('%prog', '%(prog)s')
|
||||
self._attrs["help"] = a
|
||||
return self._attrs
|
||||
|
||||
def _set_opt_strings(self, opts: Sequence[str]) -> None:
|
||||
|
|
|
@ -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 _pytest.warning_types import PytestDeprecationWarning
|
||||
from _pytest.warning_types import PytestRemovedIn8Warning
|
||||
from _pytest.warning_types import PytestRemovedIn9Warning
|
||||
from _pytest.warning_types import UnformattedWarning
|
||||
|
||||
|
@ -23,21 +22,6 @@ DEPRECATED_EXTERNAL_PLUGINS = {
|
|||
"pytest_faulthandler",
|
||||
}
|
||||
|
||||
NOSE_SUPPORT = UnformattedWarning(
|
||||
PytestRemovedIn8Warning,
|
||||
"Support for nose tests is deprecated and will be removed in a future release.\n"
|
||||
"{nodeid} is using nose method: `{method}` ({stage})\n"
|
||||
"See docs: https://docs.pytest.org/en/stable/deprecations.html#support-for-tests-written-for-nose",
|
||||
)
|
||||
|
||||
NOSE_SUPPORT_METHOD = UnformattedWarning(
|
||||
PytestRemovedIn8Warning,
|
||||
"Support for nose tests is deprecated and will be removed in a future release.\n"
|
||||
"{nodeid} is using nose-specific method: `{method}(self)`\n"
|
||||
"To remove this warning, rename it to `{method}_method(self)`\n"
|
||||
"See docs: https://docs.pytest.org/en/stable/deprecations.html#support-for-tests-written-for-nose",
|
||||
)
|
||||
|
||||
|
||||
# This can be* removed pytest 8, but it's harmless and common, so no rush to remove.
|
||||
# * If you're in the future: "could have been".
|
||||
|
@ -46,74 +30,10 @@ YIELD_FIXTURE = PytestDeprecationWarning(
|
|||
"Use @pytest.fixture instead; they are the same."
|
||||
)
|
||||
|
||||
WARNING_CMDLINE_PREPARSE_HOOK = PytestRemovedIn8Warning(
|
||||
"The pytest_cmdline_preparse hook is deprecated and will be removed in a future release. \n"
|
||||
"Please use pytest_load_initial_conftests hook instead."
|
||||
)
|
||||
|
||||
FSCOLLECTOR_GETHOOKPROXY_ISINITPATH = PytestRemovedIn8Warning(
|
||||
"The gethookproxy() and isinitpath() methods of FSCollector and Package are deprecated; "
|
||||
"use self.session.gethookproxy() and self.session.isinitpath() instead. "
|
||||
)
|
||||
|
||||
STRICT_OPTION = PytestRemovedIn8Warning(
|
||||
"The --strict option is deprecated, use --strict-markers instead."
|
||||
)
|
||||
|
||||
# This deprecation is never really meant to be removed.
|
||||
PRIVATE = PytestDeprecationWarning("A private pytest class or function was used.")
|
||||
|
||||
ARGUMENT_PERCENT_DEFAULT = PytestRemovedIn8Warning(
|
||||
'pytest now uses argparse. "%default" should be changed to "%(default)s"',
|
||||
)
|
||||
|
||||
ARGUMENT_TYPE_STR_CHOICE = UnformattedWarning(
|
||||
PytestRemovedIn8Warning,
|
||||
"`type` argument to addoption() is the string {typ!r}."
|
||||
" For choices this is optional and can be omitted, "
|
||||
" but when supplied should be a type (for example `str` or `int`)."
|
||||
" (options: {names})",
|
||||
)
|
||||
|
||||
ARGUMENT_TYPE_STR = UnformattedWarning(
|
||||
PytestRemovedIn8Warning,
|
||||
"`type` argument to addoption() is the string {typ!r}, "
|
||||
" but when supplied should be a type (for example `str` or `int`)."
|
||||
" (options: {names})",
|
||||
)
|
||||
|
||||
|
||||
HOOK_LEGACY_PATH_ARG = UnformattedWarning(
|
||||
PytestRemovedIn8Warning,
|
||||
"The ({pylib_path_arg}: py.path.local) argument is deprecated, please use ({pathlib_path_arg}: pathlib.Path)\n"
|
||||
"see https://docs.pytest.org/en/latest/deprecations.html"
|
||||
"#py-path-local-arguments-for-hooks-replaced-with-pathlib-path",
|
||||
)
|
||||
|
||||
NODE_CTOR_FSPATH_ARG = UnformattedWarning(
|
||||
PytestRemovedIn8Warning,
|
||||
"The (fspath: py.path.local) argument to {node_type_name} is deprecated. "
|
||||
"Please use the (path: pathlib.Path) argument instead.\n"
|
||||
"See https://docs.pytest.org/en/latest/deprecations.html"
|
||||
"#fspath-argument-for-node-constructors-replaced-with-pathlib-path",
|
||||
)
|
||||
|
||||
WARNS_NONE_ARG = PytestRemovedIn8Warning(
|
||||
"Passing None has been deprecated.\n"
|
||||
"See https://docs.pytest.org/en/latest/how-to/capture-warnings.html"
|
||||
"#additional-use-cases-of-warnings-in-tests"
|
||||
" for alternatives in common use cases."
|
||||
)
|
||||
|
||||
KEYWORD_MSG_ARG = UnformattedWarning(
|
||||
PytestRemovedIn8Warning,
|
||||
"pytest.{func}(msg=...) is now deprecated, use pytest.{func}(reason=...) instead",
|
||||
)
|
||||
|
||||
INSTANCE_COLLECTOR = PytestRemovedIn8Warning(
|
||||
"The pytest.Instance collector type is deprecated and is no longer used. "
|
||||
"See https://docs.pytest.org/en/latest/deprecations.html#the-pytest-instance-collector",
|
||||
)
|
||||
HOOK_LEGACY_MARKING = UnformattedWarning(
|
||||
PytestDeprecationWarning,
|
||||
"The hook{type} {fullname} uses old-style configuration options (marks or attributes).\n"
|
||||
|
|
|
@ -13,8 +13,6 @@ from typing import Union
|
|||
|
||||
from pluggy import HookspecMarker
|
||||
|
||||
from _pytest.deprecated import WARNING_CMDLINE_PREPARSE_HOOK
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import pdb
|
||||
import warnings
|
||||
|
@ -42,7 +40,6 @@ if TYPE_CHECKING:
|
|||
from _pytest.runner import CallInfo
|
||||
from _pytest.terminal import TerminalReporter
|
||||
from _pytest.terminal import TestShortLogReport
|
||||
from _pytest.compat import LEGACY_PATH
|
||||
|
||||
|
||||
hookspec = HookspecMarker("pytest")
|
||||
|
@ -159,21 +156,6 @@ def pytest_cmdline_parse(
|
|||
"""
|
||||
|
||||
|
||||
@hookspec(warn_on_impl=WARNING_CMDLINE_PREPARSE_HOOK)
|
||||
def pytest_cmdline_preparse(config: "Config", args: List[str]) -> None:
|
||||
"""(**Deprecated**) modify command line arguments before option parsing.
|
||||
|
||||
This hook is considered deprecated and will be removed in a future pytest version. Consider
|
||||
using :hook:`pytest_load_initial_conftests` instead.
|
||||
|
||||
.. note::
|
||||
This hook will not be called for ``conftest.py`` files, only for setuptools plugins.
|
||||
|
||||
:param config: The pytest config object.
|
||||
:param args: Arguments passed on the command line.
|
||||
"""
|
||||
|
||||
|
||||
@hookspec(firstresult=True)
|
||||
def pytest_cmdline_main(config: "Config") -> Optional[Union["ExitCode", int]]:
|
||||
"""Called for performing the main command line action. The default
|
||||
|
@ -263,9 +245,7 @@ def pytest_collection_finish(session: "Session") -> None:
|
|||
|
||||
|
||||
@hookspec(firstresult=True)
|
||||
def pytest_ignore_collect(
|
||||
collection_path: Path, path: "LEGACY_PATH", config: "Config"
|
||||
) -> Optional[bool]:
|
||||
def pytest_ignore_collect(collection_path: Path, config: "Config") -> Optional[bool]:
|
||||
"""Return True to prevent considering this path for collection.
|
||||
|
||||
This hook is consulted for all files and directories prior to calling
|
||||
|
@ -279,8 +259,10 @@ def pytest_ignore_collect(
|
|||
|
||||
.. versionchanged:: 7.0.0
|
||||
The ``collection_path`` parameter was added as a :class:`pathlib.Path`
|
||||
equivalent of the ``path`` parameter. The ``path`` parameter
|
||||
has been deprecated.
|
||||
equivalent of the ``path`` parameter.
|
||||
|
||||
.. versionchanged:: 8.0.0
|
||||
The ``path`` parameter has been removed.
|
||||
"""
|
||||
|
||||
|
||||
|
@ -305,9 +287,7 @@ def pytest_collect_directory(path: Path, parent: "Collector") -> "Optional[Colle
|
|||
"""
|
||||
|
||||
|
||||
def pytest_collect_file(
|
||||
file_path: Path, path: "LEGACY_PATH", parent: "Collector"
|
||||
) -> "Optional[Collector]":
|
||||
def pytest_collect_file(file_path: Path, parent: "Collector") -> "Optional[Collector]":
|
||||
"""Create a :class:`~pytest.Collector` for the given path, or None if not relevant.
|
||||
|
||||
For best results, the returned collector should be a subclass of
|
||||
|
@ -320,8 +300,10 @@ def pytest_collect_file(
|
|||
|
||||
.. versionchanged:: 7.0.0
|
||||
The ``file_path`` parameter was added as a :class:`pathlib.Path`
|
||||
equivalent of the ``path`` parameter. The ``path`` parameter
|
||||
has been deprecated.
|
||||
equivalent of the ``path`` parameter.
|
||||
|
||||
.. versionchanged:: 8.0.0
|
||||
The ``path`` parameter was removed.
|
||||
"""
|
||||
|
||||
|
||||
|
@ -380,9 +362,7 @@ def pytest_make_collect_report(collector: "Collector") -> "Optional[CollectRepor
|
|||
|
||||
|
||||
@hookspec(firstresult=True)
|
||||
def pytest_pycollect_makemodule(
|
||||
module_path: Path, path: "LEGACY_PATH", parent
|
||||
) -> Optional["Module"]:
|
||||
def pytest_pycollect_makemodule(module_path: Path, parent) -> Optional["Module"]:
|
||||
"""Return a :class:`pytest.Module` collector or None for the given path.
|
||||
|
||||
This hook will be called for each matching test module path.
|
||||
|
@ -398,7 +378,8 @@ def pytest_pycollect_makemodule(
|
|||
The ``module_path`` parameter was added as a :class:`pathlib.Path`
|
||||
equivalent of the ``path`` parameter.
|
||||
|
||||
The ``path`` parameter has been deprecated in favor of ``fspath``.
|
||||
.. versionchanged:: 8.0.0
|
||||
The ``path`` parameter has been removed in favor of ``module_path``.
|
||||
"""
|
||||
|
||||
|
||||
|
@ -768,7 +749,7 @@ def pytest_assertion_pass(item: "Item", lineno: int, orig: str, expl: str) -> No
|
|||
|
||||
|
||||
def pytest_report_header( # type:ignore[empty-body]
|
||||
config: "Config", start_path: Path, startdir: "LEGACY_PATH"
|
||||
config: "Config", start_path: Path
|
||||
) -> Union[str, List[str]]:
|
||||
"""Return a string or list of strings to be displayed as header info for terminal reporting.
|
||||
|
||||
|
@ -791,15 +772,16 @@ def pytest_report_header( # type:ignore[empty-body]
|
|||
|
||||
.. versionchanged:: 7.0.0
|
||||
The ``start_path`` parameter was added as a :class:`pathlib.Path`
|
||||
equivalent of the ``startdir`` parameter. The ``startdir`` parameter
|
||||
has been deprecated.
|
||||
equivalent of the ``startdir`` parameter.
|
||||
|
||||
.. versionchanged:: 8.0.0
|
||||
The ``startdir`` parameter has been removed.
|
||||
"""
|
||||
|
||||
|
||||
def pytest_report_collectionfinish( # type:ignore[empty-body]
|
||||
config: "Config",
|
||||
start_path: Path,
|
||||
startdir: "LEGACY_PATH",
|
||||
items: Sequence["Item"],
|
||||
) -> Union[str, List[str]]:
|
||||
"""Return a string or list of strings to be displayed after collection
|
||||
|
@ -823,8 +805,10 @@ def pytest_report_collectionfinish( # type:ignore[empty-body]
|
|||
|
||||
.. versionchanged:: 7.0.0
|
||||
The ``start_path`` parameter was added as a :class:`pathlib.Path`
|
||||
equivalent of the ``startdir`` parameter. The ``startdir`` parameter
|
||||
has been deprecated.
|
||||
equivalent of the ``startdir`` parameter.
|
||||
|
||||
.. versionchanged:: 8.0.0
|
||||
The ``startdir`` parameter has been removed.
|
||||
"""
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
"""Add backward compatibility support for the legacy py path type."""
|
||||
import dataclasses
|
||||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
@ -12,9 +13,8 @@ from typing import Union
|
|||
|
||||
from iniconfig import SectionWrapper
|
||||
|
||||
import py
|
||||
from _pytest.cacheprovider import Cache
|
||||
from _pytest.compat import LEGACY_PATH
|
||||
from _pytest.compat import legacy_path
|
||||
from _pytest.config import Config
|
||||
from _pytest.config import hookimpl
|
||||
from _pytest.config import PytestPluginManager
|
||||
|
@ -36,6 +36,20 @@ if TYPE_CHECKING:
|
|||
import pexpect
|
||||
|
||||
|
||||
#: constant to prepare valuing pylib path replacements/lazy proxies later on
|
||||
# intended for removal in pytest 8.0 or 9.0
|
||||
|
||||
# fmt: off
|
||||
# intentional space to create a fake difference for the verification
|
||||
LEGACY_PATH = py.path. local
|
||||
# fmt: on
|
||||
|
||||
|
||||
def legacy_path(path: Union[str, "os.PathLike[str]"]) -> LEGACY_PATH:
|
||||
"""Internal wrapper to prepare lazy proxies for legacy_path instances"""
|
||||
return LEGACY_PATH(path)
|
||||
|
||||
|
||||
@final
|
||||
class Testdir:
|
||||
"""
|
||||
|
|
|
@ -33,7 +33,6 @@ from _pytest.config import hookimpl
|
|||
from _pytest.config import PytestPluginManager
|
||||
from _pytest.config import UsageError
|
||||
from _pytest.config.argparsing import Parser
|
||||
from _pytest.config.compat import PathAwareHookProxy
|
||||
from _pytest.fixtures import FixtureManager
|
||||
from _pytest.outcomes import exit
|
||||
from _pytest.pathlib import absolutepath
|
||||
|
@ -542,7 +541,6 @@ class Session(nodes.Collector):
|
|||
super().__init__(
|
||||
name="",
|
||||
path=config.rootpath,
|
||||
fspath=None,
|
||||
parent=None,
|
||||
config=config,
|
||||
session=self,
|
||||
|
@ -644,7 +642,7 @@ class Session(nodes.Collector):
|
|||
proxy: pluggy.HookRelay
|
||||
if remove_mods:
|
||||
# One or more conftests are not in use at this path.
|
||||
proxy = PathAwareHookProxy(FSHookProxy(pm, remove_mods)) # type: ignore[arg-type,assignment]
|
||||
proxy = FSHookProxy(pm, remove_mods) # type: ignore[arg-type,assignment]
|
||||
else:
|
||||
# All plugins are active for this fspath.
|
||||
proxy = self.config.hook
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import abc
|
||||
import os
|
||||
import pathlib
|
||||
import warnings
|
||||
from functools import cached_property
|
||||
from inspect import signature
|
||||
|
@ -28,12 +27,8 @@ from _pytest._code import getfslineno
|
|||
from _pytest._code.code import ExceptionInfo
|
||||
from _pytest._code.code import TerminalRepr
|
||||
from _pytest._code.code import Traceback
|
||||
from _pytest.compat import LEGACY_PATH
|
||||
from _pytest.config import Config
|
||||
from _pytest.config import ConftestImportFailure
|
||||
from _pytest.config.compat import _check_path
|
||||
from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH
|
||||
from _pytest.deprecated import NODE_CTOR_FSPATH_ARG
|
||||
from _pytest.mark.structures import Mark
|
||||
from _pytest.mark.structures import MarkDecorator
|
||||
from _pytest.mark.structures import NodeKeywords
|
||||
|
@ -99,27 +94,6 @@ def iterparentnodeids(nodeid: str) -> Iterator[str]:
|
|||
yield nodeid
|
||||
|
||||
|
||||
def _imply_path(
|
||||
node_type: Type["Node"],
|
||||
path: Optional[Path],
|
||||
fspath: Optional[LEGACY_PATH],
|
||||
) -> Path:
|
||||
if fspath is not None:
|
||||
warnings.warn(
|
||||
NODE_CTOR_FSPATH_ARG.format(
|
||||
node_type_name=node_type.__name__,
|
||||
),
|
||||
stacklevel=6,
|
||||
)
|
||||
if path is not None:
|
||||
if fspath is not None:
|
||||
_check_path(path, fspath)
|
||||
return path
|
||||
else:
|
||||
assert fspath is not None
|
||||
return Path(fspath)
|
||||
|
||||
|
||||
_NodeType = TypeVar("_NodeType", bound="Node")
|
||||
|
||||
|
||||
|
@ -174,14 +148,6 @@ class Node(abc.ABC, metaclass=NodeMeta):
|
|||
``Collector``\'s are the internal nodes of the tree, and ``Item``\'s are the
|
||||
leaf nodes.
|
||||
"""
|
||||
|
||||
# Implemented in the legacypath plugin.
|
||||
#: A ``LEGACY_PATH`` copy of the :attr:`path` attribute. Intended for usage
|
||||
#: for methods not migrated to ``pathlib.Path`` yet, such as
|
||||
#: :meth:`Item.reportinfo <pytest.Item.reportinfo>`. Will be deprecated in
|
||||
#: a future release, prefer using :attr:`path` instead.
|
||||
fspath: LEGACY_PATH
|
||||
|
||||
# Use __slots__ to make attribute access faster.
|
||||
# Note that __dict__ is still available.
|
||||
__slots__ = (
|
||||
|
@ -201,7 +167,6 @@ class Node(abc.ABC, metaclass=NodeMeta):
|
|||
parent: "Optional[Node]" = None,
|
||||
config: Optional[Config] = None,
|
||||
session: "Optional[Session]" = None,
|
||||
fspath: Optional[LEGACY_PATH] = None,
|
||||
path: Optional[Path] = None,
|
||||
nodeid: Optional[str] = None,
|
||||
) -> None:
|
||||
|
@ -227,10 +192,11 @@ class Node(abc.ABC, metaclass=NodeMeta):
|
|||
raise TypeError("session or parent must be provided")
|
||||
self.session = parent.session
|
||||
|
||||
if path is None and fspath is None:
|
||||
if path is None:
|
||||
path = getattr(parent, "path", None)
|
||||
assert path is not None
|
||||
#: Filesystem path where this node was collected from (can be None).
|
||||
self.path: pathlib.Path = _imply_path(type(self), path, fspath=fspath)
|
||||
self.path = path
|
||||
|
||||
# The explicit annotation is to avoid publicly exposing NodeKeywords.
|
||||
#: Keywords/markers collected from all scopes.
|
||||
|
@ -596,7 +562,6 @@ class FSCollector(Collector, abc.ABC):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
fspath: Optional[LEGACY_PATH] = None,
|
||||
path_or_parent: Optional[Union[Path, Node]] = None,
|
||||
path: Optional[Path] = None,
|
||||
name: Optional[str] = None,
|
||||
|
@ -612,8 +577,8 @@ class FSCollector(Collector, abc.ABC):
|
|||
elif isinstance(path_or_parent, Path):
|
||||
assert path is None
|
||||
path = path_or_parent
|
||||
assert path is not None
|
||||
|
||||
path = _imply_path(type(self), path, fspath=fspath)
|
||||
if name is None:
|
||||
name = path.name
|
||||
if parent is not None and parent.path != path:
|
||||
|
@ -653,20 +618,11 @@ class FSCollector(Collector, abc.ABC):
|
|||
cls,
|
||||
parent,
|
||||
*,
|
||||
fspath: Optional[LEGACY_PATH] = None,
|
||||
path: Optional[Path] = None,
|
||||
**kw,
|
||||
):
|
||||
"""The public constructor."""
|
||||
return super().from_parent(parent=parent, fspath=fspath, path=path, **kw)
|
||||
|
||||
def gethookproxy(self, fspath: "os.PathLike[str]"):
|
||||
warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2)
|
||||
return self.session.gethookproxy(fspath)
|
||||
|
||||
def isinitpath(self, path: Union[str, "os.PathLike[str]"]) -> bool:
|
||||
warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2)
|
||||
return self.session.isinitpath(path)
|
||||
return super().from_parent(parent=parent, path=path, **kw)
|
||||
|
||||
|
||||
class File(FSCollector, abc.ABC):
|
||||
|
|
|
@ -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
|
||||
functions creating them."""
|
||||
import sys
|
||||
import warnings
|
||||
from typing import Any
|
||||
from typing import Callable
|
||||
from typing import cast
|
||||
|
@ -11,8 +10,6 @@ from typing import Protocol
|
|||
from typing import Type
|
||||
from typing import TypeVar
|
||||
|
||||
from _pytest.deprecated import KEYWORD_MSG_ARG
|
||||
|
||||
|
||||
class OutcomeException(BaseException):
|
||||
"""OutcomeException and its subclass instances indicate and contain info
|
||||
|
@ -103,7 +100,8 @@ def _with_exception(exception_type: _ET) -> Callable[[_F], _WithException[_F, _E
|
|||
|
||||
@_with_exception(Exit)
|
||||
def exit(
|
||||
reason: str = "", returncode: Optional[int] = None, *, msg: Optional[str] = None
|
||||
reason: str = "",
|
||||
returncode: Optional[int] = None,
|
||||
) -> NoReturn:
|
||||
"""Exit testing process.
|
||||
|
||||
|
@ -113,28 +111,16 @@ def exit(
|
|||
|
||||
:param returncode:
|
||||
Return code to be used when exiting pytest. None means the same as ``0`` (no error), same as :func:`sys.exit`.
|
||||
|
||||
:param msg:
|
||||
Same as ``reason``, but deprecated. Will be removed in a future version, use ``reason`` instead.
|
||||
"""
|
||||
__tracebackhide__ = True
|
||||
from _pytest.config import UsageError
|
||||
|
||||
if reason and msg:
|
||||
raise UsageError(
|
||||
"cannot pass reason and msg to exit(), `msg` is deprecated, use `reason`."
|
||||
)
|
||||
if not reason:
|
||||
if msg is None:
|
||||
raise UsageError("exit() requires a reason argument")
|
||||
warnings.warn(KEYWORD_MSG_ARG.format(func="exit"), stacklevel=2)
|
||||
reason = msg
|
||||
raise Exit(reason, returncode)
|
||||
|
||||
|
||||
@_with_exception(Skipped)
|
||||
def skip(
|
||||
reason: str = "", *, allow_module_level: bool = False, msg: Optional[str] = None
|
||||
reason: str = "",
|
||||
*,
|
||||
allow_module_level: bool = False,
|
||||
) -> NoReturn:
|
||||
"""Skip an executing test with the given message.
|
||||
|
||||
|
@ -153,9 +139,6 @@ def skip(
|
|||
|
||||
Defaults to False.
|
||||
|
||||
:param msg:
|
||||
Same as ``reason``, but deprecated. Will be removed in a future version, use ``reason`` instead.
|
||||
|
||||
.. note::
|
||||
It is better to use the :ref:`pytest.mark.skipif ref` marker when
|
||||
possible to declare a test to be skipped under certain conditions
|
||||
|
@ -164,12 +147,11 @@ def skip(
|
|||
to skip a doctest statically.
|
||||
"""
|
||||
__tracebackhide__ = True
|
||||
reason = _resolve_msg_to_reason("skip", reason, msg)
|
||||
raise Skipped(msg=reason, allow_module_level=allow_module_level)
|
||||
|
||||
|
||||
@_with_exception(Failed)
|
||||
def fail(reason: str = "", pytrace: bool = True, msg: Optional[str] = None) -> NoReturn:
|
||||
def fail(reason: str = "", pytrace: bool = True) -> NoReturn:
|
||||
"""Explicitly fail an executing test with the given message.
|
||||
|
||||
:param reason:
|
||||
|
@ -178,51 +160,11 @@ def fail(reason: str = "", pytrace: bool = True, msg: Optional[str] = None) -> N
|
|||
:param pytrace:
|
||||
If False, msg represents the full failure information and no
|
||||
python traceback will be reported.
|
||||
|
||||
:param msg:
|
||||
Same as ``reason``, but deprecated. Will be removed in a future version, use ``reason`` instead.
|
||||
"""
|
||||
__tracebackhide__ = True
|
||||
reason = _resolve_msg_to_reason("fail", reason, msg)
|
||||
raise Failed(msg=reason, pytrace=pytrace)
|
||||
|
||||
|
||||
def _resolve_msg_to_reason(
|
||||
func_name: str, reason: str, msg: Optional[str] = None
|
||||
) -> str:
|
||||
"""
|
||||
Handles converting the deprecated msg parameter if provided into
|
||||
reason, raising a deprecation warning. This function will be removed
|
||||
when the optional msg argument is removed from here in future.
|
||||
|
||||
:param str func_name:
|
||||
The name of the offending function, this is formatted into the deprecation message.
|
||||
|
||||
:param str reason:
|
||||
The reason= passed into either pytest.fail() or pytest.skip()
|
||||
|
||||
:param str msg:
|
||||
The msg= passed into either pytest.fail() or pytest.skip(). This will
|
||||
be converted into reason if it is provided to allow pytest.skip(msg=) or
|
||||
pytest.fail(msg=) to continue working in the interim period.
|
||||
|
||||
:returns:
|
||||
The value to use as reason.
|
||||
|
||||
"""
|
||||
__tracebackhide__ = True
|
||||
if msg is not None:
|
||||
if reason:
|
||||
from pytest import UsageError
|
||||
|
||||
raise UsageError(
|
||||
f"Passing both ``reason`` and ``msg`` to pytest.{func_name}(...) is not permitted."
|
||||
)
|
||||
warnings.warn(KEYWORD_MSG_ARG.format(func=func_name), stacklevel=3)
|
||||
reason = msg
|
||||
return reason
|
||||
|
||||
|
||||
class XFailed(Failed):
|
||||
"""Raised from an explicit call to pytest.xfail()."""
|
||||
|
||||
|
|
|
@ -47,7 +47,6 @@ from _pytest.compat import getimfunc
|
|||
from _pytest.compat import getlocation
|
||||
from _pytest.compat import is_async_function
|
||||
from _pytest.compat import is_generator
|
||||
from _pytest.compat import LEGACY_PATH
|
||||
from _pytest.compat import NOTSET
|
||||
from _pytest.compat import safe_getattr
|
||||
from _pytest.compat import safe_isclass
|
||||
|
@ -57,8 +56,6 @@ from _pytest.config import ExitCode
|
|||
from _pytest.config import hookimpl
|
||||
from _pytest.config.argparsing import Parser
|
||||
from _pytest.deprecated import check_ispytest
|
||||
from _pytest.deprecated import INSTANCE_COLLECTOR
|
||||
from _pytest.deprecated import NOSE_SUPPORT_METHOD
|
||||
from _pytest.fixtures import FixtureDef
|
||||
from _pytest.fixtures import FixtureRequest
|
||||
from _pytest.fixtures import FuncFixtureInfo
|
||||
|
@ -597,23 +594,12 @@ class Module(nodes.File, PyCollector):
|
|||
Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with
|
||||
other fixtures (#517).
|
||||
"""
|
||||
has_nose = self.config.pluginmanager.has_plugin("nose")
|
||||
setup_module = _get_first_non_fixture_func(
|
||||
self.obj, ("setUpModule", "setup_module")
|
||||
)
|
||||
if setup_module is None and has_nose:
|
||||
# The name "setup" is too common - only treat as fixture if callable.
|
||||
setup_module = _get_first_non_fixture_func(self.obj, ("setup",))
|
||||
if not callable(setup_module):
|
||||
setup_module = None
|
||||
teardown_module = _get_first_non_fixture_func(
|
||||
self.obj, ("tearDownModule", "teardown_module")
|
||||
)
|
||||
if teardown_module is None and has_nose:
|
||||
teardown_module = _get_first_non_fixture_func(self.obj, ("teardown",))
|
||||
# Same as "setup" above - only treat as fixture if callable.
|
||||
if not callable(teardown_module):
|
||||
teardown_module = None
|
||||
|
||||
if setup_module is None and teardown_module is None:
|
||||
return
|
||||
|
@ -685,7 +671,6 @@ class Package(nodes.Directory):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
fspath: Optional[LEGACY_PATH],
|
||||
parent: nodes.Collector,
|
||||
# NOTE: following args are unused:
|
||||
config=None,
|
||||
|
@ -697,7 +682,6 @@ class Package(nodes.Directory):
|
|||
# super().__init__(self, fspath, parent=parent)
|
||||
session = parent.session
|
||||
super().__init__(
|
||||
fspath=fspath,
|
||||
path=path,
|
||||
parent=parent,
|
||||
config=config,
|
||||
|
@ -854,21 +838,10 @@ class Class(PyCollector):
|
|||
Using a fixture to invoke these methods ensures we play nicely and unsurprisingly with
|
||||
other fixtures (#517).
|
||||
"""
|
||||
has_nose = self.config.pluginmanager.has_plugin("nose")
|
||||
setup_name = "setup_method"
|
||||
setup_method = _get_first_non_fixture_func(self.obj, (setup_name,))
|
||||
emit_nose_setup_warning = False
|
||||
if setup_method is None and has_nose:
|
||||
setup_name = "setup"
|
||||
emit_nose_setup_warning = True
|
||||
setup_method = _get_first_non_fixture_func(self.obj, (setup_name,))
|
||||
teardown_name = "teardown_method"
|
||||
teardown_method = _get_first_non_fixture_func(self.obj, (teardown_name,))
|
||||
emit_nose_teardown_warning = False
|
||||
if teardown_method is None and has_nose:
|
||||
teardown_name = "teardown"
|
||||
emit_nose_teardown_warning = True
|
||||
teardown_method = _get_first_non_fixture_func(self.obj, (teardown_name,))
|
||||
if setup_method is None and teardown_method is None:
|
||||
return
|
||||
|
||||
|
@ -883,42 +856,14 @@ class Class(PyCollector):
|
|||
if setup_method is not None:
|
||||
func = getattr(self, setup_name)
|
||||
_call_with_optional_argument(func, method)
|
||||
if emit_nose_setup_warning:
|
||||
warnings.warn(
|
||||
NOSE_SUPPORT_METHOD.format(
|
||||
nodeid=request.node.nodeid, method="setup"
|
||||
),
|
||||
stacklevel=2,
|
||||
)
|
||||
yield
|
||||
if teardown_method is not None:
|
||||
func = getattr(self, teardown_name)
|
||||
_call_with_optional_argument(func, method)
|
||||
if emit_nose_teardown_warning:
|
||||
warnings.warn(
|
||||
NOSE_SUPPORT_METHOD.format(
|
||||
nodeid=request.node.nodeid, method="teardown"
|
||||
),
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
self.obj.__pytest_setup_method = xunit_setup_method_fixture
|
||||
|
||||
|
||||
class InstanceDummy:
|
||||
"""Instance used to be a node type between Class and Function. It has been
|
||||
removed in pytest 7.0. Some plugins exist which reference `pytest.Instance`
|
||||
only to ignore it; this dummy class keeps them working. This will be removed
|
||||
in pytest 8."""
|
||||
|
||||
|
||||
def __getattr__(name: str) -> object:
|
||||
if name == "Instance":
|
||||
warnings.warn(INSTANCE_COLLECTOR, 2)
|
||||
return InstanceDummy
|
||||
raise AttributeError(f"module {__name__} has no attribute {name}")
|
||||
|
||||
|
||||
def hasinit(obj: object) -> bool:
|
||||
init: object = getattr(obj, "__init__", None)
|
||||
if init:
|
||||
|
|
|
@ -18,7 +18,6 @@ from typing import TypeVar
|
|||
from typing import Union
|
||||
|
||||
from _pytest.deprecated import check_ispytest
|
||||
from _pytest.deprecated import WARNS_NONE_ARG
|
||||
from _pytest.fixtures import fixture
|
||||
from _pytest.outcomes import fail
|
||||
|
||||
|
@ -264,9 +263,7 @@ class WarningsRecorder(warnings.catch_warnings): # type:ignore[type-arg]
|
|||
class WarningsChecker(WarningsRecorder):
|
||||
def __init__(
|
||||
self,
|
||||
expected_warning: Optional[
|
||||
Union[Type[Warning], Tuple[Type[Warning], ...]]
|
||||
] = Warning,
|
||||
expected_warning: Union[Type[Warning], Tuple[Type[Warning], ...]] = Warning,
|
||||
match_expr: Optional[Union[str, Pattern[str]]] = None,
|
||||
*,
|
||||
_ispytest: bool = False,
|
||||
|
@ -275,15 +272,14 @@ class WarningsChecker(WarningsRecorder):
|
|||
super().__init__(_ispytest=True)
|
||||
|
||||
msg = "exceptions must be derived from Warning, not %s"
|
||||
if expected_warning is None:
|
||||
warnings.warn(WARNS_NONE_ARG, stacklevel=4)
|
||||
expected_warning_tup = None
|
||||
elif isinstance(expected_warning, tuple):
|
||||
if isinstance(expected_warning, tuple):
|
||||
for exc in expected_warning:
|
||||
if not issubclass(exc, Warning):
|
||||
raise TypeError(msg % type(exc))
|
||||
expected_warning_tup = expected_warning
|
||||
elif issubclass(expected_warning, Warning):
|
||||
elif isinstance(expected_warning, type) and issubclass(
|
||||
expected_warning, Warning
|
||||
):
|
||||
expected_warning_tup = (expected_warning,)
|
||||
else:
|
||||
raise TypeError(msg % type(expected_warning))
|
||||
|
@ -307,10 +303,6 @@ class WarningsChecker(WarningsRecorder):
|
|||
|
||||
__tracebackhide__ = True
|
||||
|
||||
if self.expected_warning is None:
|
||||
# nothing to do in this deprecated case, see WARNS_NONE_ARG above
|
||||
return
|
||||
|
||||
def found_str():
|
||||
return pformat([record.message for record in self], indent=2)
|
||||
|
||||
|
|
|
@ -49,12 +49,6 @@ class PytestDeprecationWarning(PytestWarning, DeprecationWarning):
|
|||
__module__ = "pytest"
|
||||
|
||||
|
||||
class PytestRemovedIn8Warning(PytestDeprecationWarning):
|
||||
"""Warning class for features that will be removed in pytest 8."""
|
||||
|
||||
__module__ = "pytest"
|
||||
|
||||
|
||||
class PytestRemovedIn9Warning(PytestDeprecationWarning):
|
||||
"""Warning class for features that will be removed in pytest 9."""
|
||||
|
||||
|
|
|
@ -46,7 +46,8 @@ def catch_warnings_for_item(
|
|||
warnings.filterwarnings("always", category=DeprecationWarning)
|
||||
warnings.filterwarnings("always", category=PendingDeprecationWarning)
|
||||
|
||||
warnings.filterwarnings("error", category=pytest.PytestRemovedIn8Warning)
|
||||
# To be enabled in pytest 9.0.0.
|
||||
# warnings.filterwarnings("error", category=pytest.PytestRemovedIn9Warning)
|
||||
|
||||
apply_warning_filters(config_filters, cmdline_filters)
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
# PYTHON_ARGCOMPLETE_OK
|
||||
"""pytest: unit and functional testing with Python."""
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from _pytest import __version__
|
||||
from _pytest import version_tuple
|
||||
from _pytest._code import ExceptionInfo
|
||||
|
@ -75,7 +73,6 @@ from _pytest.warning_types import PytestCollectionWarning
|
|||
from _pytest.warning_types import PytestConfigWarning
|
||||
from _pytest.warning_types import PytestDeprecationWarning
|
||||
from _pytest.warning_types import PytestExperimentalApiWarning
|
||||
from _pytest.warning_types import PytestRemovedIn8Warning
|
||||
from _pytest.warning_types import PytestRemovedIn9Warning
|
||||
from _pytest.warning_types import PytestReturnNotNoneWarning
|
||||
from _pytest.warning_types import PytestUnhandledCoroutineWarning
|
||||
|
@ -139,7 +136,6 @@ __all__ = [
|
|||
"PytestConfigWarning",
|
||||
"PytestDeprecationWarning",
|
||||
"PytestExperimentalApiWarning",
|
||||
"PytestRemovedIn8Warning",
|
||||
"PytestRemovedIn9Warning",
|
||||
"PytestReturnNotNoneWarning",
|
||||
"Pytester",
|
||||
|
@ -170,13 +166,3 @@ __all__ = [
|
|||
"xfail",
|
||||
"yield_fixture",
|
||||
]
|
||||
|
||||
if not TYPE_CHECKING:
|
||||
|
||||
def __getattr__(name: str) -> object:
|
||||
if name == "Instance":
|
||||
# The import emits a deprecation warning.
|
||||
from _pytest.python import Instance
|
||||
|
||||
return Instance
|
||||
raise AttributeError(f"module {__name__} has no attribute {name}")
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
import re
|
||||
import sys
|
||||
import warnings
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from _pytest import deprecated
|
||||
from _pytest.compat import legacy_path
|
||||
from _pytest.pytester import Pytester
|
||||
from pytest import PytestDeprecationWarning
|
||||
|
||||
|
@ -68,50 +62,6 @@ def test_hookimpl_via_function_attributes_are_deprecated():
|
|||
assert record.filename == __file__
|
||||
|
||||
|
||||
def test_fscollector_gethookproxy_isinitpath(pytester: Pytester) -> None:
|
||||
module = pytester.getmodulecol(
|
||||
"""
|
||||
def test_foo(): pass
|
||||
""",
|
||||
withinit=True,
|
||||
)
|
||||
assert isinstance(module, pytest.Module)
|
||||
package = module.parent
|
||||
assert isinstance(package, pytest.Package)
|
||||
|
||||
with pytest.warns(pytest.PytestDeprecationWarning, match="gethookproxy"):
|
||||
package.gethookproxy(pytester.path)
|
||||
|
||||
with pytest.warns(pytest.PytestDeprecationWarning, match="isinitpath"):
|
||||
package.isinitpath(pytester.path)
|
||||
|
||||
# The methods on Session are *not* deprecated.
|
||||
session = module.session
|
||||
with warnings.catch_warnings(record=True) as rec:
|
||||
session.gethookproxy(pytester.path)
|
||||
session.isinitpath(pytester.path)
|
||||
assert len(rec) == 0
|
||||
|
||||
|
||||
def test_strict_option_is_deprecated(pytester: Pytester) -> None:
|
||||
"""--strict is a deprecated alias to --strict-markers (#7530)."""
|
||||
pytester.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
|
||||
@pytest.mark.unknown
|
||||
def test_foo(): pass
|
||||
"""
|
||||
)
|
||||
result = pytester.runpytest("--strict", "-Wdefault::pytest.PytestRemovedIn8Warning")
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"'unknown' not found in `markers` configuration option",
|
||||
"*PytestRemovedIn8Warning: The --strict option is deprecated, use --strict-markers instead.",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def test_yield_fixture_is_deprecated() -> None:
|
||||
with pytest.warns(DeprecationWarning, match=r"yield_fixture is deprecated"):
|
||||
|
||||
|
@ -134,159 +84,6 @@ def test_private_is_deprecated() -> None:
|
|||
PrivateInit(10, _ispytest=True)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hooktype", ["hook", "ihook"])
|
||||
def test_hookproxy_warnings_for_pathlib(tmp_path, hooktype, request):
|
||||
path = legacy_path(tmp_path)
|
||||
|
||||
PATH_WARN_MATCH = r".*path: py\.path\.local\) argument is deprecated, please use \(collection_path: pathlib\.Path.*"
|
||||
if hooktype == "ihook":
|
||||
hooks = request.node.ihook
|
||||
else:
|
||||
hooks = request.config.hook
|
||||
|
||||
with pytest.warns(PytestDeprecationWarning, match=PATH_WARN_MATCH) as r:
|
||||
l1 = sys._getframe().f_lineno
|
||||
hooks.pytest_ignore_collect(
|
||||
config=request.config, path=path, collection_path=tmp_path
|
||||
)
|
||||
l2 = sys._getframe().f_lineno
|
||||
|
||||
(record,) = r
|
||||
assert record.filename == __file__
|
||||
assert l1 < record.lineno < l2
|
||||
|
||||
hooks.pytest_ignore_collect(config=request.config, collection_path=tmp_path)
|
||||
|
||||
# Passing entirely *different* paths is an outright error.
|
||||
with pytest.raises(ValueError, match=r"path.*fspath.*need to be equal"):
|
||||
with pytest.warns(PytestDeprecationWarning, match=PATH_WARN_MATCH) as r:
|
||||
hooks.pytest_ignore_collect(
|
||||
config=request.config, path=path, collection_path=Path("/bla/bla")
|
||||
)
|
||||
|
||||
|
||||
def test_warns_none_is_deprecated():
|
||||
with pytest.warns(
|
||||
PytestDeprecationWarning,
|
||||
match=re.escape(
|
||||
"Passing None has been deprecated.\n"
|
||||
"See https://docs.pytest.org/en/latest/how-to/capture-warnings.html"
|
||||
"#additional-use-cases-of-warnings-in-tests"
|
||||
" for alternatives in common use cases."
|
||||
),
|
||||
):
|
||||
with pytest.warns(None): # type: ignore[call-overload]
|
||||
pass
|
||||
|
||||
|
||||
class TestSkipMsgArgumentDeprecated:
|
||||
def test_skip_with_msg_is_deprecated(self, pytester: Pytester) -> None:
|
||||
p = pytester.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
|
||||
def test_skipping_msg():
|
||||
pytest.skip(msg="skippedmsg")
|
||||
"""
|
||||
)
|
||||
result = pytester.runpytest(p, "-Wdefault::pytest.PytestRemovedIn8Warning")
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"*PytestRemovedIn8Warning: pytest.skip(msg=...) is now deprecated, "
|
||||
"use pytest.skip(reason=...) instead",
|
||||
'*pytest.skip(msg="skippedmsg")*',
|
||||
]
|
||||
)
|
||||
result.assert_outcomes(skipped=1, warnings=1)
|
||||
|
||||
def test_fail_with_msg_is_deprecated(self, pytester: Pytester) -> None:
|
||||
p = pytester.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
|
||||
def test_failing_msg():
|
||||
pytest.fail(msg="failedmsg")
|
||||
"""
|
||||
)
|
||||
result = pytester.runpytest(p, "-Wdefault::pytest.PytestRemovedIn8Warning")
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"*PytestRemovedIn8Warning: pytest.fail(msg=...) is now deprecated, "
|
||||
"use pytest.fail(reason=...) instead",
|
||||
'*pytest.fail(msg="failedmsg")',
|
||||
]
|
||||
)
|
||||
result.assert_outcomes(failed=1, warnings=1)
|
||||
|
||||
def test_exit_with_msg_is_deprecated(self, pytester: Pytester) -> None:
|
||||
p = pytester.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
|
||||
def test_exit_msg():
|
||||
pytest.exit(msg="exitmsg")
|
||||
"""
|
||||
)
|
||||
result = pytester.runpytest(p, "-Wdefault::pytest.PytestRemovedIn8Warning")
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"*PytestRemovedIn8Warning: pytest.exit(msg=...) is now deprecated, "
|
||||
"use pytest.exit(reason=...) instead",
|
||||
]
|
||||
)
|
||||
result.assert_outcomes(warnings=1)
|
||||
|
||||
|
||||
def test_deprecation_of_cmdline_preparse(pytester: Pytester) -> None:
|
||||
pytester.makeconftest(
|
||||
"""
|
||||
def pytest_cmdline_preparse(config, args):
|
||||
...
|
||||
|
||||
"""
|
||||
)
|
||||
result = pytester.runpytest("-Wdefault::pytest.PytestRemovedIn8Warning")
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"*PytestRemovedIn8Warning: The pytest_cmdline_preparse hook is deprecated*",
|
||||
"*Please use pytest_load_initial_conftests hook instead.*",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def test_node_ctor_fspath_argument_is_deprecated(pytester: Pytester) -> None:
|
||||
mod = pytester.getmodulecol("")
|
||||
|
||||
class MyFile(pytest.File):
|
||||
def collect(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
with pytest.warns(
|
||||
pytest.PytestDeprecationWarning,
|
||||
match=re.escape(
|
||||
"The (fspath: py.path.local) argument to MyFile is deprecated."
|
||||
),
|
||||
):
|
||||
MyFile.from_parent(
|
||||
parent=mod.parent,
|
||||
fspath=legacy_path("bla"),
|
||||
)
|
||||
|
||||
|
||||
def test_importing_instance_is_deprecated(pytester: Pytester) -> None:
|
||||
with pytest.warns(
|
||||
pytest.PytestDeprecationWarning,
|
||||
match=re.escape("The pytest.Instance collector type is deprecated"),
|
||||
):
|
||||
pytest.Instance # type:ignore[attr-defined]
|
||||
|
||||
with pytest.warns(
|
||||
pytest.PytestDeprecationWarning,
|
||||
match=re.escape("The pytest.Instance collector type is deprecated"),
|
||||
):
|
||||
from _pytest.python import Instance # noqa: F401
|
||||
|
||||
|
||||
def test_fixture_disallow_on_marked_functions():
|
||||
"""Test that applying @pytest.fixture to a marked function warns (#3364)."""
|
||||
with pytest.warns(
|
||||
|
@ -336,62 +133,3 @@ def test_fixture_disallowed_between_marks():
|
|||
raise NotImplementedError()
|
||||
|
||||
assert len(record) == 2 # one for each mark decorator
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("default")
|
||||
def test_nose_deprecated_with_setup(pytester: Pytester) -> None:
|
||||
pytest.importorskip("nose")
|
||||
pytester.makepyfile(
|
||||
"""
|
||||
from nose.tools import with_setup
|
||||
|
||||
def setup_fn_no_op():
|
||||
...
|
||||
|
||||
def teardown_fn_no_op():
|
||||
...
|
||||
|
||||
@with_setup(setup_fn_no_op, teardown_fn_no_op)
|
||||
def test_omits_warnings():
|
||||
...
|
||||
"""
|
||||
)
|
||||
output = pytester.runpytest("-Wdefault::pytest.PytestRemovedIn8Warning")
|
||||
message = [
|
||||
"*PytestRemovedIn8Warning: Support for nose tests is deprecated and will be removed in a future release.",
|
||||
"*test_nose_deprecated_with_setup.py::test_omits_warnings is using nose method: `setup_fn_no_op` (setup)",
|
||||
"*PytestRemovedIn8Warning: Support for nose tests is deprecated and will be removed in a future release.",
|
||||
"*test_nose_deprecated_with_setup.py::test_omits_warnings is using nose method: `teardown_fn_no_op` (teardown)",
|
||||
]
|
||||
output.stdout.fnmatch_lines(message)
|
||||
output.assert_outcomes(passed=1)
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("default")
|
||||
def test_nose_deprecated_setup_teardown(pytester: Pytester) -> None:
|
||||
pytest.importorskip("nose")
|
||||
pytester.makepyfile(
|
||||
"""
|
||||
class Test:
|
||||
|
||||
def setup(self):
|
||||
...
|
||||
|
||||
def teardown(self):
|
||||
...
|
||||
|
||||
def test(self):
|
||||
...
|
||||
"""
|
||||
)
|
||||
output = pytester.runpytest("-Wdefault::pytest.PytestRemovedIn8Warning")
|
||||
message = [
|
||||
"*PytestRemovedIn8Warning: Support for nose tests is deprecated and will be removed in a future release.",
|
||||
"*test_nose_deprecated_setup_teardown.py::Test::test is using nose-specific method: `setup(self)`",
|
||||
"*To remove this warning, rename it to `setup_method(self)`",
|
||||
"*PytestRemovedIn8Warning: Support for nose tests is deprecated and will be removed in a future release.",
|
||||
"*test_nose_deprecated_setup_teardown.py::Test::test is using nose-specific method: `teardown(self)`",
|
||||
"*To remove this warning, rename it to `teardown_method(self)`",
|
||||
]
|
||||
output.stdout.fnmatch_lines(message)
|
||||
output.assert_outcomes(passed=1)
|
||||
|
|
|
@ -1253,17 +1253,6 @@ def test_plugin_loading_order(pytester: Pytester) -> None:
|
|||
assert result.ret == 0
|
||||
|
||||
|
||||
def test_cmdline_processargs_simple(pytester: Pytester) -> None:
|
||||
pytester.makeconftest(
|
||||
"""
|
||||
def pytest_cmdline_preparse(args):
|
||||
args.append("-h")
|
||||
"""
|
||||
)
|
||||
result = pytester.runpytest("-Wignore::pytest.PytestRemovedIn8Warning")
|
||||
result.stdout.fnmatch_lines(["*pytest*", "*-h*"])
|
||||
|
||||
|
||||
def test_invalid_options_show_extra_information(pytester: Pytester) -> None:
|
||||
"""Display extra information when pytest exits due to unrecognized
|
||||
options in the command-line."""
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from _pytest.compat import LEGACY_PATH
|
||||
from _pytest.fixtures import TopRequest
|
||||
from _pytest.legacypath import LEGACY_PATH
|
||||
from _pytest.legacypath import TempdirFactory
|
||||
from _pytest.legacypath import Testdir
|
||||
|
||||
|
@ -15,7 +15,7 @@ def test_item_fspath(pytester: pytest.Pytester) -> None:
|
|||
items2, hookrec = pytester.inline_genitems(item.nodeid)
|
||||
(item2,) = items2
|
||||
assert item2.name == item.name
|
||||
assert item2.fspath == item.fspath
|
||||
assert item2.fspath == item.fspath # type: ignore[attr-defined]
|
||||
assert item2.path == item.path
|
||||
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ from typing import Type
|
|||
|
||||
import pytest
|
||||
from _pytest import nodes
|
||||
from _pytest.compat import legacy_path
|
||||
from _pytest.outcomes import OutcomeException
|
||||
from _pytest.pytester import Pytester
|
||||
from _pytest.warning_types import PytestWarning
|
||||
|
@ -69,9 +68,9 @@ def test_subclassing_both_item_and_collector_deprecated(
|
|||
warnings.simplefilter("error")
|
||||
|
||||
class SoWrong(nodes.Item, nodes.File):
|
||||
def __init__(self, fspath, parent):
|
||||
def __init__(self, path, parent):
|
||||
"""Legacy ctor with legacy call # don't wana see"""
|
||||
super().__init__(fspath, parent)
|
||||
super().__init__(parent, path)
|
||||
|
||||
def collect(self):
|
||||
raise NotImplementedError()
|
||||
|
@ -80,9 +79,7 @@ def test_subclassing_both_item_and_collector_deprecated(
|
|||
raise NotImplementedError()
|
||||
|
||||
with pytest.warns(PytestWarning) as rec:
|
||||
SoWrong.from_parent(
|
||||
request.session, fspath=legacy_path(tmp_path / "broken.txt")
|
||||
)
|
||||
SoWrong.from_parent(request.session, path=tmp_path / "broken.txt", wrong=10)
|
||||
messages = [str(x.message) for x in rec]
|
||||
assert any(
|
||||
re.search(".*SoWrong.* not using a cooperative constructor.*", x)
|
||||
|
|
|
@ -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
|
||||
argument = parseopt.Argument("-t", dest="abc", type=float)
|
||||
assert argument.type is float
|
||||
with pytest.warns(DeprecationWarning):
|
||||
with pytest.raises(KeyError):
|
||||
argument = parseopt.Argument("-t", dest="abc", type="choice")
|
||||
argument = parseopt.Argument(
|
||||
"-t", dest="abc", type=str, choices=["red", "blue"]
|
||||
)
|
||||
|
|
|
@ -345,17 +345,9 @@ class TestWarns:
|
|||
assert str(record[0].message) == "user"
|
||||
assert str(record[1].message) == "runtime"
|
||||
|
||||
def test_record_only_none_deprecated_warn(self) -> None:
|
||||
# This should become an error when WARNS_NONE_ARG is removed in Pytest 8.0
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
with pytest.warns(None) as record: # type: ignore[call-overload]
|
||||
warnings.warn("user", UserWarning)
|
||||
warnings.warn("runtime", RuntimeWarning)
|
||||
|
||||
assert len(record) == 2
|
||||
assert str(record[0].message) == "user"
|
||||
assert str(record[1].message) == "runtime"
|
||||
def test_record_only_none_type_error(self) -> None:
|
||||
with pytest.raises(TypeError):
|
||||
pytest.warns(None) # type: ignore[call-overload]
|
||||
|
||||
def test_record_by_subclass(self) -> None:
|
||||
with pytest.warns(Warning) as record:
|
||||
|
|
|
@ -1494,54 +1494,6 @@ def test_fail_using_reason_works_ok(pytester: Pytester) -> None:
|
|||
result.assert_outcomes(failed=1)
|
||||
|
||||
|
||||
def test_fail_fails_with_msg_and_reason(pytester: Pytester) -> None:
|
||||
p = pytester.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
|
||||
def test_fail_both_arguments():
|
||||
pytest.fail(reason="foo", msg="bar")
|
||||
"""
|
||||
)
|
||||
result = pytester.runpytest(p)
|
||||
result.stdout.fnmatch_lines(
|
||||
"*UsageError: Passing both ``reason`` and ``msg`` to pytest.fail(...) is not permitted.*"
|
||||
)
|
||||
result.assert_outcomes(failed=1)
|
||||
|
||||
|
||||
def test_skip_fails_with_msg_and_reason(pytester: Pytester) -> None:
|
||||
p = pytester.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
|
||||
def test_skip_both_arguments():
|
||||
pytest.skip(reason="foo", msg="bar")
|
||||
"""
|
||||
)
|
||||
result = pytester.runpytest(p)
|
||||
result.stdout.fnmatch_lines(
|
||||
"*UsageError: Passing both ``reason`` and ``msg`` to pytest.skip(...) is not permitted.*"
|
||||
)
|
||||
result.assert_outcomes(failed=1)
|
||||
|
||||
|
||||
def test_exit_with_msg_and_reason_fails(pytester: Pytester) -> None:
|
||||
p = pytester.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
|
||||
def test_exit_both_arguments():
|
||||
pytest.exit(reason="foo", msg="bar")
|
||||
"""
|
||||
)
|
||||
result = pytester.runpytest(p)
|
||||
result.stdout.fnmatch_lines(
|
||||
"*UsageError: cannot pass reason and msg to exit(), `msg` is deprecated, use `reason`.*"
|
||||
)
|
||||
result.assert_outcomes(failed=1)
|
||||
|
||||
|
||||
def test_exit_with_reason_works_ok(pytester: Pytester) -> None:
|
||||
p = pytester.makepyfile(
|
||||
"""
|
||||
|
|
|
@ -530,13 +530,11 @@ class TestRmRf:
|
|||
assert fn.is_file()
|
||||
|
||||
# ignored function
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
with pytest.warns(None) as warninfo: # type: ignore[call-overload]
|
||||
exc_info4 = PermissionError()
|
||||
on_rm_rf_error(os.open, str(fn), exc_info4, start_path=tmp_path)
|
||||
assert fn.is_file()
|
||||
assert not [x.message for x in warninfo]
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
exc_info4 = PermissionError()
|
||||
on_rm_rf_error(os.open, str(fn), exc_info4, start_path=tmp_path)
|
||||
assert fn.is_file()
|
||||
assert not [x.message for x in w]
|
||||
|
||||
exc_info5 = PermissionError()
|
||||
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()
|
||||
|
||||
|
||||
# In 8.1, uncomment below and change RemovedIn8 -> RemovedIn9.
|
||||
# @pytest.mark.skip("not relevant until pytest 9.0")
|
||||
@pytest.mark.skip("not relevant until pytest 9.0")
|
||||
@pytest.mark.parametrize("change_default", [None, "ini", "cmdline"])
|
||||
def test_removed_in_x_warning_as_error(pytester: Pytester, change_default) -> None:
|
||||
"""This ensures that PytestRemovedInXWarnings raised by pytest are turned into errors.
|
||||
|
@ -531,7 +530,7 @@ def test_removed_in_x_warning_as_error(pytester: Pytester, change_default) -> No
|
|||
"""
|
||||
import warnings, pytest
|
||||
def test():
|
||||
warnings.warn(pytest.PytestRemovedIn8Warning("some warning"))
|
||||
warnings.warn(pytest.PytestRemovedIn9Warning("some warning"))
|
||||
"""
|
||||
)
|
||||
if change_default == "ini":
|
||||
|
@ -539,12 +538,12 @@ def test_removed_in_x_warning_as_error(pytester: Pytester, change_default) -> No
|
|||
"""
|
||||
[pytest]
|
||||
filterwarnings =
|
||||
ignore::pytest.PytestRemovedIn8Warning
|
||||
ignore::pytest.PytestRemovedIn9Warning
|
||||
"""
|
||||
)
|
||||
|
||||
args = (
|
||||
("-Wignore::pytest.PytestRemovedIn8Warning",)
|
||||
("-Wignore::pytest.PytestRemovedIn9Warning",)
|
||||
if change_default == "cmdline"
|
||||
else ()
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue