Compare commits
1 Commits
7.0.1
...
7.1.0.dev0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ad452bcba |
16
.github/workflows/main.yml
vendored
16
.github/workflows/main.yml
vendored
@@ -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
|
||||
|
||||
1
.github/workflows/update-plugin-list.yml
vendored
1
.github/workflows/update-plugin-list.yml
vendored
@@ -12,7 +12,6 @@ permissions: {}
|
||||
|
||||
jobs:
|
||||
createPullRequest:
|
||||
if: github.repository_owner == 'pytest-dev'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
2
AUTHORS
2
AUTHORS
@@ -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é
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -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 |
@@ -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):
|
||||
|
||||
@@ -22,7 +22,7 @@ Install ``pytest``
|
||||
.. code-block:: bash
|
||||
|
||||
$ pytest --version
|
||||
pytest 7.0.1
|
||||
pytest 7.0.0rc1
|
||||
|
||||
.. _`simpletest`:
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
"""
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",)
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)`?"
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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}")
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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().
|
||||
@@ -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
|
||||
|
||||
@@ -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:]
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
5
tox.ini
5
tox.ini
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user