Compare commits

..

1 Commits

Author SHA1 Message Date
Florian Bruhin
0ad452bcba Prepare release version 7.0.0rc1 (#9375) (#9377)
Co-authored-by: pytest bot <pytestbot@gmail.com>
(cherry picked from commit 85897eddc6)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2021-12-07 10:13:36 +01:00
68 changed files with 309 additions and 957 deletions

View File

@@ -23,7 +23,7 @@ permissions: {}
jobs:
build:
runs-on: ${{ matrix.os }}
timeout-minutes: 45
timeout-minutes: 30
permissions:
contents: read
@@ -37,7 +37,6 @@ jobs:
"windows-py38",
"windows-py39",
"windows-py310",
"windows-py311",
"ubuntu-py36",
"ubuntu-py37",
@@ -46,7 +45,6 @@ jobs:
"ubuntu-py38",
"ubuntu-py39",
"ubuntu-py310",
"ubuntu-py311",
"ubuntu-pypy3",
"macos-py37",
@@ -80,13 +78,9 @@ jobs:
os: windows-latest
tox_env: "py39-xdist"
- name: "windows-py310"
python: "3.10"
python: "3.10-dev"
os: windows-latest
tox_env: "py310-xdist"
- name: "windows-py311"
python: "3.11-dev"
os: windows-latest
tox_env: "py311"
- name: "ubuntu-py36"
python: "3.6"
@@ -114,13 +108,9 @@ jobs:
os: ubuntu-latest
tox_env: "py39-xdist"
- name: "ubuntu-py310"
python: "3.10"
python: "3.10-dev"
os: ubuntu-latest
tox_env: "py310-xdist"
- name: "ubuntu-py311"
python: "3.11-dev"
os: ubuntu-latest
tox_env: "py311"
- name: "ubuntu-pypy3"
python: "pypy-3.7"
os: ubuntu-latest

View File

@@ -12,7 +12,6 @@ permissions: {}
jobs:
createPullRequest:
if: github.repository_owner == 'pytest-dev'
runs-on: ubuntu-latest
permissions:
contents: write

View File

@@ -185,7 +185,6 @@ Katerina Koukiou
Keri Volans
Kevin Cox
Kevin J. Foley
Kian-Meng Ang
Kodi B. Arfer
Kostis Anagnostopoulos
Kristoffer Nordström
@@ -349,7 +348,6 @@ Xixi Zhao
Xuan Luong
Xuecong Liao
Yoav Caspi
Yuval Shimon
Zac Hatfield-Dodds
Zachary Kneupper
Zoltán Máté

View File

@@ -6,8 +6,6 @@ Release announcements
:maxdepth: 2
release-7.0.1
release-7.0.0
release-7.0.0rc1
release-6.2.5
release-6.2.4

View File

@@ -11,7 +11,7 @@ clear information about the circumstances and a simple example which
reproduces the problem.
The issue tracker is of course not empty now. We have many remaining
"enhancement" issues which we'll hopefully can tackle in 2014 with your
"enhacement" issues which we'll hopefully can tackle in 2014 with your
help.
For those who use older Python versions, please note that pytest is not

View File

@@ -1,74 +0,0 @@
pytest-7.0.0
=======================================
The pytest team is proud to announce the 7.0.0 release!
This release contains new features, improvements, bug fixes, and breaking changes, so users
are encouraged to take a look at the CHANGELOG carefully:
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:
* Adam J. Stewart
* Alexander King
* Amin Alaee
* Andrew Neitsch
* Anthony Sottile
* Ben Davies
* Bernát Gábor
* Brian Okken
* Bruno Oliveira
* Cristian Vera
* Dan Alvizu
* David Szotten
* Eddie
* Emmanuel Arias
* Emmanuel Meric de Bellefon
* Eric Liu
* Florian Bruhin
* GergelyKalmar
* Graeme Smecher
* Harshna
* Hugo van Kemenade
* Jakub Kulík
* James Myatt
* Jeff Rasley
* Kale Kundert
* Kian Meng, Ang
* Miro Hrončok
* Naveen-Pratap
* Oleg Höfling
* Olga Matoula
* Ran Benita
* Ronny Pfannschmidt
* Simon K
* Srip
* Sören Wegener
* Taneli Hukkinen
* Terje Runde
* Thomas Grainger
* Thomas Hisch
* William Jamir Silva
* Yuval Shimon
* Zac Hatfield-Dodds
* andrewdotn
* denivyruck
* ericluoliu
* oleg.hoefling
* symonk
* ziebam
* Éloi Rivard
* Éric
Happy testing,
The pytest Development Team

View File

@@ -19,7 +19,7 @@ You can upgrade from PyPI via:
Users are encouraged to take a look at the CHANGELOG carefully:
https://docs.pytest.org/en/7.0.x/changelog.html
https://docs.pytest.org/en/stable/changelog.html
Thanks to all the contributors to this release:

View File

@@ -1,20 +0,0 @@
pytest-7.0.1
=======================================
pytest 7.0.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:
* Anthony Sottile
* Bruno Oliveira
* Ran Benita
Happy testing,
The pytest Development Team

View File

@@ -117,10 +117,10 @@ 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:295
tmpdir_factory [session scope] -- .../_pytest/legacypath.py:292
Return a :class:`pytest.TempdirFactory` instance for the test session.
tmpdir -- .../_pytest/legacypath.py:302
tmpdir -- .../_pytest/legacypath.py:299
Return a temporary directory path object which is unique to each test
function invocation, created as a sub directory of the base temporary
directory.

View File

@@ -28,75 +28,6 @@ with advance notice in the **Deprecations** section of releases.
.. towncrier release notes start
pytest 7.0.1 (2022-02-11)
=========================
Bug Fixes
---------
- `#9608 <https://github.com/pytest-dev/pytest/issues/9608>`_: Fix invalid importing of ``importlib.readers`` in Python 3.9.
- `#9610 <https://github.com/pytest-dev/pytest/issues/9610>`_: Restore `UnitTestFunction.obj` to return unbound rather than bound method.
Fixes a crash during a failed teardown in unittest TestCases with non-default `__init__`.
Regressed in pytest 7.0.0.
- `#9636 <https://github.com/pytest-dev/pytest/issues/9636>`_: The ``pythonpath`` plugin was renamed to ``python_path``. This avoids a conflict with the ``pytest-pythonpath`` plugin.
- `#9642 <https://github.com/pytest-dev/pytest/issues/9642>`_: Fix running tests by id with ``::`` in the parametrize portion.
- `#9643 <https://github.com/pytest-dev/pytest/issues/9643>`_: Delay issuing a :class:`~pytest.PytestWarning` about diamond inheritance involving :class:`~pytest.Item` and
:class:`~pytest.Collector` so it can be filtered using :ref:`standard warning filters <warnings>`.
pytest 7.0.0 (2022-02-03)
=========================
(**Please see the full set of changes for this release also in the 7.0.0rc1 notes below**)
Deprecations
------------
- `#9488 <https://github.com/pytest-dev/pytest/issues/9488>`_: If custom subclasses of nodes like :class:`pytest.Item` override the
``__init__`` method, they should take ``**kwargs``. See
:ref:`uncooperative-constructors-deprecated` for details.
Note that a deprection warning is only emitted when there is a conflict in the
arguments pytest expected to pass. This deprecation was already part of pytest
7.0.0rc1 but wasn't documented.
Bug Fixes
---------
- `#9355 <https://github.com/pytest-dev/pytest/issues/9355>`_: Fixed error message prints function decorators when using assert in Python 3.8 and above.
- `#9396 <https://github.com/pytest-dev/pytest/issues/9396>`_: Ensure :attr:`pytest.Config.inifile` is available during the :func:`pytest_cmdline_main <_pytest.hookspec.pytest_cmdline_main>` hook (regression during ``7.0.0rc1``).
Improved Documentation
----------------------
- `#9404 <https://github.com/pytest-dev/pytest/issues/9404>`_: Added extra documentation on alternatives to common misuses of `pytest.warns(None)` ahead of its deprecation.
- `#9505 <https://github.com/pytest-dev/pytest/issues/9505>`_: Clarify where the configuration files are located. To avoid confusions documentation mentions
that configuration file is located in the root of the repository.
Trivial/Internal Changes
------------------------
- `#9521 <https://github.com/pytest-dev/pytest/issues/9521>`_: Add test coverage to assertion rewrite path.
pytest 7.0.0rc1 (2021-12-06)
============================
@@ -225,7 +156,7 @@ Deprecations
See :ref:`the deprecation note <diamond-inheritance-deprecated>` for full details.
- `#8592 <https://github.com/pytest-dev/pytest/issues/8592>`_: :hook:`pytest_cmdline_preparse` has been officially deprecated. It will be removed in a future release. Use :hook:`pytest_load_initial_conftests` instead.
- `#8592 <https://github.com/pytest-dev/pytest/issues/8592>`_: :func:`pytest_cmdline_preparse <_pytest.hookspec.pytest_cmdline_preparse>` has been officially deprecated. It will be removed in a future release. Use :func:`pytest_load_initial_conftests <_pytest.hookspec.pytest_load_initial_conftests>` instead.
See :ref:`the deprecation note <cmdline-preparse-deprecated>` for full details.
@@ -242,12 +173,6 @@ Deprecations
This was changed for consistency with :func:`pytest.mark.skip <pytest.mark.skip>` and :func:`pytest.mark.xfail <pytest.mark.xfail>` which both accept
``reason`` as an argument.
- `#8174 <https://github.com/pytest-dev/pytest/issues/8174>`_: The following changes have been made to types reachable through :attr:`pytest.ExceptionInfo.traceback`:
- The ``path`` property of ``_pytest.code.Code`` returns ``Path`` instead of ``py.path.local``.
- The ``path`` property of ``_pytest.code.TracebackEntry`` returns ``Path`` instead of ``py.path.local``.
There was no deprecation period for this change (sorry!).
Features
@@ -278,11 +203,11 @@ Features
- ``pytest.Mark`` for :class:`marks <pytest.Mark>`.
- ``pytest.MarkDecorator`` for :class:`mark decorators <pytest.MarkDecorator>`.
- ``pytest.MarkGenerator`` for the :class:`pytest.mark <pytest.MarkGenerator>` singleton.
- ``pytest.Metafunc`` for the :class:`metafunc <pytest.MarkGenerator>` argument to the :hook:`pytest_generate_tests` hook.
- ``pytest.Metafunc`` for the :class:`metafunc <pytest.MarkGenerator>` argument to the :func:`pytest_generate_tests <pytest.hookspec.pytest_generate_tests>` hook.
- ``pytest.CallInfo`` for the :class:`CallInfo <pytest.CallInfo>` type passed to various hooks.
- ``pytest.PytestPluginManager`` for :class:`PytestPluginManager <pytest.PytestPluginManager>`.
- ``pytest.ExceptionInfo`` for the :class:`ExceptionInfo <pytest.ExceptionInfo>` type returned from :func:`pytest.raises` and passed to various hooks.
- ``pytest.Parser`` for the :class:`Parser <pytest.Parser>` type passed to the :hook:`pytest_addoption` hook.
- ``pytest.Parser`` for the :class:`Parser <pytest.Parser>` type passed to the :func:`pytest_addoption <pytest.hookspec.pytest_addoption>` hook.
- ``pytest.OptionGroup`` for the :class:`OptionGroup <pytest.OptionGroup>` type returned from the :func:`parser.addgroup <pytest.Parser.getgroup>` method.
- ``pytest.HookRecorder`` for the :class:`HookRecorder <pytest.HookRecorder>` type returned from :class:`~pytest.Pytester`.
- ``pytest.RecordedHookCall`` for the :class:`RecordedHookCall <pytest.HookRecorder>` type returned from :class:`~pytest.HookRecorder`.
@@ -303,11 +228,11 @@ Features
- `#8144 <https://github.com/pytest-dev/pytest/issues/8144>`_: The following hooks now receive an additional ``pathlib.Path`` argument, equivalent to an existing ``py.path.local`` argument:
- :hook:`pytest_ignore_collect` - The ``collection_path`` parameter (equivalent to existing ``path`` parameter).
- :hook:`pytest_collect_file` - The ``file_path`` parameter (equivalent to existing ``path`` parameter).
- :hook:`pytest_pycollect_makemodule` - The ``module_path`` parameter (equivalent to existing ``path`` parameter).
- :hook:`pytest_report_header` - The ``start_path`` parameter (equivalent to existing ``startdir`` parameter).
- :hook:`pytest_report_collectionfinish` - The ``start_path`` parameter (equivalent to existing ``startdir`` parameter).
- :func:`pytest_ignore_collect <_pytest.hookspec.pytest_ignore_collect>` - The ``collection_path`` parameter (equivalent to existing ``path`` parameter).
- :func:`pytest_collect_file <_pytest.hookspec.pytest_collect_file>` - The ``file_path`` parameter (equivalent to existing ``path`` parameter).
- :func:`pytest_pycollect_makemodule <_pytest.hookspec.pytest_pycollect_makemodule>` - The ``module_path`` parameter (equivalent to existing ``path`` parameter).
- :func:`pytest_report_header <_pytest.hookspec.pytest_report_header>` - The ``start_path`` parameter (equivalent to existing ``startdir`` parameter).
- :func:`pytest_report_collectionfinish <_pytest.hookspec.pytest_report_collectionfinish>` - The ``start_path`` parameter (equivalent to existing ``startdir`` parameter).
.. note::
The name of the :class:`~_pytest.nodes.Node` arguments and attributes (the
@@ -548,9 +473,10 @@ Trivial/Internal Changes
- `#8174 <https://github.com/pytest-dev/pytest/issues/8174>`_: The following changes have been made to internal pytest types/functions:
- The ``path`` property of ``_pytest.code.Code`` returns ``Path`` instead of ``py.path.local``.
- The ``path`` property of ``_pytest.code.TracebackEntry`` returns ``Path`` instead of ``py.path.local``.
- The ``_pytest.code.getfslineno()`` function returns ``Path`` instead of ``py.path.local``.
- The ``_pytest.python.path_matches_patterns()`` function takes ``Path`` instead of ``py.path.local``.
- The ``_pytest._code.Traceback.cut()`` function accepts any ``os.PathLike[str]``, not just ``py.path.local``.
- `#8248 <https://github.com/pytest-dev/pytest/issues/8248>`_: Internal Restructure: let ``python.PyObjMixin`` inherit from ``nodes.Node`` to carry over typing information.
@@ -565,7 +491,7 @@ Trivial/Internal Changes
- `#8913 <https://github.com/pytest-dev/pytest/issues/8913>`_: The private ``CallSpec2._arg2scopenum`` attribute has been removed after an internal refactoring.
- `#8967 <https://github.com/pytest-dev/pytest/issues/8967>`_: :hook:`pytest_assertion_pass` is no longer considered experimental and
- `#8967 <https://github.com/pytest-dev/pytest/issues/8967>`_: :func:`pytest_assertion_pass <_pytest.hookspec.pytest_assertion_pass>` is no longer considered experimental and
future changes to it will be considered more carefully.
@@ -612,7 +538,7 @@ Bug Fixes
the ``tmp_path``/``tmpdir`` fixture). Now the directories are created with
private permissions.
pytest used to silently use a pre-existing ``/tmp/pytest-of-<username>`` directory,
pytest used to silenty use a pre-existing ``/tmp/pytest-of-<username>`` directory,
even if owned by another user. This means another user could pre-create such a
directory and gain control of another user's temporary directory. Now such a
condition results in an error.
@@ -927,8 +853,8 @@ Deprecations
if you use this and want a replacement.
- :issue:`7255`: The :hook:`pytest_warning_captured` hook is deprecated in favor
of :hook:`pytest_warning_recorded`, and will be removed in a future version.
- :issue:`7255`: The :func:`pytest_warning_captured <_pytest.hookspec.pytest_warning_captured>` hook is deprecated in favor
of :func:`pytest_warning_recorded <_pytest.hookspec.pytest_warning_recorded>`, and will be removed in a future version.
- :issue:`7648`: The ``gethookproxy()`` and ``isinitpath()`` methods of ``FSCollector`` and ``Package`` are deprecated;
@@ -1036,7 +962,7 @@ Trivial/Internal Changes
process, pytest now ignores builtin attributes (like ``__class__``,
``__delattr__`` and ``__new__``) without consulting the :confval:`python_classes` and
:confval:`python_functions` configuration options and without passing them to plugins
using the :hook:`pytest_pycollect_makeitem` hook.
using the :func:`pytest_pycollect_makeitem <_pytest.hookspec.pytest_pycollect_makeitem>` hook.
pytest 6.0.2 (2020-09-04)
@@ -1378,7 +1304,7 @@ Improvements
is not displayed by default for passing tests. This change makes the mistake
visible during testing.
You may suppress this behavior temporarily or permanently by setting
You may supress this behavior temporarily or permanently by setting
``logging.raiseExceptions = False``.
@@ -1825,7 +1751,7 @@ Bug Fixes
- :issue:`6646`: Assertion rewriting hooks are (re)stored for the current item, which fixes them being still used after e.g. pytester's :func:`testdir.runpytest <_pytest.pytester.Testdir.runpytest>` etc.
- :issue:`6660`: :py:func:`pytest.exit` is handled when emitted from the :hook:`pytest_sessionfinish` hook. This includes quitting from a debugger.
- :issue:`6660`: :py:func:`pytest.exit` is handled when emitted from the :func:`pytest_sessionfinish <_pytest.hookspec.pytest_sessionfinish>` hook. This includes quitting from a debugger.
- :issue:`6752`: When :py:func:`pytest.raises` is used as a function (as opposed to a context manager),
@@ -1833,7 +1759,7 @@ Bug Fixes
it was swallowed and ignored (regression in pytest 5.1.0).
- :issue:`6801`: Do not display empty lines in between traceback for unexpected exceptions with doctests.
- :issue:`6801`: Do not display empty lines inbetween traceback for unexpected exceptions with doctests.
- :issue:`6802`: The :fixture:`testdir fixture <testdir>` works within doctests now.
@@ -1937,7 +1863,7 @@ Improvements
- :issue:`6231`: Improve check for misspelling of :ref:`pytest.mark.parametrize ref`.
- :issue:`6257`: Handle :func:`pytest.exit` being used via :hook:`pytest_internalerror`, e.g. when quitting pdb from post mortem.
- :issue:`6257`: Handle :py:func:`pytest.exit` being used via :py:func:`~_pytest.hookspec.pytest_internalerror`, e.g. when quitting pdb from post mortem.
@@ -2571,7 +2497,7 @@ Deprecations
Features
--------
- :issue:`3457`: New :hook:`pytest_assertion_pass`
- :issue:`3457`: New :func:`~_pytest.hookspec.pytest_assertion_pass`
hook, called with context information when an assertion *passes*.
This hook is still **experimental** so use it with caution.
@@ -4702,7 +4628,7 @@ Bug Fixes
- Fixed a bug where stdout and stderr were logged twice by junitxml when a test
was marked xfail. (:issue:`3491`)
- Fix ``usefixtures`` mark applied to unittest tests by correctly instantiating
- Fix ``usefixtures`` mark applyed to unittest tests by correctly instantiating
``FixtureInfo``. (:issue:`3498`)
- Fix assertion rewriter compatibility with libraries that monkey patch
@@ -5091,9 +5017,9 @@ Features
- Console output falls back to "classic" mode when capturing is disabled (``-s``),
otherwise the output gets garbled to the point of being useless. (:issue:`3038`)
- New :hook:`pytest_runtest_logfinish`
- New :func:`~_pytest.hookspec.pytest_runtest_logfinish`
hook which is called when a test item has finished executing, analogous to
:hook:`pytest_runtest_logstart`.
:func:`~_pytest.hookspec.pytest_runtest_logstart`.
(:issue:`3101`)
- Improve performance when collecting tests using many fixtures. (:issue:`3107`)
@@ -8083,7 +8009,7 @@ Bug fixes:
or through plugin hooks. Also introduce a "--strict" option which
will treat unregistered markers as errors
allowing to avoid typos and maintain a well described set of markers
for your test suite. See examples at http://pytest.org/en/stable/how-to/mark.html
for your test suite. See exaples at http://pytest.org/en/stable/how-to/mark.html
and its links.
- issue50: introduce "-m marker" option to select tests based on markers
(this is a stricter and more predictable version of '-k' in that "-m"
@@ -8395,7 +8321,7 @@ Bug fixes:
- fix issue57 -f|--looponfail to work with xpassing tests (thanks Ronny)
- fix issue92 collectonly reporter and --pastebin (thanks Benjamin Peterson)
- fix py.code.compile(source) to generate unique filenames
- fix assertion re-interp problems on PyPy, by deferring code
- fix assertion re-interp problems on PyPy, by defering code
compilation to the (overridable) Frame.eval class. (thanks Amaury Forgeot)
- fix py.path.local.pyimport() to work with directories
- streamline py.path.local.mkdtemp implementation and usage
@@ -8469,7 +8395,7 @@ Bug fixes:
- improve support for raises and other dynamically compiled code by
manipulating python's linecache.cache instead of the previous
rather hacky way of creating custom code objects. This makes
it seamlessly work on Jython and PyPy where it previously didn't.
it seemlessly work on Jython and PyPy where it previously didn't.
- fix issue96: make capturing more resilient against Control-C
interruptions (involved somewhat substantial refactoring

View File

@@ -445,13 +445,6 @@ def setup(app: "sphinx.application.Sphinx") -> None:
indextemplate="pair: %s; global variable interpreted by pytest",
)
app.add_crossref_type(
directivename="hook",
rolename="hook",
objname="pytest hook",
indextemplate="pair: %s; hook",
)
configure_logging(app)
# Make Sphinx mark classes with "final" when decorated with @final.

View File

@@ -56,10 +56,6 @@ 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
@@ -85,11 +81,11 @@ no matter what argument was used in the constructor. We expect to deprecate the
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``
* :func:`pytest_ignore_collect(collection_path: pathlib.Path) <_pytest.hookspec.pytest_ignore_collect>` as equivalent to ``path``
* :func:`pytest_collect_file(file_path: pathlib.Path) <_pytest.hookspec.pytest_collect_file>` as equivalent to ``path``
* :func:`pytest_pycollect_makemodule(module_path: pathlib.Path) <_pytest.hookspec.pytest_pycollect_makemodule>` as equivalent to ``path``
* :func:`pytest_report_header(start_path: pathlib.Path) <_pytest.hookspec.pytest_report_header>` as equivalent to ``startdir``
* :func:`pytest_report_collectionfinish(start_path: pathlib.Path) <_pytest.hookspec.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.
@@ -131,7 +127,7 @@ Passing ``msg=`` to ``pytest.skip``, ``pytest.fail`` or ``pytest.exit``
Passing the keyword argument ``msg`` to :func:`pytest.skip`, :func:`pytest.fail` or :func:`pytest.exit`
is now deprecated and ``reason`` should be used instead. This change is to bring consistency between these
functions and the ``@pytest.mark.skip`` and ``@pytest.mark.xfail`` markers which already accept a ``reason`` argument.
functions and the``@pytest.mark.skip`` and ``@pytest.mark.xfail`` markers which already accept a ``reason`` argument.
.. code-block:: python
@@ -161,8 +157,8 @@ Implementing the ``pytest_cmdline_preparse`` hook
.. deprecated:: 7.0
Implementing the :hook:`pytest_cmdline_preparse` hook has been officially deprecated.
Implement the :hook:`pytest_load_initial_conftests` hook instead.
Implementing the :func:`pytest_cmdline_preparse <_pytest.hookspec.pytest_cmdline_preparse>` hook has been officially deprecated.
Implement the :func:`pytest_load_initial_conftests <_pytest.hookspec.pytest_load_initial_conftests>` hook instead.
.. code-block:: python
@@ -195,40 +191,6 @@ Instead, a separate collector node should be used, which collects the item. See
.. _example pr fixing inheritance: https://github.com/asmeurer/pytest-flakes/pull/40/files
.. _uncooperative-constructors-deprecated:
Constructors of custom :class:`pytest.Node` subclasses should take ``**kwargs``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 7.0
If custom subclasses of nodes like :class:`pytest.Item` override the
``__init__`` method, they should take ``**kwargs``. Thus,
.. code-block:: python
class CustomItem(pytest.Item):
def __init__(self, name, parent, additional_arg):
super().__init__(name, parent)
self.additional_arg = additional_arg
should be turned into:
.. code-block:: python
class CustomItem(pytest.Item):
def __init__(self, *, additional_arg, **kwargs):
super().__init__(**kwargs)
self.additional_arg = additional_arg
to avoid hard-coding the arguments pytest can pass to the superclass.
See :ref:`non-python tests` for a full example.
For cases without conflicts, no deprecation warning is emitted. For cases with
conflicts (such as :class:`pytest.File` now taking ``path`` instead of
``fspath``, as :ref:`outlined above <node-ctor-fspath-deprecation>`), a
deprecation warning is now raised.
Backward compatibilities in ``Parser.addoption``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -259,11 +221,11 @@ Using ``pytest.warns(None)``
.. deprecated:: 7.0
:func:`pytest.warns(None) <pytest.warns>` is now deprecated because it was frequently misused.
Its correct usage was checking that the code emits at least one warning of any type - like ``pytest.warns()``
:func:`pytest.warns(None) <pytest.warns>` is now deprecated because many people used
it to mean "this code does not emit warnings", but it actually had the effect of
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.
The ``--strict`` command-line option
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -362,8 +324,8 @@ at some point, depending on the plans for the plugins and number of users using
.. versionremoved:: 6.0
The ``pytest_collect_directory`` hook has not worked properly for years (it was called
but the results were ignored). Users may consider using :hook:`pytest_collection_modifyitems` instead.
The ``pytest_collect_directory`` has not worked properly for years (it was called
but the results were ignored). Users may consider using :func:`pytest_collection_modifyitems <_pytest.hookspec.pytest_collection_modifyitems>` instead.
TerminalReporter.writer
~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -1,56 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="112" height="682">
<style>
text {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
dominant-baseline: middle;
text-anchor: middle;
fill: #062886;
font-size: medium;
}
ellipse.fixture, rect.test {
fill: #eeffcc;
stroke: #007020;
stroke-width: 2;
}
text.fixture {
color: #06287e;
}
circle.class {
fill: #c3e0ec;
stroke: #0e84b5;
stroke-width: 2;
}
text.class {
fill: #0e84b5;
}
path, line {
stroke: black;
stroke-width: 2;
fill: none;
}
rect.autouse {
fill: #ca7f3d;
}
</style>
<line x1="56" x2="56" y1="681" y2="26" />
<ellipse class="fixture" rx="50" ry="25" cx="56" cy="26" />
<text x="56" y="26">order</text>
<ellipse class="fixture" rx="25" ry="25" cx="56" cy="96" />
<text x="56" y="96">a</text>
<ellipse class="fixture" rx="25" ry="25" cx="56" cy="166" />
<text x="56" y="166">b</text>
<ellipse class="fixture" rx="25" ry="25" cx="56" cy="236" />
<text x="56" y="236">c</text>
<rect class="autouse" width="112" height="40" x="0" y="286" />
<text x="56" y="306">autouse</text>
<ellipse class="fixture" rx="25" ry="25" cx="56" cy="376" />
<text x="56" y="376">d</text>
<ellipse class="fixture" rx="25" ry="25" cx="56" cy="446" />
<text x="56" y="446">e</text>
<ellipse class="fixture" rx="25" ry="25" cx="56" cy="516" />
<text x="56" y="516">f</text>
<ellipse class="fixture" rx="25" ry="25" cx="56" cy="586" />
<text x="56" y="586">g</text>
<rect class="test" width="110" height="50" x="1" y="631" />
<text x="56" y="656">test_order</text>
</svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -18,8 +18,8 @@ class YamlFile(pytest.File):
class YamlItem(pytest.Item):
def __init__(self, *, spec, **kwargs):
super().__init__(**kwargs)
def __init__(self, name, parent, spec):
super().__init__(name, parent)
self.spec = spec
def runtest(self):

View File

@@ -22,7 +22,7 @@ Install ``pytest``
.. code-block:: bash
$ pytest --version
pytest 7.0.1
pytest 7.0.0rc1
.. _`simpletest`:

View File

@@ -139,7 +139,7 @@ project:
which adds ``pytest`` (rather than ``py.test``) as the recommended
command-line entry point
Due to this history, it's difficult to answer the question when pytest was started.
Due to this history, it's diffcult to answer the question when pytest was started.
It depends what point should really be seen as the start of it all. One
possible interpretation is to pick Europython 2004, i.e. around June/July
2004.

View File

@@ -42,18 +42,8 @@ Running pytest now produces this output:
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
======================= 1 passed, 1 warning in 0.12s =======================
Controlling warnings
--------------------
Similar to Python's `warning filter`_ and :option:`-W option <python:-W>` flag, pytest provides
its own ``-W`` flag to control which warnings are ignored, displayed, or turned into
errors. See the `warning filter`_ documentation for more
advanced use-cases.
.. _`warning filter`: https://docs.python.org/3/library/warnings.html#warning-filter
This code sample shows how to treat any ``UserWarning`` category class of warning
as an error:
The ``-W`` flag can be passed to control which warnings will be displayed or even turn
them into errors:
.. code-block:: pytest
@@ -106,6 +96,9 @@ all other warnings into errors.
When a warning matches more than one option in the list, the action for the last matching option
is performed.
Both ``-W`` command-line option and ``filterwarnings`` ini option are based on Python's own
:option:`-W option <python:-W>` and :func:`warnings.simplefilter`, so please refer to those sections in the Python
documentation for other examples and advanced usage.
.. _`filterwarnings`:
@@ -351,37 +344,6 @@ warnings, or index into it to get a particular recorded warning.
Full API: :class:`~_pytest.recwarn.WarningsRecorder`.
.. _`warns use cases`:
Additional use cases of warnings in tests
-----------------------------------------
Here are some use cases involving warnings that often come up in tests, and suggestions on how to deal with them:
- To ensure that **any** warning is emitted, use:
.. code-block:: python
with pytest.warns():
...
- To ensure that **no** warnings are emitted, use:
.. code-block:: python
with warnings.catch_warnings():
warnings.simplefilter("error")
...
- To suppress warnings, use:
.. code-block:: python
with warnings.catch_warnings():
warnings.simplefilter("ignore")
...
.. _custom_failure_messages:
Custom failure messages

View File

@@ -369,7 +369,7 @@ Here is a simple test file with the several usages:
Running it with the report-on-xfail option gives this output:
.. FIXME: Use $ instead of ! again to re-enable regendoc once it's fixed:
.. FIXME: Use $ instead of ! again to reenable regendoc once it's fixed:
https://github.com/pytest-dev/pytest/issues/8807
.. code-block:: pytest

View File

@@ -321,7 +321,7 @@ Plugins often need to store data on :class:`~pytest.Item`\s in one hook
implementation, and access it in another. One common solution is to just
assign some private attribute directly on the item, but type-checkers like
mypy frown upon this, and it may also cause conflicts with other plugins.
So pytest offers a better way to do this, :attr:`item.stash <_pytest.nodes.Node.stash>`.
So pytest offers a better way to do this, :attr:`_pytest.nodes.Node.stash <item.stash>`.
To use the "stash" in your plugins, first create "stash keys" somewhere at the
top level of your plugin:

View File

@@ -1,11 +1,10 @@
:orphan:
..
.. sidebar:: Next Open Trainings
.. sidebar:: Next Open Trainings
- `Professional Testing with Python <https://www.python-academy.com/courses/specialtopics/python_course_testing.html>`_, via `Python Academy <https://www.python-academy.com/>`_, February 1st to 3rd, 2022, Leipzig (Germany) and remote.
- `Professional Testing with Python <https://www.python-academy.com/courses/specialtopics/python_course_testing.html>`_, via `Python Academy <https://www.python-academy.com/>`_, February 1st to 3rd, 2022, Leipzig (Germany) and remote.
Also see `previous talks and blogposts <talks.html>`_.
Also see `previous talks and blogposts <talks.html>`_.
.. _features:

View File

@@ -20,7 +20,8 @@ Configuration file formats
--------------------------
Many :ref:`pytest settings <ini options ref>` can be set in a *configuration file*, which
by convention resides in the root directory of your repository.
by convention resides on the root of your repository or in your
tests folder.
A quick example of the configuration files supported by pytest:

View File

@@ -208,7 +208,7 @@ the one defined in ``tests/test_top.py`` would be unavailable to it because it
would have to step down a level (step inside a circle) to find it.
The first fixture the test finds is the one that will be used, so
:ref:`fixtures can be overridden <override fixtures>` if you need to change or
:ref:`fixtures can be overriden <override fixtures>` if you need to change or
extend what one does for a particular scope.
You can also use the ``conftest.py`` file to implement
@@ -401,9 +401,6 @@ the graph would look like this:
Because ``c`` can now be put above ``d`` in the graph, pytest can once again
linearize the graph to this:
.. image:: /example/fixtures/test_fixtures_order_autouse_flat.*
:align: center
In this example, ``c`` makes ``b`` and ``a`` effectively autouse fixtures as
well.

View File

@@ -9,39 +9,6 @@ This page contains the full reference to pytest's API.
:depth: 3
:local:
Constants
---------
pytest.__version__
~~~~~~~~~~~~~~~~~~
The current pytest version, as a string::
>>> import pytest
>>> pytest.__version__
'7.0.0'
.. _`version-tuple`:
pytest.version_tuple
~~~~~~~~~~~~~~~~~~~~
.. versionadded:: 7.0
The current pytest version, as a tuple::
>>> import pytest
>>> pytest.version_tuple
(7, 0, 0)
For pre-releases, the last component will be a string with the prerelease version::
>>> import pytest
>>> pytest.version_tuple
(7, 0, '0rc1')
Functions
---------
@@ -259,6 +226,37 @@ Marks a test function as *expected to fail*.
a new release of a library fixes a known bug).
pytest.__version__
~~~~~~~~~~~~~~~~~~
The current pytest version, as a string::
>>> import pytest
>>> pytest.__version__
'7.0.0'
.. _`version-tuple`:
pytest.version_tuple
~~~~~~~~~~~~~~~~~~~~
.. versionadded:: 7.0
The current pytest version, as a tuple::
>>> import pytest
>>> pytest.version_tuple
(7, 0, 0)
For pre-releases, the last component will be a string with the prerelease version::
>>> import pytest
>>> pytest.version_tuple
(7, 0, '0rc1')
Custom marks
~~~~~~~~~~~~
@@ -673,13 +671,9 @@ Bootstrapping hooks
Bootstrapping hooks called for plugins registered early enough (internal and setuptools plugins).
.. 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
.. autofunction:: pytest_cmdline_main
.. _`initialization-hooks`:
@@ -689,20 +683,13 @@ Initialization hooks
Initialization hooks called for plugins and ``conftest.py`` files.
.. hook:: pytest_addoption
.. autofunction:: pytest_addoption
.. hook:: pytest_addhooks
.. autofunction:: pytest_addhooks
.. hook:: pytest_configure
.. autofunction:: pytest_configure
.. hook:: pytest_unconfigure
.. autofunction:: pytest_unconfigure
.. hook:: pytest_sessionstart
.. autofunction:: pytest_sessionstart
.. hook:: pytest_sessionfinish
.. autofunction:: pytest_sessionfinish
.. hook:: pytest_plugin_registered
.. autofunction:: pytest_plugin_registered
Collection hooks
@@ -710,34 +697,21 @@ Collection hooks
``pytest`` calls the following hooks for collecting files and directories:
.. hook:: pytest_collection
.. autofunction:: pytest_collection
.. hook:: pytest_ignore_collect
.. autofunction:: pytest_ignore_collect
.. hook:: pytest_collect_file
.. autofunction:: pytest_collect_file
.. hook:: pytest_pycollect_makemodule
.. autofunction:: pytest_pycollect_makemodule
For influencing the collection of objects in Python modules
you can use the following hook:
.. hook:: pytest_pycollect_makeitem
.. autofunction:: pytest_pycollect_makeitem
.. hook:: pytest_generate_tests
.. autofunction:: pytest_generate_tests
.. hook:: pytest_make_parametrize_id
.. autofunction:: pytest_make_parametrize_id
Hooks for influencing test skipping:
.. hook:: pytest_markeval_namespace
.. autofunction:: pytest_markeval_namespace
After collection is complete, you can modify the order of
items, delete or otherwise amend the test items:
.. hook:: pytest_collection_modifyitems
.. autofunction:: pytest_collection_modifyitems
.. note::
@@ -751,21 +725,13 @@ Test running (runtest) hooks
All runtest related hooks receive a :py:class:`pytest.Item <pytest.Item>` object.
.. hook:: pytest_runtestloop
.. autofunction:: pytest_runtestloop
.. hook:: pytest_runtest_protocol
.. autofunction:: pytest_runtest_protocol
.. hook:: pytest_runtest_logstart
.. autofunction:: pytest_runtest_logstart
.. hook:: pytest_runtest_logfinish
.. autofunction:: pytest_runtest_logfinish
.. hook:: pytest_runtest_setup
.. autofunction:: pytest_runtest_setup
.. hook:: pytest_runtest_call
.. autofunction:: pytest_runtest_call
.. hook:: pytest_runtest_teardown
.. autofunction:: pytest_runtest_teardown
.. hook:: pytest_runtest_makereport
.. autofunction:: pytest_runtest_makereport
For deeper understanding you may look at the default implementation of
@@ -774,7 +740,6 @@ in ``_pytest.pdb`` which interacts with ``_pytest.capture``
and its input/output capturing in order to immediately drop
into interactive debugging when a test failure occurs.
.. hook:: pytest_pyfunc_call
.. autofunction:: pytest_pyfunc_call
Reporting hooks
@@ -782,47 +747,27 @@ Reporting hooks
Session related reporting hooks:
.. hook:: pytest_collectstart
.. autofunction:: pytest_collectstart
.. hook:: pytest_make_collect_report
.. autofunction:: pytest_make_collect_report
.. hook:: pytest_itemcollected
.. autofunction:: pytest_itemcollected
.. hook:: pytest_collectreport
.. autofunction:: pytest_collectreport
.. hook:: pytest_deselected
.. autofunction:: pytest_deselected
.. hook:: pytest_report_header
.. autofunction:: pytest_report_header
.. hook:: pytest_report_collectionfinish
.. autofunction:: pytest_report_collectionfinish
.. hook:: pytest_report_teststatus
.. autofunction:: pytest_report_teststatus
.. hook:: pytest_report_to_serializable
.. autofunction:: pytest_report_to_serializable
.. hook:: pytest_report_from_serializable
.. autofunction:: pytest_report_from_serializable
.. hook:: pytest_terminal_summary
.. autofunction:: pytest_terminal_summary
.. hook:: pytest_fixture_setup
.. autofunction:: pytest_fixture_setup
.. hook:: pytest_fixture_post_finalizer
.. autofunction:: pytest_fixture_post_finalizer
.. hook:: pytest_warning_captured
.. autofunction:: pytest_warning_captured
.. hook:: pytest_warning_recorded
.. autofunction:: pytest_warning_recorded
Central hook for reporting about test execution:
.. hook:: pytest_runtest_logreport
.. autofunction:: pytest_runtest_logreport
Assertion related hooks:
.. hook:: pytest_assertrepr_compare
.. autofunction:: pytest_assertrepr_compare
.. hook:: pytest_assertion_pass
.. autofunction:: pytest_assertion_pass
@@ -832,16 +777,10 @@ Debugging/Interaction hooks
There are few hooks which can be used for special
reporting or interaction with exceptions:
.. hook:: pytest_internalerror
.. autofunction:: pytest_internalerror
.. hook:: pytest_keyboard_interrupt
.. autofunction:: pytest_keyboard_interrupt
.. hook:: pytest_exception_interact
.. autofunction:: pytest_exception_interact
.. hook:: pytest_enter_pdb
.. autofunction:: pytest_enter_pdb
.. hook:: pytest_leave_pdb
.. autofunction:: pytest_leave_pdb
Objects

View File

@@ -88,9 +88,7 @@ def prepare_release_pr(
print(f"Branch {Fore.CYAN}{release_branch}{Fore.RESET} created.")
if is_major:
template_name = "release.major.rst"
elif prerelease:
if prerelease:
template_name = "release.pre.rst"
elif is_feature_release:
template_name = "release.minor.rst"
@@ -106,7 +104,6 @@ def prepare_release_pr(
"--",
version,
template_name,
release_branch, # doc_version
"--skip-check-links",
]
print("Running", " ".join(cmdline))

View File

@@ -1,24 +0,0 @@
pytest-{version}
=======================================
The pytest team is proud to announce the {version} release!
This release contains new features, improvements, bug fixes, and breaking changes, so users
are encouraged to take a look at the CHANGELOG carefully:
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:
{contributors}
Happy testing,
The pytest Development Team

View File

@@ -3,8 +3,8 @@ pytest-{version}
The pytest team is proud to announce the {version} release!
This release contains new features, improvements, and bug fixes,
the full list of changes is available in the changelog:
This release contains new features, improvements, bug fixes, and breaking changes, so users
are encouraged to take a look at the CHANGELOG carefully:
https://docs.pytest.org/en/stable/changelog.html

View File

@@ -19,7 +19,7 @@ You can upgrade from PyPI via:
Users are encouraged to take a look at the CHANGELOG carefully:
https://docs.pytest.org/en/{doc_version}/changelog.html
https://docs.pytest.org/en/stable/changelog.html
Thanks to all the contributors to this release:

View File

@@ -10,7 +10,7 @@ from colorama import Fore
from colorama import init
def announce(version, template_name, doc_version):
def announce(version, template_name):
"""Generates a new release announcement entry in the docs."""
# Get our list of authors
stdout = check_output(["git", "describe", "--abbrev=0", "--tags"])
@@ -31,9 +31,7 @@ def announce(version, template_name, doc_version):
)
contributors_text = "\n".join(f"* {name}" for name in sorted(contributors)) + "\n"
text = template_text.format(
version=version, contributors=contributors_text, doc_version=doc_version
)
text = template_text.format(version=version, contributors=contributors_text)
target = Path(__file__).parent.joinpath(f"../doc/en/announce/release-{version}.rst")
target.write_text(text, encoding="UTF-8")
@@ -84,9 +82,9 @@ def check_links():
check_call(["tox", "-e", "docs-checklinks"])
def pre_release(version, template_name, doc_version, *, skip_check_links):
def pre_release(version, template_name, *, skip_check_links):
"""Generates new docs, release announcements and creates a local tag."""
announce(version, template_name, doc_version)
announce(version, template_name)
regen(version)
changelog(version, write_out=True)
fix_formatting()
@@ -114,15 +112,11 @@ def main():
parser.add_argument(
"template_name", help="Name of template file to use for release announcement"
)
parser.add_argument(
"doc_version", help="For prereleases, the version to link to in the docs"
)
parser.add_argument("--skip-check-links", action="store_true", default=False)
options = parser.parse_args()
pre_release(
options.version,
options.template_name,
options.doc_version,
skip_check_links=options.skip_check_links,
)

View File

@@ -1,6 +1,5 @@
import ast
import inspect
import os
import re
import sys
import traceback
@@ -344,10 +343,10 @@ class Traceback(List[TracebackEntry]):
def cut(
self,
path: Optional[Union["os.PathLike[str]", str]] = None,
path: Optional[Union[Path, str]] = None,
lineno: Optional[int] = None,
firstlineno: Optional[int] = None,
excludepath: Optional["os.PathLike[str]"] = None,
excludepath: Optional[Path] = None,
) -> "Traceback":
"""Return a Traceback instance wrapping part of this Traceback.
@@ -358,17 +357,15 @@ class Traceback(List[TracebackEntry]):
for formatting reasons (removing some uninteresting bits that deal
with handling of the exception/traceback).
"""
path_ = None if path is None else os.fspath(path)
excludepath_ = None if excludepath is None else os.fspath(excludepath)
for x in self:
code = x.frame.code
codepath = code.path
if path is not None and str(codepath) != path_:
if path is not None and codepath != path:
continue
if (
excludepath is not None
and isinstance(codepath, Path)
and excludepath_ in (str(p) for p in codepath.parents) # type: ignore[operator]
and excludepath in codepath.parents
):
continue
if lineno is not None and x.lineno != lineno:
@@ -898,7 +895,7 @@ class FormattedExcinfo:
max_frames=max_frames,
total=len(traceback),
)
# Type ignored because adding two instances of a List subtype
# Type ignored because adding two instaces of a List subtype
# currently incorrectly has type List instead of the subtype.
traceback = traceback[:max_frames] + traceback[-max_frames:] # type: ignore
else:

