Compare commits

...

38 Commits

Author SHA1 Message Date
Ronny Pfannschmidt ce7dca2680 [8.2.x] Introduce a gate/check GHA job 2024-06-19 08:50:14 +00:00
github-actions[bot] 53d2d4717d
[8.2.x] Documentation: Clean up various documentation pages (#12458)
Co-authored-by: neutraljump <162650677+neutraljump@users.noreply.github.com>
2024-06-13 12:26:21 +00:00
github-actions[bot] 56c0b1db3a
[8.2.x] doc: fix broken code blocks (#12450)
* [8.2.x] doc: fix broken code blocks

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: Ran Benita <ran@unusedvar.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-06-11 15:54:37 +00:00
github-actions[bot] eb8721d387
[8.2.x] unittest: fix assertion errors on unittest reruns (#12438)
Co-authored-by: Ran Benita <ran@unusedvar.com>
2024-06-07 22:39:52 -03:00
github-actions[bot] 36f0ea10b4
[8.2.x] remove my contact details (#12428)
Co-authored-by: holger krekel <holger@merlinux.eu>
2024-06-06 09:34:17 -03:00
Ran Benita f3a494cca3
Merge pull request #12415 from pytest-dev/release-8.2.2
Prepare release 8.2.2
2024-06-04 16:51:13 +03:00
pytest bot 329d371214 Prepare release version 8.2.2 2024-06-04 12:54:55 +00:00
Ran Benita 214d098fcc
Merge pull request #12414 from bluetech/backport-12409
[8.2.x] fixtures: fix catastrophic performance problem in `reorder_items`
2024-06-04 12:47:12 +03:00
Ran Benita 153a436bc4 [8.2.x] fixtures: fix catastrophic performance problem in `reorder_items`
Manual minimal backport from commit e89d23b247.

Fix #12355.

In the issue, it was reported that the `reorder_items` has quadratic (or
worse...) behavior with certain simple parametrizations. After some
debugging I found that the problem happens because the "Fix
items_by_argkey order" loop keeps adding the same item to the deque,
and it reaches epic sizes which causes the slowdown.

I don't claim to understand how the `reorder_items` algorithm works, but
if as far as I understand, if an item already exists in the deque, the
correct thing to do is to move it to the front. Since a deque doesn't
have such an (efficient) operation, this switches to `OrderedDict` which
can efficiently append from both sides, deduplicate and move to front.
2024-06-04 10:33:50 +03:00
Ran Benita b41d5a52bb
Merge pull request #12412 from pytest-dev/backport-12408-to-8.2.x
[8.2.x] cacheprovider: fix "Directory not empty" crash from cache directory creation
2024-06-03 13:16:29 +03:00
Ran Benita 9bb73d734f [8.2.x] cacheprovider: fix "Directory not empty" crash from cache directory creation 2024-06-03 09:44:21 +00:00
github-actions[bot] 4569a01e3d
[8.2.x] doc: Update trainings/events (#12402)
Co-authored-by: Florian Bruhin <me@the-compiler.org>
2024-05-30 17:05:32 +02:00
github-actions[bot] 1d103e5cdc
[8.2.x] Clarify pytest_ignore_collect docs (#12386)
Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
2024-05-28 17:41:35 +00:00
github-actions[bot] 240a252d34
[8.2.x] Add html_baseurl to sphinx conf.py (#12372)
Co-authored-by: James Frost <james.frost@metoffice.gov.uk>
2024-05-26 10:38:25 +00:00
Ran Benita a5ee3c4126
Merge pull request #12370 from pytest-dev/backport-12368-to-8.2.x
[8.2.x] unittest: fix class instances no longer released on test teardown since pytest 8.2.0
2024-05-26 11:04:09 +03:00
Ran Benita f7358aec28 [8.2.x] unittest: fix class instances no longer released on test teardown since pytest 8.2.0 2024-05-26 07:34:33 +00:00
github-actions[bot] 558e4fa71a
[8.2.x] Add thread safety section to flaky test docs (#12362)
Co-authored-by: Nathan Goldbaum <nathan.goldbaum@gmail.com>
2024-05-24 08:22:20 -03:00
github-actions[bot] f0e7a07667
[8.2.x] Issue #12290 - Docs using Furo Theme W/ Dark Mode (#12348)
Co-authored-by: Sam Jirovec <47160720+samjirovec@users.noreply.github.com>
2024-05-21 16:11:45 +00:00
Bruno Oliveira 22e885f109
Fix link in changelog (#12343) (#12344)
Co-authored-by: Hynek Schlawack <hs@ox.cx>
2024-05-19 18:17:40 -03:00
Ran Benita bddbeba74b
Merge pull request #12341 from pytest-dev/release-8.2.1
Prepare release 8.2.1
2024-05-19 22:09:37 +03:00
pytest bot 66ff8dffdf Prepare release version 8.2.1 2024-05-19 16:43:40 +00:00
Ran Benita 3ffcfd122c
Merge pull request #12340 from pytest-dev/backport-12334-to-8.2.x
[8.2.x] Add Python 3.13 (beta) support
2024-05-19 12:45:56 +03:00
Ran Benita 0b28313b46 [8.2.x] Add Python 3.13 (beta) support 2024-05-19 06:45:36 +00:00
github-actions[bot] f3dd93ad8d
[8.2.x] Attest package provenance (#12335)
Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
2024-05-17 08:20:32 -03:00
github-actions[bot] bb5a1257b0
[8.2.x] Spelling (#12331)
Co-authored-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2024-05-15 18:25:57 +00:00
Ran Benita f179bf252f
Merge pull request #12327 from pytest-dev/backport-12325-to-8.2.x
[8.2.x] cacheprovider: fix `.pytest_cache` not being world-readable
2024-05-15 14:15:10 +03:00
Ran Benita 2b671b5f92 [8.2.x] cacheprovider: fix `.pytest_cache` not being world-readable 2024-05-15 07:57:34 +00:00
Ran Benita 65ab7cb96c
Merge pull request #12324 from pytest-dev/backport-12320-to-8.2.x
[8.2.x] changelog: document unittest 8.2 change as breaking
2024-05-14 23:49:40 +03:00
Ran Benita 4d5fb7d71c
Merge pull request #12319 from pytest-dev/backport-12311-to-8.2.x
[8.2.x] python: add workaround for permission error crashes from non-selected directories
2024-05-14 23:07:16 +03:00
Ran Benita cbe5996cc6 [8.2.x] changelog: document unittest 8.2 change as breaking 2024-05-14 20:07:07 +00:00
Ran Benita c9e9315725 [8.2.x] python: add workaround for permission error crashes from non-selected directories 2024-05-13 17:33:00 +00:00
github-actions[bot] 328001eab1
[8.2.x] Fixes crashing under a squashfuse_ll read-only mount (#12302)
Co-authored-by: Yutian Li <hotpxless@gmail.com>
2024-05-09 18:20:57 +00:00
github-actions[bot] 8fdb72947e
[8.2.x] doc: update sprint repo link (#12297)
Co-authored-by: Florian Bruhin <me@the-compiler.org>
2024-05-08 10:16:22 -03:00
github-actions[bot] 5c1c73b961
[8.2.x] Document exceptions raised by exit, skip, xfail, fail, and importorskip (#12288)
Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
2024-05-06 01:32:21 +00:00
github-actions[bot] a152c2cee4
[8.2.x] Consider KeyboardInterrupt/SystemExit at collection time (#12282)
Co-authored-by: Anita Hammer <166057949+anitahammer@users.noreply.github.com>
2024-05-02 12:18:05 +00:00
Bruno Oliveira 69c3bcea36
Merge pull request #12257 from pytest-dev/release-8.2.0
Prepare release 8.2.0
2024-04-27 20:35:29 -03:00
Bruno Oliveira 6bd3f31344 Tweak changelog for 8.2.0 2024-04-27 10:20:51 -03:00
pytest bot 9b6219b5e8 Prepare release version 8.2.0 2024-04-27 13:07:39 +00:00
97 changed files with 777 additions and 328 deletions

View File

@ -19,6 +19,11 @@ jobs:
SETUPTOOLS_SCM_PRETEND_VERSION: ${{ github.event.inputs.version }}
timeout-minutes: 10
# Required by attest-build-provenance-github.
permissions:
id-token: write
attestations: write
steps:
- uses: actions/checkout@v4
with:
@ -26,7 +31,9 @@ jobs:
persist-credentials: false
- name: Build and Check Package
uses: hynek/build-and-inspect-python-package@v2.4.0
uses: hynek/build-and-inspect-python-package@v2.5.0
with:
attest-build-provenance-github: 'true'
deploy:
if: github.repository == 'pytest-dev/pytest'

View File

@ -35,7 +35,7 @@ jobs:
fetch-depth: 0
persist-credentials: false
- name: Build and Check Package
uses: hynek/build-and-inspect-python-package@v2.4.0
uses: hynek/build-and-inspect-python-package@v2.5.0
build:
needs: [package]
@ -55,6 +55,7 @@ jobs:
"windows-py310",
"windows-py311",
"windows-py312",
"windows-py313",
"ubuntu-py38",
"ubuntu-py38-pluggy",
@ -63,12 +64,14 @@ jobs:
"ubuntu-py310",
"ubuntu-py311",
"ubuntu-py312",
"ubuntu-py313",
"ubuntu-pypy3",
"macos-py38",
"macos-py39",
"macos-py310",
"macos-py312",
"macos-py313",
"doctesting",
"plugins",
@ -97,9 +100,13 @@ jobs:
os: windows-latest
tox_env: "py311"
- name: "windows-py312"
python: "3.12-dev"
python: "3.12"
os: windows-latest
tox_env: "py312"
- name: "windows-py313"
python: "3.13-dev"
os: windows-latest
tox_env: "py313"
- name: "ubuntu-py38"
python: "3.8"
@ -128,10 +135,15 @@ jobs:
tox_env: "py311"
use_coverage: true
- name: "ubuntu-py312"
python: "3.12-dev"
python: "3.12"
os: ubuntu-latest
tox_env: "py312"
use_coverage: true
- name: "ubuntu-py313"
python: "3.13-dev"
os: ubuntu-latest
tox_env: "py313"
use_coverage: true
- name: "ubuntu-pypy3"
python: "pypy-3.8"
os: ubuntu-latest
@ -151,9 +163,13 @@ jobs:
os: macos-latest
tox_env: "py310-xdist"
- name: "macos-py312"
python: "3.12-dev"
python: "3.12"
os: macos-latest
tox_env: "py312-xdist"
- name: "macos-py313"
python: "3.13-dev"
os: macos-latest
tox_env: "py313-xdist"
- name: "plugins"
python: "3.12"
@ -211,3 +227,17 @@ jobs:
fail_ci_if_error: true
files: ./coverage.xml
verbose: true
check: # This job does nothing and is only used for the branch protection
if: always()
needs:
- build
runs-on: ubuntu-latest
steps:
- name: Decide whether the needed jobs succeeded or failed
uses: re-actors/alls-green@198badcb65a1a44528f27d5da555c4be9f12eac6
with:
jobs: ${{ toJSON(needs) }}

View File

@ -36,6 +36,7 @@ Andrey Paramonov
Andrzej Klajnert
Andrzej Ostrowski
Andy Freeland
Anita Hammer
Anthon van der Neut
Anthony Shaw
Anthony Sottile
@ -191,6 +192,7 @@ Jake VanderPlas
Jakob van Santen
Jakub Mitoraj
James Bourbeau
James Frost
Jan Balster
Janne Vanhala
Jason R. Coombs
@ -257,6 +259,7 @@ Marc Bresson
Marco Gorelli
Mark Abramowitz
Mark Dickinson
Mark Vong
Marko Pacak
Markus Unterwaditzer
Martijn Faassen
@ -288,6 +291,7 @@ Mike Lundy
Milan Lesnek
Miro Hrončok
mrbean-bremen
Nathan Goldbaum
Nathaniel Compton
Nathaniel Waisbrot
Ned Batchelder
@ -297,6 +301,7 @@ Nicholas Devenish
Nicholas Murphy
Niclas Olofsson
Nicolas Delaby
Nico Vidal
Nikolay Kondratyev
Nipunn Koorapati
Oleg Pidsadnyi
@ -357,6 +362,7 @@ Sadra Barikbin
Saiprasad Kale
Samuel Colvin
Samuel Dion-Girardeau
Samuel Jirovec
Samuel Searles-Bryant
Samuel Therrien (Avasam)
Samuele Pedroni
@ -440,6 +446,7 @@ Yao Xiao
Yoav Caspi
Yuliang Shao
Yusuke Kadowaki
Yutian Li
Yuval Shimon
Zac Hatfield-Dodds
Zachary Kneupper

View File

@ -1,14 +1,10 @@
============================
Contribution getting started
Contributing
============================
Contributions are highly welcomed and appreciated. Every little bit of help counts,
so do not hesitate!
.. contents::
:depth: 2
:backlinks: none
.. _submitfeedback:
@ -128,7 +124,7 @@ For example:
Submitting Plugins to pytest-dev
--------------------------------
Pytest development of the core, some plugins and support code happens
Development of the pytest core, support code, and some plugins happens
in repositories living under the ``pytest-dev`` organisations:
- `pytest-dev on GitHub <https://github.com/pytest-dev>`_

View File

@ -1,5 +0,0 @@
:func:`pytest.importorskip` will now issue a warning if the module could be found, but raised :class:`ImportError` instead of :class:`ModuleNotFoundError`.
The warning can be suppressed by passing ``exc_type=ImportError`` to :func:`pytest.importorskip`.
See :ref:`import-or-skip-import-error` for details.

View File

@ -1 +0,0 @@
For ``unittest``-based tests, exceptions during class cleanup (as raised by functions registered with :meth:`TestCase.addClassCleanup <unittest.TestCase.addClassCleanup>`) are now reported instead of silently failing.

View File

@ -1 +0,0 @@
Text is no longer truncated in the ``short test summary info`` section when ``-vv`` is given.

View File

@ -1 +0,0 @@
Added support for reading command line arguments from a file using the prefix character ``@``, like e.g.: ``pytest @tests.txt``. The file must have one argument per line.

View File

@ -1,4 +0,0 @@
Fixed a regression in pytest 8.0.0 where test classes containing ``setup_method`` and tests using ``@staticmethod`` or ``@classmethod`` would crash with ``AttributeError: 'NoneType' object has no attribute 'setup_method'``.
Now the :attr:`request.instance <pytest.FixtureRequest.instance>` attribute of tests using ``@staticmethod`` and ``@classmethod`` is no longer ``None``, but a fresh instance of the class, like in non-static methods.
Previously it was ``None``, and all fixtures of such tests would share a single ``self``.

View File

@ -1,12 +0,0 @@
A deprecation warning is now raised when implementations of one of the following hooks request a deprecated ``py.path.local`` parameter instead of the ``pathlib.Path`` parameter which replaced it:
- :hook:`pytest_ignore_collect` - the ``path`` parameter - use ``collection_path`` instead.
- :hook:`pytest_collect_file` - the ``path`` parameter - use ``file_path`` instead.
- :hook:`pytest_pycollect_makemodule` - the ``path`` parameter - use ``module_path`` instead.
- :hook:`pytest_report_header` - the ``startdir`` parameter - use ``start_path`` instead.
- :hook:`pytest_report_collectionfinish` - the ``startdir`` parameter - use ``start_path`` instead.
The replacement parameters are available since pytest 7.0.0.
The old parameters will be removed in pytest 9.0.0.
See :ref:`legacy-path-hooks-deprecated` for more details.

View File

@ -1 +0,0 @@
``pluggy>=1.5.0`` is now required.

View File

@ -1 +0,0 @@
Improve namespace packages detection when :confval:`consider_namespace_packages` is enabled, covering more situations (like editable installs).

View File

@ -1 +0,0 @@
Fix fixtures adding their finalizer multiple times to fixtures they request, causing unreliable and non-intuitive teardown ordering in some instances.

View File

@ -1 +0,0 @@
cache: create cache directory supporting files (``CACHEDIR.TAG``, ``.gitignore``, etc.) in a temporary directory to provide atomic semantics.

View File

@ -1 +0,0 @@
Fixed a bug with ``--importmode=importlib`` and ``--doctest-modules`` where child modules did not appear as attributes in parent modules.

View File

@ -0,0 +1 @@
Fix crash with `assert testcase is not None` assertion failure when re-running unittest tests using plugins like pytest-rerunfailures. Regressed in 8.2.2.

View File

@ -1 +0,0 @@
Fix some instances where teardown of higher-scoped fixtures was not happening in the reverse order they were initialized in.

View File

@ -1 +0,0 @@
Added :envvar:`PYTEST_VERSION` environment variable which is defined at the start of the pytest session and undefined afterwards. It contains the value of ``pytest.__version__``, and among other things can be used to easily check if code is running from within a pytest run.

View File

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -0,0 +1,7 @@
<style>
.logo {text-align: center;}
</style>
<a class="logo" href="{{ pathto('contents') }}">
<img src="{{ pathto('_static/pytest1.png', 1) }}" width="70%" height="70%" text="Pytest Logo"/>
</a>

View File

@ -1,14 +0,0 @@
{#
basic/searchbox.html with heading removed.
#}
{%- if pagename != "search" and builder != "singlehtml" %}
<div id="searchbox" style="display: none" role="search">
<div class="searchformwrapper">
<form class="search" action="{{ pathto('search') }}" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="{{ _('Go') }}" />
</form>
</div>
</div>
<script>document.getElementById('searchbox').style.display = "block"</script>
{%- endif %}

View File

@ -0,0 +1,7 @@
<style>
ul {list-style: none;}
li {margin: 0.4em 0;}
@media (min-width: 46em) {
#features {width: 50%;}
}
</style>

View File

@ -6,6 +6,9 @@ Release announcements
:maxdepth: 2
release-8.2.2
release-8.2.1
release-8.2.0
release-8.1.2
release-8.1.1
release-8.1.0

View File

@ -62,7 +62,7 @@ New Features
- new "-q" option which decreases verbosity and prints a more
nose/unittest-style "dot" output.
- many many more detailed improvements details
- many, many, more detailed improvements details
Fixes
-----------------------
@ -109,7 +109,7 @@ Important Notes
in conftest.py files. They will cause nothing special.
- removed support for calling the pre-1.0 collection API of "run()" and "join"
- removed reading option values from conftest.py files or env variables.
This can now be done much much better and easier through the ini-file
This can now be done much, much, better and easier through the ini-file
mechanism and the "addopts" entry in particular.
- removed the "disabled" attribute in test classes. Use the skipping
and pytestmark mechanism to skip or xfail a test class.

View File

@ -4,7 +4,7 @@ pytest-2.2.2: bug fixes
pytest-2.2.2 (updated to 2.2.3 to fix packaging issues) is a minor
backward-compatible release of the versatile py.test testing tool. It
contains bug fixes and a few refinements particularly to reporting with
"--collectonly", see below for betails.
"--collectonly", see below for details.
For general information see here:

View File

@ -181,7 +181,7 @@ Bug fixes:
partially failed (finalizers would not always be called before)
- fix issue320 - fix class scope for fixtures when mixed with
module-level functions. Thanks Anatloy Bubenkoff.
module-level functions. Thanks Anatoly Bubenkoff.
- you can specify "-q" or "-qq" to get different levels of "quieter"
reporting (thanks Katarzyna Jachim)

View File

@ -83,7 +83,7 @@ holger krekel
Thanks Ralph Schmitt for the precise failure example.
- fix issue244 by implementing special index for parameters to only use
indices for paramentrized test ids
indices for parametrized test ids
- fix issue287 by running all finalizers but saving the exception
from the first failing finalizer and re-raising it so teardown will

View File

@ -73,7 +73,7 @@ holger krekel
- cleanup setup.py a bit and specify supported versions. Thanks Jurko
Gospodnetic for the PR.
- change XPASS colour to yellow rather then red when tests are run
- change XPASS colour to yellow rather than red when tests are run
with -v.
- fix issue473: work around mock putting an unbound method into a class

View File

@ -55,7 +55,7 @@ holger krekel
github. See https://pytest.org/en/stable/contributing.html .
Thanks to Anatoly for pushing and initial work on this.
- fix issue650: new option ``--docttest-ignore-import-errors`` which
- fix issue650: new option ``--doctest-ignore-import-errors`` which
will turn import errors in doctests into skips. Thanks Charles Cloud
for the complete PR.

View File

@ -0,0 +1,43 @@
pytest-8.2.0
=======================================
The pytest team is proud to announce the 8.2.0 release!
This release contains new features, improvements, and bug fixes,
the full list of changes is available in the changelog:
https://docs.pytest.org/en/stable/changelog.html
For complete documentation, please visit:
https://docs.pytest.org/en/stable/
As usual, you can upgrade from PyPI via:
pip install -U pytest
Thanks to all of the contributors to this release:
* Bruno Oliveira
* Daniel Miller
* Florian Bruhin
* HolyMagician03-UMich
* John Litborn
* Levon Saldamli
* Linghao Zhang
* Manuel López-Ibáñez
* Pierre Sassoulas
* Ran Benita
* Ronny Pfannschmidt
* Sebastian Meyer
* Shekhar verma
* Tamir Duberstein
* Tobias Stoeckmann
* dj
* jakkdl
* poulami-sau
* tserg
Happy testing,
The pytest Development Team

View File

@ -0,0 +1,19 @@
pytest-8.2.1
=======================================
pytest 8.2.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:
* Bruno Oliveira
* Ran Benita
Happy testing,
The pytest Development Team

View File

@ -0,0 +1,19 @@
pytest-8.2.2
=======================================
pytest 8.2.2 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
Thanks to all of the contributors to this release:
* Bruno Oliveira
* Ran Benita
Happy testing,
The pytest Development Team

View File

@ -49,7 +49,7 @@ place on 20th, 21st, 22nd, 24th and 25th. On the 23rd we took a break
day for some hot hiking in the Black Forest.
Sprint activity was organised heavily around pairing, with plenty of group
discusssions to take advantage of the high bandwidth, and lightning talks
discussions to take advantage of the high bandwidth, and lightning talks
as well.

View File

@ -5,30 +5,26 @@ Backwards Compatibility Policy
.. versionadded: 6.0
pytest is actively evolving and is a project that has been decades in the making,
we keep learning about new and better structures to express different details about testing.
Pytest is an actively evolving project that has been decades in the making.
We keep learning about new and better structures to express different details about testing.
While we implement those modifications we try to ensure an easy transition and don't want to impose unnecessary churn on our users and community/plugin authors.
While we implement those modifications, we try to ensure an easy transition and don't want to impose unnecessary churn on our users and community/plugin authors.
As of now, pytest considers multiple types of backward compatibility transitions:
a) trivial: APIs which trivially translate to the new mechanism,
and do not cause problematic changes.
a) trivial: APIs that trivially translate to the new mechanism and do not cause problematic changes.
We try to support those indefinitely while encouraging users to switch to newer/better mechanisms through documentation.
We try to support those indefinitely while encouraging users to switch to newer or better mechanisms through documentation.
b) transitional: the old and new API don't conflict
and we can help users transition by using warnings, while supporting both for a prolonged time.
b) transitional: the old and new APIs don't conflict, and we can help users transition by using warnings while supporting both for a prolonged period of time.
We will only start the removal of deprecated functionality in major releases (e.g. if we deprecate something in 3.0 we will start to remove it in 4.0), and keep it around for at least two minor releases (e.g. if we deprecate something in 3.9 and 4.0 is the next release, we start to remove it in 5.0, not in 4.0).
We will only start the removal of deprecated functionality in major releases (e.g., if we deprecate something in 3.0, we will start to remove it in 4.0), and keep it around for at least two minor releases (e.g., if we deprecate something in 3.9 and 4.0 is the next release, we start to remove it in 5.0, not in 4.0).
A deprecated feature scheduled to be removed in major version X will use the warning class `PytestRemovedInXWarning` (a subclass of :class:`~pytest.PytestDeprecationWarning`).
When the deprecation expires (e.g. 4.0 is released), we won't remove the deprecated functionality immediately, but will use the standard warning filters to turn `PytestRemovedInXWarning` (e.g. `PytestRemovedIn4Warning`) into **errors** by default. This approach makes it explicit that removal is imminent, and still gives you time to turn the deprecated feature into a warning instead of an error so it can be dealt with in your own time. In the next minor release (e.g. 4.1), the feature will be effectively removed.
When the deprecation expires (e.g., 4.0 is released), we won't remove the deprecated functionality immediately but will use the standard warning filters to turn `PytestRemovedInXWarning` (e.g., `PytestRemovedIn4Warning`) into **errors** by default. This approach makes it explicit that removal is imminent and still gives you time to turn the deprecated feature into a warning instead of an error so it can be dealt with in your own time. In the next minor release (e.g., 4.1), the feature will be effectively removed.
c) true breakage: should only be considered when normal transition is unreasonably unsustainable and would offset important development/features by years.
In addition, they should be limited to APIs where the number of actual users is very small (for example only impacting some plugins), and can be coordinated with the community in advance.
c) True breakage should only be considered when a normal transition is unreasonably unsustainable and would offset important developments or features by years. In addition, they should be limited to APIs where the number of actual users is very small (for example, only impacting some plugins) and can be coordinated with the community in advance.
Examples for such upcoming changes:
@ -62,11 +58,11 @@ Focus primary on smooth transition - stance (pre 6.0)
Keeping backwards compatibility has a very high priority in the pytest project. Although we have deprecated functionality over the years, most of it is still supported. All deprecations in pytest were done because simpler or more efficient ways of accomplishing the same tasks have emerged, making the old way of doing things unnecessary.
With the pytest 3.0 release we introduced a clear communication scheme for when we will actually remove the old busted joint and politely ask you to use the new hotness instead, while giving you enough time to adjust your tests or raise concerns if there are valid reasons to keep deprecated functionality around.
With the pytest 3.0 release, we introduced a clear communication scheme for when we will actually remove the old busted joint and politely ask you to use the new hotness instead, while giving you enough time to adjust your tests or raise concerns if there are valid reasons to keep deprecated functionality around.
To communicate changes we issue deprecation warnings using a custom warning hierarchy (see :ref:`internal-warnings`). These warnings may be suppressed using the standard means: ``-W`` command-line flag or ``filterwarnings`` ini options (see :ref:`warnings`), but we suggest to use these sparingly and temporarily, and heed the warnings when possible.
To communicate changes, we issue deprecation warnings using a custom warning hierarchy (see :ref:`internal-warnings`). These warnings may be suppressed using the standard means: ``-W`` command-line flag or ``filterwarnings`` ini options (see :ref:`warnings`), but we suggest to use these sparingly and temporarily, and heed the warnings when possible.
We will only start the removal of deprecated functionality in major releases (e.g. if we deprecate something in 3.0 we will start to remove it in 4.0), and keep it around for at least two minor releases (e.g. if we deprecate something in 3.9 and 4.0 is the next release, we start to remove it in 5.0, not in 4.0).
We will only start the removal of deprecated functionality in major releases (e.g. if we deprecate something in 3.0, we will start to remove it in 4.0), and keep it around for at least two minor releases (e.g. if we deprecate something in 3.9 and 4.0 is the next release, we start to remove it in 5.0, not in 4.0).
When the deprecation expires (e.g. 4.0 is released), we won't remove the deprecated functionality immediately, but will use the standard warning filters to turn them into **errors** by default. This approach makes it explicit that removal is imminent, and still gives you time to turn the deprecated feature into a warning instead of an error so it can be dealt with in your own time. In the next minor release (e.g. 4.1), the feature will be effectively removed.

View File

@ -22,7 +22,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
cachedir: .pytest_cache
rootdir: /home/sweet/project
collected 0 items
cache -- .../_pytest/cacheprovider.py:527
cache -- .../_pytest/cacheprovider.py:560
Return a cache object that can persist state between testing sessions.
cache.get(key, default)
@ -33,7 +33,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
Values can be any object handled by the json stdlib module.
capsysbinary -- .../_pytest/capture.py:1008
capsysbinary -- .../_pytest/capture.py:1003
Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``.
The captured output is made available via ``capsysbinary.readouterr()``
@ -50,7 +50,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
captured = capsysbinary.readouterr()
assert captured.out == b"hello\n"
capfd -- .../_pytest/capture.py:1035
capfd -- .../_pytest/capture.py:1030
Enable text capturing of writes to file descriptors ``1`` and ``2``.
The captured output is made available via ``capfd.readouterr()`` method
@ -67,7 +67,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
captured = capfd.readouterr()
assert captured.out == "hello\n"
capfdbinary -- .../_pytest/capture.py:1062
capfdbinary -- .../_pytest/capture.py:1057
Enable bytes capturing of writes to file descriptors ``1`` and ``2``.
The captured output is made available via ``capfd.readouterr()`` method
@ -84,7 +84,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
captured = capfdbinary.readouterr()
assert captured.out == b"hello\n"
capsys -- .../_pytest/capture.py:981
capsys -- .../_pytest/capture.py:976
Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``.
The captured output is made available via ``capsys.readouterr()`` method
@ -101,7 +101,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
captured = capsys.readouterr()
assert captured.out == "hello\n"
doctest_namespace [session scope] -- .../_pytest/doctest.py:737
doctest_namespace [session scope] -- .../_pytest/doctest.py:738
Fixture that returns a :py:class:`dict` that will be injected into the
namespace of doctests.
@ -115,7 +115,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
For more details: :ref:`doctest_namespace`.
pytestconfig [session scope] -- .../_pytest/fixtures.py:1346
pytestconfig [session scope] -- .../_pytest/fixtures.py:1338
Session-scoped fixture that returns the session's :class:`pytest.Config`
object.
@ -125,7 +125,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
if pytestconfig.getoption("verbose") > 0:
...
record_property -- .../_pytest/junitxml.py:283
record_property -- .../_pytest/junitxml.py:284
Add extra properties to the calling test.
User properties become part of the test report and are available to the
@ -139,13 +139,13 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
def test_function(record_property):
record_property("example_key", 1)
record_xml_attribute -- .../_pytest/junitxml.py:306
record_xml_attribute -- .../_pytest/junitxml.py:307
Add extra xml attributes to the tag for the calling test.
The fixture is callable with ``name, value``. The value is
automatically XML-encoded.
record_testsuite_property [session scope] -- .../_pytest/junitxml.py:344
record_testsuite_property [session scope] -- .../_pytest/junitxml.py:345
Record a new ``<property>`` tag as child of the root ``<testsuite>``.
This is suitable to writing global information regarding the entire test
@ -192,7 +192,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
.. _legacy_path: https://py.readthedocs.io/en/latest/path.html
caplog -- .../_pytest/logging.py:601
caplog -- .../_pytest/logging.py:602
Access and control log capturing.
Captured logs are available through the following properties/methods::
@ -203,7 +203,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
* caplog.record_tuples -> list of (logger_name, level, message) tuples
* caplog.clear() -> clear captured records and formatted log output string
monkeypatch -- .../_pytest/monkeypatch.py:32
monkeypatch -- .../_pytest/monkeypatch.py:33
A convenient fixture for monkey-patching.
The fixture provides these methods to modify objects, dictionaries, or
@ -227,16 +227,16 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
To undo modifications done by the fixture in a contained scope,
use :meth:`context() <pytest.MonkeyPatch.context>`.
recwarn -- .../_pytest/recwarn.py:31
recwarn -- .../_pytest/recwarn.py:32
Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions.
See https://docs.pytest.org/en/latest/how-to/capture-warnings.html for information
on warning categories.
tmp_path_factory [session scope] -- .../_pytest/tmpdir.py:241
tmp_path_factory [session scope] -- .../_pytest/tmpdir.py:242
Return a :class:`pytest.TempPathFactory` instance for the test session.
tmp_path -- .../_pytest/tmpdir.py:256
tmp_path -- .../_pytest/tmpdir.py:257
Return a temporary directory path object which is unique to each test
function invocation, created as a sub directory of the base temporary
directory.

View File

@ -28,6 +28,166 @@ with advance notice in the **Deprecations** section of releases.
.. towncrier release notes start
pytest 8.2.2 (2024-06-04)
=========================
Bug Fixes
---------
- `#12355 <https://github.com/pytest-dev/pytest/issues/12355>`_: Fix possible catastrophic performance slowdown on a certain parametrization pattern involving many higher-scoped parameters.
- `#12367 <https://github.com/pytest-dev/pytest/issues/12367>`_: Fix a regression in pytest 8.2.0 where unittest class instances (a fresh one is created for each test) were not released promptly on test teardown but only on session teardown.
- `#12381 <https://github.com/pytest-dev/pytest/issues/12381>`_: Fix possible "Directory not empty" crashes arising from concurent cache dir (``.pytest_cache``) creation. Regressed in pytest 8.2.0.
Improved Documentation
----------------------
- `#12290 <https://github.com/pytest-dev/pytest/issues/12290>`_: Updated Sphinx theme to use Furo instead of Flask, enabling Dark mode theme.
- `#12356 <https://github.com/pytest-dev/pytest/issues/12356>`_: Added a subsection to the documentation for debugging flaky tests to mention
lack of thread safety in pytest as a possible source of flakyness.
- `#12363 <https://github.com/pytest-dev/pytest/issues/12363>`_: The documentation webpages now links to a canonical version to reduce outdated documentation in search engine results.
pytest 8.2.1 (2024-05-19)
=========================
Improvements
------------
- `#12334 <https://github.com/pytest-dev/pytest/issues/12334>`_: Support for Python 3.13 (beta1 at the time of writing).
Bug Fixes
---------
- `#12120 <https://github.com/pytest-dev/pytest/issues/12120>`_: Fix `PermissionError` crashes arising from directories which are not selected on the command-line.
- `#12191 <https://github.com/pytest-dev/pytest/issues/12191>`_: Keyboard interrupts and system exits are now properly handled during the test collection.
- `#12300 <https://github.com/pytest-dev/pytest/issues/12300>`_: Fixed handling of 'Function not implemented' error under squashfuse_ll, which is a different way to say that the mountpoint is read-only.
- `#12308 <https://github.com/pytest-dev/pytest/issues/12308>`_: Fix a regression in pytest 8.2.0 where the permissions of automatically-created ``.pytest_cache`` directories became ``rwx------`` instead of the expected ``rwxr-xr-x``.
Trivial/Internal Changes
------------------------
- `#12333 <https://github.com/pytest-dev/pytest/issues/12333>`_: pytest releases are now attested using the recent `Artifact Attestation <https://github.blog/2024-05-02-introducing-artifact-attestations-now-in-public-beta/>`_ support from GitHub, allowing users to verify the provenance of pytest's sdist and wheel artifacts.
pytest 8.2.0 (2024-04-27)
=========================
Breaking Changes
----------------
- `#12089 <https://github.com/pytest-dev/pytest/pull/12089>`_: pytest now requires that :class:`unittest.TestCase` subclasses can be instantiated freely using ``MyTestCase('runTest')``.
If the class doesn't allow this, you may see an error during collection such as ``AttributeError: 'MyTestCase' object has no attribute 'runTest'``.
Classes which do not override ``__init__``, or do not access the test method in ``__init__`` using ``getattr`` or similar, are unaffected.
Classes which do should take care to not crash when ``"runTest"`` is given, as is shown in `unittest.TestCases's implementation <https://github.com/python/cpython/blob/51aefc5bf907ddffaaf083ded0de773adcdf08c8/Lib/unittest/case.py#L419-L426>`_.
Alternatively, consider using :meth:`setUp <unittest.TestCase.setUp>` instead of ``__init__``.
If you run into this issue using ``tornado.AsyncTestCase``, please see `issue 12263 <https://github.com/pytest-dev/pytest/issues/12263>`_.
If you run into this issue using an abstract ``TestCase`` subclass, please see `issue 12275 <https://github.com/pytest-dev/pytest/issues/12275>`_.
Historical note: the effect of this change on custom TestCase implementations was not properly considered initially, this is why it was done in a minor release. We apologize for the inconvenience.
Deprecations
------------
- `#12069 <https://github.com/pytest-dev/pytest/issues/12069>`_: A deprecation warning is now raised when implementations of one of the following hooks request a deprecated ``py.path.local`` parameter instead of the ``pathlib.Path`` parameter which replaced it:
- :hook:`pytest_ignore_collect` - the ``path`` parameter - use ``collection_path`` instead.
- :hook:`pytest_collect_file` - the ``path`` parameter - use ``file_path`` instead.
- :hook:`pytest_pycollect_makemodule` - the ``path`` parameter - use ``module_path`` instead.
- :hook:`pytest_report_header` - the ``startdir`` parameter - use ``start_path`` instead.
- :hook:`pytest_report_collectionfinish` - the ``startdir`` parameter - use ``start_path`` instead.
The replacement parameters are available since pytest 7.0.0.
The old parameters will be removed in pytest 9.0.0.
See :ref:`legacy-path-hooks-deprecated` for more details.
Features
--------
- `#11871 <https://github.com/pytest-dev/pytest/issues/11871>`_: Added support for reading command line arguments from a file using the prefix character ``@``, like e.g.: ``pytest @tests.txt``. The file must have one argument per line.
See :ref:`Read arguments from file <args-from-file>` for details.
Improvements
------------
- `#11523 <https://github.com/pytest-dev/pytest/issues/11523>`_: :func:`pytest.importorskip` will now issue a warning if the module could be found, but raised :class:`ImportError` instead of :class:`ModuleNotFoundError`.
The warning can be suppressed by passing ``exc_type=ImportError`` to :func:`pytest.importorskip`.
See :ref:`import-or-skip-import-error` for details.
- `#11728 <https://github.com/pytest-dev/pytest/issues/11728>`_: For ``unittest``-based tests, exceptions during class cleanup (as raised by functions registered with :meth:`TestCase.addClassCleanup <unittest.TestCase.addClassCleanup>`) are now reported instead of silently failing.
- `#11777 <https://github.com/pytest-dev/pytest/issues/11777>`_: Text is no longer truncated in the ``short test summary info`` section when ``-vv`` is given.
- `#12112 <https://github.com/pytest-dev/pytest/issues/12112>`_: Improved namespace packages detection when :confval:`consider_namespace_packages` is enabled, covering more situations (like editable installs).
- `#9502 <https://github.com/pytest-dev/pytest/issues/9502>`_: Added :envvar:`PYTEST_VERSION` environment variable which is defined at the start of the pytest session and undefined afterwards. It contains the value of ``pytest.__version__``, and among other things can be used to easily check if code is running from within a pytest run.
Bug Fixes
---------
- `#12065 <https://github.com/pytest-dev/pytest/issues/12065>`_: Fixed a regression in pytest 8.0.0 where test classes containing ``setup_method`` and tests using ``@staticmethod`` or ``@classmethod`` would crash with ``AttributeError: 'NoneType' object has no attribute 'setup_method'``.
Now the :attr:`request.instance <pytest.FixtureRequest.instance>` attribute of tests using ``@staticmethod`` and ``@classmethod`` is no longer ``None``, but a fresh instance of the class, like in non-static methods.
Previously it was ``None``, and all fixtures of such tests would share a single ``self``.
- `#12135 <https://github.com/pytest-dev/pytest/issues/12135>`_: Fixed issue where fixtures adding their finalizer multiple times to fixtures they request would cause unreliable and non-intuitive teardown ordering in some instances.
- `#12194 <https://github.com/pytest-dev/pytest/issues/12194>`_: Fixed a bug with ``--importmode=importlib`` and ``--doctest-modules`` where child modules did not appear as attributes in parent modules.
- `#1489 <https://github.com/pytest-dev/pytest/issues/1489>`_: Fixed some instances where teardown of higher-scoped fixtures was not happening in the reverse order they were initialized in.
Trivial/Internal Changes
------------------------
- `#12069 <https://github.com/pytest-dev/pytest/issues/12069>`_: ``pluggy>=1.5.0`` is now required.
- `#12167 <https://github.com/pytest-dev/pytest/issues/12167>`_: :ref:`cache <cache>`: create supporting files (``CACHEDIR.TAG``, ``.gitignore``, etc.) in a temporary directory to provide atomic semantics.
pytest 8.1.2 (2024-04-26)
=========================
@ -69,7 +229,7 @@ Improvements
- `#11311 <https://github.com/pytest-dev/pytest/issues/11311>`_: When using ``--override-ini`` for paths in invocations without a configuration file defined, the current working directory is used
as the relative directory.
Previoulsy this would raise an :class:`AssertionError`.
Previously this would raise an :class:`AssertionError`.
- `#11475 <https://github.com/pytest-dev/pytest/issues/11475>`_: :ref:`--import-mode=importlib <import-mode-importlib>` now tries to import modules using the standard import mechanism (but still without changing :py:data:`sys.path`), falling back to importing modules directly only if that fails.
@ -1312,7 +1472,7 @@ Deprecations
``__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
Note that a deprecation 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.
@ -1354,7 +1514,7 @@ Breaking Changes
- `#7259 <https://github.com/pytest-dev/pytest/issues/7259>`_: The :ref:`Node.reportinfo() <non-python tests>` function first return value type has been expanded from `py.path.local | str` to `os.PathLike[str] | str`.
Most plugins which refer to `reportinfo()` only define it as part of a custom :class:`pytest.Item` implementation.
Since `py.path.local` is an `os.PathLike[str]`, these plugins are unaffacted.
Since `py.path.local` is an `os.PathLike[str]`, these plugins are unaffected.
Plugins and users which call `reportinfo()`, use the first return value and interact with it as a `py.path.local`, would need to adjust by calling `py.path.local(fspath)`.
Although preferably, avoid the legacy `py.path.local` and use `pathlib.Path`, or use `item.location` or `item.path`, instead.
@ -1862,7 +2022,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 silently use a preexisting ``/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.
@ -2589,7 +2749,7 @@ Features
also changes ``sys.modules`` as a side-effect), which works but has a number of drawbacks, like requiring test modules
that don't live in packages to have unique names (as they need to reside under a unique name in ``sys.modules``).
``--import-mode=importlib`` uses more fine grained import mechanisms from ``importlib`` which don't
``--import-mode=importlib`` uses more fine-grained import mechanisms from ``importlib`` which don't
require pytest to change ``sys.path`` or ``sys.modules`` at all, eliminating much of the drawbacks
of the previous mode.
@ -2606,7 +2766,7 @@ Improvements
------------
- :issue:`4375`: The ``pytest`` command now suppresses the ``BrokenPipeError`` error message that
is printed to stderr when the output of ``pytest`` is piped and and the pipe is
is printed to stderr when the output of ``pytest`` is piped and the pipe is
closed by the piped-to program (common examples are ``less`` and ``head``).
@ -2908,7 +3068,7 @@ Breaking Changes
This hook has been marked as deprecated and not been even called by pytest for over 10 years now.
- :issue:`6673`: Reversed / fix meaning of "+/-" in error diffs. "-" means that sth. expected is missing in the result and "+" means that there are unexpected extras in the result.
- :issue:`6673`: Reversed / fix meaning of "+/-" in error diffs. "-" means that something expected is missing in the result and "+" means that there are unexpected extras in the result.
- :issue:`6737`: The ``cached_result`` attribute of ``FixtureDef`` is now set to ``None`` when
@ -4513,7 +4673,7 @@ Bug Fixes
Improved Documentation
----------------------
- :issue:`4974`: Update docs for ``pytest_cmdline_parse`` hook to note availability liminations
- :issue:`4974`: Update docs for ``pytest_cmdline_parse`` hook to note availability limitations
@ -6371,7 +6531,7 @@ Features
Bug Fixes
---------
- Fix hanging pexpect test on MacOS by using flush() instead of wait().
- Fix hanging pexpect test on macOS by using flush() instead of wait().
(:issue:`2022`)
- Fix restoring Python state after in-process pytest runs with the
@ -6419,7 +6579,7 @@ Trivial/Internal Changes
------------------------
- Show a simple and easy error when keyword expressions trigger a syntax error
(for example, ``"-k foo and import"`` will show an error that you can not use
(for example, ``"-k foo and import"`` will show an error that you cannot use
the ``import`` keyword in expressions). (:issue:`2953`)
- Change parametrized automatic test id generation to use the ``__name__``
@ -8195,7 +8355,7 @@ time or change existing behaviors in order to make them less surprising/more use
one will also have a "reprec" attribute with the recorded events/reports.
- fix monkeypatch.setattr("x.y", raising=False) to actually not raise
if "y" is not a pre-existing attribute. Thanks Florian Bruhin.
if "y" is not a preexisting attribute. Thanks Florian Bruhin.
- fix issue741: make running output from testdir.run copy/pasteable
Thanks Bruno Oliveira.
@ -8251,7 +8411,7 @@ time or change existing behaviors in order to make them less surprising/more use
- fix issue854: autouse yield_fixtures defined as class members of
unittest.TestCase subclasses now work as expected.
Thannks xmo-odoo for the report and Bruno Oliveira for the PR.
Thanks xmo-odoo for the report and Bruno Oliveira for the PR.
- fix issue833: --fixtures now shows all fixtures of collected test files, instead of just the
fixtures declared on the first one.
@ -8355,7 +8515,7 @@ time or change existing behaviors in order to make them less surprising/more use
github. See https://pytest.org/en/stable/contributing.html .
Thanks to Anatoly for pushing and initial work on this.
- fix issue650: new option ``--docttest-ignore-import-errors`` which
- fix issue650: new option ``--doctest-ignore-import-errors`` which
will turn import errors in doctests into skips. Thanks Charles Cloud
for the complete PR.
@ -8543,7 +8703,7 @@ time or change existing behaviors in order to make them less surprising/more use
- cleanup setup.py a bit and specify supported versions. Thanks Jurko
Gospodnetic for the PR.
- change XPASS colour to yellow rather then red when tests are run
- change XPASS colour to yellow rather than red when tests are run
with -v.
- fix issue473: work around mock putting an unbound method into a class
@ -8716,7 +8876,7 @@ time or change existing behaviors in order to make them less surprising/more use
Thanks Ralph Schmitt for the precise failure example.
- fix issue244 by implementing special index for parameters to only use
indices for paramentrized test ids
indices for parametrized test ids
- fix issue287 by running all finalizers but saving the exception
from the first failing finalizer and re-raising it so teardown will
@ -8724,7 +8884,7 @@ time or change existing behaviors in order to make them less surprising/more use
it might be the cause for other finalizers to fail.
- fix ordering when mock.patch or other standard decorator-wrappings
are used with test methods. This fixues issue346 and should
are used with test methods. This fixes issue346 and should
help with random "xdist" collection failures. Thanks to
Ronny Pfannschmidt and Donald Stufft for helping to isolate it.
@ -8981,7 +9141,7 @@ Bug fixes:
partially failed (finalizers would not always be called before)
- fix issue320 - fix class scope for fixtures when mixed with
module-level functions. Thanks Anatloy Bubenkoff.
module-level functions. Thanks Anatoly Bubenkoff.
- you can specify "-q" or "-qq" to get different levels of "quieter"
reporting (thanks Katarzyna Jachim)
@ -9403,7 +9563,7 @@ Bug fixes:
unexpected exceptions
- fix issue47: timing output in junitxml for test cases is now correct
- fix issue48: typo in MarkInfo repr leading to exception
- fix issue49: avoid confusing error when initizaliation partially fails
- fix issue49: avoid confusing error when initialization partially fails
- fix issue44: env/username expansion for junitxml file path
- show releaselevel information in test runs for pypy
- reworked doc pages for better navigation and PDF generation
@ -9528,7 +9688,7 @@ Bug fixes:
collection-before-running semantics were not
setup as with pytest 1.3.4. Note, however, that
the recommended and much cleaner way to do test
parametraization remains the "pytest_generate_tests"
parameterization remains the "pytest_generate_tests"
mechanism, see the docs.
2.0.0 (2010-11-25)

View File

@ -15,9 +15,7 @@
#
# The full version, including alpha/beta/rc tags.
# The short X.Y version.
import os
import shutil
import sys
from textwrap import dedent
from typing import TYPE_CHECKING
@ -65,7 +63,6 @@ latex_elements = {
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = [
"pallets_sphinx_themes",
"pygments_pytest",
"sphinx.ext.autodoc",
"sphinx.ext.autosummary",
@ -140,10 +137,6 @@ add_module_names = False
# output. They are ignored by default.
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
@ -216,12 +209,9 @@ nitpick_ignore = [
# -- Options for HTML output ---------------------------------------------------
sys.path.append(os.path.abspath("_themes"))
html_theme_path = ["_themes"]
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = "flask"
html_theme = "furo"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
@ -266,18 +256,24 @@ html_favicon = "img/favicon.png"
html_sidebars = {
"index": [
"slim_searchbox.html",
"sidebar/brand.html",
"sidebar/search.html",
"sidebar/scroll-start.html",
"sidebarintro.html",
"globaltoc.html",
"links.html",
"sourcelink.html",
"sidebar/scroll-end.html",
"style.html",
],
"**": [
"slim_searchbox.html",
"sidebar/brand.html",
"sidebar/search.html",
"sidebar/scroll-start.html",
"globaltoc.html",
"relations.html",
"links.html",
"sourcelink.html",
"sidebar/scroll-end.html",
"style.html",
],
}
@ -316,6 +312,9 @@ html_show_sourcelink = False
# Output file base name for HTML help builder.
htmlhelp_basename = "pytestdoc"
# The base URL which points to the root of the HTML documentation. It is used
# to indicate the location of document using the canonical link relation (#12363).
html_baseurl = "https://docs.pytest.org/en/stable/"
# -- Options for LaTeX output --------------------------------------------------
@ -337,10 +336,6 @@ latex_documents = [
)
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
latex_logo = "img/pytest1.png"
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
# latex_use_parts = False

View File

@ -26,19 +26,12 @@ Contact channels
<https://web.libera.chat/#pytest>`_, or `via Matrix
<https://matrix.to/#/%23pytest:libera.chat>`_).
- private mail to Holger.Krekel at gmail com if you want to communicate sensitive issues
- `merlinux.eu`_ offers pytest and tox-related professional teaching and
consulting.
.. _`pytest issue tracker`: https://github.com/pytest-dev/pytest/issues
.. _`old issue tracker`: https://bitbucket.org/hpk42/py-trunk/issues/
.. _`pytest discussions`: https://github.com/pytest-dev/pytest/discussions
.. _`merlinux.eu`: https://merlinux.eu/
.. _`get an account`:
.. _tetamap: https://tetamap.wordpress.com/

View File

@ -7,10 +7,6 @@ This page lists all pytest features that are currently deprecated or have been r
The objective is to give users a clear rationale why a certain feature has been removed, and what alternatives
should be used instead.
.. contents::
:depth: 3
:local:
Deprecated Features
-------------------
@ -462,7 +458,7 @@ Now :class:`~pytest.Class` collects the test methods directly.
Most plugins which reference ``Instance`` do so in order to ignore or skip it,
using a check such as ``if isinstance(node, Instance): return``.
Such plugins should simply remove consideration of ``Instance`` on pytest>=7.
However, to keep such uses working, a dummy type has been instanted in ``pytest.Instance`` and ``_pytest.python.Instance``,
However, to keep such uses working, a dummy type has been instanced in ``pytest.Instance`` and ``_pytest.python.Instance``,
and importing it emits a deprecation warning. This was removed in pytest 8.

View File

@ -21,7 +21,7 @@ class ManifestDirectory(pytest.Directory):
@pytest.hookimpl
def pytest_collect_directory(path, parent):
# Use our custom collector for directories containing a `mainfest.json` file.
# Use our custom collector for directories containing a `manifest.json` file.
if path.joinpath("manifest.json").is_file():
return ManifestDirectory.from_parent(parent=parent, path=path)
# Otherwise fallback to the standard behavior.

View File

@ -162,7 +162,7 @@ objects, they are still using the default pytest representation:
rootdir: /home/sweet/project
collected 8 items
<Dir parametrize.rst-197>
<Dir parametrize.rst-200>
<Module test_time.py>
<Function test_timedistance_v0[a0-b0-expected0]>
<Function test_timedistance_v0[a1-b1-expected1]>
@ -239,7 +239,7 @@ If you just collect tests you'll also nicely see 'advanced' and 'basic' as varia
rootdir: /home/sweet/project
collected 4 items
<Dir parametrize.rst-197>
<Dir parametrize.rst-200>
<Module test_scenarios.py>
<Class TestSampleWithScenarios>
<Function test_demo1[basic]>
@ -318,7 +318,7 @@ Let's first see how it looks like at collection time:
rootdir: /home/sweet/project
collected 2 items
<Dir parametrize.rst-197>
<Dir parametrize.rst-200>
<Module test_backends.py>
<Function test_db_initialized[d1]>
<Function test_db_initialized[d2]>

View File

@ -152,7 +152,7 @@ The test collection would look like this:
configfile: pytest.ini
collected 2 items
<Dir pythoncollection.rst-198>
<Dir pythoncollection.rst-201>
<Module check_myapp.py>
<Class CheckMyApp>
<Function simple_check>
@ -215,7 +215,7 @@ You can always peek at the collection tree without running tests like this:
configfile: pytest.ini
collected 3 items
<Dir pythoncollection.rst-198>
<Dir pythoncollection.rst-201>
<Dir CWD>
<Module pythoncollection.py>
<Function test_function>

View File

@ -18,7 +18,7 @@ System state
Broadly speaking, a flaky test indicates that the test relies on some system state that is not being appropriately controlled - the test environment is not sufficiently isolated. Higher level tests are more likely to be flaky as they rely on more state.
Flaky tests sometimes appear when a test suite is run in parallel (such as use of pytest-xdist). This can indicate a test is reliant on test ordering.
Flaky tests sometimes appear when a test suite is run in parallel (such as use of `pytest-xdist`_). This can indicate a test is reliant on test ordering.
- Perhaps a different test is failing to clean up after itself and leaving behind data which causes the flaky test to fail.
- The flaky test is reliant on data from a previous test that doesn't clean up after itself, and in parallel runs that previous test is not always present
@ -30,9 +30,22 @@ Overly strict assertion
Overly strict assertions can cause problems with floating point comparison as well as timing issues. :func:`pytest.approx` is useful here.
Thread safety
~~~~~~~~~~~~~
Pytest features
^^^^^^^^^^^^^^^
pytest is single-threaded, executing its tests always in the same thread, sequentially, never spawning any threads itself.
Even in case of plugins which run tests in parallel, for example `pytest-xdist`_, usually work by spawning multiple *processes* and running tests in batches, without using multiple threads.
It is of course possible (and common) for tests and fixtures to spawn threads themselves as part of their testing workflow (for example, a fixture that starts a server thread in the background, or a test which executes production code that spawns threads), but some care must be taken:
* Make sure to eventually wait on any spawned threads -- for example at the end of a test, or during the teardown of a fixture.
* Avoid using primitives provided by pytest (:func:`pytest.warns`, :func:`pytest.raises`, etc) from multiple threads, as they are not thread-safe.
If your test suite uses threads and your are seeing flaky test results, do not discount the possibility that the test is implicitly using global state in pytest itself.
Related features
^^^^^^^^^^^^^^^^
Xfail strict
~~~~~~~~~~~~
@ -123,3 +136,6 @@ Resources
* `Flaky Tests at Google and How We Mitigate Them <https://testing.googleblog.com/2016/05/flaky-tests-at-google-and-how-we.html>`_ by John Micco, 2016
* `Where do Google's flaky tests come from? <https://testing.googleblog.com/2017/04/where-do-our-flaky-tests-come-from.html>`_ by Jeff Listfield, 2017
.. _pytest-xdist: https://github.com/pytest-dev/pytest-xdist

View File

@ -8,15 +8,15 @@ pytest import mechanisms and ``sys.path``/``PYTHONPATH``
Import modes
------------
pytest as a testing framework needs to import test modules and ``conftest.py`` files for execution.
pytest as a testing framework that needs to import test modules and ``conftest.py`` files for execution.
Importing files in Python is a non-trivial processes, so aspects of the
Importing files in Python is a non-trivial process, so aspects of the
import process can be controlled through the ``--import-mode`` command-line flag, which can assume
these values:
.. _`import-mode-prepend`:
* ``prepend`` (default): the directory path containing each module will be inserted into the *beginning*
* ``prepend`` (default): The directory path containing each module will be inserted into the *beginning*
of :py:data:`sys.path` if not already there, and then imported with
the :func:`importlib.import_module <importlib.import_module>` function.
@ -34,7 +34,7 @@ these values:
* ``append``: the directory containing each module is appended to the end of :py:data:`sys.path` if not already
there, and imported with :func:`importlib.import_module <importlib.import_module>`.
This better allows to run test modules against installed versions of a package even if the
This better allows users to run test modules against installed versions of a package even if the
package under test has the same import root. For example:
::
@ -45,7 +45,7 @@ these values:
the tests will run against the installed version
of ``pkg_under_test`` when ``--import-mode=append`` is used whereas
with ``prepend`` they would pick up the local version. This kind of confusion is why
with ``prepend``, they would pick up the local version. This kind of confusion is why
we advocate for using :ref:`src-layouts <src-layout>`.
Same as ``prepend``, requires test module names to be unique when the test directory tree is
@ -67,7 +67,7 @@ these values:
are not importable. The recommendation in this case it to place testing utility modules together with the application/library
code, for example ``app.testing.helpers``.
Important: by "test utility modules" we mean functions/classes which are imported by
Important: by "test utility modules", we mean functions/classes which are imported by
other tests directly; this does not include fixtures, which should be placed in ``conftest.py`` files, along
with the test modules, and are discovered automatically by pytest.
@ -76,8 +76,8 @@ these values:
1. Given a certain module path, for example ``tests/core/test_models.py``, derives a canonical name
like ``tests.core.test_models`` and tries to import it.
For non-test modules this will work if they are accessible via :py:data:`sys.path`, so
for example ``.env/lib/site-packages/app/core.py`` will be importable as ``app.core``.
For non-test modules, this will work if they are accessible via :py:data:`sys.path`. So
for example, ``.env/lib/site-packages/app/core.py`` will be importable as ``app.core``.
This is happens when plugins import non-test modules (for example doctesting).
If this step succeeds, the module is returned.

View File

@ -22,7 +22,7 @@ Install ``pytest``
.. code-block:: bash
$ pytest --version
pytest 8.1.2
pytest 8.2.2
.. _`simpletest`:
@ -274,7 +274,7 @@ Continue reading
Check out additional pytest resources to help you customize tests for your unique workflow:
* ":ref:`usage`" for command line invocation examples
* ":ref:`existingtestsuite`" for working with pre-existing tests
* ":ref:`existingtestsuite`" for working with preexisting tests
* ":ref:`mark`" for information on the ``pytest.mark`` mechanism
* ":ref:`fixtures`" for providing a functional baseline to your tests
* ":ref:`plugins`" for managing and writing plugins

View File

@ -1418,7 +1418,7 @@ Running the above tests results in the following test IDs being used:
rootdir: /home/sweet/project
collected 12 items
<Dir fixtures.rst-216>
<Dir fixtures.rst-219>
<Module test_anothersmtp.py>
<Function test_showhelo[smtp.gmail.com]>
<Function test_showhelo[mail.python.org]>

View File

@ -294,9 +294,47 @@ Now if we increase verbosity even more:
test_verbosity_example.py:19: AssertionError
========================= short test summary info ==========================
FAILED test_verbosity_example.py::test_words_fail - AssertionError: asser...
FAILED test_verbosity_example.py::test_numbers_fail - AssertionError: ass...
FAILED test_verbosity_example.py::test_long_text_fail - AssertionError: a...
FAILED test_verbosity_example.py::test_words_fail - AssertionError: assert ['banana', 'apple', 'grapes', 'melon', 'kiwi'] == ['banana', 'apple', 'orange', 'melon', 'kiwi']
At index 2 diff: 'grapes' != 'orange'
Full diff:
[
'banana',
'apple',
- 'orange',
? ^ ^^
+ 'grapes',
? ^ ^ +
'melon',
'kiwi',
]
FAILED test_verbosity_example.py::test_numbers_fail - AssertionError: assert {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4} == {'0': 0, '10': 10, '20': 20, '30': 30, '40': 40}
Common items:
{'0': 0}
Left contains 4 more items:
{'1': 1, '2': 2, '3': 3, '4': 4}
Right contains 4 more items:
{'10': 10, '20': 20, '30': 30, '40': 40}
Full diff:
{
'0': 0,
- '10': 10,
? - -
+ '1': 1,
- '20': 20,
? - -
+ '2': 2,
- '30': 30,
? - -
+ '3': 3,
- '40': 40,
? - -
+ '4': 4,
}
FAILED test_verbosity_example.py::test_long_text_fail - AssertionError: assert 'hello world' in 'Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet '
======================= 3 failed, 1 passed in 0.12s ========================
Notice now that:

View File

@ -1,17 +1,16 @@
:orphan:
.. sidebar:: Next Open Trainings and Events
- `Professional Testing with Python <https://python-academy.com/courses/python_course_testing.html>`_, via `Python Academy <https://www.python-academy.com/>`_ (3 day in-depth training):
* **June 11th to 13th 2024**, Remote
* **March 4th to 6th 2025**, Leipzig, Germany / Remote
- `pytest development sprint <https://github.com/pytest-dev/pytest/discussions/11655>`_, **June 17th -- 22nd 2024**
- pytest tips and tricks for a better testsuite, `Europython 2024 <https://ep2024.europython.eu/>`_, **July 8th -- 14th 2024** (3h), Prague
Also see :doc:`previous talks and blogposts <talks>`.
.. _features:
.. sidebar:: **Next Open Trainings and Events**
- `pytest development sprint <https://github.com/pytest-dev/sprint>`_, **June 17th -- 22nd 2024**, Klaus (AT) / Remote
- `pytest tips and tricks for a better testsuite <https://ep2024.europython.eu/session/pytest-tips-and-tricks-for-a-better-testsuite>`_, at `Europython 2024 <https://ep2024.europython.eu/>`_, **July 8th -- 14th 2024** (3h), Prague (CZ)
- `pytest: Professionelles Testen (nicht nur) für Python <https://pretalx.com/workshoptage-2024/talk/9VUHYB/>`_, at `CH Open Workshoptage <https://workshoptage.ch/>`_, **September 2nd 2024**, HSLU Rotkreuz (CH)
- `Professional Testing with Python <https://python-academy.com/courses/python_course_testing.html>`_, via `Python Academy <https://www.python-academy.com/>`_ (3 day in-depth training), **March 4th -- 6th 2025**, Leipzig (DE) / Remote
Also see :doc:`previous talks and blogposts <talks>`
pytest: helps you write better programs
=======================================
@ -25,7 +24,6 @@ scale to support complex functional testing for applications and libraries.
**PyPI package name**: :pypi:`pytest`
A quick example
---------------

View File

@ -8,7 +8,7 @@ If you used older version of the ``py`` distribution (which
included the py.test command line tool and Python name space)
you accessed helpers and possibly collection classes through
the ``py.test`` Python namespaces. The new ``pytest``
Python module flaty provides the same objects, following
Python module flatly provides the same objects, following
these renaming rules::
py.test.XYZ -> pytest.XYZ

View File

@ -39,7 +39,7 @@ Built-in fixtures
Store and retrieve values across pytest runs.
:fixture:`doctest_namespace`
Provide a dict injected into the docstests namespace.
Provide a dict injected into the doctests namespace.
:fixture:`monkeypatch`
Temporarily modify classes, functions, dictionaries,

View File

@ -7,9 +7,6 @@ API Reference
This page contains the full reference to pytest's API.
.. contents::
:depth: 3
:local:
Constants
---------
@ -59,11 +56,19 @@ pytest.fail
.. autofunction:: pytest.fail(reason, [pytrace=True, msg=None])
.. class:: pytest.fail.Exception
The exception raised by :func:`pytest.fail`.
pytest.skip
~~~~~~~~~~~
.. autofunction:: pytest.skip(reason, [allow_module_level=False, msg=None])
.. class:: pytest.skip.Exception
The exception raised by :func:`pytest.skip`.
.. _`pytest.importorskip ref`:
pytest.importorskip
@ -76,11 +81,19 @@ pytest.xfail
.. autofunction:: pytest.xfail
.. class:: pytest.xfail.Exception
The exception raised by :func:`pytest.xfail`.
pytest.exit
~~~~~~~~~~~
.. autofunction:: pytest.exit(reason, [returncode=None, msg=None])
.. class:: pytest.exit.Exception
The exception raised by :func:`pytest.exit`.
pytest.main
~~~~~~~~~~~
@ -246,9 +259,10 @@ Marks a test function as *expected to fail*.
to specify ``reason`` (see :ref:`condition string <string conditions>`).
:keyword str reason:
Reason why the test function is marked as xfail.
:keyword Type[Exception] raises:
:keyword raises:
Exception class (or tuple of classes) expected to be raised by the test function; other exceptions will fail the test.
Note that subclasses of the classes passed will also result in a match (similar to how the ``except`` statement works).
:type raises: Type[:py:exc:`Exception`]
:keyword bool run:
Whether the test function should actually be executed. If ``False``, the function will always xfail and will
@ -1923,7 +1937,7 @@ All the command-line flags can be obtained by running ``pytest --help``::
general:
-k EXPRESSION Only run tests which match the given substring
expression. An expression is a Python evaluatable
expression. An expression is a Python evaluable
expression where all names are substring-matched
against test names and their parent classes.
Example: -k 'test_method or test_other' matches all

View File

@ -1,4 +1,3 @@
pallets-sphinx-themes
pluggy>=1.5.0
pygments-pytest>=2.3.0
sphinx-removed-in>=0.2.0
@ -8,4 +7,5 @@ sphinxcontrib-svg2pdfconverter
# Pin packaging because it no longer handles 'latest' version, which
# is the version that is assigned to the docs.
# See https://github.com/pytest-dev/pytest/pull/10578#issuecomment-1348249045.
packaging <22
packaging
furo

View File

@ -31,6 +31,7 @@ classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Software Development :: Libraries",
"Topic :: Software Development :: Testing",
"Topic :: Utilities",

View File

@ -424,15 +424,14 @@ class Traceback(List[TracebackEntry]):
# which generates code objects that have hash/value equality
# XXX needs a test
key = entry.frame.code.path, id(entry.frame.code.raw), entry.lineno
# print "checking for recursion at", key
values = cache.setdefault(key, [])
# Since Python 3.13 f_locals is a proxy, freeze it.
loc = dict(entry.frame.f_locals)
if values:
f = entry.frame
loc = f.f_locals
for otherloc in values:
if otherloc == loc:
return i
values.append(entry.frame.f_locals)
values.append(loc)
return None

View File

@ -41,7 +41,7 @@ _winerrnomap = {
3: errno.ENOENT,
17: errno.EEXIST,
18: errno.EXDEV,
13: errno.EBUSY, # empty cd drive, but ENOMEDIUM seems unavailiable
13: errno.EBUSY, # empty cd drive, but ENOMEDIUM seems unavailable
22: errno.ENOTDIR,
20: errno.ENOTDIR,
267: errno.ENOTDIR,

View File

@ -836,7 +836,7 @@ class LocalPath:
def copy(self, target, mode=False, stat=False):
"""Copy path to target.
If mode is True, will copy copy permission from path to target.
If mode is True, will copy permission from path to target.
If stat is True, copy permission, last modification
time, last access time, and flags from path to target.
"""
@ -1047,7 +1047,7 @@ class LocalPath:
def pypkgpath(self):
"""Return the Python package path by looking for the last
directory upwards which still contains an __init__.py.
Return None if a pkgpath can not be determined.
Return None if a pkgpath cannot be determined.
"""
pkgpath = None
for parent in self.parts(reverse=True):

View File

@ -584,7 +584,7 @@ def _get_assertion_exprs(src: bytes) -> Dict[int, str]:
# multi-line assert with message
elif lineno in seen_lines:
lines[-1] = lines[-1][:offset]
# multi line assert with escapd newline before message
# multi line assert with escaped newline before message
else:
lines.append(line[:offset])
_write_and_reset()
@ -1171,7 +1171,10 @@ def try_makedirs(cache_dir: Path) -> bool:
return False
except OSError as e:
# as of now, EROFS doesn't have an equivalent OSError-subclass
if e.errno == errno.EROFS:
#
# squashfuse_ll returns ENOSYS "OSError: [Errno 38] Function not
# implemented" for a read-only error
if e.errno in {errno.EROFS, errno.ENOSYS}:
return False
raise
return True

View File

@ -325,7 +325,7 @@ def _diff_text(left: str, right: str, verbose: int = 0) -> List[str]:
def _compare_eq_iterable(
left: Iterable[Any],
right: Iterable[Any],
highligher: _HighlightFunc,
highlighter: _HighlightFunc,
verbose: int = 0,
) -> List[str]:
if verbose <= 0 and not running_on_ci():
@ -340,7 +340,7 @@ def _compare_eq_iterable(
# "right" is the expected base against which we compare "left",
# see https://github.com/pytest-dev/pytest/issues/3333
explanation.extend(
highligher(
highlighter(
"\n".join(
line.rstrip()
for line in difflib.ndiff(right_formatting, left_formatting)

View File

@ -4,6 +4,7 @@
# This plugin was not named "cache" to avoid conflicts with the external
# pytest-cache version.
import dataclasses
import errno
import json
import os
from pathlib import Path
@ -213,6 +214,13 @@ class Cache:
dir=self._cachedir.parent,
) as newpath:
path = Path(newpath)
# Reset permissions to the default, see #12308.
# Note: there's no way to get the current umask atomically, eek.
umask = os.umask(0o022)
os.umask(umask)
path.chmod(0o777 - umask)
with open(path.joinpath("README.md"), "xt", encoding="UTF-8") as f:
f.write(README_CONTENT)
with open(path.joinpath(".gitignore"), "xt", encoding="UTF-8") as f:
@ -220,14 +228,24 @@ class Cache:
with open(path.joinpath("CACHEDIR.TAG"), "xb") as f:
f.write(CACHEDIR_TAG_CONTENT)
path.rename(self._cachedir)
# Create a directory in place of the one we just moved so that `TemporaryDirectory`'s
# cleanup doesn't complain.
#
# TODO: pass ignore_cleanup_errors=True when we no longer support python < 3.10. See
# https://github.com/python/cpython/issues/74168. Note that passing delete=False would
# do the wrong thing in case of errors and isn't supported until python 3.12.
path.mkdir()
try:
path.rename(self._cachedir)
except OSError as e:
# If 2 concurrent pytests both race to the rename, the loser
# gets "Directory not empty" from the rename. In this case,
# everything is handled so just continue (while letting the
# temporary directory be cleaned up).
if e.errno != errno.ENOTEMPTY:
raise
else:
# Create a directory in place of the one we just moved so that
# `TemporaryDirectory`'s cleanup doesn't complain.
#
# TODO: pass ignore_cleanup_errors=True when we no longer support python < 3.10.
# See https://github.com/python/cpython/issues/74168. Note that passing
# delete=False would do the wrong thing in case of errors and isn't supported
# until python 3.12.
path.mkdir()
class LFPluginCollWrapper:
@ -244,7 +262,7 @@ class LFPluginCollWrapper:
# Sort any lf-paths to the beginning.
lf_paths = self.lfplugin._last_failed_paths
# Use stable sort to priorize last failed.
# Use stable sort to prioritize last failed.
def sort_key(node: Union[nodes.Item, nodes.Collector]) -> bool:
return node.path in lf_paths

View File

@ -53,7 +53,7 @@ def iscoroutinefunction(func: object) -> bool:
def syntax, and doesn't contain yield), or a function decorated with
@asyncio.coroutine.
Note: copied and modified from Python 3.5's builtin couroutines.py to avoid
Note: copied and modified from Python 3.5's builtin coroutines.py to avoid
importing asyncio directly, which in turns also initializes the "logging"
module as a side-effect (see issue #8).
"""

View File

@ -462,7 +462,7 @@ class PytestPluginManager(PluginManager):
# (see issue #1073).
if not name.startswith("pytest_"):
return None
# Ignore names which can not be hooks.
# Ignore names which cannot be hooks.
if name == "pytest_plugins":
return None
@ -574,8 +574,8 @@ class PytestPluginManager(PluginManager):
self._noconftest = noconftest
self._using_pyargs = pyargs
foundanchor = False
for intitial_path in args:
path = str(intitial_path)
for initial_path in args:
path = str(initial_path)
# remove node-id syntax
i = path.find("::")
if i != -1:

View File

@ -23,6 +23,7 @@ from typing import List
from typing import MutableMapping
from typing import NoReturn
from typing import Optional
from typing import OrderedDict
from typing import overload
from typing import Sequence
from typing import Set
@ -75,8 +76,6 @@ if sys.version_info < (3, 11):
if TYPE_CHECKING:
from typing import Deque
from _pytest.main import Session
from _pytest.python import CallSpec2
from _pytest.python import Function
@ -207,16 +206,18 @@ def get_parametrized_fixture_keys(
def reorder_items(items: Sequence[nodes.Item]) -> List[nodes.Item]:
argkeys_cache: Dict[Scope, Dict[nodes.Item, Dict[FixtureArgKey, None]]] = {}
items_by_argkey: Dict[Scope, Dict[FixtureArgKey, Deque[nodes.Item]]] = {}
items_by_argkey: Dict[
Scope, Dict[FixtureArgKey, OrderedDict[nodes.Item, None]]
] = {}
for scope in HIGH_SCOPES:
scoped_argkeys_cache = argkeys_cache[scope] = {}
scoped_items_by_argkey = items_by_argkey[scope] = defaultdict(deque)
scoped_items_by_argkey = items_by_argkey[scope] = defaultdict(OrderedDict)
for item in items:
keys = dict.fromkeys(get_parametrized_fixture_keys(item, scope), None)
if keys:
scoped_argkeys_cache[item] = keys
for key in keys:
scoped_items_by_argkey[key].append(item)
scoped_items_by_argkey[key][item] = None
items_dict = dict.fromkeys(items, None)
return list(
reorder_items_atscope(items_dict, argkeys_cache, items_by_argkey, Scope.Session)
@ -226,17 +227,19 @@ def reorder_items(items: Sequence[nodes.Item]) -> List[nodes.Item]:
def fix_cache_order(
item: nodes.Item,
argkeys_cache: Dict[Scope, Dict[nodes.Item, Dict[FixtureArgKey, None]]],
items_by_argkey: Dict[Scope, Dict[FixtureArgKey, "Deque[nodes.Item]"]],
items_by_argkey: Dict[Scope, Dict[FixtureArgKey, OrderedDict[nodes.Item, None]]],
) -> None:
for scope in HIGH_SCOPES:
scoped_items_by_argkey = items_by_argkey[scope]
for key in argkeys_cache[scope].get(item, []):
items_by_argkey[scope][key].appendleft(item)
scoped_items_by_argkey[key][item] = None
scoped_items_by_argkey[key].move_to_end(item, last=False)
def reorder_items_atscope(
items: Dict[nodes.Item, None],
argkeys_cache: Dict[Scope, Dict[nodes.Item, Dict[FixtureArgKey, None]]],
items_by_argkey: Dict[Scope, Dict[FixtureArgKey, "Deque[nodes.Item]"]],
items_by_argkey: Dict[Scope, Dict[FixtureArgKey, OrderedDict[nodes.Item, None]]],
scope: Scope,
) -> Dict[nodes.Item, None]:
if scope is Scope.Function or len(items) < 3:
@ -325,7 +328,7 @@ class FuncFixtureInfo:
working_set = set(self.initialnames)
while working_set:
argname = working_set.pop()
# Argname may be smth not included in the original names_closure,
# Argname may be something not included in the original names_closure,
# in which case we ignore it. This currently happens with pseudo
# FixtureDefs which wrap 'get_direct_param_fixture_func(request)'.
# So they introduce the new dependency 'request' which might have
@ -1701,7 +1704,7 @@ class FixtureManager:
If `node_or_object` is a collection node (with an underlying Python
object), the node's object is traversed and the node's nodeid is used to
determine the fixtures' visibilty. `nodeid` must not be specified in
determine the fixtures' visibility. `nodeid` must not be specified in
this case.
If `node_or_object` is an object (e.g. a plugin), the object is

View File

@ -310,7 +310,12 @@ def pytest_collection_finish(session: "Session") -> None:
def pytest_ignore_collect(
collection_path: Path, path: "LEGACY_PATH", config: "Config"
) -> Optional[bool]:
"""Return True to prevent considering this path for collection.
"""Return ``True`` to ignore this path for collection.
Return ``None`` to let other plugins ignore the path for collection.
Returning ``False`` will forcefully *not* ignore this path for collection,
without giving a chance for other plugins to ignore this path.
This hook is consulted for all files and directories prior to calling
more specific hooks.
@ -650,7 +655,7 @@ def pytest_runtest_protocol(
- ``pytest_runtest_logreport(report)``
- ``pytest_exception_interact(call, report)`` if an interactive exception occurred
- Call phase, if the the setup passed and the ``setuponly`` pytest option is not set:
- Call phase, if the setup passed and the ``setuponly`` pytest option is not set:
- ``call = pytest_runtest_call(item)`` (wrapped in ``CallInfo(when="call")``)
- ``report = pytest_runtest_makereport(item, call)``
- ``pytest_runtest_logreport(report)``
@ -860,7 +865,7 @@ def pytest_fixture_setup(
) -> Optional[object]:
"""Perform fixture setup execution.
:param fixturdef:
:param fixturedef:
The fixture definition object.
:param request:
The fixture request object.
@ -890,7 +895,7 @@ def pytest_fixture_post_finalizer(
the fixture result ``fixturedef.cached_result`` is still available (not
``None``).
:param fixturdef:
:param fixturedef:
The fixture definition object.
:param request:
The fixture request object.

View File

@ -384,7 +384,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_startdir(self: Session) -> LEGACY_PATH:
"""The path from which pytest was invoked.
Prefer to use ``startpath`` which is a :class:`pathlib.Path`.
@ -439,7 +439,7 @@ def pytest_load_initial_conftests(early_config: Config) -> None:
mp.setattr(Config, "inifile", property(Config_inifile), raising=False)
# Add Session.startdir property.
mp.setattr(Session, "startdir", property(Session_stardir), raising=False)
mp.setattr(Session, "startdir", property(Session_startdir), raising=False)
# Add pathlist configuration type.
mp.setattr(Config, "_getini_unknown_type", Config__getini_unknown_type)

View File

@ -78,7 +78,7 @@ def pytest_addoption(parser: Parser) -> None:
default="",
metavar="EXPRESSION",
help="Only run tests which match the given substring expression. "
"An expression is a Python evaluatable expression "
"An expression is a Python evaluable expression "
"where all names are substring-matched against test names "
"and their parent classes. Example: -k 'test_method or test_"
"other' matches all test functions and classes whose name "

View File

@ -114,6 +114,9 @@ def exit(
:param returncode:
Return code to be used when exiting pytest. None means the same as ``0`` (no error), same as :func:`sys.exit`.
:raises pytest.exit.Exception:
The exception that is raised.
"""
__tracebackhide__ = True
raise Exit(reason, returncode)
@ -142,6 +145,9 @@ def skip(
Defaults to False.
:raises pytest.skip.Exception:
The exception that is raised.
.. note::
It is better to use the :ref:`pytest.mark.skipif ref` marker when
possible to declare a test to be skipped under certain conditions
@ -163,6 +169,9 @@ def fail(reason: str = "", pytrace: bool = True) -> NoReturn:
:param pytrace:
If False, msg represents the full failure information and no
python traceback will be reported.
:raises pytest.fail.Exception:
The exception that is raised.
"""
__tracebackhide__ = True
raise Failed(msg=reason, pytrace=pytrace)
@ -188,6 +197,9 @@ def xfail(reason: str = "") -> NoReturn:
It is better to use the :ref:`pytest.mark.xfail ref` marker when
possible to declare a test to be xfailed under certain conditions
like known bugs or missing features.
:raises pytest.xfail.Exception:
The exception that is raised.
"""
__tracebackhide__ = True
raise XFailed(reason)
@ -227,6 +239,9 @@ def importorskip(
:returns:
The imported module. This should be assigned to its canonical name.
:raises pytest.skip.Exception:
If the module cannot be imported.
Example::
docutils = pytest.importorskip("docutils")

View File

@ -173,7 +173,7 @@ def rm_rf(path: Path) -> None:
def find_prefixed(root: Path, prefix: str) -> Iterator["os.DirEntry[str]"]:
"""Find all elements in root that begin with the prefix, case insensitive."""
"""Find all elements in root that begin with the prefix, case-insensitive."""
l_prefix = prefix.lower()
for x in os.scandir(root):
if x.name.lower().startswith(l_prefix):
@ -776,7 +776,7 @@ def resolve_package_path(path: Path) -> Optional[Path]:
"""Return the Python package path by looking for the last
directory upwards which still contains an __init__.py.
Returns None if it can not be determined.
Returns None if it cannot be determined.
"""
result = None
for parent in itertools.chain((path,), path.parents):

View File

@ -289,7 +289,8 @@ class HookRecorder:
__tracebackhide__ = True
i = 0
entries = list(entries)
backlocals = sys._getframe(1).f_locals
# Since Python 3.13, f_locals is not a dict, but eval requires a dict.
backlocals = dict(sys._getframe(1).f_locals)
while entries:
name, check = entries.pop(0)
for ind, call in enumerate(self.calls[i:]):
@ -760,6 +761,9 @@ class Pytester:
) -> Path:
items = list(files.items())
if ext is None:
raise TypeError("ext must not be None")
if ext and not ext.startswith("."):
raise ValueError(
f"pytester.makefile expects a file extension, try .{ext} instead of {ext}"

View File

@ -176,7 +176,12 @@ def pytest_collect_directory(
path: Path, parent: nodes.Collector
) -> Optional[nodes.Collector]:
pkginit = path / "__init__.py"
if pkginit.is_file():
try:
has_pkginit = pkginit.is_file()
except PermissionError:
# See https://github.com/pytest-dev/pytest/issues/12120#issuecomment-2106349096.
return None
if has_pkginit:
return Package.from_parent(parent, path=path)
return None

View File

@ -388,7 +388,9 @@ def pytest_make_collect_report(collector: Collector) -> CollectReport:
return list(collector.collect())
call = CallInfo.from_call(collect, "collect")
call = CallInfo.from_call(
collect, "collect", reraise=(KeyboardInterrupt, SystemExit)
)
longrepr: Union[None, Tuple[str, int, str], str, TerminalRepr] = None
if not call.excinfo:
outcome: Literal["passed", "skipped", "failed"] = "passed"

View File

@ -40,7 +40,7 @@ def pytest_addoption(parser: Parser) -> None:
@pytest.hookimpl
def pytest_configure(config: Config) -> None:
if config.option.stepwise_skip:
# allow --stepwise-skip to work on it's own merits.
# allow --stepwise-skip to work on its own merits.
config.option.stepwise = True
if config.getoption("stepwise"):
config.pluginmanager.register(StepwisePlugin(config), "stepwiseplugin")

View File

@ -212,11 +212,12 @@ class TestCaseFunction(Function):
super().setup()
def teardown(self) -> None:
super().teardown()
if self._explicit_tearDown is not None:
self._explicit_tearDown()
self._explicit_tearDown = None
self._obj = None
del self._instance
super().teardown()
def startTest(self, testcase: "unittest.TestCase") -> None:
pass

View File

@ -667,7 +667,7 @@ class TestLocalPath(CommonFSTests):
assert p == os.path.expanduser("~")
@pytest.mark.skipif(
not sys.platform.startswith("win32"), reason="case insensitive only on windows"
not sys.platform.startswith("win32"), reason="case-insensitive only on windows"
)
def test_eq_hash_are_case_insensitive_on_windows(self):
a = local("/some/path")
@ -898,7 +898,7 @@ class TestExecutionOnWindows:
class TestExecution:
pytestmark = skiponwin32
def test_sysfind_no_permisson_ignored(self, monkeypatch, tmpdir):
def test_sysfind_no_permission_ignored(self, monkeypatch, tmpdir):
noperm = tmpdir.ensure("noperm", dir=True)
monkeypatch.setenv("PATH", str(noperm), prepend=":")
noperm.chmod(0)

View File

@ -1,6 +1,7 @@
# mypy: allow-untyped-defs
from __future__ import annotations
import fnmatch
import importlib
import io
import operator
@ -237,7 +238,7 @@ class TestTraceback_f_g_h:
n += 1
f(n)
excinfo = pytest.raises(RuntimeError, f, 8)
excinfo = pytest.raises(RecursionError, f, 8)
traceback = excinfo.traceback
recindex = traceback.recursionindex()
assert recindex == 3
@ -373,7 +374,10 @@ def test_excinfo_no_sourcecode():
except ValueError:
excinfo = _pytest._code.ExceptionInfo.from_current()
s = str(excinfo.traceback[-1])
assert s == " File '<string>':1 in <module>\n ???\n"
# TODO: Since Python 3.13b1 under pytest-xdist, the * is `import
# sys;exec(eval(sys.stdin.readline()))` (execnet bootstrap code)
# instead of `???` like before. Is this OK?
fnmatch.fnmatch(s, " File '<string>':1 in <module>\n *\n")
def test_excinfo_no_python_sourcecode(tmp_path: Path) -> None:
@ -1515,7 +1519,7 @@ def test_cwd_deleted(pytester: Pytester) -> None:
result.stderr.no_fnmatch_line("*INTERNALERROR*")
def test_regression_nagative_line_index(pytester: Pytester) -> None:
def test_regression_negative_line_index(pytester: Pytester) -> None:
"""
With Python 3.10 alphas, there was an INTERNALERROR reported in
https://github.com/pytest-dev/pytest/pull/8227

View File

@ -255,7 +255,7 @@ def test_getfuncsource_dynamic() -> None:
assert str(g_source).strip() == "def g():\n pass # pragma: no cover"
def test_getfuncsource_with_multine_string() -> None:
def test_getfuncsource_with_multiline_string() -> None:
def f():
c = """while True:
pass
@ -370,7 +370,11 @@ def test_getfslineno() -> None:
pass
B.__name__ = B.__qualname__ = "B2"
assert getfslineno(B)[1] == -1
# Since Python 3.13 this started working.
if sys.version_info >= (3, 13):
assert getfslineno(B)[1] != -1
else:
assert getfslineno(B)[1] == -1
def test_code_of_object_instance_with_call() -> None:

View File

@ -117,7 +117,7 @@ def test_change_disabled_level_undo(pytester: Pytester) -> None:
result.stdout.no_fnmatch_line("*log from test2*")
def test_change_level_undos_handler_level(pytester: Pytester) -> None:
def test_change_level_undoes_handler_level(pytester: Pytester) -> None:
"""Ensure that 'set_level' is undone after the end of the test (handler).
Issue #7569. Tests the handler level specifically.
@ -302,7 +302,15 @@ def logging_during_setup_and_teardown(
assert [x.message for x in caplog.get_records("teardown")] == ["a_teardown_log"]
def test_caplog_captures_for_all_stages(
def private_assert_caplog_records_is_setup_call(
caplog: pytest.LogCaptureFixture,
) -> None:
# This reaches into private API, don't use this type of thing in real tests!
caplog_records = caplog._item.stash[caplog_records_key]
assert set(caplog_records) == {"setup", "call"}
def test_captures_for_all_stages(
caplog: pytest.LogCaptureFixture, logging_during_setup_and_teardown: None
) -> None:
assert not caplog.records
@ -312,9 +320,7 @@ def test_caplog_captures_for_all_stages(
assert [x.message for x in caplog.get_records("setup")] == ["a_setup_log"]
# This reaches into private API, don't use this type of thing in real tests!
caplog_records = caplog._item.stash[caplog_records_key]
assert set(caplog_records) == {"setup", "call"}
private_assert_caplog_records_is_setup_call(caplog)
def test_clear_for_call_stage(
@ -323,21 +329,18 @@ def test_clear_for_call_stage(
logger.info("a_call_log")
assert [x.message for x in caplog.get_records("call")] == ["a_call_log"]
assert [x.message for x in caplog.get_records("setup")] == ["a_setup_log"]
caplog_records = caplog._item.stash[caplog_records_key]
assert set(caplog_records) == {"setup", "call"}
private_assert_caplog_records_is_setup_call(caplog)
caplog.clear()
assert caplog.get_records("call") == []
assert [x.message for x in caplog.get_records("setup")] == ["a_setup_log"]
caplog_records = caplog._item.stash[caplog_records_key]
assert set(caplog_records) == {"setup", "call"}
private_assert_caplog_records_is_setup_call(caplog)
logging.info("a_call_log_after_clear")
assert [x.message for x in caplog.get_records("call")] == ["a_call_log_after_clear"]
assert [x.message for x in caplog.get_records("setup")] == ["a_setup_log"]
caplog_records = caplog._item.stash[caplog_records_key]
assert set(caplog_records) == {"setup", "call"}
private_assert_caplog_records_is_setup_call(caplog)
def test_ini_controls_global_log_level(pytester: Pytester) -> None:
@ -363,11 +366,11 @@ def test_ini_controls_global_log_level(pytester: Pytester) -> None:
)
result = pytester.runpytest()
# make sure that that we get a '0' exit code for the testsuite
# make sure that we get a '0' exit code for the testsuite
assert result.ret == 0
def test_caplog_can_override_global_log_level(pytester: Pytester) -> None:
def test_can_override_global_log_level(pytester: Pytester) -> None:
pytester.makepyfile(
"""
import pytest
@ -406,7 +409,7 @@ def test_caplog_can_override_global_log_level(pytester: Pytester) -> None:
assert result.ret == 0
def test_caplog_captures_despite_exception(pytester: Pytester) -> None:
def test_captures_despite_exception(pytester: Pytester) -> None:
pytester.makepyfile(
"""
import pytest

View File

@ -0,0 +1,11 @@
import unittest
class MyTestCase(unittest.TestCase):
first_time = True
def test_fail_the_first_time(self) -> None:
"""Regression test for issue #12424."""
if self.first_time:
type(self).first_time = False
self.fail()

View File

@ -1,9 +1,7 @@
anyio[curio,trio]==4.3.0
django==5.0.4
pytest-asyncio==0.23.6
# Temporarily not installed until pytest-bdd is fixed:
# https://github.com/pytest-dev/pytest/pull/11785
# pytest-bdd==7.0.1
anyio[curio,trio]==4.4.0
django==5.0.6
pytest-asyncio==0.23.7
pytest-bdd==7.1.2
pytest-cov==5.0.0
pytest-django==4.8.0
pytest-flakes==4.0.5
@ -11,7 +9,7 @@ pytest-html==4.1.1
pytest-mock==3.14.0
pytest-rerunfailures==14.0
pytest-sugar==1.0.0
pytest-trio==0.7.0
pytest-trio==0.8.0
pytest-twisted==1.14.1
twisted==24.3.0
pytest-xvfb==3.0.0

View File

@ -933,7 +933,7 @@ class TestRequestBasic:
) -> None:
"""
Ensure exceptions raised during teardown by finalizers are suppressed
until all finalizers are called, then re-reaised together in an
until all finalizers are called, then re-raised together in an
exception group (#2440)
"""
pytester.makepyfile(
@ -2219,6 +2219,25 @@ class TestAutouseManagement:
reprec = pytester.inline_run("-s")
reprec.assertoutcome(passed=2)
def test_reordering_catastrophic_performance(self, pytester: Pytester) -> None:
"""Check that a certain high-scope parametrization pattern doesn't cause
a catasrophic slowdown.
Regression test for #12355.
"""
pytester.makepyfile("""
import pytest
params = tuple("abcdefghijklmnopqrstuvwxyz")
@pytest.mark.parametrize(params, [range(len(params))] * 3, scope="module")
def test_parametrize(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z):
pass
""")
result = pytester.runpytest()
result.assert_outcomes(passed=3)
class TestFixtureMarker:
def test_parametrize(self, pytester: Pytester) -> None:

View File

@ -163,7 +163,7 @@ class TestMockDecoration:
@mock.patch("os.path.abspath")
@mock.patch("os.path.normpath")
@mock.patch("os.path.basename", new=mock_basename)
def test_someting(normpath, abspath, tmp_path):
def test_something(normpath, abspath, tmp_path):
abspath.return_value = "this"
os.path.normpath(os.path.abspath("hello"))
normpath.assert_any_call("this")
@ -176,7 +176,7 @@ class TestMockDecoration:
funcnames = [
call.report.location[2] for call in calls if call.report.when == "call"
]
assert funcnames == ["T.test_hello", "test_someting"]
assert funcnames == ["T.test_hello", "test_something"]
def test_mock_sorting(self, pytester: Pytester) -> None:
pytest.importorskip("mock", "1.0.1")

View File

@ -1974,6 +1974,11 @@ def test_try_makedirs(monkeypatch, tmp_path: Path) -> None:
monkeypatch.setattr(os, "makedirs", partial(fake_mkdir, exc=err))
assert not try_makedirs(p)
err = OSError()
err.errno = errno.ENOSYS
monkeypatch.setattr(os, "makedirs", partial(fake_mkdir, exc=err))
assert not try_makedirs(p)
# unhandled OSError should raise
err = OSError()
err.errno = errno.ECHILD

View File

@ -31,6 +31,21 @@ class TestNewAPI:
p = config.cache.mkdir("name")
assert p.is_dir()
def test_cache_dir_permissions(self, pytester: Pytester) -> None:
"""The .pytest_cache directory should have world-readable permissions
(depending on umask).
Regression test for #12308.
"""
pytester.makeini("[pytest]")
config = pytester.parseconfigure()
assert config.cache is not None
p = config.cache.mkdir("name")
assert p.is_dir()
# Instead of messing with umask, make sure .pytest_cache has the same
# permissions as the default that `mkdir` gives `p`.
assert (p.parent.stat().st_mode & 0o777) == (p.stat().st_mode & 0o777)
def test_config_cache_dataerror(self, pytester: Pytester) -> None:
pytester.makeini("[pytest]")
config = pytester.parseconfigure()
@ -43,7 +58,7 @@ class TestNewAPI:
assert val == -2
@pytest.mark.filterwarnings("ignore:could not create cache path")
def test_cache_writefail_cachfile_silent(self, pytester: Pytester) -> None:
def test_cache_writefail_cachefile_silent(self, pytester: Pytester) -> None:
pytester.makeini("[pytest]")
pytester.path.joinpath(".pytest_cache").write_text(
"gone wrong", encoding="utf-8"
@ -179,7 +194,7 @@ class TestNewAPI:
assert pytester.path.joinpath("custom_cache_dir").is_dir()
@pytest.mark.parametrize("env", ((), ("TOX_ENV_DIR", "/tox_env_dir")))
@pytest.mark.parametrize("env", ((), ("TOX_ENV_DIR", "mydir/tox-env")))
def test_cache_reportheader(
env: Sequence[str], pytester: Pytester, monkeypatch: MonkeyPatch
) -> None:

View File

@ -7,6 +7,7 @@ import sys
import tempfile
import textwrap
from typing import List
from typing import Type
from _pytest.assertion.util import running_on_ci
from _pytest.config import ExitCode
@ -284,6 +285,23 @@ class TestCollectFS:
items, reprec = pytester.inline_genitems()
assert [x.name for x in items] == ["test_%s" % dirname]
def test_missing_permissions_on_unselected_directory_doesnt_crash(
self, pytester: Pytester
) -> None:
"""Regression test for #12120."""
test = pytester.makepyfile(test="def test(): pass")
bad = pytester.mkdir("bad")
try:
bad.chmod(0)
result = pytester.runpytest(test)
finally:
bad.chmod(750)
bad.rmdir()
assert result.ret == ExitCode.OK
result.assert_outcomes(passed=1)
class TestCollectPluginHookRelay:
def test_pytest_collect_file(self, pytester: Pytester) -> None:
@ -1857,3 +1875,33 @@ def test_do_not_collect_symlink_siblings(
# Ensure we collect it only once if we pass the symlinked directory.
result = pytester.runpytest(symlink_path, "-sv")
result.assert_outcomes(passed=1)
@pytest.mark.parametrize(
"exception_class, msg",
[
(KeyboardInterrupt, "*!!! KeyboardInterrupt !!!*"),
(SystemExit, "INTERNALERROR> SystemExit"),
],
)
def test_respect_system_exceptions(
pytester: Pytester,
exception_class: Type[BaseException],
msg: str,
):
head = "Before exception"
tail = "After exception"
ensure_file(pytester.path / "test_eggs.py").write_text(
f"print('{head}')", encoding="UTF-8"
)
ensure_file(pytester.path / "test_ham.py").write_text(
f"raise {exception_class.__name__}()", encoding="UTF-8"
)
ensure_file(pytester.path / "test_spam.py").write_text(
f"print('{tail}')", encoding="UTF-8"
)
result = pytester.runpytest_subprocess("-s")
result.stdout.fnmatch_lines([f"*{head}*"])
result.stdout.fnmatch_lines([msg])
result.stdout.no_fnmatch_line(f"*{tail}*")

View File

@ -94,7 +94,7 @@ 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")
@pytest.mark.skipif(sys.version_info >= (3, 11), reason="coroutine removed")
def test_is_generator_asyncio(pytester: Pytester) -> None:
pytester.makepyfile(
"""

View File

@ -217,7 +217,7 @@ class TestParseIni:
def test_confcutdir_default_without_configfile(self, pytester: Pytester) -> None:
# If --confcutdir is not specified, and there is no configfile, default
# to the roothpath.
# to the rootpath.
sub = pytester.mkdir("sub")
os.chdir(sub)
config = pytester.parseconfigure()
@ -1740,7 +1740,7 @@ class TestOverrideIniArgs:
)
pytester.makepyfile(
r"""
def test_overriden(pytestconfig):
def test_overridden(pytestconfig):
config_paths = pytestconfig.getini("paths")
print(config_paths)
for cpf in config_paths:

View File

@ -70,7 +70,7 @@ class TestConftestValueAccessGlobal:
)
assert conftest._rget_with_confmod("a", p)[1] == 1
def test_immediate_initialiation_and_incremental_are_the_same(
def test_immediate_initialization_and_incremental_are_the_same(
self, basedir: Path
) -> None:
conftest = PytestPluginManager()
@ -396,7 +396,7 @@ def test_conftest_symlink_files(pytester: Pytester) -> None:
@pytest.mark.skipif(
os.path.normcase("x") != os.path.normcase("X"),
reason="only relevant for case insensitive file systems",
reason="only relevant for case-insensitive file systems",
)
def test_conftest_badcase(pytester: Pytester) -> None:
"""Check conftest.py loading when directory casing is wrong (#5792)."""

View File

@ -1122,7 +1122,7 @@ class TestTraceOption:
def test_trace_after_runpytest(pytester: Pytester) -> None:
"""Test that debugging's pytest_configure is re-entrant."""
"""Test that debugging's pytest_configure is reentrant."""
p1 = pytester.makepyfile(
"""
from _pytest.debugging import pytestPDB
@ -1153,7 +1153,7 @@ def test_trace_after_runpytest(pytester: Pytester) -> None:
def test_quit_with_swallowed_SystemExit(pytester: Pytester) -> None:
"""Test that debugging's pytest_configure is re-entrant."""
"""Test that debugging's pytest_configure is reentrant."""
p1 = pytester.makepyfile(
"""
def call_pdb_set_trace():

View File

@ -224,11 +224,7 @@ class TestDoctests:
"Traceback (most recent call last):",
' File "*/doctest.py", line *, in __run',
" *",
*(
(" *^^^^*",)
if (3, 11, 0, "beta", 4) > sys.version_info >= (3, 11)
else ()
),
*((" *^^^^*", " *", " *") if sys.version_info >= (3, 13) else ()),
' File "<doctest test_doctest_unexpected_exception.txt[1]>", line 1, in <module>',
"ZeroDivisionError: division by zero",
"*/test_doctest_unexpected_exception.txt:2: UnexpectedException",
@ -385,7 +381,7 @@ class TestDoctests:
"*= FAILURES =*",
"*_ [[]doctest[]] test_doctest_linedata_on_property.Sample.some_property _*",
"004 ",
"005 >>> Sample().some_property",
"005 *>>> Sample().some_property",
"Expected:",
" 'another thing'",
"Got:",
@ -396,7 +392,7 @@ class TestDoctests:
]
)
def test_doctest_no_linedata_on_overriden_property(self, pytester: Pytester):
def test_doctest_no_linedata_on_overridden_property(self, pytester: Pytester):
pytester.makepyfile(
"""
class Sample(object):
@ -414,7 +410,7 @@ class TestDoctests:
result.stdout.fnmatch_lines(
[
"*= FAILURES =*",
"*_ [[]doctest[]] test_doctest_no_linedata_on_overriden_property.Sample.some_property _*",
"*_ [[]doctest[]] test_doctest_no_linedata_on_overridden_property.Sample.some_property _*",
"EXAMPLE LOCATION UNKNOWN, not showing all tests of that example",
"[?][?][?] >>> Sample().some_property",
"Expected:",
@ -422,7 +418,7 @@ class TestDoctests:
"Got:",
" 'something'",
"",
"*/test_doctest_no_linedata_on_overriden_property.py:None: DocTestFailure",
"*/test_doctest_no_linedata_on_overridden_property.py:None: DocTestFailure",
"*= 1 failed in *",
]
)

View File

@ -1002,7 +1002,7 @@ class TestNonPython:
@pytest.mark.parametrize("junit_logging", ["no", "system-out"])
def test_nullbyte(pytester: Pytester, junit_logging: str) -> None:
# A null byte can not occur in XML (see section 2.2 of the spec)
# A null byte cannot occur in XML (see section 2.2 of the spec)
pytester.makepyfile(
"""
import sys

View File

@ -155,7 +155,7 @@ def test_override_ini_paths(pytester: pytest.Pytester) -> None:
)
pytester.makepyfile(
r"""
def test_overriden(pytestconfig):
def test_overridden(pytestconfig):
config_paths = pytestconfig.getini("paths")
print(config_paths)
for cpf in config_paths:

View File

@ -3,7 +3,6 @@ import argparse
import os
from pathlib import Path
import re
import sys
from typing import Optional
from _pytest.config import ExitCode
@ -45,32 +44,18 @@ 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 (3, 11, 0, "beta", 4) > sys.version_info >= (3, 11)
else result.stdout.lines[-3:]
)
end_lines = result.stdout.lines[-3:]
if exc == SystemExit:
assert end_lines == [
f'INTERNALERROR> File "{c1}", line 4, in pytest_sessionstart',
'INTERNALERROR> raise SystemExit("boom")',
*(
("INTERNALERROR> ^^^^^^^^^^^^^^^^^^^^^^^^",)
if (3, 11, 0, "beta", 4) > sys.version_info >= (3, 11)
else ()
),
"INTERNALERROR> SystemExit: boom",
]
else:
assert end_lines == [
f'INTERNALERROR> File "{c1}", line 4, in pytest_sessionstart',
'INTERNALERROR> raise ValueError("boom")',
*(
("INTERNALERROR> ^^^^^^^^^^^^^^^^^^^^^^^^",)
if (3, 11, 0, "beta", 4) > sys.version_info >= (3, 11)
else ()
),
"INTERNALERROR> ValueError: boom",
]
if returncode is False:

View File

@ -61,7 +61,7 @@ def test_basic(expr: str, expected: bool) -> None:
("not not not not not true", False),
),
)
def test_syntax_oddeties(expr: str, expected: bool) -> None:
def test_syntax_oddities(expr: str, expected: bool) -> None:
matcher = {"true": True, "false": False}.__getitem__
assert evaluate(expr, matcher) is expected

View File

@ -420,7 +420,7 @@ class TestPytestPluginManager:
pytestpm.consider_conftest(mod, registration_name="unused")
class TestPytestPluginManagerBootstrapming:
class TestPytestPluginManagerBootstrapping:
def test_preparse_args(self, pytestpm: PytestPluginManager) -> None:
pytest.raises(
ImportError, lambda: pytestpm.consider_preparse(["xyz", "-p", "hello123"])
@ -446,7 +446,7 @@ class TestPytestPluginManagerBootstrapming:
assert len(l2) == len(l1)
assert 42 not in l2
def test_plugin_prevent_register_unregistered_alredy_registered(
def test_plugin_prevent_register_unregistered_already_registered(
self, pytestpm: PytestPluginManager
) -> None:
pytestpm.register(42, name="abc")

View File

@ -409,7 +409,7 @@ class BaseFunctionalTests:
# assert rep.outcome.when == "setup"
# assert rep.outcome.where.lineno == 3
# assert rep.outcome.where.path.basename == "test_func.py"
# assert instanace(rep.failed.failurerepr, PythonFailureRepr)
# assert isinstance(rep.failed.failurerepr, PythonFailureRepr)
def test_systemexit_does_not_bail_out(self, pytester: Pytester) -> None:
try:

View File

@ -926,7 +926,7 @@ class TestTerminalFunctional:
def test_header_absolute_testpath(
self, pytester: Pytester, monkeypatch: MonkeyPatch
) -> None:
"""Regresstion test for #7814."""
"""Regression test for #7814."""
tests = pytester.path.joinpath("tests")
tests.mkdir()
pytester.makepyprojecttoml(

View File

@ -1,5 +1,4 @@
# mypy: allow-untyped-defs
import gc
import sys
from typing import List
@ -192,30 +191,35 @@ def test_teardown(pytester: Pytester) -> None:
def test_teardown_issue1649(pytester: Pytester) -> None:
"""
Are TestCase objects cleaned up? Often unittest TestCase objects set
attributes that are large and expensive during setUp.
attributes that are large and expensive during test run or setUp.
The TestCase will not be cleaned up if the test fails, because it
would then exist in the stackframe.
Regression test for #1649 (see also #12367).
"""
testpath = pytester.makepyfile(
pytester.makepyfile(
"""
import unittest
class TestCaseObjectsShouldBeCleanedUp(unittest.TestCase):
def setUp(self):
self.an_expensive_object = 1
def test_demo(self):
pass
import gc
"""
class TestCaseObjectsShouldBeCleanedUp(unittest.TestCase):
def test_expensive(self):
self.an_expensive_obj = object()
def test_is_it_still_alive(self):
gc.collect()
for obj in gc.get_objects():
if type(obj).__name__ == "TestCaseObjectsShouldBeCleanedUp":
assert not hasattr(obj, "an_expensive_obj")
break
else:
assert False, "Could not find TestCaseObjectsShouldBeCleanedUp instance"
"""
)
pytester.inline_run("-s", testpath)
gc.collect()
# Either already destroyed, or didn't run setUp.
for obj in gc.get_objects():
if type(obj).__name__ == "TestCaseObjectsShouldBeCleanedUp":
assert not hasattr(obj, "an_expensive_obj")
result = pytester.runpytest()
assert result.ret == ExitCode.OK
def test_unittest_skip_issue148(pytester: Pytester) -> None:
@ -299,7 +303,7 @@ def test_setup_setUpClass(pytester: Pytester) -> None:
@classmethod
def tearDownClass(cls):
cls.x -= 1
def test_teareddown():
def test_torn_down():
assert MyTestCase.x == 0
"""
)
@ -346,7 +350,7 @@ def test_setup_class(pytester: Pytester) -> None:
assert self.x == 1
def teardown_class(cls):
cls.x -= 1
def test_teareddown():
def test_torn_down():
assert MyTestCase.x == 0
"""
)
@ -881,7 +885,7 @@ def test_non_unittest_no_setupclass_support(pytester: Pytester) -> None:
def tearDownClass(cls):
cls.x = 1
def test_not_teareddown():
def test_not_torn_down():
assert TestFoo.x == 0
"""

View File

@ -9,6 +9,7 @@ envlist =
py310
py311
py312
py313
pypy3
py38-{pexpect,xdist,unittestextras,numpy,pluggymain,pylib}
doctesting
@ -142,7 +143,7 @@ commands =
pytest --cov=. simple_integration.py
pytest --ds=django_settings simple_integration.py
pytest --html=simple.html simple_integration.py
pytest --reruns 5 simple_integration.py
pytest --reruns 5 simple_integration.py pytest_rerunfailures_integration.py
pytest pytest_anyio_integration.py
pytest pytest_asyncio_integration.py
pytest pytest_mock_integration.py