Compare commits
19 Commits
Author | SHA1 | Date |
---|---|---|
|
826d7576c2 | |
|
7df3dbc545 | |
|
cf84163ac3 | |
|
0675383bac | |
|
a43b09882d | |
|
f55c660763 | |
|
be7c8be6ce | |
|
c5e3921b9f | |
|
83614e1f3e | |
|
cd585a1abe | |
|
6c14139c9f | |
|
abb0cf4922 | |
|
81653ee385 | |
|
e60b4b9ed8 | |
|
15fbe57c44 | |
|
86c3aab005 | |
|
5b82b0cd20 | |
|
0a536810dc | |
|
b9a167f9bb |
2
AUTHORS
2
AUTHORS
|
@ -188,6 +188,7 @@ Jake VanderPlas
|
|||
Jakob van Santen
|
||||
Jakub Mitoraj
|
||||
James Bourbeau
|
||||
James Frost
|
||||
Jan Balster
|
||||
Janne Vanhala
|
||||
Jason R. Coombs
|
||||
|
@ -318,6 +319,7 @@ Pierre Sassoulas
|
|||
Pieter Mulder
|
||||
Piotr Banaszkiewicz
|
||||
Piotr Helm
|
||||
Poulami Sau
|
||||
Prakhar Gurunani
|
||||
Prashant Anand
|
||||
Prashant Sharma
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
:func:`pytest.warns` now validates that :func:`warnings.warn` was called with a `str` or a `Warning`.
|
||||
Currently in Python it is possible to use other types, however this causes an exception when :func:`warnings.filterwarnings` is used to filter those warnings (see `CPython #103577 <https://github.com/python/cpython/issues/103577>`__ for a discussion).
|
||||
While this can be considered a bug in CPython, we decided to put guards in pytest as the error message produced without this check in place is confusing.
|
|
@ -1,4 +0,0 @@
|
|||
When using ``--override-ini`` for paths in invocations without a configuration file defined, the current working directory is used
|
||||
as the relative directory.
|
||||
|
||||
Previoulsy this would raise an :class:`AssertionError`.
|
|
@ -1,3 +0,0 @@
|
|||
Added the new :confval:`consider_namespace_packages` configuration option, defaulting to ``False``.
|
||||
|
||||
If set to ``True``, pytest will attempt to identify modules that are part of `namespace packages <https://packaging.python.org/en/latest/guides/packaging-namespace-packages>`__ when importing modules.
|
|
@ -1,3 +0,0 @@
|
|||
:ref:`--import-mode=importlib <import-mode-importlib>` now tries to import modules using the standard import mechanism (but still without changing :py:data:`sys.path`), falling back to importing modules directly only if that fails.
|
||||
|
||||
This means that installed packages will be imported under their canonical name if possible first, for example ``app.core.models``, instead of having the module name always be derived from their path (for example ``.env310.lib.site_packages.app.core.models``).
|
|
@ -1,2 +0,0 @@
|
|||
Added the new :confval:`verbosity_test_cases` configuration option for fine-grained control of test execution verbosity.
|
||||
See :ref:`Fine-grained verbosity <pytest.fine_grained_verbosity>` for more details.
|
|
@ -1,7 +0,0 @@
|
|||
Some changes were made to private functions which may affect plugins which access them:
|
||||
|
||||
- ``FixtureManager._getautousenames()`` now takes a ``Node`` itself instead of the nodeid.
|
||||
- ``FixtureManager.getfixturedefs()`` now takes the ``Node`` itself instead of the nodeid.
|
||||
- The ``_pytest.nodes.iterparentnodeids()`` function is removed without replacement.
|
||||
Prefer to traverse the node hierarchy itself instead.
|
||||
If you really need to, copy the function from the previous pytest release.
|
|
@ -1 +0,0 @@
|
|||
Documented the retention of temporary directories created using the ``tmp_path`` fixture in more detail.
|
|
@ -1,2 +0,0 @@
|
|||
Added the :func:`iter_parents() <_pytest.nodes.Node.iter_parents>` helper method on nodes.
|
||||
It is similar to :func:`listchain <_pytest.nodes.Node.listchain>`, but goes from bottom to top, and returns an iterator, not a list.
|
|
@ -1 +0,0 @@
|
|||
Added support for :data:`sys.last_exc` for post-mortem debugging on Python>=3.12.
|
|
@ -1,3 +0,0 @@
|
|||
Fixed a regression in pytest 8.0.0 that would cause test collection to fail due to permission errors when using ``--pyargs``.
|
||||
|
||||
This change improves the collection tree for tests specified using ``--pyargs``, see :pull:`12043` for a comparison with pytest 8.0 and <8.
|
|
@ -1 +0,0 @@
|
|||
In case no other suitable candidates for configuration file are found, a ``pyproject.toml`` (even without a ``[tool.pytest.ini_options]`` table) will be considered as the configuration file and define the ``rootdir``.
|
|
@ -1,3 +0,0 @@
|
|||
Add ``--log-file-mode`` option to the logging plugin, enabling appending to log-files. This option accepts either ``"w"`` or ``"a"`` and defaults to ``"w"``.
|
||||
|
||||
Previously, the mode was hard-coded to be ``"w"`` which truncates the file before logging.
|
|
@ -1 +0,0 @@
|
|||
Fixed a regression in 8.0.1 whereby ``setup_module`` xunit-style fixtures are not executed when ``--doctest-modules`` is passed.
|
|
@ -1 +0,0 @@
|
|||
Fix the ``stacklevel`` used when warning about marks used on fixtures.
|
|
@ -1 +0,0 @@
|
|||
Fixed a regression in ``8.0.2`` where tests created using :fixture:`tmp_path` have been collected multiple times in CI under Windows.
|
|
@ -1,2 +0,0 @@
|
|||
When multiple finalizers of a fixture raise an exception, now all exceptions are reported as an exception group.
|
||||
Previously, only the first exception was reported.
|
|
@ -0,0 +1 @@
|
|||
The documentation webpages now links to a canonical version to reduce outdated documentation in search engine results.
|
|
@ -6,6 +6,9 @@ Release announcements
|
|||
:maxdepth: 2
|
||||
|
||||
|
||||
release-8.1.2
|
||||
release-8.1.1
|
||||
release-8.1.0
|
||||
release-8.0.2
|
||||
release-8.0.1
|
||||
release-8.0.0
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
pytest-8.1.0
|
||||
=======================================
|
||||
|
||||
The pytest team is proud to announce the 8.1.0 release!
|
||||
|
||||
This release contains new features, improvements, and bug fixes,
|
||||
the full list of changes is available in the changelog:
|
||||
|
||||
https://docs.pytest.org/en/stable/changelog.html
|
||||
|
||||
For complete documentation, please visit:
|
||||
|
||||
https://docs.pytest.org/en/stable/
|
||||
|
||||
As usual, you can upgrade from PyPI via:
|
||||
|
||||
pip install -U pytest
|
||||
|
||||
Thanks to all of the contributors to this release:
|
||||
|
||||
* Ben Brown
|
||||
* Ben Leith
|
||||
* Bruno Oliveira
|
||||
* Clément Robert
|
||||
* Dave Hall
|
||||
* Dương Quốc Khánh
|
||||
* Eero Vaher
|
||||
* Eric Larson
|
||||
* Fabian Sturm
|
||||
* Faisal Fawad
|
||||
* Florian Bruhin
|
||||
* Franck Charras
|
||||
* Joachim B Haga
|
||||
* John Litborn
|
||||
* Loïc Estève
|
||||
* Marc Bresson
|
||||
* Patrick Lannigan
|
||||
* Pierre Sassoulas
|
||||
* Ran Benita
|
||||
* Reagan Lee
|
||||
* Ronny Pfannschmidt
|
||||
* Russell Martin
|
||||
* clee2000
|
||||
* donghui
|
||||
* faph
|
||||
* jakkdl
|
||||
* mrbean-bremen
|
||||
* robotherapist
|
||||
* whysage
|
||||
* woutdenolf
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
|
@ -0,0 +1,18 @@
|
|||
pytest-8.1.1
|
||||
=======================================
|
||||
|
||||
pytest 8.1.1 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
|
||||
|
||||
Thanks to all of the contributors to this release:
|
||||
|
||||
* Ran Benita
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
|
@ -0,0 +1,18 @@
|
|||
pytest-8.1.2
|
||||
=======================================
|
||||
|
||||
pytest 8.1.2 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
|
||||
|
||||
Thanks to all of the contributors to this release:
|
||||
|
||||
* Bruno Oliveira
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
|
@ -33,7 +33,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
|||
|
||||
Values can be any object handled by the json stdlib module.
|
||||
|
||||
capsysbinary -- .../_pytest/capture.py:1007
|
||||
capsysbinary -- .../_pytest/capture.py:1008
|
||||
Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``.
|
||||
|
||||
The captured output is made available via ``capsysbinary.readouterr()``
|
||||
|
@ -50,7 +50,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
|||
captured = capsysbinary.readouterr()
|
||||
assert captured.out == b"hello\n"
|
||||
|
||||
capfd -- .../_pytest/capture.py:1034
|
||||
capfd -- .../_pytest/capture.py:1035
|
||||
Enable text capturing of writes to file descriptors ``1`` and ``2``.
|
||||
|
||||
The captured output is made available via ``capfd.readouterr()`` method
|
||||
|
@ -67,7 +67,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
|||
captured = capfd.readouterr()
|
||||
assert captured.out == "hello\n"
|
||||
|
||||
capfdbinary -- .../_pytest/capture.py:1061
|
||||
capfdbinary -- .../_pytest/capture.py:1062
|
||||
Enable bytes capturing of writes to file descriptors ``1`` and ``2``.
|
||||
|
||||
The captured output is made available via ``capfd.readouterr()`` method
|
||||
|
@ -84,7 +84,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
|||
captured = capfdbinary.readouterr()
|
||||
assert captured.out == b"hello\n"
|
||||
|
||||
capsys -- .../_pytest/capture.py:980
|
||||
capsys -- .../_pytest/capture.py:981
|
||||
Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``.
|
||||
|
||||
The captured output is made available via ``capsys.readouterr()`` method
|
||||
|
@ -101,7 +101,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
|||
captured = capsys.readouterr()
|
||||
assert captured.out == "hello\n"
|
||||
|
||||
doctest_namespace [session scope] -- .../_pytest/doctest.py:745
|
||||
doctest_namespace [session scope] -- .../_pytest/doctest.py:737
|
||||
Fixture that returns a :py:class:`dict` that will be injected into the
|
||||
namespace of doctests.
|
||||
|
||||
|
@ -115,7 +115,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
|||
|
||||
For more details: :ref:`doctest_namespace`.
|
||||
|
||||
pytestconfig [session scope] -- .../_pytest/fixtures.py:1354
|
||||
pytestconfig [session scope] -- .../_pytest/fixtures.py:1346
|
||||
Session-scoped fixture that returns the session's :class:`pytest.Config`
|
||||
object.
|
||||
|
||||
|
@ -170,18 +170,18 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
|||
`pytest-xdist <https://github.com/pytest-dev/pytest-xdist>`__ plugin. See
|
||||
:issue:`7767` for details.
|
||||
|
||||
tmpdir_factory [session scope] -- .../_pytest/legacypath.py:302
|
||||
tmpdir_factory [session scope] -- .../_pytest/legacypath.py:303
|
||||
Return a :class:`pytest.TempdirFactory` instance for the test session.
|
||||
|
||||
tmpdir -- .../_pytest/legacypath.py:309
|
||||
tmpdir -- .../_pytest/legacypath.py:310
|
||||
Return a temporary directory path object which is unique to each test
|
||||
function invocation, created as a sub directory of the base temporary
|
||||
directory.
|
||||
|
||||
By default, a new base temporary directory is created each test session,
|
||||
and old bases are removed after 3 sessions, to aid in debugging. If
|
||||
``--basetemp`` is used then it is cleared each session. See :ref:`base
|
||||
temporary directory`.
|
||||
``--basetemp`` is used then it is cleared each session. See
|
||||
:ref:`temporary directory location and retention`.
|
||||
|
||||
The returned object is a `legacy_path`_ object.
|
||||
|
||||
|
@ -192,7 +192,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
|||
|
||||
.. _legacy_path: https://py.readthedocs.io/en/latest/path.html
|
||||
|
||||
caplog -- .../_pytest/logging.py:594
|
||||
caplog -- .../_pytest/logging.py:601
|
||||
Access and control log capturing.
|
||||
|
||||
Captured logs are available through the following properties/methods::
|
||||
|
@ -227,7 +227,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
|||
To undo modifications done by the fixture in a contained scope,
|
||||
use :meth:`context() <pytest.MonkeyPatch.context>`.
|
||||
|
||||
recwarn -- .../_pytest/recwarn.py:32
|
||||
recwarn -- .../_pytest/recwarn.py:31
|
||||
Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions.
|
||||
|
||||
See https://docs.pytest.org/en/latest/how-to/capture-warnings.html for information
|
||||
|
@ -245,8 +245,8 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
|||
and old bases are removed after 3 sessions, to aid in debugging.
|
||||
This behavior can be configured with :confval:`tmp_path_retention_count` and
|
||||
:confval:`tmp_path_retention_policy`.
|
||||
If ``--basetemp`` is used then it is cleared each session. See :ref:`base
|
||||
temporary directory`.
|
||||
If ``--basetemp`` is used then it is cleared each session. See
|
||||
:ref:`temporary directory location and retention`.
|
||||
|
||||
The returned object is a :class:`pathlib.Path` object.
|
||||
|
||||
|
|
|
@ -28,6 +28,136 @@ with advance notice in the **Deprecations** section of releases.
|
|||
|
||||
.. towncrier release notes start
|
||||
|
||||
pytest 8.1.2 (2024-04-26)
|
||||
=========================
|
||||
|
||||
Bug Fixes
|
||||
---------
|
||||
|
||||
- `#12114 <https://github.com/pytest-dev/pytest/issues/12114>`_: Fixed error in :func:`pytest.approx` when used with `numpy` arrays and comparing with other types.
|
||||
|
||||
|
||||
pytest 8.1.1 (2024-03-08)
|
||||
=========================
|
||||
|
||||
.. note::
|
||||
|
||||
This release is not a usual bug fix release -- it contains features and improvements, being a follow up
|
||||
to ``8.1.0``, which has been yanked from PyPI.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
- `#11475 <https://github.com/pytest-dev/pytest/issues/11475>`_: Added the new :confval:`consider_namespace_packages` configuration option, defaulting to ``False``.
|
||||
|
||||
If set to ``True``, pytest will attempt to identify modules that are part of `namespace packages <https://packaging.python.org/en/latest/guides/packaging-namespace-packages>`__ when importing modules.
|
||||
|
||||
|
||||
- `#11653 <https://github.com/pytest-dev/pytest/issues/11653>`_: Added the new :confval:`verbosity_test_cases` configuration option for fine-grained control of test execution verbosity.
|
||||
See :ref:`Fine-grained verbosity <pytest.fine_grained_verbosity>` for more details.
|
||||
|
||||
|
||||
|
||||
Improvements
|
||||
------------
|
||||
|
||||
- `#10865 <https://github.com/pytest-dev/pytest/issues/10865>`_: :func:`pytest.warns` now validates that :func:`warnings.warn` was called with a `str` or a `Warning`.
|
||||
Currently in Python it is possible to use other types, however this causes an exception when :func:`warnings.filterwarnings` is used to filter those warnings (see `CPython #103577 <https://github.com/python/cpython/issues/103577>`__ for a discussion).
|
||||
While this can be considered a bug in CPython, we decided to put guards in pytest as the error message produced without this check in place is confusing.
|
||||
|
||||
|
||||
- `#11311 <https://github.com/pytest-dev/pytest/issues/11311>`_: When using ``--override-ini`` for paths in invocations without a configuration file defined, the current working directory is used
|
||||
as the relative directory.
|
||||
|
||||
Previoulsy this would raise an :class:`AssertionError`.
|
||||
|
||||
|
||||
- `#11475 <https://github.com/pytest-dev/pytest/issues/11475>`_: :ref:`--import-mode=importlib <import-mode-importlib>` now tries to import modules using the standard import mechanism (but still without changing :py:data:`sys.path`), falling back to importing modules directly only if that fails.
|
||||
|
||||
This means that installed packages will be imported under their canonical name if possible first, for example ``app.core.models``, instead of having the module name always be derived from their path (for example ``.env310.lib.site_packages.app.core.models``).
|
||||
|
||||
|
||||
- `#11801 <https://github.com/pytest-dev/pytest/issues/11801>`_: Added the :func:`iter_parents() <_pytest.nodes.Node.iter_parents>` helper method on nodes.
|
||||
It is similar to :func:`listchain <_pytest.nodes.Node.listchain>`, but goes from bottom to top, and returns an iterator, not a list.
|
||||
|
||||
|
||||
- `#11850 <https://github.com/pytest-dev/pytest/issues/11850>`_: Added support for :data:`sys.last_exc` for post-mortem debugging on Python>=3.12.
|
||||
|
||||
|
||||
- `#11962 <https://github.com/pytest-dev/pytest/issues/11962>`_: In case no other suitable candidates for configuration file are found, a ``pyproject.toml`` (even without a ``[tool.pytest.ini_options]`` table) will be considered as the configuration file and define the ``rootdir``.
|
||||
|
||||
|
||||
- `#11978 <https://github.com/pytest-dev/pytest/issues/11978>`_: Add ``--log-file-mode`` option to the logging plugin, enabling appending to log-files. This option accepts either ``"w"`` or ``"a"`` and defaults to ``"w"``.
|
||||
|
||||
Previously, the mode was hard-coded to be ``"w"`` which truncates the file before logging.
|
||||
|
||||
|
||||
- `#12047 <https://github.com/pytest-dev/pytest/issues/12047>`_: When multiple finalizers of a fixture raise an exception, now all exceptions are reported as an exception group.
|
||||
Previously, only the first exception was reported.
|
||||
|
||||
|
||||
|
||||
Bug Fixes
|
||||
---------
|
||||
|
||||
- `#11475 <https://github.com/pytest-dev/pytest/issues/11475>`_: Fixed regression where ``--importmode=importlib`` would import non-test modules more than once.
|
||||
|
||||
|
||||
- `#11904 <https://github.com/pytest-dev/pytest/issues/11904>`_: Fixed a regression in pytest 8.0.0 that would cause test collection to fail due to permission errors when using ``--pyargs``.
|
||||
|
||||
This change improves the collection tree for tests specified using ``--pyargs``, see :pull:`12043` for a comparison with pytest 8.0 and <8.
|
||||
|
||||
|
||||
- `#12011 <https://github.com/pytest-dev/pytest/issues/12011>`_: Fixed a regression in 8.0.1 whereby ``setup_module`` xunit-style fixtures are not executed when ``--doctest-modules`` is passed.
|
||||
|
||||
|
||||
- `#12014 <https://github.com/pytest-dev/pytest/issues/12014>`_: Fix the ``stacklevel`` used when warning about marks used on fixtures.
|
||||
|
||||
|
||||
- `#12039 <https://github.com/pytest-dev/pytest/issues/12039>`_: Fixed a regression in ``8.0.2`` where tests created using :fixture:`tmp_path` have been collected multiple times in CI under Windows.
|
||||
|
||||
|
||||
Improved Documentation
|
||||
----------------------
|
||||
|
||||
- `#11790 <https://github.com/pytest-dev/pytest/issues/11790>`_: Documented the retention of temporary directories created using the ``tmp_path`` fixture in more detail.
|
||||
|
||||
|
||||
|
||||
Trivial/Internal Changes
|
||||
------------------------
|
||||
|
||||
- `#11785 <https://github.com/pytest-dev/pytest/issues/11785>`_: Some changes were made to private functions which may affect plugins which access them:
|
||||
|
||||
- ``FixtureManager._getautousenames()`` now takes a ``Node`` itself instead of the nodeid.
|
||||
- ``FixtureManager.getfixturedefs()`` now takes the ``Node`` itself instead of the nodeid.
|
||||
- The ``_pytest.nodes.iterparentnodeids()`` function is removed without replacement.
|
||||
Prefer to traverse the node hierarchy itself instead.
|
||||
If you really need to, copy the function from the previous pytest release.
|
||||
|
||||
|
||||
- `#12069 <https://github.com/pytest-dev/pytest/issues/12069>`_: Delayed the deprecation of the following features to ``9.0.0``:
|
||||
|
||||
* :ref:`node-ctor-fspath-deprecation`.
|
||||
* :ref:`legacy-path-hooks-deprecated`.
|
||||
|
||||
It was discovered after ``8.1.0`` was released that the warnings about the impeding removal were not being displayed, so the team decided to revert the removal.
|
||||
|
||||
This is the reason for ``8.1.0`` being yanked.
|
||||
|
||||
|
||||
pytest 8.1.0 (YANKED)
|
||||
=====================
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
This release has been **yanked**: it broke some plugins without the proper warning period, due to
|
||||
some warnings not showing up as expected.
|
||||
|
||||
See `#12069 <https://github.com/pytest-dev/pytest/issues/12069>`__.
|
||||
|
||||
|
||||
pytest 8.0.2 (2024-02-24)
|
||||
=========================
|
||||
|
||||
|
|
|
@ -200,6 +200,7 @@ 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"),
|
||||
|
@ -315,6 +316,9 @@ html_show_sourcelink = False
|
|||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = "pytestdoc"
|
||||
|
||||
# The base URL which points to the root of the HTML documentation. It is used
|
||||
# to indicate the location of document using the canonical link relation (#12363).
|
||||
html_baseurl = "https://docs.pytest.org/en/stable/"
|
||||
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
|
@ -394,7 +398,7 @@ epub_copyright = "2013, holger krekel et alii"
|
|||
# The format is a list of tuples containing the path and title.
|
||||
# epub_pre_files = []
|
||||
|
||||
# HTML files shat should be inserted after the pages created by sphinx.
|
||||
# HTML files that should be inserted after the pages created by sphinx.
|
||||
# The format is a list of tuples containing the path and title.
|
||||
# epub_post_files = []
|
||||
|
||||
|
|
|
@ -19,7 +19,45 @@ Below is a complete list of all pytest features which are considered deprecated.
|
|||
:class:`~pytest.PytestWarning` or subclasses, which can be filtered using :ref:`standard warning filters <warnings>`.
|
||||
|
||||
|
||||
.. _legacy-path-hooks-deprecated:
|
||||
.. _node-ctor-fspath-deprecation:
|
||||
|
||||
``fspath`` argument for Node constructors replaced with ``pathlib.Path``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 7.0
|
||||
|
||||
In order to support the transition from ``py.path.local`` to :mod:`pathlib`,
|
||||
the ``fspath`` argument to :class:`~_pytest.nodes.Node` constructors like
|
||||
:func:`pytest.Function.from_parent()` and :func:`pytest.Class.from_parent()`
|
||||
is now deprecated.
|
||||
|
||||
Plugins which construct nodes should pass the ``path`` argument, of type
|
||||
:class:`pathlib.Path`, instead of the ``fspath`` argument.
|
||||
|
||||
Plugins which implement custom items and collectors are encouraged to replace
|
||||
``fspath`` parameters (``py.path.local``) with ``path`` parameters
|
||||
(``pathlib.Path``), and drop any other usage of the ``py`` library if possible.
|
||||
|
||||
If possible, plugins with custom items should use :ref:`cooperative
|
||||
constructors <uncooperative-constructors-deprecated>` to avoid hardcoding
|
||||
arguments they only pass on to the superclass.
|
||||
|
||||
.. note::
|
||||
The name of the :class:`~_pytest.nodes.Node` arguments and attributes (the
|
||||
new attribute being ``path``) is **the opposite** of the situation for
|
||||
hooks, :ref:`outlined below <legacy-path-hooks-deprecated>` (the old
|
||||
argument being ``path``).
|
||||
|
||||
This is an unfortunate artifact due to historical reasons, which should be
|
||||
resolved in future versions as we slowly get rid of the :pypi:`py`
|
||||
dependency (see :issue:`9283` for a longer discussion).
|
||||
|
||||
Due to the ongoing migration of methods like :meth:`~pytest.Item.reportinfo`
|
||||
which still is expected to return a ``py.path.local`` object, nodes still have
|
||||
both ``fspath`` (``py.path.local``) and ``path`` (``pathlib.Path``) attributes,
|
||||
no matter what argument was used in the constructor. We expect to deprecate the
|
||||
``fspath`` attribute in a future release.
|
||||
|
||||
|
||||
Configuring hook specs/impls using markers
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -62,6 +100,33 @@ Changed ``hookwrapper`` attributes:
|
|||
* ``historic``
|
||||
|
||||
|
||||
.. _legacy-path-hooks-deprecated:
|
||||
|
||||
``py.path.local`` arguments for hooks replaced with ``pathlib.Path``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 7.0
|
||||
|
||||
In order to support the transition from ``py.path.local`` to :mod:`pathlib`, the following hooks now receive additional arguments:
|
||||
|
||||
* :hook:`pytest_ignore_collect(collection_path: pathlib.Path) <pytest_ignore_collect>` as equivalent to ``path``
|
||||
* :hook:`pytest_collect_file(file_path: pathlib.Path) <pytest_collect_file>` as equivalent to ``path``
|
||||
* :hook:`pytest_pycollect_makemodule(module_path: pathlib.Path) <pytest_pycollect_makemodule>` as equivalent to ``path``
|
||||
* :hook:`pytest_report_header(start_path: pathlib.Path) <pytest_report_header>` as equivalent to ``startdir``
|
||||
* :hook:`pytest_report_collectionfinish(start_path: pathlib.Path) <pytest_report_collectionfinish>` as equivalent to ``startdir``
|
||||
|
||||
The accompanying ``py.path.local`` based paths have been deprecated: plugins which manually invoke those hooks should only pass the new ``pathlib.Path`` arguments, and users should change their hook implementations to use the new ``pathlib.Path`` arguments.
|
||||
|
||||
.. note::
|
||||
The name of the :class:`~_pytest.nodes.Node` arguments and attributes,
|
||||
:ref:`outlined above <node-ctor-fspath-deprecation>` (the new attribute
|
||||
being ``path``) is **the opposite** of the situation for hooks (the old
|
||||
argument being ``path``).
|
||||
|
||||
This is an unfortunate artifact due to historical reasons, which should be
|
||||
resolved in future versions as we slowly get rid of the :pypi:`py`
|
||||
dependency (see :issue:`9283` for a longer discussion).
|
||||
|
||||
Directly constructing internal classes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -208,73 +273,6 @@ an appropriate period of deprecation has passed.
|
|||
|
||||
Some breaking changes which could not be deprecated are also listed.
|
||||
|
||||
.. _node-ctor-fspath-deprecation:
|
||||
|
||||
``fspath`` argument for Node constructors replaced with ``pathlib.Path``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 7.0
|
||||
|
||||
In order to support the transition from ``py.path.local`` to :mod:`pathlib`,
|
||||
the ``fspath`` argument to :class:`~_pytest.nodes.Node` constructors like
|
||||
:func:`pytest.Function.from_parent()` and :func:`pytest.Class.from_parent()`
|
||||
is now deprecated.
|
||||
|
||||
Plugins which construct nodes should pass the ``path`` argument, of type
|
||||
:class:`pathlib.Path`, instead of the ``fspath`` argument.
|
||||
|
||||
Plugins which implement custom items and collectors are encouraged to replace
|
||||
``fspath`` parameters (``py.path.local``) with ``path`` parameters
|
||||
(``pathlib.Path``), and drop any other usage of the ``py`` library if possible.
|
||||
|
||||
If possible, plugins with custom items should use :ref:`cooperative
|
||||
constructors <uncooperative-constructors-deprecated>` to avoid hardcoding
|
||||
arguments they only pass on to the superclass.
|
||||
|
||||
.. note::
|
||||
The name of the :class:`~_pytest.nodes.Node` arguments and attributes (the
|
||||
new attribute being ``path``) is **the opposite** of the situation for
|
||||
hooks, :ref:`outlined below <legacy-path-hooks-deprecated>` (the old
|
||||
argument being ``path``).
|
||||
|
||||
This is an unfortunate artifact due to historical reasons, which should be
|
||||
resolved in future versions as we slowly get rid of the :pypi:`py`
|
||||
dependency (see :issue:`9283` for a longer discussion).
|
||||
|
||||
Due to the ongoing migration of methods like :meth:`~pytest.Item.reportinfo`
|
||||
which still is expected to return a ``py.path.local`` object, nodes still have
|
||||
both ``fspath`` (``py.path.local``) and ``path`` (``pathlib.Path``) attributes,
|
||||
no matter what argument was used in the constructor. We expect to deprecate the
|
||||
``fspath`` attribute in a future release.
|
||||
|
||||
|
||||
``py.path.local`` arguments for hooks replaced with ``pathlib.Path``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 7.0
|
||||
.. versionremoved:: 8.0
|
||||
|
||||
In order to support the transition from ``py.path.local`` to :mod:`pathlib`, the following hooks now receive additional arguments:
|
||||
|
||||
* :hook:`pytest_ignore_collect(collection_path: pathlib.Path) <pytest_ignore_collect>` as equivalent to ``path``
|
||||
* :hook:`pytest_collect_file(file_path: pathlib.Path) <pytest_collect_file>` as equivalent to ``path``
|
||||
* :hook:`pytest_pycollect_makemodule(module_path: pathlib.Path) <pytest_pycollect_makemodule>` as equivalent to ``path``
|
||||
* :hook:`pytest_report_header(start_path: pathlib.Path) <pytest_report_header>` as equivalent to ``startdir``
|
||||
* :hook:`pytest_report_collectionfinish(start_path: pathlib.Path) <pytest_report_collectionfinish>` as equivalent to ``startdir``
|
||||
|
||||
The accompanying ``py.path.local`` based paths have been deprecated: plugins which manually invoke those hooks should only pass the new ``pathlib.Path`` arguments, and users should change their hook implementations to use the new ``pathlib.Path`` arguments.
|
||||
|
||||
.. note::
|
||||
The name of the :class:`~_pytest.nodes.Node` arguments and attributes,
|
||||
:ref:`outlined above <node-ctor-fspath-deprecation>` (the new attribute
|
||||
being ``path``) is **the opposite** of the situation for hooks (the old
|
||||
argument being ``path``).
|
||||
|
||||
This is an unfortunate artifact due to historical reasons, which should be
|
||||
resolved in future versions as we slowly get rid of the :pypi:`py`
|
||||
dependency (see :issue:`9283` for a longer discussion).
|
||||
|
||||
|
||||
.. _nose-deprecation:
|
||||
|
||||
Support for tests written for nose
|
||||
|
|
|
@ -162,7 +162,7 @@ objects, they are still using the default pytest representation:
|
|||
rootdir: /home/sweet/project
|
||||
collected 8 items
|
||||
|
||||
<Dir parametrize.rst-194>
|
||||
<Dir parametrize.rst-197>
|
||||
<Module test_time.py>
|
||||
<Function test_timedistance_v0[a0-b0-expected0]>
|
||||
<Function test_timedistance_v0[a1-b1-expected1]>
|
||||
|
@ -239,7 +239,7 @@ If you just collect tests you'll also nicely see 'advanced' and 'basic' as varia
|
|||
rootdir: /home/sweet/project
|
||||
collected 4 items
|
||||
|
||||
<Dir parametrize.rst-194>
|
||||
<Dir parametrize.rst-197>
|
||||
<Module test_scenarios.py>
|
||||
<Class TestSampleWithScenarios>
|
||||
<Function test_demo1[basic]>
|
||||
|
@ -318,7 +318,7 @@ Let's first see how it looks like at collection time:
|
|||
rootdir: /home/sweet/project
|
||||
collected 2 items
|
||||
|
||||
<Dir parametrize.rst-194>
|
||||
<Dir parametrize.rst-197>
|
||||
<Module test_backends.py>
|
||||
<Function test_db_initialized[d1]>
|
||||
<Function test_db_initialized[d2]>
|
||||
|
|
|
@ -152,7 +152,7 @@ The test collection would look like this:
|
|||
configfile: pytest.ini
|
||||
collected 2 items
|
||||
|
||||
<Dir pythoncollection.rst-195>
|
||||
<Dir pythoncollection.rst-198>
|
||||
<Module check_myapp.py>
|
||||
<Class CheckMyApp>
|
||||
<Function simple_check>
|
||||
|
@ -215,7 +215,7 @@ You can always peek at the collection tree without running tests like this:
|
|||
configfile: pytest.ini
|
||||
collected 3 items
|
||||
|
||||
<Dir pythoncollection.rst-195>
|
||||
<Dir pythoncollection.rst-198>
|
||||
<Dir CWD>
|
||||
<Module pythoncollection.py>
|
||||
<Function test_function>
|
||||
|
|
|
@ -445,7 +445,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
|
|||
self = <failure_demo.TestRaises object at 0xdeadbeef0020>
|
||||
|
||||
def test_tupleerror(self):
|
||||
> a, b = [1] # NOQA
|
||||
> a, b = [1] # noqa: F841
|
||||
E ValueError: not enough values to unpack (expected 2, got 1)
|
||||
|
||||
failure_demo.py:175: ValueError
|
||||
|
@ -467,7 +467,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
|
|||
self = <failure_demo.TestRaises object at 0xdeadbeef0022>
|
||||
|
||||
def test_some_error(self):
|
||||
> if namenotexi: # NOQA
|
||||
> if namenotexi: # noqa: F821
|
||||
E NameError: name 'namenotexi' is not defined
|
||||
|
||||
failure_demo.py:183: NameError
|
||||
|
|
|
@ -52,10 +52,9 @@ Plugins
|
|||
|
||||
Rerunning any failed tests can mitigate the negative effects of flaky tests by giving them additional chances to pass, so that the overall build does not fail. Several pytest plugins support this:
|
||||
|
||||
* `flaky <https://github.com/box/flaky>`_
|
||||
* `pytest-flakefinder <https://github.com/dropbox/pytest-flakefinder>`_ - `blog post <https://blogs.dropbox.com/tech/2016/03/open-sourcing-pytest-tools/>`_
|
||||
* `pytest-rerunfailures <https://github.com/pytest-dev/pytest-rerunfailures>`_
|
||||
* `pytest-replay <https://github.com/ESSS/pytest-replay>`_: This plugin helps to reproduce locally crashes or flaky tests observed during CI runs.
|
||||
* `pytest-flakefinder <https://github.com/dropbox/pytest-flakefinder>`_ - `blog post <https://blogs.dropbox.com/tech/2016/03/open-sourcing-pytest-tools/>`_
|
||||
|
||||
Plugins to deliberately randomize tests can help expose tests with state problems:
|
||||
|
||||
|
@ -106,7 +105,7 @@ This is a limited list, please submit an issue or pull request to expand it!
|
|||
* Gao, Zebao, Yalan Liang, Myra B. Cohen, Atif M. Memon, and Zhen Wang. "Making system user interactive tests repeatable: When and what should we control?." In *Software Engineering (ICSE), 2015 IEEE/ACM 37th IEEE International Conference on*, vol. 1, pp. 55-65. IEEE, 2015. `PDF <http://www.cs.umd.edu/~atif/pubs/gao-icse15.pdf>`__
|
||||
* Palomba, Fabio, and Andy Zaidman. "Does refactoring of test smells induce fixing flaky tests?." In *Software Maintenance and Evolution (ICSME), 2017 IEEE International Conference on*, pp. 1-12. IEEE, 2017. `PDF in Google Drive <https://drive.google.com/file/d/10HdcCQiuQVgW3yYUJD-TSTq1NbYEprl0/view>`__
|
||||
* Bell, Jonathan, Owolabi Legunsen, Michael Hilton, Lamyaa Eloussi, Tifany Yung, and Darko Marinov. "DeFlaker: Automatically detecting flaky tests." In *Proceedings of the 2018 International Conference on Software Engineering*. 2018. `PDF <https://www.jonbell.net/icse18-deflaker.pdf>`__
|
||||
|
||||
* Dutta, Saikat and Shi, August and Choudhary, Rutvik and Zhang, Zhekun and Jain, Aryaman and Misailovic, Sasa. "Detecting flaky tests in probabilistic and machine learning applications." In *Proceedings of the 29th ACM SIGSOFT International Symposium on Software Testing and Analysis (ISSTA)*, pp. 211-224. ACM, 2020. `PDF <https://www.cs.cornell.edu/~saikatd/papers/flash-issta20.pdf>`__
|
||||
|
||||
Resources
|
||||
^^^^^^^^^
|
||||
|
|
|
@ -22,7 +22,7 @@ Install ``pytest``
|
|||
.. code-block:: bash
|
||||
|
||||
$ pytest --version
|
||||
pytest 8.0.2
|
||||
pytest 8.1.2
|
||||
|
||||
.. _`simpletest`:
|
||||
|
||||
|
|
|
@ -224,6 +224,7 @@ place the objects you want to appear in the doctest namespace:
|
|||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
import pytest
|
||||
import numpy
|
||||
|
||||
|
||||
|
|
|
@ -1418,7 +1418,7 @@ Running the above tests results in the following test IDs being used:
|
|||
rootdir: /home/sweet/project
|
||||
collected 12 items
|
||||
|
||||
<Dir fixtures.rst-213>
|
||||
<Dir fixtures.rst-216>
|
||||
<Module test_anothersmtp.py>
|
||||
<Function test_showhelo[smtp.gmail.com]>
|
||||
<Function test_showhelo[mail.python.org]>
|
||||
|
|
|
@ -100,7 +100,7 @@ object, the wrapper may modify that result, but it's probably better to avoid it
|
|||
|
||||
If the hook implementation failed with an exception, the wrapper can handle that
|
||||
exception using a ``try-catch-finally`` around the ``yield``, by propagating it,
|
||||
supressing it, or raising a different exception entirely.
|
||||
suppressing it, or raising a different exception entirely.
|
||||
|
||||
For more information, consult the
|
||||
:ref:`pluggy documentation about hook wrappers <pluggy:hookwrappers>`.
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
- `Professional Testing with Python <https://python-academy.com/courses/python_course_testing.html>`_, via `Python Academy <https://www.python-academy.com/>`_ (3 day in-depth training):
|
||||
* **June 11th to 13th 2024**, Remote
|
||||
* **March 4th to 6th 2025**, Leipzig, Germany / Remote
|
||||
- `pytest development sprint <https://github.com/pytest-dev/pytest/discussions/11655>`_, June 2024 (`date poll <https://nuudel.digitalcourage.de/2tEsEpRcwMNcAXVO>`_)
|
||||
- `pytest development sprint <https://github.com/pytest-dev/pytest/discussions/11655>`_, **June 17th -- 22nd 2024**
|
||||
- pytest tips and tricks for a better testsuite, `Europython 2024 <https://ep2024.europython.eu/>`_, **July 8th -- 14th 2024** (3h), Prague
|
||||
|
||||
Also see :doc:`previous talks and blogposts <talks>`.
|
||||
|
||||
|
@ -87,7 +88,7 @@ Features
|
|||
Documentation
|
||||
-------------
|
||||
|
||||
* :ref:`Get started <get-started>` - install pytest and grasp its basics just twenty minutes
|
||||
* :ref:`Get started <get-started>` - install pytest and grasp its basics in just twenty minutes
|
||||
* :ref:`How-to guides <how-to>` - step-by-step guides, covering a vast range of use-cases and needs
|
||||
* :ref:`Reference guides <reference>` - includes the complete pytest API reference, lists of plugins and more
|
||||
* :ref:`Explanation <explanation>` - background, discussion of key topics, answers to higher-level questions
|
||||
|
|
|
@ -2103,6 +2103,8 @@ All the command-line flags can be obtained by running ``pytest --help``::
|
|||
--log-cli-date-format=LOG_CLI_DATE_FORMAT
|
||||
Log date format used by the logging module
|
||||
--log-file=LOG_FILE Path to a file when logging will be written to
|
||||
--log-file-mode={w,a}
|
||||
Log file open mode
|
||||
--log-file-level=LOG_FILE_LEVEL
|
||||
Log file logging level
|
||||
--log-file-format=LOG_FILE_FORMAT
|
||||
|
@ -2128,6 +2130,9 @@ All the command-line flags can be obtained by running ``pytest --help``::
|
|||
Each line specifies a pattern for
|
||||
warnings.filterwarnings. Processed after
|
||||
-W/--pythonwarnings.
|
||||
consider_namespace_packages (bool):
|
||||
Consider namespace packages when resolving module
|
||||
names during import
|
||||
usefixtures (args): List of default fixtures to be used with this
|
||||
project
|
||||
python_files (args): Glob-style file patterns for Python test module
|
||||
|
@ -2146,6 +2151,11 @@ All the command-line flags can be obtained by running ``pytest --help``::
|
|||
progress information ("progress" (percentage) |
|
||||
"count" | "progress-even-when-capture-no" (forces
|
||||
progress even when capture=no)
|
||||
verbosity_test_cases (string):
|
||||
Specify a verbosity level for test case execution,
|
||||
overriding the main level. Higher levels will
|
||||
provide more detailed information about each test
|
||||
case executed.
|
||||
xfail_strict (bool): Default for the strict parameter of xfail markers
|
||||
when not given explicitly (default: False)
|
||||
tmp_path_retention_count (string):
|
||||
|
@ -2193,6 +2203,8 @@ All the command-line flags can be obtained by running ``pytest --help``::
|
|||
log_cli_date_format (string):
|
||||
Default value for --log-cli-date-format
|
||||
log_file (string): Default value for --log-file
|
||||
log_file_mode (string):
|
||||
Default value for --log-file-mode
|
||||
log_file_level (string):
|
||||
Default value for --log-file-level
|
||||
log_file_format (string):
|
||||
|
|
|
@ -788,6 +788,8 @@ class ExceptionInfo(Generic[E]):
|
|||
If `None`, will search for a matching exception at any nesting depth.
|
||||
If >= 1, will only match an exception if it's at the specified depth (depth = 1 being
|
||||
the exceptions contained within the topmost exception group).
|
||||
|
||||
.. versionadded:: 8.0
|
||||
"""
|
||||
msg = "Captured exception is not an instance of `BaseExceptionGroup`"
|
||||
assert isinstance(self.value, BaseExceptionGroup), msg
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# mypy: allow-untyped-defs
|
||||
"""Python version compatibility code."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
|
@ -16,6 +17,22 @@ from typing import Callable
|
|||
from typing import Final
|
||||
from typing import NoReturn
|
||||
|
||||
import py
|
||||
|
||||
|
||||
#: constant to prepare valuing pylib path replacements/lazy proxies later on
|
||||
# intended for removal in pytest 8.0 or 9.0
|
||||
|
||||
# fmt: off
|
||||
# intentional space to create a fake difference for the verification
|
||||
LEGACY_PATH = py.path. local
|
||||
# fmt: on
|
||||
|
||||
|
||||
def legacy_path(path: str | os.PathLike[str]) -> LEGACY_PATH:
|
||||
"""Internal wrapper to prepare lazy proxies for legacy_path instances"""
|
||||
return LEGACY_PATH(path)
|
||||
|
||||
|
||||
# fmt: off
|
||||
# Singleton type for NOTSET, as described in:
|
||||
|
|
|
@ -38,12 +38,14 @@ from typing import TYPE_CHECKING
|
|||
from typing import Union
|
||||
import warnings
|
||||
|
||||
import pluggy
|
||||
from pluggy import HookimplMarker
|
||||
from pluggy import HookimplOpts
|
||||
from pluggy import HookspecMarker
|
||||
from pluggy import HookspecOpts
|
||||
from pluggy import PluginManager
|
||||
|
||||
from .compat import PathAwareHookProxy
|
||||
from .exceptions import PrintHelp as PrintHelp
|
||||
from .exceptions import UsageError as UsageError
|
||||
from .findpaths import determine_setup
|
||||
|
@ -1068,7 +1070,7 @@ class Config:
|
|||
self._store = self.stash
|
||||
|
||||
self.trace = self.pluginmanager.trace.root.get("config")
|
||||
self.hook = self.pluginmanager.hook # type: ignore[assignment]
|
||||
self.hook: pluggy.HookRelay = PathAwareHookProxy(self.pluginmanager.hook) # type: ignore[assignment]
|
||||
self._inicache: Dict[str, Any] = {}
|
||||
self._override_ini: Sequence[str] = ()
|
||||
self._opt2dest: Dict[str, str] = {}
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import functools
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from typing import Mapping
|
||||
import warnings
|
||||
|
||||
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: Any) -> Any:
|
||||
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]
|
|
@ -36,6 +36,21 @@ YIELD_FIXTURE = PytestDeprecationWarning(
|
|||
PRIVATE = PytestDeprecationWarning("A private pytest class or function was used.")
|
||||
|
||||
|
||||
HOOK_LEGACY_PATH_ARG = UnformattedWarning(
|
||||
PytestRemovedIn9Warning,
|
||||
"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(
|
||||
PytestRemovedIn9Warning,
|
||||
"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",
|
||||
)
|
||||
|
||||
HOOK_LEGACY_MARKING = UnformattedWarning(
|
||||
PytestDeprecationWarning,
|
||||
"The hook{type} {fullname} uses old-style configuration options (marks or attributes).\n"
|
||||
|
|
|
@ -22,6 +22,7 @@ if TYPE_CHECKING:
|
|||
|
||||
from _pytest._code.code import ExceptionInfo
|
||||
from _pytest._code.code import ExceptionRepr
|
||||
from _pytest.compat import LEGACY_PATH
|
||||
from _pytest.config import _PluggyPlugin
|
||||
from _pytest.config import Config
|
||||
from _pytest.config import ExitCode
|
||||
|
@ -296,7 +297,9 @@ def pytest_collection_finish(session: "Session") -> None:
|
|||
|
||||
|
||||
@hookspec(firstresult=True)
|
||||
def pytest_ignore_collect(collection_path: Path, config: "Config") -> Optional[bool]:
|
||||
def pytest_ignore_collect(
|
||||
collection_path: Path, path: "LEGACY_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
|
||||
|
@ -310,10 +313,8 @@ def pytest_ignore_collect(collection_path: Path, config: "Config") -> Optional[b
|
|||
|
||||
.. versionchanged:: 7.0.0
|
||||
The ``collection_path`` parameter was added as a :class:`pathlib.Path`
|
||||
equivalent of the ``path`` parameter.
|
||||
|
||||
.. versionchanged:: 8.0.0
|
||||
The ``path`` parameter has been removed.
|
||||
equivalent of the ``path`` parameter. The ``path`` parameter
|
||||
has been deprecated.
|
||||
|
||||
Use in conftest plugins
|
||||
=======================
|
||||
|
@ -354,7 +355,9 @@ def pytest_collect_directory(path: Path, parent: "Collector") -> "Optional[Colle
|
|||
"""
|
||||
|
||||
|
||||
def pytest_collect_file(file_path: Path, parent: "Collector") -> "Optional[Collector]":
|
||||
def pytest_collect_file(
|
||||
file_path: Path, path: "LEGACY_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
|
||||
|
@ -367,10 +370,8 @@ def pytest_collect_file(file_path: Path, parent: "Collector") -> "Optional[Colle
|
|||
|
||||
.. versionchanged:: 7.0.0
|
||||
The ``file_path`` parameter was added as a :class:`pathlib.Path`
|
||||
equivalent of the ``path`` parameter.
|
||||
|
||||
.. versionchanged:: 8.0.0
|
||||
The ``path`` parameter was removed.
|
||||
equivalent of the ``path`` parameter. The ``path`` parameter
|
||||
has been deprecated.
|
||||
|
||||
Use in conftest plugins
|
||||
=======================
|
||||
|
@ -467,7 +468,9 @@ def pytest_make_collect_report(collector: "Collector") -> "Optional[CollectRepor
|
|||
|
||||
|
||||
@hookspec(firstresult=True)
|
||||
def pytest_pycollect_makemodule(module_path: Path, parent) -> Optional["Module"]:
|
||||
def pytest_pycollect_makemodule(
|
||||
module_path: Path, path: "LEGACY_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.
|
||||
|
@ -483,8 +486,7 @@ def pytest_pycollect_makemodule(module_path: Path, parent) -> Optional["Module"]
|
|||
The ``module_path`` parameter was added as a :class:`pathlib.Path`
|
||||
equivalent of the ``path`` parameter.
|
||||
|
||||
.. versionchanged:: 8.0.0
|
||||
The ``path`` parameter has been removed in favor of ``module_path``.
|
||||
The ``path`` parameter has been deprecated in favor of ``fspath``.
|
||||
|
||||
Use in conftest plugins
|
||||
=======================
|
||||
|
@ -992,7 +994,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
|
||||
config: "Config", start_path: Path, startdir: "LEGACY_PATH"
|
||||
) -> Union[str, List[str]]:
|
||||
"""Return a string or list of strings to be displayed as header info for terminal reporting.
|
||||
|
||||
|
@ -1009,10 +1011,8 @@ 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.
|
||||
|
||||
.. versionchanged:: 8.0.0
|
||||
The ``startdir`` parameter has been removed.
|
||||
equivalent of the ``startdir`` parameter. The ``startdir`` parameter
|
||||
has been deprecated.
|
||||
|
||||
Use in conftest plugins
|
||||
=======================
|
||||
|
@ -1024,6 +1024,7 @@ def pytest_report_header( # type:ignore[empty-body]
|
|||
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
|
||||
|
@ -1047,10 +1048,8 @@ 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.
|
||||
|
||||
.. versionchanged:: 8.0.0
|
||||
The ``startdir`` parameter has been removed.
|
||||
equivalent of the ``startdir`` parameter. The ``startdir`` parameter
|
||||
has been deprecated.
|
||||
|
||||
Use in conftest plugins
|
||||
=======================
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# mypy: allow-untyped-defs
|
||||
"""Add backward compatibility support for the legacy py path type."""
|
||||
|
||||
import dataclasses
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shlex
|
||||
import subprocess
|
||||
|
@ -14,9 +14,9 @@ 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
|
||||
|
@ -39,20 +39,6 @@ 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:
|
||||
"""
|
||||
|
|
|
@ -37,6 +37,7 @@ 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
|
||||
|
@ -557,6 +558,7 @@ class Session(nodes.Collector):
|
|||
super().__init__(
|
||||
name="",
|
||||
path=config.rootpath,
|
||||
fspath=None,
|
||||
parent=None,
|
||||
config=config,
|
||||
session=self,
|
||||
|
@ -694,7 +696,7 @@ class Session(nodes.Collector):
|
|||
proxy: pluggy.HookRelay
|
||||
if remove_mods:
|
||||
# One or more conftests are not in use at this path.
|
||||
proxy = FSHookProxy(pm, remove_mods) # type: ignore[arg-type,assignment]
|
||||
proxy = PathAwareHookProxy(FSHookProxy(pm, remove_mods)) # type: ignore[arg-type,assignment]
|
||||
else:
|
||||
# All plugins are active for this fspath.
|
||||
proxy = self.config.hook
|
||||
|
|
|
@ -3,6 +3,7 @@ import abc
|
|||
from functools import cached_property
|
||||
from inspect import signature
|
||||
import os
|
||||
import pathlib
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from typing import Callable
|
||||
|
@ -29,8 +30,11 @@ 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 NODE_CTOR_FSPATH_ARG
|
||||
from _pytest.mark.structures import Mark
|
||||
from _pytest.mark.structures import MarkDecorator
|
||||
from _pytest.mark.structures import NodeKeywords
|
||||
|
@ -55,6 +59,29 @@ tracebackcutdir = Path(_pytest.__file__).parent
|
|||
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
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")
|
||||
|
||||
|
||||
|
@ -110,6 +137,13 @@ class Node(abc.ABC, metaclass=NodeMeta):
|
|||
leaf nodes.
|
||||
"""
|
||||
|
||||
# Implemented in the legacypath plugin.
|
||||
#: A ``LEGACY_PATH`` copy of the :attr:`path` attribute. Intended for usage
|
||||
#: for methods not migrated to ``pathlib.Path`` yet, such as
|
||||
#: :meth:`Item.reportinfo <pytest.Item.reportinfo>`. Will be deprecated in
|
||||
#: a future release, prefer using :attr:`path` instead.
|
||||
fspath: LEGACY_PATH
|
||||
|
||||
# Use __slots__ to make attribute access faster.
|
||||
# Note that __dict__ is still available.
|
||||
__slots__ = (
|
||||
|
@ -129,6 +163,7 @@ 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:
|
||||
|
@ -154,11 +189,10 @@ class Node(abc.ABC, metaclass=NodeMeta):
|
|||
raise TypeError("session or parent must be provided")
|
||||
self.session = parent.session
|
||||
|
||||
if path is None:
|
||||
if path is None and fspath 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 = path
|
||||
self.path: pathlib.Path = _imply_path(type(self), path, fspath=fspath)
|
||||
|
||||
# The explicit annotation is to avoid publicly exposing NodeKeywords.
|
||||
#: Keywords/markers collected from all scopes.
|
||||
|
@ -529,6 +563,7 @@ 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,
|
||||
|
@ -544,8 +579,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:
|
||||
|
@ -585,11 +620,12 @@ class FSCollector(Collector, abc.ABC):
|
|||
cls,
|
||||
parent,
|
||||
*,
|
||||
fspath: Optional[LEGACY_PATH] = None,
|
||||
path: Optional[Path] = None,
|
||||
**kw,
|
||||
) -> "Self":
|
||||
"""The public constructor."""
|
||||
return super().from_parent(parent=parent, path=path, **kw)
|
||||
return super().from_parent(parent=parent, fspath=fspath, path=path, **kw)
|
||||
|
||||
|
||||
class File(FSCollector, abc.ABC):
|
||||
|
|
|
@ -539,6 +539,10 @@ def import_path(
|
|||
except CouldNotResolvePathError:
|
||||
pass
|
||||
else:
|
||||
# If the given module name is already in sys.modules, do not import it again.
|
||||
with contextlib.suppress(KeyError):
|
||||
return sys.modules[module_name]
|
||||
|
||||
mod = _import_module_using_spec(
|
||||
module_name, path, pkg_root, insert_modules=False
|
||||
)
|
||||
|
|
|
@ -48,6 +48,7 @@ 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
|
||||
|
@ -665,6 +666,7 @@ class Package(nodes.Directory):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
fspath: Optional[LEGACY_PATH],
|
||||
parent: nodes.Collector,
|
||||
# NOTE: following args are unused:
|
||||
config=None,
|
||||
|
@ -676,6 +678,7 @@ class Package(nodes.Directory):
|
|||
# super().__init__(self, fspath, parent=parent)
|
||||
session = parent.session
|
||||
super().__init__(
|
||||
fspath=fspath,
|
||||
path=path,
|
||||
parent=parent,
|
||||
config=config,
|
||||
|
|
|
@ -142,7 +142,7 @@ class ApproxNumpy(ApproxBase):
|
|||
)
|
||||
return f"approx({list_scalars!r})"
|
||||
|
||||
def _repr_compare(self, other_side: "ndarray") -> List[str]:
|
||||
def _repr_compare(self, other_side: Union["ndarray", List[Any]]) -> List[str]:
|
||||
import itertools
|
||||
import math
|
||||
|
||||
|
@ -163,10 +163,14 @@ class ApproxNumpy(ApproxBase):
|
|||
self._approx_scalar, self.expected.tolist()
|
||||
)
|
||||
|
||||
if np_array_shape != other_side.shape:
|
||||
# Convert other_side to numpy array to ensure shape attribute is available.
|
||||
other_side_as_array = _as_numpy_array(other_side)
|
||||
assert other_side_as_array is not None
|
||||
|
||||
if np_array_shape != other_side_as_array.shape:
|
||||
return [
|
||||
"Impossible to compare arrays with different shapes.",
|
||||
f"Shapes: {np_array_shape} and {other_side.shape}",
|
||||
f"Shapes: {np_array_shape} and {other_side_as_array.shape}",
|
||||
]
|
||||
|
||||
number_of_elements = self.expected.size
|
||||
|
@ -175,7 +179,7 @@ class ApproxNumpy(ApproxBase):
|
|||
different_ids = []
|
||||
for index in itertools.product(*(range(i) for i in np_array_shape)):
|
||||
approx_value = get_value_from_nested_list(approx_side_as_seq, index)
|
||||
other_value = get_value_from_nested_list(other_side, index)
|
||||
other_value = get_value_from_nested_list(other_side_as_array, index)
|
||||
if approx_value != other_value:
|
||||
abs_diff = abs(approx_value.expected - other_value)
|
||||
max_abs_diff = max(max_abs_diff, abs_diff)
|
||||
|
@ -188,7 +192,7 @@ class ApproxNumpy(ApproxBase):
|
|||
message_data = [
|
||||
(
|
||||
str(index),
|
||||
str(get_value_from_nested_list(other_side, index)),
|
||||
str(get_value_from_nested_list(other_side_as_array, index)),
|
||||
str(get_value_from_nested_list(approx_side_as_seq, index)),
|
||||
)
|
||||
for index in different_ids
|
||||
|
|
|
@ -19,6 +19,8 @@ class StashKey(Generic[T]):
|
|||
A ``StashKey`` is associated with the type ``T`` of the value of the key.
|
||||
|
||||
A ``StashKey`` is unique and cannot conflict with another key.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
@ -61,6 +63,8 @@ class Stash:
|
|||
some_str = stash[some_str_key]
|
||||
# The static type of some_bool is bool.
|
||||
some_bool = stash[some_bool_key]
|
||||
|
||||
.. versionadded:: 7.0
|
||||
"""
|
||||
|
||||
__slots__ = ("_storage",)
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
# mypy: allow-untyped-defs
|
||||
from pathlib import Path
|
||||
import re
|
||||
import sys
|
||||
|
||||
from _pytest import deprecated
|
||||
from _pytest.compat import legacy_path
|
||||
from _pytest.pytester import Pytester
|
||||
import pytest
|
||||
from pytest import PytestDeprecationWarning
|
||||
|
@ -85,6 +90,56 @@ 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_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_fixture_disallow_on_marked_functions():
|
||||
"""Test that applying @pytest.fixture to a marked function warns (#3364)."""
|
||||
with pytest.warns(
|
||||
|
|
|
@ -763,6 +763,24 @@ class TestApprox:
|
|||
assert a12 != approx(a21)
|
||||
assert a21 != approx(a12)
|
||||
|
||||
def test_numpy_array_implicit_conversion(self) -> None:
|
||||
"""#12114."""
|
||||
np = pytest.importorskip("numpy")
|
||||
|
||||
class ImplicitArray:
|
||||
"""Type which is implicitly convertible to a numpy array."""
|
||||
|
||||
def __init__(self, vals):
|
||||
self.vals = vals
|
||||
|
||||
def __array__(self, dtype=None, copy=None):
|
||||
return np.array(self.vals)
|
||||
|
||||
vec1 = ImplicitArray([1.0, 2.0, 3.0])
|
||||
vec2 = ImplicitArray([1.0, 2.0, 4.0])
|
||||
# see issue #12114 for test case
|
||||
assert vec1 != approx(vec2)
|
||||
|
||||
def test_numpy_array_protocol(self):
|
||||
"""
|
||||
array-like objects such as tensorflow's DeviceArray are handled like ndarray.
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# mypy: allow-untyped-defs
|
||||
from pathlib import Path
|
||||
|
||||
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
|
||||
import pytest
|
||||
|
@ -16,7 +16,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 # type: ignore[attr-defined]
|
||||
assert item2.fspath == item.fspath
|
||||
assert item2.path == item.path
|
||||
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ from typing import Type
|
|||
import warnings
|
||||
|
||||
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
|
||||
|
@ -44,9 +45,9 @@ def test_subclassing_both_item_and_collector_deprecated(
|
|||
warnings.simplefilter("error")
|
||||
|
||||
class SoWrong(nodes.Item, nodes.File):
|
||||
def __init__(self, path, parent):
|
||||
def __init__(self, fspath, parent):
|
||||
"""Legacy ctor with legacy call # don't wana see"""
|
||||
super().__init__(parent, path)
|
||||
super().__init__(fspath, parent)
|
||||
|
||||
def collect(self):
|
||||
raise NotImplementedError()
|
||||
|
@ -55,7 +56,9 @@ def test_subclassing_both_item_and_collector_deprecated(
|
|||
raise NotImplementedError()
|
||||
|
||||
with pytest.warns(PytestWarning) as rec:
|
||||
SoWrong.from_parent(request.session, path=tmp_path / "broken.txt", wrong=10)
|
||||
SoWrong.from_parent(
|
||||
request.session, fspath=legacy_path(tmp_path / "broken.txt")
|
||||
)
|
||||
messages = [str(x.message) for x in rec]
|
||||
assert any(
|
||||
re.search(".*SoWrong.* not using a cooperative constructor.*", x)
|
||||
|
|
|
@ -587,6 +587,12 @@ class TestImportLibMode:
|
|||
assert data.value == "foo"
|
||||
assert data.__module__ == "_src.tests.test_dataclass"
|
||||
|
||||
# Ensure we do not import the same module again (#11475).
|
||||
module2 = import_path(
|
||||
fn, mode="importlib", root=tmp_path, consider_namespace_packages=ns_param
|
||||
)
|
||||
assert module is module2
|
||||
|
||||
def test_importmode_importlib_with_pickle(
|
||||
self, tmp_path: Path, ns_param: bool
|
||||
) -> None:
|
||||
|
@ -616,6 +622,12 @@ class TestImportLibMode:
|
|||
action = round_trip()
|
||||
assert action() == 42
|
||||
|
||||
# Ensure we do not import the same module again (#11475).
|
||||
module2 = import_path(
|
||||
fn, mode="importlib", root=tmp_path, consider_namespace_packages=ns_param
|
||||
)
|
||||
assert module is module2
|
||||
|
||||
def test_importmode_importlib_with_pickle_separate_modules(
|
||||
self, tmp_path: Path, ns_param: bool
|
||||
) -> None:
|
||||
|
@ -816,6 +828,14 @@ class TestImportLibMode:
|
|||
consider_namespace_packages=ns_param,
|
||||
)
|
||||
assert len(mod.instance.INSTANCES) == 1
|
||||
# Ensure we do not import the same module again (#11475).
|
||||
mod2 = import_path(
|
||||
init,
|
||||
root=tmp_path,
|
||||
mode=ImportMode.importlib,
|
||||
consider_namespace_packages=ns_param,
|
||||
)
|
||||
assert mod is mod2
|
||||
|
||||
def test_importlib_root_is_package(self, pytester: Pytester) -> None:
|
||||
"""
|
||||
|
@ -942,6 +962,15 @@ class TestImportLibMode:
|
|||
assert mod.__name__ == "app.core"
|
||||
assert mod.__file__ and Path(mod.__file__) == core_py
|
||||
|
||||
# Ensure we do not import the same module again (#11475).
|
||||
mod2 = import_path(
|
||||
core_py,
|
||||
mode="importlib",
|
||||
root=pytester.path,
|
||||
consider_namespace_packages=ns_param,
|
||||
)
|
||||
assert mod is mod2
|
||||
|
||||
# tests are not reachable from sys.path, so they are imported as a standalone modules.
|
||||
# Instead of '.tests.a.test_core', we import as "_tests.a.test_core" because
|
||||
# importlib considers module names starting with '.' to be local imports.
|
||||
|
@ -952,6 +981,16 @@ class TestImportLibMode:
|
|||
consider_namespace_packages=ns_param,
|
||||
)
|
||||
assert mod.__name__ == "_tests.a.test_core"
|
||||
|
||||
# Ensure we do not import the same module again (#11475).
|
||||
mod2 = import_path(
|
||||
test_path1,
|
||||
mode="importlib",
|
||||
root=pytester.path,
|
||||
consider_namespace_packages=ns_param,
|
||||
)
|
||||
assert mod is mod2
|
||||
|
||||
mod = import_path(
|
||||
test_path2,
|
||||
mode="importlib",
|
||||
|
@ -960,6 +999,15 @@ class TestImportLibMode:
|
|||
)
|
||||
assert mod.__name__ == "_tests.b.test_core"
|
||||
|
||||
# Ensure we do not import the same module again (#11475).
|
||||
mod2 = import_path(
|
||||
test_path2,
|
||||
mode="importlib",
|
||||
root=pytester.path,
|
||||
consider_namespace_packages=ns_param,
|
||||
)
|
||||
assert mod is mod2
|
||||
|
||||
def test_import_using_normal_mechanism_first_integration(
|
||||
self, monkeypatch: MonkeyPatch, pytester: Pytester, ns_param: bool
|
||||
) -> None:
|
||||
|
@ -1021,6 +1069,14 @@ class TestImportLibMode:
|
|||
assert mod.__file__ and Path(mod.__file__) == x_in_sub_folder
|
||||
assert mod.X == "a/b/x"
|
||||
|
||||
mod2 = import_path(
|
||||
x_in_sub_folder,
|
||||
mode=ImportMode.importlib,
|
||||
root=pytester.path,
|
||||
consider_namespace_packages=ns_param,
|
||||
)
|
||||
assert mod is mod2
|
||||
|
||||
# Attempt to import root 'x.py'.
|
||||
with pytest.raises(AssertionError, match="x at root"):
|
||||
_ = import_path(
|
||||
|
@ -1124,6 +1180,12 @@ class TestNamespacePackages:
|
|||
assert mod.__name__ == "com.company.app.core.models"
|
||||
assert mod.__file__ == str(models_py)
|
||||
|
||||
# Ensure we do not import the same module again (#11475).
|
||||
mod2 = import_path(
|
||||
models_py, mode=import_mode, root=tmp_path, consider_namespace_packages=True
|
||||
)
|
||||
assert mod is mod2
|
||||
|
||||
pkg_root, module_name = resolve_pkg_root_and_module_name(
|
||||
algorithms_py, consider_namespace_packages=True
|
||||
)
|
||||
|
@ -1141,6 +1203,15 @@ class TestNamespacePackages:
|
|||
assert mod.__name__ == "com.company.calc.algo.algorithms"
|
||||
assert mod.__file__ == str(algorithms_py)
|
||||
|
||||
# Ensure we do not import the same module again (#11475).
|
||||
mod2 = import_path(
|
||||
algorithms_py,
|
||||
mode=import_mode,
|
||||
root=tmp_path,
|
||||
consider_namespace_packages=True,
|
||||
)
|
||||
assert mod is mod2
|
||||
|
||||
@pytest.mark.parametrize("import_mode", ["prepend", "append", "importlib"])
|
||||
def test_incorrect_namespace_package(
|
||||
self,
|
||||
|
|
Loading…
Reference in New Issue