View File

@@ -149,11 +149,6 @@ def get_statement_startend2(lineno: int, node: ast.AST) -> Tuple[int, Optional[i
values: List[int] = []
for x in ast.walk(node):
if isinstance(x, (ast.stmt, ast.ExceptHandler)):
# Before Python 3.8, the lineno of a decorated class or function pointed at the decorator.
# Since Python 3.8, the lineno points to the class/def, so need to include the decorators.
if isinstance(x, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)):
for d in x.decorator_list:
values.append(d.lineno - 1)
values.append(x.lineno - 1)
for name in ("finalbody", "orelse"):
val: Optional[List[ast.stmt]] = getattr(x, name, None)

View File

@@ -276,15 +276,13 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader)
with open(pathname, "rb") as f:
return f.read()
if sys.version_info >= (3, 10):
if sys.version_info >= (3, 9):
def get_resource_reader(self, name: str) -> importlib.abc.TraversableResources: # type: ignore
if sys.version_info < (3, 11):
from importlib.readers import FileReader
else:
from importlib.resources.readers import FileReader
from types import SimpleNamespace
from importlib.readers import FileReader
return FileReader(types.SimpleNamespace(path=self._rewritten_names[name]))
return FileReader(SimpleNamespace(path=self._rewritten_names[name]))
def _write_pyc_fp(
@@ -516,7 +514,7 @@ def _call_assertion_pass(lineno: int, orig: str, expl: str) -> None:
def _check_if_assertion_pass_impl() -> bool:
"""Check if any plugins implement the pytest_assertion_pass hook
in order not to generate explanation unnecessarily (might be expensive)."""
in order not to generate explanation unecessarily (might be expensive)."""
return True if util._assertion_pass else False

View File

@@ -255,7 +255,7 @@ default_plugins = essential_plugins + (
"warnings",
"logging",
"reports",
"python_path",
"pythonpath",
*(["unraisableexception", "threadexception"] if sys.version_info >= (3, 8) else []),
"faulthandler",
)
@@ -331,14 +331,6 @@ def _prepareconfig(
raise
def _get_directory(path: Path) -> Path:
"""Get the directory of a path - itself if already a directory."""
if path.is_file():
return path.parent
else:
return path
@final
class PytestPluginManager(PluginManager):
"""A :py:class:`pluggy.PluginManager <pluggy.PluginManager>` with
@@ -361,12 +353,6 @@ class PytestPluginManager(PluginManager):
self._conftestpath2mod: Dict[Path, types.ModuleType] = {}
self._confcutdir: Optional[Path] = None
self._noconftest = False
# _getconftestmodules()'s call to _get_directory() causes a stat
# storm when it's called potentially thousands of times in a test
# session (#9478), often with the same path, so cache it.
self._get_directory = lru_cache(256)(_get_directory)
self._duplicatepaths: Set[Path] = set()
# plugins that were explicitly skipped with pytest.skip
@@ -544,12 +530,15 @@ class PytestPluginManager(PluginManager):
if self._noconftest:
return []
directory = self._get_directory(path)
if path.is_file():
directory = path.parent
else:
directory = path
# Optimization: avoid repeated searches in the same directory.
# Assumes always called with same importmode and rootpath.
existing_clist = self._dirpath2confmods.get(directory)
if existing_clist is not None:
if existing_clist:
return existing_clist
# XXX these days we may rather want to use config.rootpath
@@ -981,7 +970,7 @@ class Config:
def add_cleanup(self, func: Callable[[], None]) -> None:
"""Add a function to be called when the config object gets out of
use (usually coinciding with pytest_unconfigure)."""
use (usually coninciding with pytest_unconfigure)."""
self._cleanup.append(func)
def _do_configure(self) -> None:

View File

@@ -23,7 +23,7 @@ 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,
it currently doesnt return full hook caller proxies for fixed hooks,
this may have to be changed later depending on bugs
"""

View File

@@ -115,10 +115,8 @@ NODE_CTOR_FSPATH_ARG = UnformattedWarning(
)
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."
"Passing None to catch any warning has been deprecated, pass no arguments instead:\n"
" Replace pytest.warns(None) by simply pytest.warns()."
)
KEYWORD_MSG_ARG = UnformattedWarning(

View File

@@ -163,7 +163,7 @@ 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.
using :func:`pytest_load_initial_conftests` instead.
.. note::
This hook will not be called for ``conftest.py`` files, only for setuptools plugins.
@@ -467,7 +467,7 @@ def pytest_runtest_logstart(
) -> None:
"""Called at the start of running the runtest protocol for a single item.
See :hook:`pytest_runtest_protocol` for a description of the runtest protocol.
See :func:`pytest_runtest_protocol` for a description of the runtest protocol.
:param str nodeid: Full node ID of the item.
:param location: A tuple of ``(filename, lineno, testname)``.
@@ -479,7 +479,7 @@ def pytest_runtest_logfinish(
) -> None:
"""Called at the end of running the runtest protocol for a single item.
See :hook:`pytest_runtest_protocol` for a description of the runtest protocol.
See :func:`pytest_runtest_protocol` for a description of the runtest protocol.
:param str nodeid: Full node ID of the item.
:param location: A tuple of ``(filename, lineno, testname)``.
@@ -526,7 +526,7 @@ def pytest_runtest_makereport(
"""Called to create a :class:`~pytest.TestReport` for each of
the setup, call and teardown runtest phases of a test item.
See :hook:`pytest_runtest_protocol` for a description of the runtest protocol.
See :func:`pytest_runtest_protocol` for a description of the runtest protocol.
:param call: The :class:`~pytest.CallInfo` for the phase.
@@ -538,7 +538,7 @@ def pytest_runtest_logreport(report: "TestReport") -> None:
"""Process the :class:`~pytest.TestReport` produced for each
of the setup, call and teardown runtest phases of an item.
See :hook:`pytest_runtest_protocol` for a description of the runtest protocol.
See :func:`pytest_runtest_protocol` for a description of the runtest protocol.
"""
@@ -557,7 +557,7 @@ def pytest_report_from_serializable(
data: Dict[str, Any],
) -> Optional[Union["CollectReport", "TestReport"]]:
"""Restore a report object previously serialized with
:hook:`pytest_report_to_serializable`."""
:func:`pytest_report_to_serializable`."""
# -------------------------------------------------------------------------
@@ -895,10 +895,10 @@ def pytest_exception_interact(
"""Called when an exception was raised which can potentially be
interactively handled.
May be called during collection (see :hook:`pytest_make_collect_report`),
May be called during collection (see :py:func:`pytest_make_collect_report`),
in which case ``report`` is a :class:`CollectReport`.
May be called during runtest of an item (see :hook:`pytest_runtest_protocol`),
May be called during runtest of an item (see :py:func:`pytest_runtest_protocol`),
in which case ``report`` is a :class:`TestReport`.
This hook is not called if the exception that was raised is an internal

View File

@@ -256,7 +256,7 @@ class _NodeReporter:
def finalize(self) -> None:
data = self.to_xml()
self.__dict__.clear()
# Type ignored because mypy doesn't like overriding a method.
# Type ignored becuase mypy doesn't like overriding a method.
# Also the return value doesn't match...
self.to_xml = lambda: data # type: ignore[assignment]

View File

@@ -10,26 +10,13 @@ from typing import Union
import attr
from iniconfig import SectionWrapper
from _pytest.cacheprovider import Cache
import pytest
from _pytest.compat import final
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
from _pytest.deprecated import check_ispytest
from _pytest.fixtures import fixture
from _pytest.fixtures import FixtureRequest
from _pytest.main import Session
from _pytest.monkeypatch import MonkeyPatch
from _pytest.nodes import Collector
from _pytest.nodes import Item
from _pytest.nodes import Node
from _pytest.pytester import HookRecorder
from _pytest.pytester import Pytester
from _pytest.pytester import RunResult
from _pytest.terminal import TerminalReporter
from _pytest.tmpdir import TempPathFactory
if TYPE_CHECKING:
from typing_extensions import Final
@@ -48,10 +35,10 @@ class Testdir:
__test__ = False
CLOSE_STDIN: "Final" = Pytester.CLOSE_STDIN
TimeoutExpired: "Final" = Pytester.TimeoutExpired
CLOSE_STDIN: "Final" = pytest.Pytester.CLOSE_STDIN
TimeoutExpired: "Final" = pytest.Pytester.TimeoutExpired
def __init__(self, pytester: Pytester, *, _ispytest: bool = False) -> None:
def __init__(self, pytester: pytest.Pytester, *, _ispytest: bool = False) -> None:
check_ispytest(_ispytest)
self._pytester = pytester
@@ -77,10 +64,10 @@ class Testdir:
self._pytester.plugins = plugins
@property
def monkeypatch(self) -> MonkeyPatch:
def monkeypatch(self) -> pytest.MonkeyPatch:
return self._pytester._monkeypatch
def make_hook_recorder(self, pluginmanager) -> HookRecorder:
def make_hook_recorder(self, pluginmanager) -> pytest.HookRecorder:
"""See :meth:`Pytester.make_hook_recorder`."""
return self._pytester.make_hook_recorder(pluginmanager)
@@ -144,7 +131,9 @@ class Testdir:
"""See :meth:`Pytester.copy_example`."""
return legacy_path(self._pytester.copy_example(name))
def getnode(self, config: Config, arg) -> Optional[Union[Item, Collector]]:
def getnode(
self, config: pytest.Config, arg
) -> Optional[Union[pytest.Item, pytest.Collector]]:
"""See :meth:`Pytester.getnode`."""
return self._pytester.getnode(config, arg)
@@ -152,7 +141,9 @@ class Testdir:
"""See :meth:`Pytester.getpathnode`."""
return self._pytester.getpathnode(path)
def genitems(self, colitems: List[Union[Item, Collector]]) -> List[Item]:
def genitems(
self, colitems: List[Union[pytest.Item, pytest.Collector]]
) -> List[pytest.Item]:
"""See :meth:`Pytester.genitems`."""
return self._pytester.genitems(colitems)
@@ -174,19 +165,19 @@ class Testdir:
*args, plugins=plugins, no_reraise_ctrlc=no_reraise_ctrlc
)
def runpytest_inprocess(self, *args, **kwargs) -> RunResult:
def runpytest_inprocess(self, *args, **kwargs) -> pytest.RunResult:
"""See :meth:`Pytester.runpytest_inprocess`."""
return self._pytester.runpytest_inprocess(*args, **kwargs)
def runpytest(self, *args, **kwargs) -> RunResult:
def runpytest(self, *args, **kwargs) -> pytest.RunResult:
"""See :meth:`Pytester.runpytest`."""
return self._pytester.runpytest(*args, **kwargs)
def parseconfig(self, *args) -> Config:
def parseconfig(self, *args) -> pytest.Config:
"""See :meth:`Pytester.parseconfig`."""
return self._pytester.parseconfig(*args)
def parseconfigure(self, *args) -> Config:
def parseconfigure(self, *args) -> pytest.Config:
"""See :meth:`Pytester.parseconfigure`."""
return self._pytester.parseconfigure(*args)
@@ -205,8 +196,8 @@ class Testdir:
)
def collect_by_name(
self, modcol: Collector, name: str
) -> Optional[Union[Item, Collector]]:
self, modcol: pytest.Collector, name: str
) -> Optional[Union[pytest.Item, pytest.Collector]]:
"""See :meth:`Pytester.collect_by_name`."""
return self._pytester.collect_by_name(modcol, name)
@@ -221,11 +212,11 @@ class Testdir:
"""See :meth:`Pytester.popen`."""
return self._pytester.popen(cmdargs, stdout, stderr, stdin, **kw)
def run(self, *cmdargs, timeout=None, stdin=CLOSE_STDIN) -> RunResult:
def run(self, *cmdargs, timeout=None, stdin=CLOSE_STDIN) -> pytest.RunResult:
"""See :meth:`Pytester.run`."""
return self._pytester.run(*cmdargs, timeout=timeout, stdin=stdin)
def runpython(self, script) -> RunResult:
def runpython(self, script) -> pytest.RunResult:
"""See :meth:`Pytester.runpython`."""
return self._pytester.runpython(script)
@@ -233,7 +224,7 @@ class Testdir:
"""See :meth:`Pytester.runpython_c`."""
return self._pytester.runpython_c(command)
def runpytest_subprocess(self, *args, timeout=None) -> RunResult:
def runpytest_subprocess(self, *args, timeout=None) -> pytest.RunResult:
"""See :meth:`Pytester.runpytest_subprocess`."""
return self._pytester.runpytest_subprocess(*args, timeout=timeout)
@@ -254,10 +245,13 @@ class Testdir:
return str(self.tmpdir)
pytest.Testdir = Testdir # type: ignore[attr-defined]
class LegacyTestdirPlugin:
@staticmethod
@fixture
def testdir(pytester: Pytester) -> Testdir:
@pytest.fixture
def testdir(pytester: pytest.Pytester) -> Testdir:
"""
Identical to :fixture:`pytester`, and provides an instance whose methods return
legacy ``LEGACY_PATH`` objects instead when applicable.
@@ -273,10 +267,10 @@ class TempdirFactory:
"""Backward compatibility wrapper that implements :class:``_pytest.compat.LEGACY_PATH``
for :class:``TempPathFactory``."""
_tmppath_factory: TempPathFactory
_tmppath_factory: pytest.TempPathFactory
def __init__(
self, tmppath_factory: TempPathFactory, *, _ispytest: bool = False
self, tmppath_factory: pytest.TempPathFactory, *, _ispytest: bool = False
) -> None:
check_ispytest(_ispytest)
self._tmppath_factory = tmppath_factory
@@ -290,16 +284,19 @@ class TempdirFactory:
return legacy_path(self._tmppath_factory.getbasetemp().resolve())
pytest.TempdirFactory = TempdirFactory # type: ignore[attr-defined]
class LegacyTmpdirPlugin:
@staticmethod
@fixture(scope="session")
def tmpdir_factory(request: FixtureRequest) -> TempdirFactory:
@pytest.fixture(scope="session")
def tmpdir_factory(request: pytest.FixtureRequest) -> TempdirFactory:
"""Return a :class:`pytest.TempdirFactory` instance for the test session."""
# Set dynamically by pytest_configure().
return request.config._tmpdirhandler # type: ignore
@staticmethod
@fixture
@pytest.fixture
def tmpdir(tmp_path: Path) -> LEGACY_PATH:
"""Return a temporary directory path object which is unique to each test
function invocation, created as a sub directory of the base temporary
@@ -317,7 +314,7 @@ class LegacyTmpdirPlugin:
return legacy_path(tmp_path)
def Cache_makedir(self: Cache, name: str) -> LEGACY_PATH:
def Cache_makedir(self: pytest.Cache, name: str) -> LEGACY_PATH:
"""Return a directory path object with the given name.
Same as :func:`mkdir`, but returns a legacy py path instance.
@@ -325,7 +322,7 @@ def Cache_makedir(self: Cache, name: str) -> LEGACY_PATH:
return legacy_path(self.mkdir(name))
def FixtureRequest_fspath(self: FixtureRequest) -> LEGACY_PATH:
def FixtureRequest_fspath(self: pytest.FixtureRequest) -> LEGACY_PATH:
"""(deprecated) The file system path of the test module which collected this test."""
return legacy_path(self.path)
@@ -340,7 +337,7 @@ def TerminalReporter_startdir(self: TerminalReporter) -> LEGACY_PATH:
return legacy_path(self.startpath)
def Config_invocation_dir(self: Config) -> LEGACY_PATH:
def Config_invocation_dir(self: pytest.Config) -> LEGACY_PATH:
"""The directory from which pytest was invoked.
Prefer to use :attr:`invocation_params.dir <InvocationParams.dir>`,
@@ -351,7 +348,7 @@ def Config_invocation_dir(self: Config) -> LEGACY_PATH:
return legacy_path(str(self.invocation_params.dir))
def Config_rootdir(self: Config) -> LEGACY_PATH:
def Config_rootdir(self: pytest.Config) -> LEGACY_PATH:
"""The path to the :ref:`rootdir <rootdir>`.
Prefer to use :attr:`rootpath`, which is a :class:`pathlib.Path`.
@@ -361,7 +358,7 @@ def Config_rootdir(self: Config) -> LEGACY_PATH:
return legacy_path(str(self.rootpath))
def Config_inifile(self: Config) -> Optional[LEGACY_PATH]:
def Config_inifile(self: pytest.Config) -> Optional[LEGACY_PATH]:
"""The path to the :ref:`configfile <configfiles>`.
Prefer to use :attr:`inipath`, which is a :class:`pathlib.Path`.
@@ -371,7 +368,7 @@ def Config_inifile(self: Config) -> Optional[LEGACY_PATH]:
return legacy_path(str(self.inipath)) if self.inipath else None
def Session_stardir(self: Session) -> LEGACY_PATH:
def Session_stardir(self: pytest.Session) -> LEGACY_PATH:
"""The path from which pytest was invoked.
Prefer to use ``startpath`` which is a :class:`pathlib.Path`.
@@ -403,44 +400,12 @@ def Node_fspath_set(self: Node, value: LEGACY_PATH) -> None:
self.path = Path(value)
@hookimpl(tryfirst=True)
def pytest_load_initial_conftests(early_config: Config) -> None:
"""Monkeypatch legacy path attributes in several classes, as early as possible."""
mp = MonkeyPatch()
early_config.add_cleanup(mp.undo)
@pytest.hookimpl
def pytest_configure(config: pytest.Config) -> None:
mp = pytest.MonkeyPatch()
config.add_cleanup(mp.undo)
# Add Cache.makedir().
mp.setattr(Cache, "makedir", Cache_makedir, raising=False)
# Add FixtureRequest.fspath property.
mp.setattr(FixtureRequest, "fspath", property(FixtureRequest_fspath), raising=False)
# Add TerminalReporter.startdir property.
mp.setattr(
TerminalReporter, "startdir", property(TerminalReporter_startdir), raising=False
)
# Add Config.{invocation_dir,rootdir,inifile} properties.
mp.setattr(Config, "invocation_dir", property(Config_invocation_dir), raising=False)
mp.setattr(Config, "rootdir", property(Config_rootdir), raising=False)
mp.setattr(Config, "inifile", property(Config_inifile), raising=False)
# Add Session.startdir property.
mp.setattr(Session, "startdir", property(Session_stardir), raising=False)
# Add pathlist configuration type.
mp.setattr(Config, "_getini_unknown_type", Config__getini_unknown_type)
# Add Node.fspath property.
mp.setattr(Node, "fspath", property(Node_fspath, Node_fspath_set), raising=False)
@hookimpl
def pytest_configure(config: Config) -> None:
"""Installs the LegacyTmpdirPlugin if the ``tmpdir`` plugin is also installed."""
if config.pluginmanager.has_plugin("tmpdir"):
mp = MonkeyPatch()
config.add_cleanup(mp.undo)
# Create TmpdirFactory and attach it to the config object.
#
# This is to comply with existing plugins which expect the handler to be
@@ -457,9 +422,40 @@ def pytest_configure(config: Config) -> None:
config.pluginmanager.register(LegacyTmpdirPlugin, "legacypath-tmpdir")
# Add Cache.makedir().
mp.setattr(pytest.Cache, "makedir", Cache_makedir, raising=False)
@hookimpl
def pytest_plugin_registered(plugin: object, manager: PytestPluginManager) -> None:
# Add FixtureRequest.fspath property.
mp.setattr(
pytest.FixtureRequest, "fspath", property(FixtureRequest_fspath), raising=False
)
# Add TerminalReporter.startdir property.
mp.setattr(
TerminalReporter, "startdir", property(TerminalReporter_startdir), raising=False
)
# Add Config.{invocation_dir,rootdir,inifile} properties.
mp.setattr(
pytest.Config, "invocation_dir", property(Config_invocation_dir), raising=False
)
mp.setattr(pytest.Config, "rootdir", property(Config_rootdir), raising=False)
mp.setattr(pytest.Config, "inifile", property(Config_inifile), raising=False)
# Add Session.startdir property.
mp.setattr(pytest.Session, "startdir", property(Session_stardir), raising=False)
# Add pathlist configuration type.
mp.setattr(pytest.Config, "_getini_unknown_type", Config__getini_unknown_type)
# Add Node.fspath property.
mp.setattr(Node, "fspath", property(Node_fspath, Node_fspath_set), raising=False)
@pytest.hookimpl
def pytest_plugin_registered(
plugin: object, manager: pytest.PytestPluginManager
) -> None:
# pytester is not loaded by default and is commonly loaded from a conftest,
# so checking for it in `pytest_configure` is not enough.
is_pytester = plugin is manager.get_plugin("pytester")

View File

@@ -605,7 +605,8 @@ class Session(nodes.FSCollector):
) -> Sequence[Union[nodes.Item, nodes.Collector]]:
"""Perform the collection phase for this session.
This is called by the default :hook:`pytest_collection` hook
This is called by the default
:func:`pytest_collection <_pytest.hookspec.pytest_collection>` hook
implementation; see the documentation of this hook for more details.
For testing purposes, it may also be called directly on a fresh
``Session``.
@@ -871,10 +872,7 @@ def resolve_collection_argument(
If the path doesn't exist, raise UsageError.
If the path is a directory and selection parts are present, raise UsageError.
"""
base, squacket, rest = str(arg).partition("[")
strpath, *parts = base.split("::")
if parts:
parts[-1] = f"{parts[-1]}{squacket}{rest}"
strpath, *parts = str(arg).split("::")
if as_pypath:
strpath = search_pypath(strpath)
fspath = invocation_path / strpath

View File

@@ -190,7 +190,7 @@ class MatcherAdapter(Mapping[str, bool]):
class Expression:
"""A compiled match expression as used by -k and -m.
The expression can be evaluated against different matchers.
The expression can be evaulated against different matchers.
"""
__slots__ = ("code",)

View File

@@ -145,10 +145,7 @@ class NodeMeta(type):
warnings.warn(
PytestDeprecationWarning(
f"{self} is not using a cooperative constructor and only takes {set(known_kw)}.\n"
"See https://docs.pytest.org/en/stable/deprecations.html"
"#constructors-of-custom-pytest-node-subclasses-should-take-kwargs "
"for more details."
f"{self} is not using a cooperative constructor and only takes {set(known_kw)}"
)
)
@@ -656,6 +653,20 @@ class Item(Node):
nextitem = None
def __init_subclass__(cls) -> None:
problems = ", ".join(
base.__name__ for base in cls.__bases__ if issubclass(base, Collector)
)
if problems:
warnings.warn(
f"{cls.__name__} is an Item subclass and should not be a collector, "
f"however its bases {problems} are collectors.\n"
"Please split the Collectors and the Item into separate node types.\n"
"Pytest Doc example: https://docs.pytest.org/en/latest/example/nonpython.html\n"
"example pull request on a plugin: https://github.com/asmeurer/pytest-flakes/pull/40/",
PytestWarning,
)
def __init__(
self,
name,
@@ -683,37 +694,6 @@ class Item(Node):
#: for this test.
self.user_properties: List[Tuple[str, object]] = []
self._check_item_and_collector_diamond_inheritance()
def _check_item_and_collector_diamond_inheritance(self) -> None:
"""
Check if the current type inherits from both File and Collector
at the same time, emitting a warning accordingly (#8447).
"""
cls = type(self)
# We inject an attribute in the type to avoid issuing this warning
# for the same class more than once, which is not helpful.
# It is a hack, but was deemed acceptable in order to avoid
# flooding the user in the common case.
attr_name = "_pytest_diamond_inheritance_warning_shown"
if getattr(cls, attr_name, False):
return
setattr(cls, attr_name, True)
problems = ", ".join(
base.__name__ for base in cls.__bases__ if issubclass(base, Collector)
)
if problems:
warnings.warn(
f"{cls.__name__} is an Item subclass and should not be a collector, "
f"however its bases {problems} are collectors.\n"
"Please split the Collectors and the Item into separate node types.\n"
"Pytest Doc example: https://docs.pytest.org/en/latest/example/nonpython.html\n"
"example pull request on a plugin: https://github.com/asmeurer/pytest-flakes/pull/40/",
PytestWarning,
)
def runtest(self) -> None:
"""Run the test case for this item.

View File

@@ -596,15 +596,11 @@ class RunResult:
errors: int = 0,
xpassed: int = 0,
xfailed: int = 0,
warnings: Optional[int] = None,
deselected: Optional[int] = None,
warnings: int = 0,
deselected: int = 0,
) -> None:
"""
Assert that the specified outcomes appear with the respective
numbers (0 means it didn't occur) in the text output from a test run.
``warnings`` and ``deselected`` are only checked if not None.
"""
"""Assert that the specified outcomes appear with the respective
numbers (0 means it didn't occur) in the text output from a test run."""
__tracebackhide__ = True
from _pytest.pytester_assertions import assert_outcomes
@@ -1292,7 +1288,7 @@ class Pytester:
) -> Optional[Union[Item, Collector]]:
"""Return the collection node for name from the module collection.
Searches a module collection node for a collection node matching the
Searchs a module collection node for a collection node matching the
given name.
:param modcol: A module collection node; see :py:meth:`getmodulecol`.

View File

@@ -4,7 +4,6 @@
# hence cannot be subject to assertion rewriting, which requires a
# module to not be already imported.
from typing import Dict
from typing import Optional
from typing import Sequence
from typing import Tuple
from typing import Union
@@ -43,8 +42,8 @@ def assert_outcomes(
errors: int = 0,
xpassed: int = 0,
xfailed: int = 0,
warnings: Optional[int] = None,
deselected: Optional[int] = None,
warnings: int = 0,
deselected: int = 0,
) -> None:
"""Assert that the specified outcomes appear with the respective
numbers (0 means it didn't occur) in the text output from a test run."""
@@ -57,6 +56,8 @@ def assert_outcomes(
"errors": outcomes.get("errors", 0),
"xpassed": outcomes.get("xpassed", 0),
"xfailed": outcomes.get("xfailed", 0),
"warnings": outcomes.get("warnings", 0),
"deselected": outcomes.get("deselected", 0),
}
expected = {
"passed": passed,
@@ -65,11 +66,7 @@ def assert_outcomes(
"errors": errors,
"xpassed": xpassed,
"xfailed": xfailed,
"warnings": warnings,
"deselected": deselected,
}
if warnings is not None:
obtained["warnings"] = outcomes.get("warnings", 0)
expected["warnings"] = warnings
if deselected is not None:
obtained["deselected"] = outcomes.get("deselected", 0)
expected["deselected"] = deselected
assert obtained == expected

View File

@@ -278,16 +278,6 @@ class PyobjMixin(nodes.Node):
node = self.getparent(Class)
return node.obj if node is not None else None
@property
def instance(self):
"""Python instance object the function is bound to.
Returns None if not a test method, e.g. for a standalone test function,
a staticmethod, a class or a module.
"""
node = self.getparent(Function)
return getattr(node.obj, "__self__", None) if node is not None else None
@property
def obj(self):
"""Underlying Python object."""
@@ -527,18 +517,12 @@ class Module(nodes.File, PyCollector):
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
@@ -996,7 +980,7 @@ class CallSpec2:
@final
class Metafunc:
"""Objects passed to the :hook:`pytest_generate_tests` hook.
"""Objects passed to the :func:`pytest_generate_tests <_pytest.hookspec.pytest_generate_tests>` hook.
They help to inspect a test function and to generate tests according to
test configuration or values specified in the class or module where a
@@ -1660,7 +1644,7 @@ class Function(PyobjMixin, nodes.Item):
# this will be redeemed later
for mark in callspec.marks:
# feel free to cry, this was broken for years before
# and keywords can't fix it per design
# and keywords cant fix it per design
self.keywords[mark.name] = mark
self.own_markers.extend(normalize_mark_list(callspec.marks))
if keywords:
@@ -1699,6 +1683,15 @@ class Function(PyobjMixin, nodes.Item):
"""Underlying python 'function' object."""
return getimfunc(self.obj)
@property
def instance(self):
"""Python instance object the function is bound to.
Returns None if not a test method, e.g. for a standalone test function
or a staticmethod.
"""
return getattr(self.obj, "__self__", None)
def _getobj(self):
assert self.parent is not None
if isinstance(self.parent, Class):

View File

@@ -101,7 +101,6 @@ class ApproxBase:
)
def __bool__(self):
__tracebackhide__ = True
raise AssertionError(
"approx() is not supported in a boolean context.\nDid you mean: `assert a == approx(b)`?"
)

View File

@@ -185,15 +185,6 @@ class TestCaseFunction(Function):
_excinfo: Optional[List[_pytest._code.ExceptionInfo[BaseException]]] = None
_testcase: Optional["unittest.TestCase"] = None
def _getobj(self):
assert self.parent is not None
# Unlike a regular Function in a Class, where `item.obj` returns
# a *bound* method (attached to an instance), TestCaseFunction's
# `obj` returns an *unbound* method (not attached to an instance).
# This inconsistency is probably not desirable, but needs some
# consideration before changing.
return getattr(self.parent.obj, self.originalname) # type: ignore[attr-defined]
def setup(self) -> None:
# A bound method to be called during teardown() if set (see 'runtest()').
self._explicit_tearDown: Optional[Callable[[], None]] = None

View File

@@ -25,8 +25,6 @@ from _pytest.fixtures import FixtureLookupError
from _pytest.fixtures import FixtureRequest
from _pytest.fixtures import yield_fixture
from _pytest.freeze_support import freeze_includes
from _pytest.legacypath import TempdirFactory
from _pytest.legacypath import Testdir
from _pytest.logging import LogCaptureFixture
from _pytest.main import Session
from _pytest.mark import Mark
@@ -150,9 +148,7 @@ __all__ = [
"Stash",
"StashKey",
"version_tuple",
"TempdirFactory",
"TempPathFactory",
"Testdir",
"TestReport",
"UsageError",
"WarningsRecorder",

View File

@@ -1281,7 +1281,7 @@ def test_tee_stdio_captures_and_live_prints(pytester: Pytester) -> None:
reason="Windows raises `OSError: [Errno 22] Invalid argument` instead",
)
def test_no_brokenpipeerror_message(pytester: Pytester) -> None:
"""Ensure that the broken pipe error message is suppressed.
"""Ensure that the broken pipe error message is supressed.
In some Python versions, it reaches sys.unraisablehook, in others
a BrokenPipeError exception is propagated, but either way it prints

View File

@@ -484,7 +484,7 @@ def test_source_with_decorator() -> None:
src = inspect.getsource(deco_fixture)
assert src == " @pytest.fixture\n def deco_fixture():\n assert False\n"
# currently Source does not unwrap decorators, testing the
# currenly Source does not unwrap decorators, testing the
# existing behavior here for explicitness, but perhaps we should revisit/change this
# in the future
assert str(Source(deco_fixture)).startswith("@functools.wraps(function)")
@@ -618,19 +618,6 @@ def something():
assert str(source) == "def func(): raise ValueError(42)"
def test_decorator() -> None:
s = """\
def foo(f):
pass
@foo
def bar():
pass
"""
source = getstatement(3, s)
assert "@foo" in str(source)
def XXX_test_expression_multiline() -> None:
source = """\
something

View File

@@ -194,10 +194,8 @@ 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."
"Passing None to catch any warning has been deprecated, pass no arguments instead:\n "
"Replace pytest.warns(None) by simply pytest.warns()."
),
):
with pytest.warns(None): # type: ignore[call-overload]

View File

@@ -1616,7 +1616,7 @@ def test_raise_unprintable_assertion_error(pytester: Pytester) -> None:
)
def test_raise_assertion_error_raising_repr(pytester: Pytester) -> None:
def test_raise_assertion_error_raisin_repr(pytester: Pytester) -> None:
pytester.makepyfile(
"""
class RaisingRepr(object):
@@ -1627,15 +1627,9 @@ def test_raise_assertion_error_raising_repr(pytester: Pytester) -> None:
"""
)
result = pytester.runpytest()
if sys.version_info >= (3, 11):
# python 3.11 has native support for un-str-able exceptions
result.stdout.fnmatch_lines(
["E AssertionError: <exception str() failed>"]
)
else:
result.stdout.fnmatch_lines(
["E AssertionError: <unprintable AssertionError object>"]
)
result.stdout.fnmatch_lines(
["E AssertionError: <unprintable AssertionError object>"]
)
def test_issue_1944(pytester: Pytester) -> None:

View File

@@ -1123,28 +1123,6 @@ class TestAssertionRewriteHookDetails:
assert _read_pyc(source, pyc) is None # no error
def test_read_pyc_success(self, tmp_path: Path, pytester: Pytester) -> None:
"""
Ensure that the _rewrite_test() -> _write_pyc() produces a pyc file
that can be properly read with _read_pyc()
"""
from _pytest.assertion import AssertionState
from _pytest.assertion.rewrite import _read_pyc
from _pytest.assertion.rewrite import _rewrite_test
from _pytest.assertion.rewrite import _write_pyc
config = pytester.parseconfig()
state = AssertionState(config, "rewrite")
fn = tmp_path / "source.py"
pyc = Path(str(fn) + "c")
fn.write_text("def test(): assert True")
source_stat, co = _rewrite_test(fn, config)
_write_pyc(state, co, source_stat, pyc)
assert _read_pyc(fn, pyc, state.trace) is not None
@pytest.mark.skipif(
sys.version_info < (3, 7), reason="Only the Python 3.7 format for simplicity"
)

View File

@@ -64,7 +64,7 @@ class TestCollector:
assert pytester.collect_by_name(modcol, "doesnotexist") is None
def test_getparent_and_accessors(self, pytester: Pytester) -> None:
def test_getparent(self, pytester: Pytester) -> None:
modcol = pytester.getmodulecol(
"""
class TestClass:
@@ -77,21 +77,14 @@ class TestCollector:
fn = pytester.collect_by_name(cls, "test_foo")
assert isinstance(fn, pytest.Function)
assert fn.getparent(pytest.Module) is modcol
assert modcol.module is not None
assert modcol.cls is None
assert modcol.instance is None
module_parent = fn.getparent(pytest.Module)
assert module_parent is modcol
assert fn.getparent(pytest.Class) is cls
assert cls.module is not None
assert cls.cls is not None
assert cls.instance is None
function_parent = fn.getparent(pytest.Function)
assert function_parent is fn
assert fn.getparent(pytest.Function) is fn
assert fn.module is not None
assert fn.cls is not None
assert fn.instance is not None
assert fn.function is not None
class_parent = fn.getparent(pytest.Class)
assert class_parent is cls
def test_getcustomfile_roundtrip(self, pytester: Pytester) -> None:
hello = pytester.makefile(".xxx", hello="world")

View File

@@ -1,5 +1,4 @@
import enum
import sys
from functools import partial
from functools import wraps
from typing import TYPE_CHECKING
@@ -92,7 +91,6 @@ def test_get_real_func_partial() -> None:
assert get_real_func(partial(foo)) is foo
@pytest.mark.skipif(sys.version_info >= (3, 11), reason="couroutine removed")
def test_is_generator_asyncio(pytester: Pytester) -> None:
pytester.makepyfile(
"""
@@ -158,11 +156,11 @@ class ErrorsHelper:
@property
def raise_exception(self):
raise Exception("exception should be caught")
raise Exception("exception should be catched")
@property
def raise_fail_outcome(self):
pytest.fail("fail should be caught")
pytest.fail("fail should be catched")
def test_helper_failures() -> None:

View File

@@ -386,7 +386,7 @@ class TestParseIni:
pytest.param(
"""
[some_other_header]
required_plugins = won't be triggered
required_plugins = wont be triggered
[pytest]
""",
"1.5",
@@ -807,7 +807,7 @@ class TestConfigAPI:
with pytest.raises(pytest.UsageError, match=exp_match):
pytester.parseconfig("--confcutdir", pytester.path.joinpath("file"))
with pytest.raises(pytest.UsageError, match=exp_match):
pytester.parseconfig("--confcutdir", pytester.path.joinpath("nonexistent"))
pytester.parseconfig("--confcutdir", pytester.path.joinpath("inexistant"))
p = pytester.mkdir("dir")
config = pytester.parseconfig("--confcutdir", p)
@@ -1264,21 +1264,15 @@ def test_load_initial_conftest_last_ordering(_config_for_test):
m = My()
pm.register(m)
hc = pm.hook.pytest_load_initial_conftests
hookimpls = [
(
hookimpl.function.__module__,
"wrapper" if hookimpl.hookwrapper else "nonwrapper",
)
for hookimpl in hc.get_hookimpls()
]
assert hookimpls == [
("_pytest.config", "nonwrapper"),
(m.__module__, "nonwrapper"),
("_pytest.legacypath", "nonwrapper"),
("_pytest.python_path", "nonwrapper"),
("_pytest.capture", "wrapper"),
("_pytest.warnings", "wrapper"),
values = hc._nonwrappers + hc._wrappers
expected = [
"_pytest.config",
m.__module__,
"_pytest.pythonpath",
"_pytest.capture",
"_pytest.warnings",
]
assert [x.function.__module__ for x in values] == expected
def test_get_plugin_specs_as_list() -> None:

View File

@@ -1,5 +1,4 @@
import inspect
import sys
import textwrap
from pathlib import Path
from typing import Callable
@@ -81,7 +80,7 @@ class TestDoctests:
'# Empty'
def my_func():
">>> magic = 42 "
def useless():
def unuseful():
'''
# This is a function
# >>> # it doesn't have any doctest
@@ -201,7 +200,6 @@ class TestDoctests:
"Traceback (most recent call last):",
' File "*/doctest.py", line *, in __run',
" *",
*((" *^^^^*",) if sys.version_info >= (3, 11) else ()),
' File "<doctest test_doctest_unexpected_exception.txt[1]>", line 1, in <module>',
"ZeroDivisionError: division by zero",
"*/test_doctest_unexpected_exception.txt:2: UnexpectedException",
@@ -566,7 +564,7 @@ class TestDoctests:
>>> magic - 42
0
'''
def useless():
def unuseful():
pass
def another():
'''
@@ -804,12 +802,11 @@ class TestDoctests:
p = pytester.makepyfile(
setup="""
from setuptools import setup, find_packages
if __name__ == '__main__':
setup(name='sample',
version='0.0',
description='description',
packages=find_packages()
)
setup(name='sample',
version='0.0',
description='description',
packages=find_packages()
)
"""
)
result = pytester.runpytest(p, "--doctest-modules")

View File

@@ -161,20 +161,3 @@ def test_override_ini_paths(pytester: pytest.Pytester) -> None:
)
result = pytester.runpytest("--override-ini", "paths=foo/bar1.py foo/bar2.py", "-s")
result.stdout.fnmatch_lines(["user_path:bar1.py", "user_path:bar2.py"])
def test_inifile_from_cmdline_main_hook(pytester: pytest.Pytester) -> None:
"""Ensure Config.inifile is available during pytest_cmdline_main (#9396)."""
p = pytester.makeini(
"""
[pytest]
"""
)
pytester.makeconftest(
"""
def pytest_cmdline_main(config):
print("pytest_cmdline_main inifile =", config.inifile)
"""
)
result = pytester.runpytest_subprocess("-s")
result.stdout.fnmatch_lines(f"*pytest_cmdline_main inifile = {p}")

View File

@@ -1,7 +1,6 @@
import argparse
import os
import re
import sys
from pathlib import Path
from typing import Optional
@@ -45,32 +44,16 @@ def test_wrap_session_notify_exception(ret_exc, pytester: Pytester) -> None:
assert result.ret == ExitCode.INTERNAL_ERROR
assert result.stdout.lines[0] == "INTERNALERROR> Traceback (most recent call last):"
end_lines = (
result.stdout.lines[-4:]
if sys.version_info >= (3, 11)
else result.stdout.lines[-3:]
)
if exc == SystemExit:
assert end_lines == [
assert result.stdout.lines[-3:] == [
f'INTERNALERROR> File "{c1}", line 4, in pytest_sessionstart',
'INTERNALERROR> raise SystemExit("boom")',
*(
("INTERNALERROR> ^^^^^^^^^^^^^^^^^^^^^^^^",)
if sys.version_info >= (3, 11)
else ()
),
"INTERNALERROR> SystemExit: boom",
]
else:
assert end_lines == [
assert result.stdout.lines[-3:] == [
f'INTERNALERROR> File "{c1}", line 4, in pytest_sessionstart',
'INTERNALERROR> raise ValueError("boom")',
*(
("INTERNALERROR> ^^^^^^^^^^^^^^^^^^^^^^^^",)
if sys.version_info >= (3, 11)
else ()
),
"INTERNALERROR> ValueError: boom",
]
if returncode is False:
@@ -188,12 +171,6 @@ class TestResolveCollectionArgument:
invocation_path, "pkg::foo::bar", as_pypath=True
)
def test_parametrized_name_with_colons(self, invocation_path: Path) -> None:
ret = resolve_collection_argument(
invocation_path, "src/pkg/test.py::test[a::b]"
)
assert ret == (invocation_path / "src/pkg/test.py", ["test[a::b]"])
def test_does_not_exist(self, invocation_path: Path) -> None:
"""Given a file/module that does not exist raises UsageError."""
with pytest.raises(

View File

@@ -1,5 +1,3 @@
import re
import warnings
from pathlib import Path
from typing import cast
from typing import List
@@ -60,31 +58,30 @@ def test_subclassing_both_item_and_collector_deprecated(
request, tmp_path: Path
) -> None:
"""
Verifies we warn on diamond inheritance as well as correctly managing legacy
inheritance constructors with missing args as found in plugins.
Verifies we warn on diamond inheritance
as well as correctly managing legacy inheritance ctors with missing args
as found in plugins
"""
# We do not expect any warnings messages to issued during class definition.
with warnings.catch_warnings():
warnings.simplefilter("error")
with pytest.warns(
PytestWarning,
match=(
"(?m)SoWrong is an Item subclass and should not be a collector, however its bases File are collectors.\n"
"Please split the Collectors and the Item into separate node types.\n.*"
),
):
class SoWrong(nodes.Item, nodes.File):
def __init__(self, fspath, parent):
"""Legacy ctor with legacy call # don't wana see"""
super().__init__(fspath, parent)
with pytest.warns(PytestWarning) as rec:
with pytest.warns(
PytestWarning, match=".*SoWrong.* not using a cooperative constructor.*"
):
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)
for x in messages
)
assert any(
re.search("(?m)SoWrong .* should not be a collector", x) for x in messages
)
@pytest.mark.parametrize(

View File

@@ -477,22 +477,3 @@ def test_raises(pytester: Pytester) -> None:
"* 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, "-p", "nose")
assert result.ret == 0

View File

@@ -743,8 +743,8 @@ def test_run_result_repr() -> None:
# known exit code
r = pytester_mod.RunResult(1, outlines, errlines, duration=0.5)
assert repr(r) == (
f"<RunResult ret={str(pytest.ExitCode.TESTS_FAILED)} len(stdout.lines)=3"
assert (
repr(r) == "<RunResult ret=ExitCode.TESTS_FAILED len(stdout.lines)=3"
" len(stderr.lines)=4 duration=0.50s>"
)
@@ -835,8 +835,6 @@ def test_pytester_assert_outcomes_warnings(pytester: Pytester) -> None:
)
result = pytester.runpytest()
result.assert_outcomes(passed=1, warnings=1)
# If warnings is not passed, it is not checked at all.
result.assert_outcomes(passed=1)
def test_pytester_outcomes_deselected(pytester: Pytester) -> None:
@@ -851,5 +849,3 @@ def test_pytester_outcomes_deselected(pytester: Pytester) -> None:
)
result = pytester.runpytest("-k", "test_one")
result.assert_outcomes(passed=1, deselected=1)
# If deselected is not passed, it is not checked at all.
result.assert_outcomes(passed=1)

View File

@@ -81,7 +81,7 @@ def test_no_ini(pytester: Pytester, file_structure) -> None:
def test_clean_up(pytester: Pytester) -> None:
"""Test that the plugin cleans up after itself."""
"""Test that the pythonpath plugin cleans up after itself."""
# This is tough to test behaviorly because the cleanup really runs last.
# So the test make several implementation assumptions:
# - Cleanup is done in pytest_unconfigure().

View File

@@ -287,7 +287,7 @@ class BaseFunctionalTests:
def test_func():
import sys
# on python2 exc_info is kept till a function exits
# on python2 exc_info is keept till a function exits
# so we would end up calling test functions while
# sys.exc_info would return the indexerror
# from guessing the lastitem

View File

@@ -1143,6 +1143,8 @@ def test_errors_in_xfail_skip_expressions(pytester: Pytester) -> None:
pypy_version_info = getattr(sys, "pypy_version_info", None)
if pypy_version_info is not None and pypy_version_info < (6,):
markline = markline[5:]
elif sys.version_info[:2] >= (3, 10):
markline = markline[11:]
elif sys.version_info >= (3, 8) or hasattr(sys, "pypy_version_info"):
markline = markline[4:]

View File

@@ -56,7 +56,7 @@ def test_stash() -> None:
with pytest.raises(AttributeError):
stash.foo = "nope" # type: ignore[attr-defined]
# No interaction with another stash.
# No interaction with anoter stash.
stash2 = Stash()
key3 = StashKey[int]()
assert key2 not in stash2

View File

@@ -1472,29 +1472,3 @@ def test_do_cleanups_on_teardown_failure(pytester: Pytester) -> None:
passed, skipped, failed = reprec.countoutcomes()
assert failed == 2
assert passed == 1
def test_traceback_pruning(pytester: Pytester) -> None:
"""Regression test for #9610 - doesn't crash during traceback pruning."""
pytester.makepyfile(
"""
import unittest
class MyTestCase(unittest.TestCase):
def __init__(self, test_method):
unittest.TestCase.__init__(self, test_method)
class TestIt(MyTestCase):
@classmethod
def tearDownClass(cls) -> None:
assert False
def test_it(self):
pass
"""
)
reprec = pytester.inline_run()
passed, skipped, failed = reprec.countoutcomes()
assert passed == 1
assert failed == 1
assert reprec.ret == 1

View File

@@ -289,7 +289,7 @@ def test_warning_captured_hook(pytester: Pytester) -> None:
assert collected_result[2] == expected_result[2], str(collected)
# NOTE: collected_result[3] is location, which differs based on the platform you are on
# thus, the best we can do here is assert the types of the parameters match what we expect
# thus, the best we can do here is assert the types of the paremeters match what we expect
# and not try and preload it in the expected array
if collected_result[3] is not None:
assert type(collected_result[3][0]) is str, str(collected)
@@ -707,7 +707,7 @@ class TestStackLevel:
pytester.parseconfig("--help")
# with stacklevel=2 the warning should originate from config._preparse and is
# thrown by an erroneous conftest.py
# thrown by an errorneous conftest.py
assert len(capwarn.captured) == 1
warning, location = capwarn.captured.pop()
file, _, func = location

View File

@@ -9,7 +9,6 @@ envlist =
py38
py39
py310
py311
pypy3
py37-{pexpect,xdist,unittestextras,numpy,pluggymain}
doctesting
@@ -131,7 +130,7 @@ commands =
{envpython} tox_run.py
[testenv:release]
description = do a release, required posarg of the version number
decription = do a release, required posarg of the version number
basepython = python3
usedevelop = True
passenv = *
@@ -145,7 +144,7 @@ deps =
commands = python scripts/release.py {posargs}
[testenv:prepare-release-pr]
description = prepare a release PR from a manual trigger in GitHub actions
decription = prepare a release PR from a manual trigger in GitHub actions
usedevelop = {[testenv:release]usedevelop}
passenv = {[testenv:release]passenv}
deps = {[testenv:release]deps}