diff --git a/README.rst b/README.rst index bbf41a183..6e4772b04 100644 --- a/README.rst +++ b/README.rst @@ -97,8 +97,8 @@ Features - `Modular fixtures `_ for managing small or parametrized long-lived test resources -- Can run `unittest `_ (or trial), - `nose `_ test suites out of the box +- Can run `unittest `_ (or trial) + test suites out of the box - Python 3.8+ or PyPy3 diff --git a/doc/en/adopt.rst b/doc/en/adopt.rst index 13d82bf01..b95a117de 100644 --- a/doc/en/adopt.rst +++ b/doc/en/adopt.rst @@ -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 diff --git a/doc/en/changelog.rst b/doc/en/changelog.rst index 85ba6140a..755f386c6 100644 --- a/doc/en/changelog.rst +++ b/doc/en/changelog.rst @@ -227,7 +227,7 @@ These are breaking changes where deprecation was not possible. Deprecations ------------ -- `#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 `_: 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 `_: 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 ` for full details. -- `#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 `_: ``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 ` for full details. diff --git a/doc/en/conf.py b/doc/en/conf.py index d3a98015a..2bc18be58 100644 --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -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"), diff --git a/doc/en/contents.rst b/doc/en/contents.rst index ae42884f6..181207203 100644 --- a/doc/en/contents.rst +++ b/doc/en/contents.rst @@ -44,7 +44,6 @@ How-to guides how-to/existingtestsuite how-to/unittest - how-to/nose how-to/xunit_setup how-to/bash-completion diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index caa7cb3e7..f5334ace5 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -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 `. +.. _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 `), 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 ` 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 ` (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) ` as equivalent to ``path`` +* :hook:`pytest_collect_file(file_path: pathlib.Path) ` as equivalent to ``path`` +* :hook:`pytest_pycollect_makemodule(module_path: pathlib.Path) ` as equivalent to ``path`` +* :hook:`pytest_report_header(start_path: pathlib.Path) ` as equivalent to ``startdir`` +* :hook:`pytest_report_collectionfinish(start_path: pathlib.Path) ` 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 ` (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 `__ 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 ` 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 ` (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) ` as equivalent to ``path`` -* :hook:`pytest_collect_file(file_path: pathlib.Path) ` as equivalent to ``path`` -* :hook:`pytest_pycollect_makemodule(module_path: pathlib.Path) ` as equivalent to ``path`` -* :hook:`pytest_report_header(start_path: pathlib.Path) ` as equivalent to ``startdir`` -* :hook:`pytest_report_collectionfinish(start_path: pathlib.Path) ` 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 ` (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) ` 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 ` 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 `), 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 ` 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) ` 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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/en/example/index.rst b/doc/en/example/index.rst index e8835aae9..840819002 100644 --- a/doc/en/example/index.rst +++ b/doc/en/example/index.rst @@ -18,7 +18,6 @@ For basic examples, see - :ref:`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. diff --git a/doc/en/explanation/fixtures.rst b/doc/en/explanation/fixtures.rst index 322718873..0bb3bf49f 100644 --- a/doc/en/explanation/fixtures.rst +++ b/doc/en/explanation/fixtures.rst @@ -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 ` or :ref:`nose based ` projects. +style `. diff --git a/doc/en/how-to/existingtestsuite.rst b/doc/en/how-to/existingtestsuite.rst index 9909e7d11..1c37023c7 100644 --- a/doc/en/how-to/existingtestsuite.rst +++ b/doc/en/how-to/existingtestsuite.rst @@ -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 ` 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 `. diff --git a/doc/en/how-to/index.rst b/doc/en/how-to/index.rst index 6f52aaecd..225f28965 100644 --- a/doc/en/how-to/index.rst +++ b/doc/en/how-to/index.rst @@ -52,7 +52,6 @@ pytest and other test systems existingtestsuite unittest - nose xunit_setup pytest development environment diff --git a/doc/en/how-to/nose.rst b/doc/en/how-to/nose.rst deleted file mode 100644 index 45d3357cf..000000000 --- a/doc/en/how-to/nose.rst +++ /dev/null @@ -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 `_. - - 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 `_ 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/ diff --git a/doc/en/index.rst b/doc/en/index.rst index 50c84f6ae..bef42716f 100644 --- a/doc/en/index.rst +++ b/doc/en/index.rst @@ -74,7 +74,7 @@ Features - :ref:`Modular fixtures ` for managing small or parametrized long-lived test resources -- Can run :ref:`unittest ` (including trial) and :ref:`nose ` test suites out of the box +- Can run :ref:`unittest ` (including trial) test suites out of the box - Python 3.8+ or PyPy 3 diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst index 2796fa4f5..46aba0af9 100644 --- a/doc/en/reference/reference.rst +++ b/doc/en/reference/reference.rst @@ -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: diff --git a/setup.cfg b/setup.cfg index 3b1c627de..02f6031bd 100644 --- a/setup.cfg +++ b/setup.cfg @@ -69,7 +69,6 @@ testing = attrs>=19.2.0 hypothesis>=3.56 mock - nose pygments>=2.7.2 requests setuptools diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index 73d77f978..1e9c38ca8 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -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: diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index e5775546d..49d63a357 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -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( diff --git a/src/_pytest/config/argparsing.py b/src/_pytest/config/argparsing.py index 331abb85d..39e417605 100644 --- a/src/_pytest/config/argparsing.py +++ b/src/_pytest/config/argparsing.py @@ -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: diff --git a/src/_pytest/config/compat.py b/src/_pytest/config/compat.py deleted file mode 100644 index afb38bbcc..000000000 --- a/src/_pytest/config/compat.py +++ /dev/null @@ -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] diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index 77279d634..1bc2cf57e 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -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" diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py index 3c65234da..c1963b4d7 100644 --- a/src/_pytest/hookspec.py +++ b/src/_pytest/hookspec.py @@ -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. """ diff --git a/src/_pytest/legacypath.py b/src/_pytest/legacypath.py index 4876a083a..b2dd87436 100644 --- a/src/_pytest/legacypath.py +++ b/src/_pytest/legacypath.py @@ -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: """ diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 3672df05a..9fb96840e 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -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 diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 530714108..4cf6768e6 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -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 `. 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): diff --git a/src/_pytest/nose.py b/src/_pytest/nose.py deleted file mode 100644 index 273bd045f..000000000 --- a/src/_pytest/nose.py +++ /dev/null @@ -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 diff --git a/src/_pytest/outcomes.py b/src/_pytest/outcomes.py index 0f64f91d9..8710ba3e8 100644 --- a/src/_pytest/outcomes.py +++ b/src/_pytest/outcomes.py @@ -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().""" diff --git a/src/_pytest/python.py b/src/_pytest/python.py index e0f7a447a..184399080 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -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: diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index d1d83ea2a..175d545b9 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -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) diff --git a/src/_pytest/warning_types.py b/src/_pytest/warning_types.py index 4219f1439..6c109b03f 100644 --- a/src/_pytest/warning_types.py +++ b/src/_pytest/warning_types.py @@ -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.""" diff --git a/src/_pytest/warnings.py b/src/_pytest/warnings.py index 6f20a872c..6ef4fafdc 100644 --- a/src/_pytest/warnings.py +++ b/src/_pytest/warnings.py @@ -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) diff --git a/src/pytest/__init__.py b/src/pytest/__init__.py index 4e0c23ddb..449cb39b8 100644 --- a/src/pytest/__init__.py +++ b/src/pytest/__init__.py @@ -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}") diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index 0736ed1dc..ebff49ce6 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -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) diff --git a/testing/test_config.py b/testing/test_config.py index 18022977c..2d95fb4cc 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -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.""" diff --git a/testing/test_legacypath.py b/testing/test_legacypath.py index b4fd1bf2c..700499f24 100644 --- a/testing/test_legacypath.py +++ b/testing/test_legacypath.py @@ -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 diff --git a/testing/test_nodes.py b/testing/test_nodes.py index 84c377cf9..880e2a44f 100644 --- a/testing/test_nodes.py +++ b/testing/test_nodes.py @@ -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) diff --git a/testing/test_nose.py b/testing/test_nose.py deleted file mode 100644 index 7ec4026f2..000000000 --- a/testing/test_nose.py +++ /dev/null @@ -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: ()*"]) - - -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 diff --git a/testing/test_parseopt.py b/testing/test_parseopt.py index 1b80883ee..2a6291984 100644 --- a/testing/test_parseopt.py +++ b/testing/test_parseopt.py @@ -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"] ) diff --git a/testing/test_recwarn.py b/testing/test_recwarn.py index 19a1cd534..2508e22a2 100644 --- a/testing/test_recwarn.py +++ b/testing/test_recwarn.py @@ -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: diff --git a/testing/test_skipping.py b/testing/test_skipping.py index b7e448df3..a002ba6e8 100644 --- a/testing/test_skipping.py +++ b/testing/test_skipping.py @@ -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( """ diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index 1e1446af1..2215e978a 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -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) diff --git a/testing/test_warnings.py b/testing/test_warnings.py index 96ecad6f6..e7834dc4d 100644 --- a/testing/test_warnings.py +++ b/testing/test_warnings.py @@ -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 () )