diff --git a/.coveragerc b/.coveragerc index 4eab3e33381..e5a68198b84 100644 --- a/.coveragerc +++ b/.coveragerc @@ -10,6 +10,9 @@ include = *\Lib\site-packages\pytest.py parallel = 1 branch = 1 +# The sysmon core (default since Python 3.14) is much slower. +# Perhaps: https://github.com/coveragepy/coveragepy/issues/2082 +core = ctrace [paths] source = src/ diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 176daa1b4d7..c44ef2d8210 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -135,6 +135,7 @@ jobs: - name: Publish GitHub Release env: VERSION: ${{ github.event.inputs.version }} + GH_REPO: ${{ github.repository }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | gh release create --notes-file gh-release-notes.md --verify-tag "$VERSION" dist/* diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fbee593f7c2..b7f0634d08d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -139,6 +139,7 @@ jobs: python: "3.14" os: windows-latest tox_env: "py314" + use_coverage: true # Use separate jobs for different unittest flavors (twisted, asynctest) to ensure proper coverage. - name: "ubuntu-py310-unittest-asynctest" @@ -276,11 +277,15 @@ jobs: - name: Test without coverage if: "! matrix.use_coverage" shell: bash + env: + _PYTEST_TOX_POSARGS_JUNIT: --junitxml=junit.xml run: tox run -e ${{ matrix.tox_env }} --installpkg `find dist/*.tar.gz` - name: Test with coverage if: "matrix.use_coverage" shell: bash + env: + _PYTEST_TOX_POSARGS_JUNIT: --junitxml=junit.xml run: tox run -e ${{ matrix.tox_env }}-coverage --installpkg `find dist/*.tar.gz` - name: Generate coverage report @@ -295,6 +300,14 @@ jobs: files: ./coverage.xml verbose: true + - name: Upload JUnit report to Codecov + uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 + with: + fail_ci_if_error: false + files: junit.xml + report_type: test_results + verbose: true + check: # This job does nothing and is only used for the branch protection if: always() @@ -305,6 +318,6 @@ jobs: steps: - name: Decide whether the needed jobs succeeded or failed - uses: re-actors/alls-green@2765efec08f0fd63e83ad900f5fd75646be69ff6 + uses: re-actors/alls-green@a638d6464689bbb24c325bb3fe9404d63a913030 with: jobs: ${{ toJSON(needs) }} diff --git a/.gitignore b/.gitignore index c4557b33a1c..d0e8dc54ba1 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,7 @@ coverage.xml .vscode __pycache__/ .python-version +.claude/settings.local.json # generated by pip pip-wheel-metadata/ diff --git a/.mailmap b/.mailmap new file mode 100644 index 00000000000..682334c7430 --- /dev/null +++ b/.mailmap @@ -0,0 +1,2 @@ +Freya Bruhin +Freya Bruhin diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a4af51ee0f2..2f9f56256b6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,3 +1,4 @@ +minimum_pre_commit_version: "4.4.0" repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: "v0.14.3" @@ -92,10 +93,10 @@ repos: stages: [manual] - id: rst name: rst - entry: rst-lint --encoding utf-8 + entry: rst-lint files: ^(RELEASING.rst|README.rst|TIDELIFT.rst)$ language: python - additional_dependencies: [pygments, restructuredtext_lint] + additional_dependencies: [pygments, restructuredtext_lint>=2.0.0] - id: changelogs-rst name: changelog filenames language: fail diff --git a/.readthedocs.yaml b/.readthedocs.yaml index f7370f1bb98..6380b34adec 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -17,7 +17,7 @@ build: os: ubuntu-24.04 tools: python: >- - 3.12 + 3.13 apt_packages: - inkscape jobs: diff --git a/AUTHORS b/AUTHORS index 83509281035..af895ed1855 100644 --- a/AUTHORS +++ b/AUTHORS @@ -14,12 +14,14 @@ Ahn Ki-Wook Akhilesh Ramakrishnan Akiomi Kamakura Alan Velasco +Alejandro Villate Alessio Izzo Alex Jones Alex Lambson Alexander Johnson Alexander King Alexei Kozlenok +algojogacor Alice Purcell Allan Feldman Aly Sivji @@ -130,6 +132,7 @@ David Szotten David Vierra Daw-Ran Liou Debi Mishra +Denis Cherednichenko Denis Kirisov Denivy Braiam Rück Deysha Rivera @@ -170,13 +173,14 @@ faph Felix Hofstätter Felix Nieuwenhuizen Feng Ma -Florian Bruhin Florian Dahlitz Floris Bruynooghe Frank Hoffmann Fraser Stark +Freya Bruhin Gabriel Landau Gabriel Reis +Garion Milazzo Garvit Shubham Gene Wood George Kussumoto @@ -311,6 +315,7 @@ Michael Goerz Michael Krebs Michael Seifert Michael Vogt +Michael Reznik Michal Wajszczuk Michał Górny Michał Zięba @@ -369,6 +374,7 @@ Piotr Banaszkiewicz Piotr Helm Poulami Sau Prakhar Gurunani +Praneeth Kodumagulla Prashant Anand Prashant Sharma Pulkit Goyal @@ -379,6 +385,7 @@ Ralf Schmitt Ralph Giles Ram Rachum Ran Benita +Randy Döring Raphael Castaneda Raphael Pierzina Rafal Semik @@ -459,6 +466,7 @@ TJ Bruno Tobias Diez Tobias Petersen Tom Dalton +Tom Most Tom Viner Tomáš Gavenčiak Tomer Keren diff --git a/CITATION b/CITATION index 98beee72209..ac7c5d6f312 100644 --- a/CITATION +++ b/CITATION @@ -10,19 +10,19 @@ BibLaTeX: @software{pytest, title = {pytest x.y}, - author = {Holger Krekel and Bruno Oliveira and Ronny Pfannschmidt and Floris Bruynooghe and Brianna Laugher and Florian Bruhin}, + author = {Holger Krekel and Bruno Oliveira and Ronny Pfannschmidt and Floris Bruynooghe and Brianna Laugher and Freya Bruhin}, year = {2004}, version = {x.y}, url = {https://github.com/pytest-dev/pytest}, - note = {Contributors: Holger Krekel and Bruno Oliveira and Ronny Pfannschmidt and Floris Bruynooghe and Brianna Laugher and Florian Bruhin and others} + note = {Contributors: Holger Krekel and Bruno Oliveira and Ronny Pfannschmidt and Floris Bruynooghe and Brianna Laugher and Freya Bruhin and others} } BibTeX: @misc{pytest, - author = {Holger Krekel and Bruno Oliveira and Ronny Pfannschmidt and Floris Bruynooghe and Brianna Laugher and Florian Bruhin}, + author = {Holger Krekel and Bruno Oliveira and Ronny Pfannschmidt and Floris Bruynooghe and Brianna Laugher and Freya Bruhin}, title = {pytest x.y}, year = {2004}, howpublished = {\url{https://github.com/pytest-dev/pytest}}, - note = {Version x.y. Contributors include Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin, and others.} + note = {Version x.y. Contributors include Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Freya Bruhin, and others.} } diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index f0ca304be4e..14d56263449 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -70,7 +70,7 @@ contacted individually: - Brianna Laugher ([@pfctdayelise](https://github.com/pfctdayelise)): brianna@laugher.id.au - Bruno Oliveira ([@nicoddemus](https://github.com/nicoddemus)): nicoddemus@gmail.com -- Florian Bruhin ([@the-compiler](https://github.com/the-compiler)): pytest@the-compiler.org +- Freya Bruhin ([@the-compiler](https://github.com/the-compiler)): pytest@the-compiler.org ## Attribution diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index f31c14aec49..fb9f7f4d53d 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -146,7 +146,7 @@ the following: - PyPI presence with packaging metadata that contains a ``pytest-`` prefixed name, version number, authors, short and long description. -- a `tox configuration `_ +- a `tox configuration `_ for running tests using `tox `_. - a ``README`` describing how to use the plugin and on which @@ -280,7 +280,7 @@ Here is a simple overview, with pytest-specific bits: #. You can now edit your local working copy and run the tests again as necessary. Please follow `PEP-8 `_ for naming. You can pass different options to ``tox``. For example, to run tests on Python 3.13 and pass options to pytest - (e.g. enter pdb on failure) to pytest you can do:: + (e.g. enter pdb on failure) you can do:: $ tox -e py313 -- --pdb @@ -346,7 +346,7 @@ For example, to ensure a simple test passes you can write: result.assert_outcomes(failed=0, passed=1) -Alternatively, it is possible to make checks based on the actual output of the termal using +Alternatively, it is possible to make checks based on the actual output of the terminal using *glob-like* expressions: .. code-block:: python @@ -479,10 +479,10 @@ above? to do the backport. 2. However, often the merge is done by another maintainer, in which case it is nice of them to do the backport procedure if they have the time. -3. For bugs submitted by non-maintainers, it is expected that a core developer will to do +3. For bugs submitted by non-maintainers, it is expected that a core developer will do the backport, normally the one that merged the PR on ``main``. -4. If a non-maintainers notices a bug which is fixed on ``main`` but has not been backported - (due to maintainers forgetting to apply the *needs backport* label, or just plain missing it), +4. If a non-maintainer notices a bug which is fixed on ``main`` but has not been backported + (due to maintainers forgetting to apply the *needs backport* or *backport x.x.x* labels, or just plain missing it), they are also welcome to open a PR with the backport. The procedure is simple and really helps with the maintenance of the project. @@ -512,7 +512,7 @@ can always reopen the issue/pull request in their own time later if it makes sen When to close ~~~~~~~~~~~~~ -Here are a few general rules the maintainers use deciding when to close issues/PRs because +Here are a few general rules the maintainers use to decide when to close issues/PRs because of lack of inactivity: * Issues labeled ``question`` or ``needs information``: closed after 14 days inactive. @@ -524,7 +524,7 @@ The above are **not hard rules**, but merely **guidelines**, and can be (and oft Closing pull requests ~~~~~~~~~~~~~~~~~~~~~ -When closing a Pull Request, it needs to be acknowledging the time, effort, and interest demonstrated by the person which submitted it. As mentioned previously, it is not the intent of the team to dismiss a stalled pull request entirely but to merely to clear up our queue, so a message like the one below is warranted when closing a pull request that went stale: +When closing a Pull Request, we should acknowledge the time, effort, and interest demonstrated by the person who submitted it. As mentioned previously, it is not the intent of the team to dismiss a stalled pull request entirely but to merely to clear up our queue, so a message like the one below is warranted when closing a pull request that went stale: Hi , @@ -532,7 +532,7 @@ When closing a Pull Request, it needs to be acknowledging the time, effort, and We noticed it has been awhile since you have updated this PR, however. pytest is a high activity project, with many issues/PRs being opened daily, so it is hard for us maintainers to track which PRs are ready for merging, for review, or need more attention. - So for those reasons we, think it is best to close the PR for now, but with the only intention to clean up our queue, it is by no means a rejection of your changes. We still encourage you to re-open this PR (it is just a click of a button away) when you are ready to get back to it. + So for those reasons, we think it is best to close the PR for now, but with the only intention to clean up our queue, it is by no means a rejection of your changes. We still encourage you to re-open this PR (it is just a click of a button away) when you are ready to get back to it. Again we appreciate your time for working on this, and hope you might get back to this at a later time! diff --git a/RELEASING.rst b/RELEASING.rst index 79b4e2f764d..2b00e658e7a 100644 --- a/RELEASING.rst +++ b/RELEASING.rst @@ -117,7 +117,7 @@ To release a version ``MAJOR.MINOR.PATCH``, follow these steps: #. Create a branch ``release-MAJOR.MINOR.PATCH`` from the ``MAJOR.MINOR.x`` branch. - Ensure your are updated and in a clean working tree. + Ensure your local checkout is up to date and in a clean working tree. #. Using ``tox``, generate docs, changelog, announcements:: diff --git a/changelog/12083.breaking.rst b/changelog/12083.breaking.rst deleted file mode 100644 index 53a8393dfb4..00000000000 --- a/changelog/12083.breaking.rst +++ /dev/null @@ -1,9 +0,0 @@ -Fixed a bug where an invocation such as `pytest a/ a/b` would cause only tests from `a/b` to run, and not other tests under `a/`. - -The fix entails a few breaking changes to how such overlapping arguments and duplicates are handled: - -1. `pytest a/b a/` or `pytest a/ a/b` are equivalent to `pytest a`; if an argument overlaps another arguments, only the prefix remains. - -2. `pytest x.py x.py` is equivalent to `pytest x.py`; previously such an invocation was taken as an explicit request to run the tests from the file twice. - -If you rely on these behaviors, consider using :ref:`--keep-duplicates `, which retains its existing behavior (including the bug). diff --git a/changelog/12244.contrib.rst b/changelog/12244.contrib.rst deleted file mode 100644 index e39dad1cfbf..00000000000 --- a/changelog/12244.contrib.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed self-test failures when `TERM=dumb`. diff --git a/changelog/12474.contrib.rst b/changelog/12474.contrib.rst deleted file mode 100644 index bb668fd66c5..00000000000 --- a/changelog/12474.contrib.rst +++ /dev/null @@ -1 +0,0 @@ -Added scheduled GitHub Action Workflow to run Sphinx linkchecks in repo documentation. diff --git a/changelog/13072.feature.rst b/changelog/13072.feature.rst deleted file mode 100644 index 6f9fd11e540..00000000000 --- a/changelog/13072.feature.rst +++ /dev/null @@ -1,8 +0,0 @@ -Added support for displaying test session progress in the terminal tab using the `OSC 9;4; `_ ANSI sequence. -When pytest runs in a supported terminal emulator like ConEmu, Gnome Terminal, Ptyxis, Windows Terminal, Kitty or Ghostty, -you'll see the progress in the terminal tab or window, -allowing you to monitor pytest's progress at a glance. - -This feature is automatically enabled when running in a TTY. It is implemented as an internal plugin. If needed, it can be disabled as follows: -- On a user level, using ``-p no:terminalprogress`` on the command line or via an environment variable ``PYTEST_ADDOPTS='-p no:terminalprogress'``. -- On a project configuration level, using ``addopts = "-p no:terminalprogress"``. diff --git a/changelog/13155.doc.rst b/changelog/13155.doc.rst new file mode 100644 index 00000000000..a4faf22fa4a --- /dev/null +++ b/changelog/13155.doc.rst @@ -0,0 +1 @@ +Clarified how the ``request`` fixture provides indirect parametrization values via ``request.param``. diff --git a/changelog/13330.improvement.rst b/changelog/13330.improvement.rst deleted file mode 100644 index 58350ea30ef..00000000000 --- a/changelog/13330.improvement.rst +++ /dev/null @@ -1,3 +0,0 @@ -Having pytest configuration spread over more than one file (for example having both a ``pytest.ini`` file and ``pyproject.toml`` with a ``[tool.pytest.ini_options]`` table) will now print a warning to make it clearer to the user that only one of them is actually used. - --- by :user:`sgaist` diff --git a/changelog/13445.bugfix.rst b/changelog/13445.bugfix.rst deleted file mode 100644 index 1c601a1045e..00000000000 --- a/changelog/13445.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Made the type annotations of :func:`pytest.skip` and friends more spec-complaint to have them work across more type checkers. diff --git a/changelog/13522.bugfix.rst b/changelog/13522.bugfix.rst deleted file mode 100644 index 683304251aa..00000000000 --- a/changelog/13522.bugfix.rst +++ /dev/null @@ -1,5 +0,0 @@ -Fixed :fixture:`pytester` in subprocess mode ignored all :attr`pytester.plugins ` except the first. - -Fixed :fixture:`pytester` in subprocess mode silently ignored non-str :attr:`pytester.plugins `. -Now it errors instead. -If you are affected by this, specify the plugin by name, or switch the affected tests to use :func:`pytester.runpytest_inprocess ` explicitly instead. diff --git a/changelog/13537.bugfix.rst b/changelog/13537.bugfix.rst deleted file mode 100644 index 47743b24f05..00000000000 --- a/changelog/13537.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fix bug in which ExceptionGroup with only Skipped exceptions in teardown was not handled correctly and showed as error. diff --git a/changelog/13574.improvement.rst b/changelog/13574.improvement.rst deleted file mode 100644 index 7820cd03fac..00000000000 --- a/changelog/13574.improvement.rst +++ /dev/null @@ -1,7 +0,0 @@ -The single argument ``--version`` no longer loads the entire plugin infrastructure, making it faster and more reliable when displaying only the pytest version. - -Passing ``--version`` twice (e.g., ``pytest --version --version``) retains the original behavior, showing both the pytest version and plugin information. - -.. note:: - - Since ``--version`` is now processed early, it only takes effect when passed directly via the command line. It will not work if set through other mechanisms, such as :envvar:`PYTEST_ADDOPTS` or :confval:`addopts`. diff --git a/changelog/13598.bugfix.rst b/changelog/13598.bugfix.rst deleted file mode 100644 index dd195112eb8..00000000000 --- a/changelog/13598.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed possible collection confusion on Windows when short paths and symlinks are involved. diff --git a/changelog/13621.contrib.rst b/changelog/13621.contrib.rst deleted file mode 100644 index c5e622b7619..00000000000 --- a/changelog/13621.contrib.rst +++ /dev/null @@ -1 +0,0 @@ -pytest's own testsuite now handles the ``lsof`` command hanging (e.g. due to unreachable network filesystems), with the affected selftests being skipped after 10 seconds. diff --git a/changelog/13625.doc.rst b/changelog/13625.doc.rst deleted file mode 100644 index 850c81583b6..00000000000 --- a/changelog/13625.doc.rst +++ /dev/null @@ -1 +0,0 @@ -Added missing docstrings to ``pytest_addoption()``, ``pytest_configure()``, and ``cacheshow()`` functions in ``cacheprovider.py``. diff --git a/changelog/13638.contrib.rst b/changelog/13638.contrib.rst deleted file mode 100644 index 8eb06298c77..00000000000 --- a/changelog/13638.contrib.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixed deprecated :command:`gh pr new` command in :file:`scripts/prepare-release-pr.py`. -The script now uses :command:`gh pr create` which is compatible with GitHub CLI v2.0+. diff --git a/changelog/1367.feature.rst b/changelog/1367.feature.rst deleted file mode 100644 index b88480338b5..00000000000 --- a/changelog/1367.feature.rst +++ /dev/null @@ -1,28 +0,0 @@ -**Support for subtests** has been added. - -:ref:`subtests ` are an alternative to parametrization, useful in situations where the parametrization values are not all known at collection time. - -**Example** - -.. code-block:: python - - def contains_docstring(p: Path) -> bool: - """Return True if the given Python file contains a top-level docstring.""" - ... - - - def test_py_files_contain_docstring(subtests: pytest.Subtests) -> None: - for path in Path.cwd().glob("*.py"): - with subtests.test(path=str(path)): - assert contains_docstring(path) - - -Each assert failure or error is caught by the context manager and reported individually, giving a clear picture of all files that are missing a docstring. - -In addition, :meth:`unittest.TestCase.subTest` is now also supported. - -This feature was originally implemented as a separate plugin in `pytest-subtests `__, but since then has been merged into the core. - -.. note:: - - This feature is experimental and will likely evolve in future releases. By that we mean that we might change how subtests are reported on failure, but the functionality and how to use it are stable. diff --git a/changelog/13678.feature.rst b/changelog/13678.feature.rst deleted file mode 100644 index 63c7dfa399f..00000000000 --- a/changelog/13678.feature.rst +++ /dev/null @@ -1,3 +0,0 @@ -Added a new `faulthandler_exit_on_timeout` ini option set to "false" by default to let `faulthandler` interrupt the `pytest` process after a timeout in case of deadlock -- by :user:`ogrisel`. - -Previously, a `faulthandler` timeout would only dump the traceback of all threads to stderr, but would not interrupt the `pytest` process. diff --git a/changelog/13695.contrib.rst b/changelog/13695.contrib.rst deleted file mode 100644 index 675e1fc96a0..00000000000 --- a/changelog/13695.contrib.rst +++ /dev/null @@ -1 +0,0 @@ -Flush `stdout` and `stderr` in `Pytester.run` to avoid truncated outputs in `test_faulthandler.py::test_timeout` on CI -- by :user:`ogrisel`. diff --git a/changelog/13700.improvement.rst b/changelog/13700.improvement.rst deleted file mode 100644 index 40753ae5d5f..00000000000 --- a/changelog/13700.improvement.rst +++ /dev/null @@ -1 +0,0 @@ -`--junitxml` no longer prints the `generated xml file` summary at the end of the pytest session when `--quiet` is given. diff --git a/changelog/13716.bugfix.rst b/changelog/13716.bugfix.rst deleted file mode 100644 index 5eeef4b7624..00000000000 --- a/changelog/13716.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed a bug where a nonsensical invocation like ``pytest x.py[a]`` (a file cannot be parametrized) was silently treated as ``pytest x.py``. This is now a usage error. diff --git a/changelog/13719.breaking.rst b/changelog/13719.breaking.rst deleted file mode 100644 index 328c7dfcf2b..00000000000 --- a/changelog/13719.breaking.rst +++ /dev/null @@ -1 +0,0 @@ -Support for Python 3.9 is dropped following its end of life. diff --git a/changelog/13722.bugfix.rst b/changelog/13722.bugfix.rst deleted file mode 100644 index ec36f466815..00000000000 --- a/changelog/13722.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed a misleading assertion failure message when using :func:`pytest.approx` on mappings with differing lengths. diff --git a/changelog/13732.improvement.rst b/changelog/13732.improvement.rst deleted file mode 100644 index 07b3d620cf6..00000000000 --- a/changelog/13732.improvement.rst +++ /dev/null @@ -1 +0,0 @@ -Previously, when filtering warnings, pytest would fail if the filter referenced a class that could not be imported. Now, this only outputs a message indicating the problem. diff --git a/changelog/13737.feature.rst b/changelog/13737.feature.rst deleted file mode 100644 index e7fe7fd3ab7..00000000000 --- a/changelog/13737.feature.rst +++ /dev/null @@ -1,4 +0,0 @@ -Added the :confval:`strict_parametrization_ids` configuration option. - -When set, pytest emits an error if it detects non-unique parameter set IDs, -rather than automatically making the IDs unique by adding `0`, `1`, ... to them. diff --git a/changelog/13743.feature.rst b/changelog/13743.feature.rst deleted file mode 100644 index b36782487f5..00000000000 --- a/changelog/13743.feature.rst +++ /dev/null @@ -1,37 +0,0 @@ -Added support for native TOML configuration files. - -While pytest, since version 6, supports configuration in ``pyproject.toml`` files under ``[tool.pytest.ini_options]``, -it does so in an "INI compatibility mode", where all configuration values are treated as strings or list of strings. -Now, pytest supports the native TOML data model. - -In ``pyproject.toml``, the native TOML configuration is under the ``[tool.pytest]`` table. - -.. code-block:: toml - - # pyproject.toml - [tool.pytest] - minversion = "9.0" - addopts = ["-ra", "-q"] - testpaths = [ - "tests", - "integration", - ] - -The ``[tool.pytest.ini_options]`` table remains supported, but both tables cannot be used at the same time. - -If you prefer to use a separate configuration file, or don't use ``pyproject.toml``, you can use ``pytest.toml`` or ``.pytest.toml``: - -.. code-block:: toml - - # pytest.toml or .pytest.toml - [pytest] - minversion = "9.0" - addopts = ["-ra", "-q"] - testpaths = [ - "tests", - "integration", - ] - -The documentation now shows configuration snippets in both TOML and INI formats, in a tabbed interface. - -See :ref:`config file formats` for full details. diff --git a/changelog/13766.breaking.rst b/changelog/13766.breaking.rst deleted file mode 100644 index d7bd5a9a0ee..00000000000 --- a/changelog/13766.breaking.rst +++ /dev/null @@ -1,2 +0,0 @@ -Previously, pytest would assume it was running in a CI/CD environment if either of the environment variables `$CI` or `$BUILD_NUMBER` was defined; -now, CI mode is only activated if at least one of those variables is defined and set to a *non-empty* value. diff --git a/changelog/13771.contrib.rst b/changelog/13771.contrib.rst deleted file mode 100644 index fa5cc8afee5..00000000000 --- a/changelog/13771.contrib.rst +++ /dev/null @@ -1 +0,0 @@ -Skip `test_do_not_collect_symlink_siblings` on Windows environments without symlink support to avoid false negatives. diff --git a/changelog/13773.bugfix.rst b/changelog/13773.bugfix.rst deleted file mode 100644 index e3a9ff4b7a1..00000000000 --- a/changelog/13773.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed the static fixture closure calculation to properly consider transitive dependencies requested by overridden fixtures. diff --git a/changelog/13779.breaking.rst b/changelog/13779.breaking.rst deleted file mode 100644 index f7e44c27dfa..00000000000 --- a/changelog/13779.breaking.rst +++ /dev/null @@ -1,22 +0,0 @@ -**PytestRemovedIn9Warning deprecation warnings are now errors by default.** - -Following our plan to remove deprecated features with as little disruption as -possible, all warnings of type ``PytestRemovedIn9Warning`` now generate errors -instead of warning messages by default. - -**The affected features will be effectively removed in pytest 9.1**, so please consult the -:ref:`deprecations` section in the docs for directions on how to update existing code. - -In the pytest ``9.0.X`` series, it is possible to change the errors back into warnings as a -stopgap measure by adding this to your ``pytest.ini`` file: - -.. code-block:: ini - - [pytest] - filterwarnings = - ignore::pytest.PytestRemovedIn9Warning - -But this will stop working when pytest ``9.1`` is released. - -**If you have concerns** about the removal of a specific feature, please add a -comment to :issue:`13779`. diff --git a/changelog/13791.packaging.rst b/changelog/13791.packaging.rst deleted file mode 100644 index d16065e85a5..00000000000 --- a/changelog/13791.packaging.rst +++ /dev/null @@ -1,2 +0,0 @@ -Minimum requirements on ``iniconfig`` and ``packaging`` were bumped -to ``1.0.1`` and ``22.0.0``, respectively. diff --git a/changelog/13807.deprecation.rst b/changelog/13807.deprecation.rst deleted file mode 100644 index 59bd62214e1..00000000000 --- a/changelog/13807.deprecation.rst +++ /dev/null @@ -1,3 +0,0 @@ -:meth:`monkeypatch.syspath_prepend() ` now issues a deprecation warning when the prepended path contains legacy namespace packages (those using ``pkg_resources.declare_namespace()``). -Users should migrate to native namespace packages (:pep:`420`). -See :ref:`monkeypatch-fixup-namespace-packages` for details. diff --git a/changelog/13816.bugfix.rst b/changelog/13816.bugfix.rst deleted file mode 100644 index 432fc519ed5..00000000000 --- a/changelog/13816.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed :func:`pytest.approx` which now returns a clearer error message when comparing mappings with different keys. diff --git a/changelog/13823.feature.rst b/changelog/13823.feature.rst deleted file mode 100644 index b6152ff50ea..00000000000 --- a/changelog/13823.feature.rst +++ /dev/null @@ -1,18 +0,0 @@ -Added a :confval:`strict` configuration option to enable all strictness-related options. - -When set to ``true``, the :confval:`strict` option currently enables - -* :confval:`strict_config` -* :confval:`strict_markers` -* :confval:`strict_parametrization_ids` -* :confval:`strict_xfail` - -The individual strictness options can be explicitly set to override the global :confval:`strict` setting. - -The previously-deprecated ``--strict`` command-line flag now enables strict mode. - -If pytest adds new strictness options in the future, they will also be enabled in strict mode. -Therefore, you should only enable strict mode if you use a pinned/locked version of pytest, -or if you want to proactively adopt new strictness options as they are added. - -See :ref:`strict mode` for more details. diff --git a/changelog/13823.improvement.rst b/changelog/13823.improvement.rst deleted file mode 100644 index 4346d702c8c..00000000000 --- a/changelog/13823.improvement.rst +++ /dev/null @@ -1,4 +0,0 @@ -Added :confval:`strict_xfail` as an alias to the ``xfail_strict`` option, -:confval:`strict_config` as an alias to the ``--strict-config`` flag, -and :confval:`strict_markers` as an alias to the ``--strict-markers`` flag. -This makes all strictness options consistently have configuration options with the prefix ``strict_``. diff --git a/changelog/13829.feature.rst b/changelog/13829.feature.rst deleted file mode 100644 index 5f80ca5ac2f..00000000000 --- a/changelog/13829.feature.rst +++ /dev/null @@ -1,5 +0,0 @@ -Added support for ini option aliases via the ``aliases`` parameter in :meth:`Parser.addini() `. - -Plugins can now register alternative names for ini options, -allowing for more flexibility in configuration naming and supporting backward compatibility when renaming options. -The canonical name always takes precedence if both the canonical name and an alias are specified in the configuration file. diff --git a/changelog/13830.misc.rst b/changelog/13830.misc.rst deleted file mode 100644 index 0d81ce2f14f..00000000000 --- a/changelog/13830.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Configuration overrides (``-o``/``--override-ini``) are now processed during startup rather than during :func:`config.getini() `. diff --git a/changelog/13841.contrib.rst b/changelog/13841.contrib.rst deleted file mode 100644 index e5672634700..00000000000 --- a/changelog/13841.contrib.rst +++ /dev/null @@ -1 +0,0 @@ -``tox>=4`` is now required when contributing to pytest. diff --git a/changelog/13849.bugfix.rst b/changelog/13849.bugfix.rst deleted file mode 100644 index cdcc7b83591..00000000000 --- a/changelog/13849.bugfix.rst +++ /dev/null @@ -1,2 +0,0 @@ -Hidden ``.pytest.ini`` files are now picked up as the config file even if empty. -This was an inconsistency with non-hidden ``pytest.ini``. diff --git a/changelog/13859.improvement.rst b/changelog/13859.improvement.rst deleted file mode 100644 index b785381fd68..00000000000 --- a/changelog/13859.improvement.rst +++ /dev/null @@ -1 +0,0 @@ -Clarify the error message for `pytest.raises()` when a regex `match` fails. diff --git a/changelog/13861.improvement.rst b/changelog/13861.improvement.rst deleted file mode 100644 index 2f062404e1c..00000000000 --- a/changelog/13861.improvement.rst +++ /dev/null @@ -1 +0,0 @@ -Better sentence structure in a test's expected error message. Previously, the error message would be "expected exception must be , but got ". Now, it is "Expected , but got ". diff --git a/changelog/13865.bugfix.rst b/changelog/13865.bugfix.rst deleted file mode 100644 index f7cd94c2a30..00000000000 --- a/changelog/13865.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed `--show-capture` with `--tb=line`. diff --git a/changelog/13902.doc.rst b/changelog/13902.doc.rst new file mode 100644 index 00000000000..f11a1936051 --- /dev/null +++ b/changelog/13902.doc.rst @@ -0,0 +1 @@ +Clarified how subtest progress markers are shown in the documentation. diff --git a/changelog/14148.doc.rst b/changelog/14148.doc.rst new file mode 100644 index 00000000000..b54ae2cd8e3 --- /dev/null +++ b/changelog/14148.doc.rst @@ -0,0 +1,2 @@ +Documented a safe ``pytestconfig.cache`` access pattern when the +``cacheprovider`` plugin is disabled. diff --git a/changelog/14381.bugfix.rst b/changelog/14381.bugfix.rst new file mode 100644 index 00000000000..d3552a6304d --- /dev/null +++ b/changelog/14381.bugfix.rst @@ -0,0 +1 @@ +Fixed ``-V`` (short form of ``--version``) to properly display the current version. diff --git a/changelog/14389.bugfix.rst b/changelog/14389.bugfix.rst new file mode 100644 index 00000000000..f0d85169d0b --- /dev/null +++ b/changelog/14389.bugfix.rst @@ -0,0 +1 @@ +Improved :func:`pytest.raises(..., match=...) ` failures to suppress the mismatched exception as a cause of the resulting ``AssertionError``. diff --git a/changelog/14442.bugfix.rst b/changelog/14442.bugfix.rst new file mode 100644 index 00000000000..90999cc9572 --- /dev/null +++ b/changelog/14442.bugfix.rst @@ -0,0 +1,3 @@ +Fixed a regression in pytest 9.0 where :option:`--strict-markers` and :option:`--strict-config` specified through :confval:`addopts` were silently ignored. + +Note that when targeting pytest >= 9.0, it's nicer to use :confval:`strict_markers` and :confval:`strict_config`, or :ref:`strict mode `. diff --git a/changelog/14456.bugfix.rst b/changelog/14456.bugfix.rst new file mode 100644 index 00000000000..ccd9ca20a8f --- /dev/null +++ b/changelog/14456.bugfix.rst @@ -0,0 +1 @@ +Fixed :func:`pytest.approx` not recognizing types with ``__array_interface__`` as numpy-like arrays. diff --git a/changelog/14474.bugfix.rst b/changelog/14474.bugfix.rst new file mode 100644 index 00000000000..333d4d34d9a --- /dev/null +++ b/changelog/14474.bugfix.rst @@ -0,0 +1 @@ +Fixed a regression where ``-k`` and ``-m`` expressions containing both backslash characters in identifiers and string literal arguments would incorrectly raise a ``SyntaxError`` about escaping. diff --git a/changelog/14483.bugfix.rst b/changelog/14483.bugfix.rst new file mode 100644 index 00000000000..cdc5d0f5af7 --- /dev/null +++ b/changelog/14483.bugfix.rst @@ -0,0 +1 @@ +Fixed JUnit XML report incorrectly escaping high Unicode codepoints (supplementary plane characters like emoji) in test failure messages. -- by :user:`EternalRights` diff --git a/changelog/14492.bugfix.rst b/changelog/14492.bugfix.rst new file mode 100644 index 00000000000..1e5c6b778d3 --- /dev/null +++ b/changelog/14492.bugfix.rst @@ -0,0 +1 @@ +Fixed ``Code.getargs()`` incorrectly including local variable names in the returned argument tuple for functions with ``*args`` and/or ``**kwargs``. The method was using ``co_flags`` bitmask values (``4`` and ``8``) directly as counts instead of converting them to ``1`` via ``bool()``, and was not accounting for ``co_kwonlyargcount`` when ``var=True``. diff --git a/changelog/478.feature.rst b/changelog/478.feature.rst deleted file mode 100644 index cd13ddd9733..00000000000 --- a/changelog/478.feature.rst +++ /dev/null @@ -1,3 +0,0 @@ -Support PEP420 (implicit namespace packages) as `--pyargs` target when :confval:`consider_namespace_packages` is `true` in the config. - -Previously, this option only impacted package names, now it also impacts tests discovery. diff --git a/changelog/README.rst b/changelog/README.rst index fdaa573d427..f1ba2cbd0bd 100644 --- a/changelog/README.rst +++ b/changelog/README.rst @@ -16,12 +16,12 @@ Each file should be named like ``..rst``, where * ``feature``: new user facing features, like new command-line options and new behavior. * ``improvement``: improvement of existing functionality, usually without requiring user intervention (for example, new fields being written in ``--junit-xml``, improved colors in terminal, etc). * ``bugfix``: fixes a bug. -* ``doc``: documentation improvement, like rewording an entire session or adding missing docs. +* ``doc``: documentation improvement, like rewording an entire section or adding missing docs. * ``deprecation``: feature deprecation. * ``breaking``: a change which may break existing suites, such as feature removal or behavior change. * ``vendor``: changes in packages vendored in pytest. * ``packaging``: notes for downstreams about unobvious side effects - and tooling. changes in the test invocation considerations and + and tooling. Changes in the test invocation considerations and runtime assumptions. * ``contrib``: stuff that affects the contributor experience. e.g. Running tests, building the docs, setting up the development diff --git a/doc/en/announce/index.rst b/doc/en/announce/index.rst index 1cc8fbaf7c0..4a5e8b86544 100644 --- a/doc/en/announce/index.rst +++ b/doc/en/announce/index.rst @@ -6,6 +6,10 @@ Release announcements :maxdepth: 2 + release-9.0.3 + release-9.0.2 + release-9.0.1 + release-9.0.0 release-8.4.2 release-8.4.1 release-8.4.0 diff --git a/doc/en/announce/release-2.8.2.rst b/doc/en/announce/release-2.8.2.rst index e4726338852..f64ea9bb29a 100644 --- a/doc/en/announce/release-2.8.2.rst +++ b/doc/en/announce/release-2.8.2.rst @@ -17,7 +17,7 @@ Thanks to all who contributed to this release, among them: Bruno Oliveira Demian Brecht - Florian Bruhin + Freya Bruhin Ionel Cristian Mărieș Raphael Pierzina Ronny Pfannschmidt diff --git a/doc/en/announce/release-2.8.3.rst b/doc/en/announce/release-2.8.3.rst index 3f357252bb6..1ea7aac6d74 100644 --- a/doc/en/announce/release-2.8.3.rst +++ b/doc/en/announce/release-2.8.3.rst @@ -16,7 +16,7 @@ As usual, you can upgrade from pypi via:: Thanks to all who contributed to this release, among them: Bruno Oliveira - Florian Bruhin + Freya Bruhin Gabe Hollombe Gabriel Reis Hartmut Goebel diff --git a/doc/en/announce/release-2.8.4.rst b/doc/en/announce/release-2.8.4.rst index adbdecc87ea..0605c986928 100644 --- a/doc/en/announce/release-2.8.4.rst +++ b/doc/en/announce/release-2.8.4.rst @@ -16,7 +16,7 @@ As usual, you can upgrade from pypi via:: Thanks to all who contributed to this release, among them: Bruno Oliveira - Florian Bruhin + Freya Bruhin Jeff Widman Mehdy Khoshnoody Nicholas Chammas @@ -43,10 +43,10 @@ The py.test Development Team non-ascii characters. Thanks Bruno Oliveira for the PR. - fix #1204: another error when collecting with a nasty __getattr__(). - Thanks Florian Bruhin for the PR. + Thanks Freya Bruhin for the PR. - fix the summary printed when no tests did run. - Thanks Florian Bruhin for the PR. + Thanks Freya Bruhin for the PR. - a number of documentation modernizations wrt good practices. Thanks Bruno Oliveira for the PR. diff --git a/doc/en/announce/release-2.8.6.rst b/doc/en/announce/release-2.8.6.rst index 5d6565b16a3..a63c7f1e38d 100644 --- a/doc/en/announce/release-2.8.6.rst +++ b/doc/en/announce/release-2.8.6.rst @@ -18,7 +18,7 @@ Thanks to all who contributed to this release, among them: AMiT Kumar Bruno Oliveira Erik M. Bray - Florian Bruhin + Freya Bruhin Georgy Dyuldin Jeff Widman Kartik Singhal diff --git a/doc/en/announce/release-2.9.0.rst b/doc/en/announce/release-2.9.0.rst index 753bb7bf6f0..9477f0a9ba3 100644 --- a/doc/en/announce/release-2.9.0.rst +++ b/doc/en/announce/release-2.9.0.rst @@ -18,7 +18,7 @@ Thanks to all who contributed to this release, among them: Bruno Oliveira Buck Golemon David Vierra - Florian Bruhin + Freya Bruhin Galaczi Endre Georgy Dyuldin Lukas Bednar diff --git a/doc/en/announce/release-2.9.1.rst b/doc/en/announce/release-2.9.1.rst index 7a46d2ae690..3880218d233 100644 --- a/doc/en/announce/release-2.9.1.rst +++ b/doc/en/announce/release-2.9.1.rst @@ -17,7 +17,7 @@ Thanks to all who contributed to this release, among them: Bruno Oliveira Daniel Hahler Dmitry Malinovsky - Florian Bruhin + Freya Bruhin Floris Bruynooghe Matt Bachmann Ronny Pfannschmidt diff --git a/doc/en/announce/release-2.9.2.rst b/doc/en/announce/release-2.9.2.rst index 3e75af7fe69..3dc00b46729 100644 --- a/doc/en/announce/release-2.9.2.rst +++ b/doc/en/announce/release-2.9.2.rst @@ -17,7 +17,7 @@ Thanks to all who contributed to this release, among them: Adam Chainz Benjamin Dopplinger Bruno Oliveira - Florian Bruhin + Freya Bruhin John Towler Martin Prusse Meng Jue diff --git a/doc/en/announce/release-3.0.0.rst b/doc/en/announce/release-3.0.0.rst index 5de38911482..b201b901eb7 100644 --- a/doc/en/announce/release-3.0.0.rst +++ b/doc/en/announce/release-3.0.0.rst @@ -39,7 +39,7 @@ Thanks to all who contributed to this release, among them: Dmitry Dygalo Edoardo Batini Eli Boyarski - Florian Bruhin + Freya Bruhin Floris Bruynooghe Greg Price Guyzmo diff --git a/doc/en/announce/release-3.0.1.rst b/doc/en/announce/release-3.0.1.rst index 8f5cfe411aa..b36587f983a 100644 --- a/doc/en/announce/release-3.0.1.rst +++ b/doc/en/announce/release-3.0.1.rst @@ -17,7 +17,7 @@ Thanks to all who contributed to this release, among them: Bruno Oliveira Daniel Hahler Dmitry Dygalo - Florian Bruhin + Freya Bruhin Marcin Bachry Ronny Pfannschmidt matthiasha diff --git a/doc/en/announce/release-3.0.2.rst b/doc/en/announce/release-3.0.2.rst index 86ba82ca6e6..9b1f2acd60d 100644 --- a/doc/en/announce/release-3.0.2.rst +++ b/doc/en/announce/release-3.0.2.rst @@ -14,7 +14,7 @@ Thanks to all who contributed to this release, among them: * Ahn Ki-Wook * Bruno Oliveira -* Florian Bruhin +* Freya Bruhin * Jordan Guymon * Raphael Pierzina * Ronny Pfannschmidt diff --git a/doc/en/announce/release-3.0.3.rst b/doc/en/announce/release-3.0.3.rst index 89a2e0c744e..05bdf4dcd16 100644 --- a/doc/en/announce/release-3.0.3.rst +++ b/doc/en/announce/release-3.0.3.rst @@ -13,7 +13,7 @@ The changelog is available at http://doc.pytest.org/en/stable/changelog.html. Thanks to all who contributed to this release, among them: * Bruno Oliveira -* Florian Bruhin +* Freya Bruhin * Floris Bruynooghe * Huayi Zhang * Lev Maximov diff --git a/doc/en/announce/release-3.0.4.rst b/doc/en/announce/release-3.0.4.rst index 72c2d29464d..ba37bba2111 100644 --- a/doc/en/announce/release-3.0.4.rst +++ b/doc/en/announce/release-3.0.4.rst @@ -14,7 +14,7 @@ Thanks to all who contributed to this release, among them: * Bruno Oliveira * Dan Wandschneider -* Florian Bruhin +* Freya Bruhin * Georgy Dyuldin * Grigorii Eremeev * Jason R. Coombs diff --git a/doc/en/announce/release-3.0.7.rst b/doc/en/announce/release-3.0.7.rst index 4b7e075e76a..782910ae6a4 100644 --- a/doc/en/announce/release-3.0.7.rst +++ b/doc/en/announce/release-3.0.7.rst @@ -14,7 +14,7 @@ Thanks to all who contributed to this release, among them: * Anthony Sottile * Barney Gale * Bruno Oliveira -* Florian Bruhin +* Freya Bruhin * Floris Bruynooghe * Ionel Cristian Mărieș * Katerina Koukiou diff --git a/doc/en/announce/release-3.1.0.rst b/doc/en/announce/release-3.1.0.rst index 55277067948..454c04c6430 100644 --- a/doc/en/announce/release-3.1.0.rst +++ b/doc/en/announce/release-3.1.0.rst @@ -27,7 +27,7 @@ Thanks to all who contributed to this release, among them: * David Giese * David Szotten * Dmitri Pribysh -* Florian Bruhin +* Freya Bruhin * Florian Schulze * Floris Bruynooghe * John Towler diff --git a/doc/en/announce/release-3.1.1.rst b/doc/en/announce/release-3.1.1.rst index 135b2fe8443..99fb0d0f801 100644 --- a/doc/en/announce/release-3.1.1.rst +++ b/doc/en/announce/release-3.1.1.rst @@ -12,7 +12,7 @@ The full changelog is available at http://doc.pytest.org/en/stable/changelog.htm Thanks to all who contributed to this release, among them: * Bruno Oliveira -* Florian Bruhin +* Freya Bruhin * Floris Bruynooghe * Jason R. Coombs * Ronny Pfannschmidt diff --git a/doc/en/announce/release-3.1.2.rst b/doc/en/announce/release-3.1.2.rst index a9b85c4715c..3e988b17e84 100644 --- a/doc/en/announce/release-3.1.2.rst +++ b/doc/en/announce/release-3.1.2.rst @@ -14,7 +14,7 @@ Thanks to all who contributed to this release, among them: * Andreas Pelme * ApaDoctor * Bruno Oliveira -* Florian Bruhin +* Freya Bruhin * Ronny Pfannschmidt * Segev Finer diff --git a/doc/en/announce/release-3.2.0.rst b/doc/en/announce/release-3.2.0.rst index edc66a28e78..68694493907 100644 --- a/doc/en/announce/release-3.2.0.rst +++ b/doc/en/announce/release-3.2.0.rst @@ -25,7 +25,7 @@ Thanks to all who contributed to this release, among them: * Andras Tim * Bruno Oliveira * Daniel Hahler -* Florian Bruhin +* Freya Bruhin * Floris Bruynooghe * John Still * Jordan Moldow diff --git a/doc/en/announce/release-3.2.1.rst b/doc/en/announce/release-3.2.1.rst index c40217d311d..a492390fa58 100644 --- a/doc/en/announce/release-3.2.1.rst +++ b/doc/en/announce/release-3.2.1.rst @@ -13,7 +13,7 @@ Thanks to all who contributed to this release, among them: * Alex Gaynor * Bruno Oliveira -* Florian Bruhin +* Freya Bruhin * Ronny Pfannschmidt * Srinivas Reddy Thatiparthy diff --git a/doc/en/announce/release-3.2.4.rst b/doc/en/announce/release-3.2.4.rst index ff0b35781b1..9bde3afab3b 100644 --- a/doc/en/announce/release-3.2.4.rst +++ b/doc/en/announce/release-3.2.4.rst @@ -15,7 +15,7 @@ Thanks to all who contributed to this release, among them: * Christian Boelsen * Christoph Buchner * Daw-Ran Liou -* Florian Bruhin +* Freya Bruhin * Franck Michea * Leonard Lausen * Matty G diff --git a/doc/en/announce/release-3.3.0.rst b/doc/en/announce/release-3.3.0.rst index 1cbf2c448c8..d54910bea4c 100644 --- a/doc/en/announce/release-3.3.0.rst +++ b/doc/en/announce/release-3.3.0.rst @@ -27,7 +27,7 @@ Thanks to all who contributed to this release, among them: * Daniel Hahler * Dirk Thomas * Dmitry Malinovsky -* Florian Bruhin +* Freya Bruhin * George Y. Kussumoto * Hugo * Jesús Espino diff --git a/doc/en/announce/release-3.3.1.rst b/doc/en/announce/release-3.3.1.rst index 98b6fa6c1ba..a1a0a6d6f45 100644 --- a/doc/en/announce/release-3.3.1.rst +++ b/doc/en/announce/release-3.3.1.rst @@ -14,7 +14,7 @@ Thanks to all who contributed to this release, among them: * Bruno Oliveira * Daniel Hahler * Eugene Prikazchikov -* Florian Bruhin +* Freya Bruhin * Roland Puntaier * Ronny Pfannschmidt * Sebastian Rahlf diff --git a/doc/en/announce/release-3.3.2.rst b/doc/en/announce/release-3.3.2.rst index 7a2577d1ff8..8c4110cc350 100644 --- a/doc/en/announce/release-3.3.2.rst +++ b/doc/en/announce/release-3.3.2.rst @@ -15,7 +15,7 @@ Thanks to all who contributed to this release, among them: * Antony Lee * Austin * Bruno Oliveira -* Florian Bruhin +* Freya Bruhin * Floris Bruynooghe * Henk-Jaap Wagenaar * Jurko Gospodnetić diff --git a/doc/en/announce/release-3.4.0.rst b/doc/en/announce/release-3.4.0.rst index 6ab5b124a25..8a8582f7a00 100644 --- a/doc/en/announce/release-3.4.0.rst +++ b/doc/en/announce/release-3.4.0.rst @@ -30,7 +30,7 @@ Thanks to all who contributed to this release, among them: * Brian Maissy * Bruno Oliveira * Cyrus Maden -* Florian Bruhin +* Freya Bruhin * Henk-Jaap Wagenaar * Ian Lesperance * Jon Dufresne diff --git a/doc/en/announce/release-3.4.1.rst b/doc/en/announce/release-3.4.1.rst index d83949453a2..bef05752698 100644 --- a/doc/en/announce/release-3.4.1.rst +++ b/doc/en/announce/release-3.4.1.rst @@ -16,7 +16,7 @@ Thanks to all who contributed to this release, among them: * Andy Freeland * Brian Maissy * Bruno Oliveira -* Florian Bruhin +* Freya Bruhin * Jason R. Coombs * Marcin Bachry * Pedro Algarvio diff --git a/doc/en/announce/release-3.4.2.rst b/doc/en/announce/release-3.4.2.rst index 07cd9d3a8ba..5ab73986617 100644 --- a/doc/en/announce/release-3.4.2.rst +++ b/doc/en/announce/release-3.4.2.rst @@ -13,7 +13,7 @@ Thanks to all who contributed to this release, among them: * Allan Feldman * Bruno Oliveira -* Florian Bruhin +* Freya Bruhin * Jason R. Coombs * Kyle Altendorf * Maik Figura diff --git a/doc/en/announce/release-3.5.0.rst b/doc/en/announce/release-3.5.0.rst index 6bc2f3cd0cb..7ce2fe3dfe0 100644 --- a/doc/en/announce/release-3.5.0.rst +++ b/doc/en/announce/release-3.5.0.rst @@ -26,7 +26,7 @@ Thanks to all who contributed to this release, among them: * Bruno Oliveira * Carlos Jenkins * Daniel Hahler -* Florian Bruhin +* Freya Bruhin * Jason R. Coombs * Jeffrey Rackauckas * Jordan Speicher diff --git a/doc/en/announce/release-5.0.0.rst b/doc/en/announce/release-5.0.0.rst index f5e593e9d88..166d4e565c3 100644 --- a/doc/en/announce/release-5.0.0.rst +++ b/doc/en/announce/release-5.0.0.rst @@ -26,7 +26,7 @@ Thanks to all who contributed to this release, among them: * Daniel Hahler * Dirk Thomas * Evan Kepner -* Florian Bruhin +* Freya Bruhin * Hugo * Kevin J. Foley * Pulkit Goyal diff --git a/doc/en/announce/release-5.0.1.rst b/doc/en/announce/release-5.0.1.rst index e16a8f716f1..f0ffb791545 100644 --- a/doc/en/announce/release-5.0.1.rst +++ b/doc/en/announce/release-5.0.1.rst @@ -15,7 +15,7 @@ Thanks to all who contributed to this release, among them: * Andreu Vallbona Plazas * Anthony Sottile * Bruno Oliveira -* Florian Bruhin +* Freya Bruhin * Michael Moore * Niklas Meinzer * Thomas Grainger diff --git a/doc/en/announce/release-5.1.0.rst b/doc/en/announce/release-5.1.0.rst index 9ab54ff9730..6170023604a 100644 --- a/doc/en/announce/release-5.1.0.rst +++ b/doc/en/announce/release-5.1.0.rst @@ -27,7 +27,7 @@ Thanks to all who contributed to this release, among them: * Bruno Oliveira * Daniel Hahler * David Röthlisberger -* Florian Bruhin +* Freya Bruhin * Ilya Stepin * Jon Dufresne * Kaiqi diff --git a/doc/en/announce/release-5.1.1.rst b/doc/en/announce/release-5.1.1.rst index bb8de48014a..1262e94fd00 100644 --- a/doc/en/announce/release-5.1.1.rst +++ b/doc/en/announce/release-5.1.1.rst @@ -14,7 +14,7 @@ Thanks to all who contributed to this release, among them: * Anthony Sottile * Bruno Oliveira * Daniel Hahler -* Florian Bruhin +* Freya Bruhin * Hugo van Kemenade * Ran Benita * Ronny Pfannschmidt diff --git a/doc/en/announce/release-5.2.1.rst b/doc/en/announce/release-5.2.1.rst index fe42b9bf15f..904e1b59893 100644 --- a/doc/en/announce/release-5.2.1.rst +++ b/doc/en/announce/release-5.2.1.rst @@ -13,7 +13,7 @@ Thanks to all who contributed to this release, among them: * Anthony Sottile * Bruno Oliveira -* Florian Bruhin +* Freya Bruhin * Hynek Schlawack * Kevin J. Foley * tadashigaki diff --git a/doc/en/announce/release-5.2.2.rst b/doc/en/announce/release-5.2.2.rst index 89fd6a534d4..015baba52e7 100644 --- a/doc/en/announce/release-5.2.2.rst +++ b/doc/en/announce/release-5.2.2.rst @@ -16,7 +16,7 @@ Thanks to all who contributed to this release, among them: * Anthony Sottile * Bruno Oliveira * Daniel Hahler -* Florian Bruhin +* Freya Bruhin * Nattaphoom Chaipreecha * Oliver Bestwalter * Philipp Loose diff --git a/doc/en/announce/release-5.2.3.rst b/doc/en/announce/release-5.2.3.rst index bab174495d9..8c89e04540a 100644 --- a/doc/en/announce/release-5.2.3.rst +++ b/doc/en/announce/release-5.2.3.rst @@ -17,7 +17,7 @@ Thanks to all who contributed to this release, among them: * Daniel Hahler * Daniil Galiev * David Szotten -* Florian Bruhin +* Freya Bruhin * Patrick Harmon * Ran Benita * Zac Hatfield-Dodds diff --git a/doc/en/announce/release-5.3.1.rst b/doc/en/announce/release-5.3.1.rst index d575bb70e3f..5dc82ab7d88 100644 --- a/doc/en/announce/release-5.3.1.rst +++ b/doc/en/announce/release-5.3.1.rst @@ -15,7 +15,7 @@ Thanks to all who contributed to this release, among them: * Bruno Oliveira * Daniel Hahler * Felix Yan -* Florian Bruhin +* Freya Bruhin * Mark Dickinson * Nikolay Kondratyev * Steffen Schroeder diff --git a/doc/en/announce/release-6.0.0rc1.rst b/doc/en/announce/release-6.0.0rc1.rst index 5690b514baf..6f0a745cd00 100644 --- a/doc/en/announce/release-6.0.0rc1.rst +++ b/doc/en/announce/release-6.0.0rc1.rst @@ -25,7 +25,7 @@ Thanks to all who contributed to this release, among them: * David Diaz Barquero * Fabio Zadrozny * Felix Nieuwenhuizen -* Florian Bruhin +* Freya Bruhin * Florian Dahlitz * Gleb Nikonorov * Hugo van Kemenade diff --git a/doc/en/announce/release-6.1.0.rst b/doc/en/announce/release-6.1.0.rst index f4b571ae846..0c787d0bd15 100644 --- a/doc/en/announce/release-6.1.0.rst +++ b/doc/en/announce/release-6.1.0.rst @@ -23,7 +23,7 @@ Thanks to all of the contributors to this release: * C. Titus Brown * Drew Devereux * Faris A Chugthai -* Florian Bruhin +* Freya Bruhin * Hugo van Kemenade * Hynek Schlawack * Joseph Lucas diff --git a/doc/en/announce/release-6.2.0.rst b/doc/en/announce/release-6.2.0.rst index af16b830ddd..8e99d8fcda5 100644 --- a/doc/en/announce/release-6.2.0.rst +++ b/doc/en/announce/release-6.2.0.rst @@ -30,7 +30,7 @@ Thanks to all of the contributors to this release: * Cserna Zsolt * Dominic Mortlock * Emiel van de Laar -* Florian Bruhin +* Freya Bruhin * Garvit Shubham * Gustavo Camargo * Hugo Martins diff --git a/doc/en/announce/release-6.2.4.rst b/doc/en/announce/release-6.2.4.rst index fa2e3e78132..129368e73cd 100644 --- a/doc/en/announce/release-6.2.4.rst +++ b/doc/en/announce/release-6.2.4.rst @@ -14,7 +14,7 @@ Thanks to all of the contributors to this release: * Anthony Sottile * Bruno Oliveira * Christian Maurer -* Florian Bruhin +* Freya Bruhin * Ran Benita diff --git a/doc/en/announce/release-6.2.5.rst b/doc/en/announce/release-6.2.5.rst index bc6b4cf4222..daf9731c800 100644 --- a/doc/en/announce/release-6.2.5.rst +++ b/doc/en/announce/release-6.2.5.rst @@ -15,7 +15,7 @@ Thanks to all of the contributors to this release: * Bruno Oliveira * Brylie Christopher Oxley * Daniel Asztalos -* Florian Bruhin +* Freya Bruhin * Jason Haugen * MapleCCC * Michał Górny diff --git a/doc/en/announce/release-7.0.0.rst b/doc/en/announce/release-7.0.0.rst index 3ce4335564f..934064df745 100644 --- a/doc/en/announce/release-7.0.0.rst +++ b/doc/en/announce/release-7.0.0.rst @@ -34,7 +34,7 @@ Thanks to all of the contributors to this release: * Emmanuel Arias * Emmanuel Meric de Bellefon * Eric Liu -* Florian Bruhin +* Freya Bruhin * GergelyKalmar * Graeme Smecher * Harshna diff --git a/doc/en/announce/release-7.0.0rc1.rst b/doc/en/announce/release-7.0.0rc1.rst index a5bf0ed3c44..dd6ecdd131b 100644 --- a/doc/en/announce/release-7.0.0rc1.rst +++ b/doc/en/announce/release-7.0.0rc1.rst @@ -38,7 +38,7 @@ Thanks to all the contributors to this release: * Emmanuel Arias * Emmanuel Meric de Bellefon * Eric Liu -* Florian Bruhin +* Freya Bruhin * GergelyKalmar * Graeme Smecher * Harshna diff --git a/doc/en/announce/release-7.1.0.rst b/doc/en/announce/release-7.1.0.rst index 3361e1c8a32..f138524c564 100644 --- a/doc/en/announce/release-7.1.0.rst +++ b/doc/en/announce/release-7.1.0.rst @@ -28,7 +28,7 @@ Thanks to all of the contributors to this release: * Elijah DeLee * Emmanuel Arias * Fabian Egli -* Florian Bruhin +* Freya Bruhin * Gabor Szabo * Hasan Ramezani * Hugo van Kemenade diff --git a/doc/en/announce/release-7.2.0.rst b/doc/en/announce/release-7.2.0.rst index eca84aeb669..44cd553ec0f 100644 --- a/doc/en/announce/release-7.2.0.rst +++ b/doc/en/announce/release-7.2.0.rst @@ -33,7 +33,7 @@ Thanks to all of the contributors to this release: * EmptyRabbit * Ezio Melotti * Florian Best -* Florian Bruhin +* Freya Bruhin * Fredrik Berndtsson * Gabriel Landau * Gergely Kalmár diff --git a/doc/en/announce/release-7.3.0.rst b/doc/en/announce/release-7.3.0.rst index 33258dabade..b6d8379d5b5 100644 --- a/doc/en/announce/release-7.3.0.rst +++ b/doc/en/announce/release-7.3.0.rst @@ -42,7 +42,7 @@ Thanks to all of the contributors to this release: * Ezio Melotti * Felix Hofstätter * Florian Best -* Florian Bruhin +* Freya Bruhin * Fredrik Berndtsson * Gabriel Landau * Garvit Shubham diff --git a/doc/en/announce/release-7.4.0.rst b/doc/en/announce/release-7.4.0.rst index 5a0d18267d3..fef2ad6cb3d 100644 --- a/doc/en/announce/release-7.4.0.rst +++ b/doc/en/announce/release-7.4.0.rst @@ -27,7 +27,7 @@ Thanks to all of the contributors to this release: * Bryan Ricker * Chris Mahoney * Facundo Batista -* Florian Bruhin +* Freya Bruhin * Jarrett Keifer * Kenny Y * Miro Hrončok diff --git a/doc/en/announce/release-7.4.1.rst b/doc/en/announce/release-7.4.1.rst index efadcf919e8..4e22d3ead66 100644 --- a/doc/en/announce/release-7.4.1.rst +++ b/doc/en/announce/release-7.4.1.rst @@ -12,7 +12,7 @@ The full changelog is available at https://docs.pytest.org/en/stable/changelog.h Thanks to all of the contributors to this release: * Bruno Oliveira -* Florian Bruhin +* Freya Bruhin * Ran Benita diff --git a/doc/en/announce/release-8.0.0rc1.rst b/doc/en/announce/release-8.0.0rc1.rst index 547c8cbc53b..0cbfc3dad59 100644 --- a/doc/en/announce/release-8.0.0rc1.rst +++ b/doc/en/announce/release-8.0.0rc1.rst @@ -31,7 +31,7 @@ Thanks to all of the contributors to this release: * Christoph Anton Mitterer * DetachHead * Erik Hasse -* Florian Bruhin +* Freya Bruhin * Fraser Stark * Ha Pam * Hugo van Kemenade diff --git a/doc/en/announce/release-8.1.0.rst b/doc/en/announce/release-8.1.0.rst index 62cafdd78bb..6762bd412fe 100644 --- a/doc/en/announce/release-8.1.0.rst +++ b/doc/en/announce/release-8.1.0.rst @@ -28,7 +28,7 @@ Thanks to all of the contributors to this release: * Eric Larson * Fabian Sturm * Faisal Fawad -* Florian Bruhin +* Freya Bruhin * Franck Charras * Joachim B Haga * John Litborn diff --git a/doc/en/announce/release-8.2.0.rst b/doc/en/announce/release-8.2.0.rst index 2a63c8d8722..7aba492d7da 100644 --- a/doc/en/announce/release-8.2.0.rst +++ b/doc/en/announce/release-8.2.0.rst @@ -20,7 +20,7 @@ Thanks to all of the contributors to this release: * Bruno Oliveira * Daniel Miller -* Florian Bruhin +* Freya Bruhin * HolyMagician03-UMich * John Litborn * Levon Saldamli diff --git a/doc/en/announce/release-8.3.0.rst b/doc/en/announce/release-8.3.0.rst index ec5cd3d0db9..0589aedfa89 100644 --- a/doc/en/announce/release-8.3.0.rst +++ b/doc/en/announce/release-8.3.0.rst @@ -24,7 +24,7 @@ Thanks to all of the contributors to this release: * Bruno Oliveira * Cornelius Riemenschneider * Farbod Ahmadian -* Florian Bruhin +* Freya Bruhin * Hynek Schlawack * James Frost * Jason R. Coombs diff --git a/doc/en/announce/release-8.3.3.rst b/doc/en/announce/release-8.3.3.rst index 5e3eb36b921..6e73714d4f9 100644 --- a/doc/en/announce/release-8.3.3.rst +++ b/doc/en/announce/release-8.3.3.rst @@ -16,7 +16,7 @@ Thanks to all of the contributors to this release: * Bruno Oliveira * Christian Clauss * Eugene Mwangi -* Florian Bruhin +* Freya Bruhin * GTowers1 * Nauman Ahmed * Pierre Sassoulas diff --git a/doc/en/announce/release-8.3.4.rst b/doc/en/announce/release-8.3.4.rst index f76d60396dc..3ec21d73f5e 100644 --- a/doc/en/announce/release-8.3.4.rst +++ b/doc/en/announce/release-8.3.4.rst @@ -12,7 +12,7 @@ The full changelog is available at https://docs.pytest.org/en/stable/changelog.h Thanks to all of the contributors to this release: * Bruno Oliveira -* Florian Bruhin +* Freya Bruhin * Frank Hoffmann * Jakob van Santen * Leonardus Chen diff --git a/doc/en/announce/release-8.3.5.rst b/doc/en/announce/release-8.3.5.rst index 3de02c1d7a4..21bae869180 100644 --- a/doc/en/announce/release-8.3.5.rst +++ b/doc/en/announce/release-8.3.5.rst @@ -10,7 +10,7 @@ The full changelog is available at https://docs.pytest.org/en/stable/changelog.h Thanks to all of the contributors to this release: * Bruno Oliveira -* Florian Bruhin +* Freya Bruhin * John Litborn * Kenny Y * Ran Benita diff --git a/doc/en/announce/release-8.4.0.rst b/doc/en/announce/release-8.4.0.rst index 65e80a55919..f492d45070a 100644 --- a/doc/en/announce/release-8.4.0.rst +++ b/doc/en/announce/release-8.4.0.rst @@ -38,7 +38,7 @@ Thanks to all of the contributors to this release: * Deysha Rivera * Emil Hjelm * Eugene Mwangi -* Florian Bruhin +* Freya Bruhin * Frank Hoffmann * GTowers1 * Guillaume Gauvrit diff --git a/doc/en/announce/release-8.4.2.rst b/doc/en/announce/release-8.4.2.rst index 58a842c4d4b..3111e85bd0f 100644 --- a/doc/en/announce/release-8.4.2.rst +++ b/doc/en/announce/release-8.4.2.rst @@ -12,7 +12,7 @@ Thanks to all of the contributors to this release: * AD * Aditi De * Bruno Oliveira -* Florian Bruhin +* Freya Bruhin * John Litborn * Liam DeVoe * Marc Mueller diff --git a/doc/en/announce/release-9.0.0.rst b/doc/en/announce/release-9.0.0.rst new file mode 100644 index 00000000000..67d4f95a56d --- /dev/null +++ b/doc/en/announce/release-9.0.0.rst @@ -0,0 +1,69 @@ +pytest-9.0.0 +======================================= + +The pytest team is proud to announce the 9.0.0 release! + +This release contains new features, improvements, bug fixes, and breaking changes, so users +are encouraged to take a look at the CHANGELOG carefully: + + https://docs.pytest.org/en/stable/changelog.html + +For complete documentation, please visit: + + https://docs.pytest.org/en/stable/ + +As usual, you can upgrade from PyPI via: + + pip install -U pytest + +Thanks to all of the contributors to this release: + +* AD +* Aditi De +* Ali Nazzal +* Bruno Oliveira +* Charles-Meldhine Madi Mnemoi +* Clément Robert +* CoretexShadow +* Cornelius Roemer +* Eero Vaher +* Freya Bruhin +* Harsha Sai +* Hossein +* Israël Hallé +* Iwithyou2025 +* James Addison +* John Litborn +* Jordan Macdonald +* Kieran Ryan +* Liam DeVoe +* Marc Mueller +* Marcos Boger +* Michał Górny +* Mulat Mekonen +* NayeemJohn +* Olivier Grisel +* Omri Golan +* Pierre Sassoulas +* Praise Tompane +* Ran Benita +* Reilly Brogan +* Samuel Gaist +* SarahPythonista +* Sorin Sbarnea +* Stu-ops +* Tanuj Rai +* bengartner +* dariomesic +* jakkdl +* karlicoss +* popododo0720 +* sazsu +* slackline +* vyuroshchin +* zapl +* 🇺🇦 Sviatoslav Sydorenko (Святослав Сидоренко) + + +Happy testing, +The pytest Development Team diff --git a/doc/en/announce/release-9.0.1.rst b/doc/en/announce/release-9.0.1.rst new file mode 100644 index 00000000000..46af130e03c --- /dev/null +++ b/doc/en/announce/release-9.0.1.rst @@ -0,0 +1,18 @@ +pytest-9.0.1 +======================================= + +pytest 9.0.1 has just been released to PyPI. + +This is a bug-fix release, being a drop-in replacement. + +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 +* 🇺🇦 Sviatoslav Sydorenko (Святослав Сидоренко) + + +Happy testing, +The pytest Development Team diff --git a/doc/en/announce/release-9.0.2.rst b/doc/en/announce/release-9.0.2.rst new file mode 100644 index 00000000000..f184e1aa4b2 --- /dev/null +++ b/doc/en/announce/release-9.0.2.rst @@ -0,0 +1,22 @@ +pytest-9.0.2 +======================================= + +pytest 9.0.2 has just been released to PyPI. + +This is a bug-fix release, being a drop-in replacement. + +The full changelog is available at https://docs.pytest.org/en/stable/changelog.html. + +Thanks to all of the contributors to this release: + +* Alex Waygood +* Bruno Oliveira +* Fazeel Usmani +* Freya Bruhin +* Ran Benita +* Tom Most +* 🇺🇦 Sviatoslav Sydorenko (Святослав Сидоренко) + + +Happy testing, +The pytest Development Team diff --git a/doc/en/announce/release-9.0.3.rst b/doc/en/announce/release-9.0.3.rst new file mode 100644 index 00000000000..c9540218764 --- /dev/null +++ b/doc/en/announce/release-9.0.3.rst @@ -0,0 +1,38 @@ +pytest-9.0.3 +======================================= + +pytest 9.0.3 has just been released to PyPI. + +This is a bug-fix release, being a drop-in replacement. + +The full changelog is available at https://docs.pytest.org/en/stable/changelog.html. + +Thanks to all of the contributors to this release: + +* Aditya Giri +* Alejandro Villate +* Bruno Oliveira +* Bubble-Interface +* Charles-Meldhine Madi Mnemoi +* DavidAG +* Denis Cherednichenko +* Dr Alex Mitre +* Freya +* Freya Bruhin +* Hugo van Kemenade +* John Litborn +* Liam DeVoe +* Lily Wu +* Maxime Grenu +* Ran Benita +* Randy Döring +* Ronald Eddy Jr +* Samuel Newbold +* Tejas Verma +* Vladimir +* jxramos +* 🇺🇦 Sviatoslav Sydorenko (Святослав Сидоренко) + + +Happy testing, +The pytest Development Team diff --git a/doc/en/backwards-compatibility.rst b/doc/en/backwards-compatibility.rst index 82f678b4dea..a7ee2253d67 100644 --- a/doc/en/backwards-compatibility.rst +++ b/doc/en/backwards-compatibility.rst @@ -53,14 +53,14 @@ History ========= -Focus primary on smooth transition - stance (pre 6.0) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Focus primarily 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. -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: :option:`-W` command-line flag or :confval:`filterwarnings` configuration option (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). @@ -83,9 +83,10 @@ Released pytest versions support all Python versions that are actively maintaine ============== =================== pytest version min. Python version ============== =================== -8.4+ 3.9+ -8.0+ 3.8+ -7.1+ 3.7+ +9.0+ 3.10+ +8.4 3.9+ +8.0 - 8.3 3.8+ +7.1 - 7.4 3.7+ 6.2 - 7.0 3.6+ 5.0 - 6.1 3.5+ 3.3 - 4.6 2.7, 3.4+ diff --git a/doc/en/builtin.rst b/doc/en/builtin.rst index ca350c5f3dd..9d38b329454 100644 --- a/doc/en/builtin.rst +++ b/doc/en/builtin.rst @@ -12,17 +12,17 @@ For information on plugin hooks and objects, see :ref:`plugins`. For information on the ``pytest.mark`` mechanism, see :ref:`mark`. -For information about fixtures, see :ref:`fixtures`. To see a complete list of available fixtures (add ``-v`` to also see fixtures with leading ``_``), type : +For information about fixtures, see :ref:`fixtures`. To see a complete list of available fixtures (add :option:`-v` to also see fixtures with leading ``_``), type : .. code-block:: pytest $ pytest --fixtures -v =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: /home/sweet/project collected 0 items - cache -- .../_pytest/cacheprovider.py:555 + cache -- .../_pytest/cacheprovider.py:566 Return a cache object that can persist state between testing sessions. cache.get(key, default) @@ -128,7 +128,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a captured = capfdbinary.readouterr() assert captured.out == b"hello\n" - doctest_namespace [session scope] -- .../_pytest/doctest.py:740 + doctest_namespace [session scope] -- .../_pytest/doctest.py:722 Fixture that returns a :py:class:`dict` that will be injected into the namespace of doctests. @@ -142,7 +142,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:1425 + pytestconfig [session scope] -- .../_pytest/fixtures.py:1431 Session-scoped fixture that returns the session's :class:`pytest.Config` object. @@ -225,7 +225,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:31 + monkeypatch -- .../_pytest/monkeypatch.py:33 A convenient fixture for monkey-patching. The fixture provides these methods to modify objects, dictionaries, or @@ -254,10 +254,13 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a See :ref:`warnings` for information on warning categories. - tmp_path_factory [session scope] -- .../_pytest/tmpdir.py:240 + subtests -- .../_pytest/subtests.py:129 + Provides subtests functionality. + + tmp_path_factory [session scope] -- .../_pytest/tmpdir.py:265 Return a :class:`pytest.TempPathFactory` instance for the test session. - tmp_path -- .../_pytest/tmpdir.py:255 + tmp_path -- .../_pytest/tmpdir.py:280 Return a temporary directory (as :class:`pathlib.Path` object) which is unique to each test function invocation. The temporary directory is created as a subdirectory diff --git a/doc/en/changelog.rst b/doc/en/changelog.rst index 89f1c4e61b3..5bcd44c4226 100644 --- a/doc/en/changelog.rst +++ b/doc/en/changelog.rst @@ -31,6 +31,440 @@ with advance notice in the **Deprecations** section of releases. .. towncrier release notes start +pytest 9.0.3 (2026-04-07) +========================= + +Bug fixes +--------- + +- `#12444 `_: Fixed :func:`pytest.approx` which now correctly takes into account :class:`~collections.abc.Mapping` keys order to compare them. + + +- `#13634 `_: Blocking a ``conftest.py`` file using the ``-p no:`` option is now explicitly disallowed. + + Previously this resulted in an internal assertion failure during plugin loading. + + Pytest now raises a clear ``UsageError`` explaining that conftest files are not plugins and cannot be disabled via ``-p``. + + +- `#13734 `_: Fixed crash when a test raises an exceptiongroup with ``__tracebackhide__ = True``. + + +- `#14195 `_: Fixed an issue where non-string messages passed to `unittest.TestCase.subTest()` were not printed. + + +- `#14343 `_: Fixed use of insecure temporary directory (CVE-2025-71176). + + + +Improved documentation +---------------------- + +- `#13388 `_: Clarified documentation for ``-p`` vs ``PYTEST_PLUGINS`` plugin loading and fixed an incorrect ``-p`` example. + + +- `#13731 `_: Clarified that capture fixtures (e.g. ``capsys`` and ``capfd``) take precedence over the ``-s`` / ``--capture=no`` command-line options in :ref:`Accessing captured output from a test function `. + + +- `#14088 `_: Clarified that the default :hook:`pytest_collection` hook sets ``session.items`` before it calls :hook:`pytest_collection_finish`, not after. + + +- `#14255 `_: TOML integer log levels must be quoted: Updating reference documentation. + + + +Contributor-facing changes +-------------------------- + +- `#12689 `_: The test reports are now published to Codecov from GitHub Actions. + The test statistics is visible `on the web interface + `__. + + -- by :user:`aleguy02` + + +pytest 9.0.2 (2025-12-06) +========================= + +Bug fixes +--------- + +- `#13896 `_: The terminal progress feature added in pytest 9.0.0 has been disabled by default, except on Windows, due to compatibility issues with some terminal emulators. + + You may enable it again by passing ``-p terminalprogress``. We may enable it by default again once compatibility improves in the future. + + Additionally, when the environment variable ``TERM`` is ``dumb``, the escape codes are no longer emitted, even if the plugin is enabled. + + +- `#13904 `_: Fixed the TOML type of the :confval:`tmp_path_retention_count` settings in the API reference from number to string. + + +- `#13946 `_: The private ``config.inicfg`` attribute was changed in a breaking manner in pytest 9.0.0. + Due to its usage in the ecosystem, it is now restored to working order using a compatibility shim. + It will be deprecated in pytest 9.1 and removed in pytest 10. + + +- `#13965 `_: Fixed quadratic-time behavior when handling ``unittest`` subtests in Python 3.10. + + + +Improved documentation +---------------------- + +- `#4492 `_: The API Reference now contains cross-reference-able documentation of :ref:`pytest's command-line flags `. + + +pytest 9.0.1 (2025-11-12) +========================= + +Bug fixes +--------- + +- `#13895 `_: Restore support for skipping tests via ``raise unittest.SkipTest``. + + +- `#13896 `_: The terminal progress plugin added in pytest 9.0 is now automatically disabled when iTerm2 is detected, it generated desktop notifications instead of the desired functionality. + + +- `#13904 `_: Fixed the TOML type of the verbosity settings in the API reference from number to string. + + +- `#13910 `_: Fixed `UserWarning: Do not expect file_or_dir` on some earlier Python 3.12 and 3.13 point versions. + + + +Packaging updates and notes for downstreams +------------------------------------------- + +- `#13933 `_: The tox configuration has been adjusted to make sure the desired + version string can be passed into its :ref:`package_env` through + the ``SETUPTOOLS_SCM_PRETEND_VERSION_FOR_PYTEST`` environment + variable as a part of the release process -- by :user:`webknjaz`. + + + +Contributor-facing changes +-------------------------- + +- `#13891 `_, `#13942 `_: The CI/CD part of the release automation is now capable of + creating GitHub Releases without having a Git checkout on + disk -- by :user:`bluetech` and :user:`webknjaz`. + + +- `#13933 `_: The tox configuration has been adjusted to make sure the desired + version string can be passed into its :ref:`package_env` through + the ``SETUPTOOLS_SCM_PRETEND_VERSION_FOR_PYTEST`` environment + variable as a part of the release process -- by :user:`webknjaz`. + + +pytest 9.0.0 (2025-11-05) +========================= + +New features +------------ + + +- `#1367 `_: **Support for subtests** has been added. + + :ref:`subtests ` are an alternative to parametrization, useful in situations where the parametrization values are not all known at collection time. + + Example: + + .. code-block:: python + + def contains_docstring(p: Path) -> bool: + """Return True if the given Python file contains a top-level docstring.""" + ... + + + def test_py_files_contain_docstring(subtests: pytest.Subtests) -> None: + for path in Path.cwd().glob("*.py"): + with subtests.test(path=str(path)): + assert contains_docstring(path) + + + Each assert failure or error is caught by the context manager and reported individually, giving a clear picture of all files that are missing a docstring. + + In addition, :meth:`unittest.TestCase.subTest` is now also supported. + + This feature was originally implemented as a separate plugin in `pytest-subtests `__, but since then has been merged into the core. + + .. note:: + + This feature is experimental and will likely evolve in future releases. By that we mean that we might change how subtests are reported on failure, but the functionality and how to use it are stable. + + +- `#13743 `_: Added support for **native TOML configuration files**. + + While pytest, since version 6, supports configuration in ``pyproject.toml`` files under ``[tool.pytest.ini_options]``, + it does so in an "INI compatibility mode", where all configuration values are treated as strings or list of strings. + Now, pytest supports the native TOML data model. + + In ``pyproject.toml``, the native TOML configuration is under the ``[tool.pytest]`` table. + + .. code-block:: toml + + # pyproject.toml + [tool.pytest] + minversion = "9.0" + addopts = ["-ra", "-q"] + testpaths = [ + "tests", + "integration", + ] + + The ``[tool.pytest.ini_options]`` table remains supported, but both tables cannot be used at the same time. + + If you prefer to use a separate configuration file, or don't use ``pyproject.toml``, you can use ``pytest.toml`` or ``.pytest.toml``: + + .. code-block:: toml + + # pytest.toml or .pytest.toml + [pytest] + minversion = "9.0" + addopts = ["-ra", "-q"] + testpaths = [ + "tests", + "integration", + ] + + The documentation now (sometimes) shows configuration snippets in both TOML and INI formats, in a tabbed interface. + + See :ref:`config file formats` for full details. + + +- `#13823 `_: Added a **"strict mode"** enabled by the :confval:`strict` configuration option. + + When set to ``true``, the :confval:`strict` option currently enables + + * :confval:`strict_config` + * :confval:`strict_markers` + * :confval:`strict_parametrization_ids` + * :confval:`strict_xfail` + + The individual strictness options can be explicitly set to override the global :confval:`strict` setting. + + The previously-deprecated ``--strict`` command-line flag now enables strict mode. + + If pytest adds new strictness options in the future, they will also be enabled in strict mode. + Therefore, you should only enable strict mode if you use a pinned/locked version of pytest, + or if you want to proactively adopt new strictness options as they are added. + + See :ref:`strict mode` for more details. + + +- `#13737 `_: Added the :confval:`strict_parametrization_ids` configuration option. + + When set, pytest emits an error if it detects non-unique parameter set IDs, + rather than automatically making the IDs unique by adding `0`, `1`, ... to them. + This can be particularly useful for catching unintended duplicates. + + +- `#13072 `_: Added support for displaying test session **progress in the terminal tab** using the `OSC 9;4; `_ ANSI sequence. + + **Note**: *This feature has been disabled by default in version 9.0.2, except on Windows, due to compatibility issues with some terminal emulators. + You may enable it again by passing* ``-p terminalprogress``. *We may enable it by default again once compatibility improves in the future.* + + When pytest runs in a supported terminal emulator like ConEmu, Gnome Terminal, Ptyxis, Windows Terminal, Kitty or Ghostty, + you'll see the progress in the terminal tab or window, + allowing you to monitor pytest's progress at a glance. + + This feature is automatically enabled when running in a TTY. It is implemented as an internal plugin. If needed, it can be disabled as follows: + - On a user level, using ``-p no:terminalprogress`` on the command line or via an environment variable ``PYTEST_ADDOPTS='-p no:terminalprogress'``. + - On a project configuration level, using ``addopts = "-p no:terminalprogress"``. + + +- `#478 `_: Support PEP420 (implicit namespace packages) as `--pyargs` target when :confval:`consider_namespace_packages` is `true` in the config. + + Previously, this option only impacted package imports, now it also impacts tests discovery. + + +- `#13678 `_: Added a new :confval:`faulthandler_exit_on_timeout` configuration option set to "false" by default to let `faulthandler` interrupt the `pytest` process after a timeout in case of deadlock. + + Previously, a `faulthandler` timeout would only dump the traceback of all threads to stderr, but would not interrupt the `pytest` process. + + -- by :user:`ogrisel`. + + +- `#13829 `_: Added support for configuration option aliases via the ``aliases`` parameter in :meth:`Parser.addini() `. + + Plugins can now register alternative names for configuration options, + allowing for more flexibility in configuration naming and supporting backward compatibility when renaming options. + The canonical name always takes precedence if both the canonical name and an alias are specified in the configuration file. + + + +Improvements in existing functionality +-------------------------------------- + +- `#13330 `_: Having pytest configuration spread over more than one file (for example having both a ``pytest.ini`` file and ``pyproject.toml`` with a ``[tool.pytest.ini_options]`` table) will now print a warning to make it clearer to the user that only one of them is actually used. + + -- by :user:`sgaist` + + +- `#13574 `_: The single argument ``--version`` no longer loads the entire plugin infrastructure, making it faster and more reliable when displaying only the pytest version. + + Passing ``--version`` twice (e.g., ``pytest --version --version``) retains the original behavior, showing both the pytest version and plugin information. + + .. note:: + + Since ``--version`` is now processed early, it only takes effect when passed directly via the command line. It will not work if set through other mechanisms, such as :envvar:`PYTEST_ADDOPTS` or :confval:`addopts`. + + +- `#13823 `_: Added :confval:`strict_xfail` as an alias to the ``xfail_strict`` option, + :confval:`strict_config` as an alias to the ``--strict-config`` flag, + and :confval:`strict_markers` as an alias to the ``--strict-markers`` flag. + This makes all strictness options consistently have configuration options with the prefix ``strict_``. + +- `#13700 `_: `--junitxml` no longer prints the `generated xml file` summary at the end of the pytest session when `--quiet` is given. + + +- `#13732 `_: Previously, when filtering warnings, pytest would fail if the filter referenced a class that could not be imported. Now, this only outputs a message indicating the problem. + + +- `#13859 `_: Clarify the error message for `pytest.raises()` when a regex `match` fails. + + +- `#13861 `_: Better sentence structure in a test's expected error message. Previously, the error message would be "expected exception must be , but got ". Now, it is "Expected , but got ". + + +Removals and backward incompatible breaking changes +--------------------------------------------------- + +- `#12083 `_: Fixed a bug where an invocation such as `pytest a/ a/b` would cause only tests from `a/b` to run, and not other tests under `a/`. + + The fix entails a few breaking changes to how such overlapping arguments and duplicates are handled: + + 1. `pytest a/b a/` or `pytest a/ a/b` are equivalent to `pytest a`; if an argument overlaps another arguments, only the prefix remains. + + 2. `pytest x.py x.py` is equivalent to `pytest x.py`; previously such an invocation was taken as an explicit request to run the tests from the file twice. + + If you rely on these behaviors, consider using :ref:`--keep-duplicates `, which retains its existing behavior (including the bug). + + +- `#13719 `_: Support for Python 3.9 is dropped following its end of life. + + +- `#13766 `_: Previously, pytest would assume it was running in a CI/CD environment if either of the environment variables `$CI` or `$BUILD_NUMBER` was defined; + now, CI mode is only activated if at least one of those variables is defined and set to a *non-empty* value. + + +- The non-public ``config.args`` attribute used to be able to contain ``pathlib.Path`` instances; now it can only contain strings. + + +- `#13779 `_: **PytestRemovedIn9Warning deprecation warnings are now errors by default.** + + Following our plan to remove deprecated features with as little disruption as + possible, all warnings of type ``PytestRemovedIn9Warning`` now generate errors + instead of warning messages by default. + + **The affected features will be effectively removed in pytest 9.1**, so please consult the + :ref:`deprecations` section in the docs for directions on how to update existing code. + + In the pytest ``9.0.X`` series, it is possible to change the errors back into warnings as a + stopgap measure by adding this to your ``pytest.ini`` file: + + .. code-block:: ini + + [pytest] + filterwarnings = + ignore::pytest.PytestRemovedIn9Warning + + But this will stop working when pytest ``9.1`` is released. + + **If you have concerns** about the removal of a specific feature, please add a + comment to :issue:`13779`. + + + +Deprecations (removal in next major release) +-------------------------------------------- + +- `#13807 `_: :meth:`monkeypatch.syspath_prepend() ` now issues a deprecation warning when the prepended path contains legacy namespace packages (those using ``pkg_resources.declare_namespace()``). + Users should migrate to native namespace packages (:pep:`420`). + See :ref:`monkeypatch-fixup-namespace-packages` for details. + + +Bug fixes +--------- + +- `#13445 `_: Made the type annotations of :func:`pytest.skip` and friends more spec-complaint to have them work across more type checkers. + + +- `#13537 `_: Fixed a bug in which :class:`ExceptionGroup` with only ``Skipped`` exceptions in teardown was not handled correctly and showed as error. + + +- `#13598 `_: Fixed possible collection confusion on Windows when short paths and symlinks are involved. + + +- `#13716 `_: Fixed a bug where a nonsensical invocation like ``pytest x.py[a]`` (a file cannot be parametrized) was silently treated as ``pytest x.py``. This is now a usage error. + + +- `#13722 `_: Fixed a misleading assertion failure message when using :func:`pytest.approx` on mappings with differing lengths. + + +- `#13773 `_: Fixed the static fixture closure calculation to properly consider transitive dependencies requested by overridden fixtures. + + +- `#13816 `_: Fixed :func:`pytest.approx` which now returns a clearer error message when comparing mappings with different keys. + + +- `#13849 `_: Hidden ``.pytest.ini`` files are now picked up as the config file even if empty. + This was an inconsistency with non-hidden ``pytest.ini``. + + +- `#13865 `_: Fixed `--show-capture` with `--tb=line`. + + +- `#13522 `_: Fixed :fixture:`pytester` in subprocess mode ignored all :attr:`pytester.plugins ` except the first. + + Fixed :fixture:`pytester` in subprocess mode silently ignored non-str :attr:`pytester.plugins `. + Now it errors instead. + If you are affected by this, specify the plugin by name, or switch the affected tests to use :func:`pytester.runpytest_inprocess ` explicitly instead. + + + +Packaging updates and notes for downstreams +------------------------------------------- + +- `#13791 `_: Minimum requirements on ``iniconfig`` and ``packaging`` were bumped to ``1.0.1`` and ``22.0.0``, respectively. + + + +Contributor-facing changes +-------------------------- + +- `#12244 `_: Fixed self-test failures when `TERM=dumb`. + + +- `#12474 `_: Added scheduled GitHub Action Workflow to run Sphinx linkchecks in repo documentation. + + +- `#13621 `_: pytest's own testsuite now handles the ``lsof`` command hanging (e.g. due to unreachable network filesystems), with the affected selftests being skipped after 10 seconds. + + +- `#13638 `_: Fixed deprecated :command:`gh pr new` command in :file:`scripts/prepare-release-pr.py`. + The script now uses :command:`gh pr create` which is compatible with GitHub CLI v2.0+. + + +- `#13695 `_: Flush `stdout` and `stderr` in `Pytester.run` to avoid truncated outputs in `test_faulthandler.py::test_timeout` on CI -- by :user:`ogrisel`. + + +- `#13771 `_: Skip `test_do_not_collect_symlink_siblings` on Windows environments without symlink support to avoid false negatives. + + +- `#13841 `_: ``tox>=4`` is now required when contributing to pytest. + +- `#13625 `_: Added missing docstrings to ``pytest_addoption()``, ``pytest_configure()``, and ``cacheshow()`` functions in ``cacheprovider.py``. + + + +Miscellaneous internal changes +------------------------------ + +- `#13830 `_: Configuration overrides (``-o``/``--override-ini``) are now processed during startup rather than during :func:`config.getini() `. + + pytest 8.4.2 (2025-09-03) ========================= @@ -2423,6 +2857,7 @@ Breaking Changes - `#8246 `_: ``--version`` now writes version information to ``stdout`` rather than ``stderr``. +- `#8592 `_: The ``pytest_cmdline_preparse`` hook has been removed following its deprecation. See :ref:`the deprecation note ` for more details. - `#8733 `_: Drop a workaround for `pyreadline `__ that made it work with ``--pdb``. @@ -8991,10 +9426,10 @@ time or change existing behaviors in order to make them less surprising/more use non-ascii characters. Thanks Bruno Oliveira for the PR. - fix #1204: another error when collecting with a nasty __getattr__(). - Thanks Florian Bruhin for the PR. + Thanks Freya Bruhin for the PR. - fix the summary printed when no tests did run. - Thanks Florian Bruhin for the PR. + Thanks Freya Bruhin for the PR. - fix #1185 - ensure MANIFEST.in exactly matches what should go to a sdist - a number of documentation modernizations wrt good practices. @@ -9116,7 +9551,7 @@ time or change existing behaviors in order to make them less surprising/more use - fix issue934: when string comparison fails and a diff is too large to display without passing -vv, still show a few lines of the diff. - Thanks Florian Bruhin for the report and Bruno Oliveira for the PR. + Thanks Freya Bruhin for the report and Bruno Oliveira for the PR. - fix issue736: Fix a bug where fixture params would be discarded when combined with parametrization markers. @@ -9129,7 +9564,7 @@ time or change existing behaviors in order to make them less surprising/more use - parametrize now also generates meaningful test IDs for enum, regex and class objects (as opposed to class instances). - Thanks to Florian Bruhin for the PR. + Thanks to Freya Bruhin for the PR. - Add 'warns' to assert that warnings are thrown (like 'raises'). Thanks to Eric Hunsberger for the PR. @@ -9256,7 +9691,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 preexisting attribute. Thanks Florian Bruhin. + if "y" is not a preexisting attribute. Thanks Freya Bruhin. - fix issue741: make running output from testdir.run copy/pasteable Thanks Bruno Oliveira. @@ -9316,7 +9751,7 @@ time or change existing behaviors in order to make them less surprising/more use - fix issue833: --fixtures now shows all fixtures of collected test files, instead of just the fixtures declared on the first one. - Thanks Florian Bruhin for reporting and Bruno Oliveira for the PR. + Thanks Freya Bruhin for reporting and Bruno Oliveira for the PR. - fix issue863: skipped tests now report the correct reason when a skip/xfail condition is met when using multiple markers. diff --git a/doc/en/contact.rst b/doc/en/contact.rst index b2a1368eaba..311224eeef0 100644 --- a/doc/en/contact.rst +++ b/doc/en/contact.rst @@ -40,7 +40,7 @@ Mail in the pytest core team, who can also be contacted individually: * Bruno Oliveira (:user:`nicoddemus`, `bruno@pytest.org `_) - * Florian Bruhin (:user:`The-Compiler`, `florian@pytest.org `_) + * Freya Bruhin (:user:`The-Compiler`, `freya@pytest.org `_) * Pierre Sassoulas (:user:`Pierre-Sassoulas`, `pierre@pytest.org `_) * Ran Benita (:user:`bluetech`, `ran@pytest.org `_) * Ronny Pfannschmidt (:user:`RonnyPfannschmidt`, `ronny@pytest.org `_) @@ -51,7 +51,7 @@ Other - The :doc:`contribution guide ` for help on submitting pull requests to GitHub. -- Florian Bruhin (:user:`The-Compiler`) offers pytest professional teaching and +- Freya Bruhin (:user:`The-Compiler`) offers pytest professional teaching and consulting via `Bruhin Software `_. .. _`pytest issue tracker`: https://github.com/pytest-dev/pytest/issues diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index 65a05823517..57c583fd852 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -139,7 +139,7 @@ In ``8.2`` the ``exc_type`` parameter has been added, giving users the ability o to skip tests only if the module cannot really be found, and not because of some other error. Catching only :class:`ModuleNotFoundError` by default (and letting other errors propagate) would be the best solution, -however for backward compatibility, pytest will keep the existing behavior but raise an warning if: +however for backward compatibility, pytest will keep the existing behavior but raise a warning if: 1. The captured exception is of type :class:`ImportError`, and: 2. The user does not pass ``exc_type`` explicitly. @@ -358,7 +358,7 @@ The ``yield_fixture`` function/decorator ``pytest.yield_fixture`` is a deprecated alias for :func:`pytest.fixture`. -It has been so for a very long time, so can be search/replaced safely. +It has been so for a very long time, so it can be searched/replaced safely. Removed Features and Breaking Changes @@ -774,7 +774,7 @@ The ``pytest._fillfuncargs`` function This function was kept for backward compatibility with an older plugin. -It's functionality is not meant to be used directly, but if you must replace +Its functionality is not meant to be used directly, but if you must replace it, use `function._request._fillfixtures()` instead, though note this is not a public API and may break in the future. @@ -805,7 +805,7 @@ The ``--result-log`` option produces a stream of test reports which can be analysed at runtime, but it uses a custom format which requires users to implement their own parser. -The `pytest-reportlog `__ plugin provides a ``--report-log`` option, a more standard and extensible alternative, producing +The :pypi:`pytest-reportlog` plugin provides a ``--report-log`` option, a more standard and extensible alternative, producing one JSON object per-line, and should cover the same use cases. Please try it out and provide feedback. The ``pytest-reportlog`` plugin might even be merged into the core diff --git a/doc/en/example/attic.rst b/doc/en/example/attic.rst index 2b1f2766dce..3a2e228337e 100644 --- a/doc/en/example/attic.rst +++ b/doc/en/example/attic.rst @@ -75,7 +75,7 @@ decorate its result. This mechanism allows us to stay ignorant of how/where the function argument is provided - in our example from a `conftest plugin`_. -sidenote: the temporary directory used here are instances of +Side note: the temporary directories used here are instances of the `py.path.local`_ class which provides many of the os.path methods in a convenient way. diff --git a/doc/en/example/customdirectory.rst b/doc/en/example/customdirectory.rst index 1e4d7e370de..705a3373654 100644 --- a/doc/en/example/customdirectory.rst +++ b/doc/en/example/customdirectory.rst @@ -36,13 +36,13 @@ You can create a ``manifest.json`` file and some test files: .. include:: customdirectory/tests/test_third.py :literal: -An you can now execute the test specification: +And you can now execute the test specification: .. code-block:: pytest customdirectory $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project/customdirectory configfile: pytest.ini collected 2 items @@ -62,7 +62,7 @@ You can verify that your custom collector appears in the collection tree: customdirectory $ pytest --collect-only =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project/customdirectory configfile: pytest.ini collected 2 items diff --git a/doc/en/example/markers.rst b/doc/en/example/markers.rst index c8f7c8c2fa2..c8e4172a696 100644 --- a/doc/en/example/markers.rst +++ b/doc/en/example/markers.rst @@ -47,7 +47,7 @@ You can then restrict a test run to only run tests marked with ``webtest``: $ pytest -v -m webtest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: /home/sweet/project collecting ... collected 4 items / 3 deselected / 1 selected @@ -62,7 +62,7 @@ Or the inverse, running all tests except the webtest ones: $ pytest -v -m "not webtest" =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: /home/sweet/project collecting ... collected 4 items / 1 deselected / 3 selected @@ -82,7 +82,7 @@ keyword arguments, e.g. to run only tests marked with ``device`` and the specifi $ pytest -v -m "device(serial='123')" =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: /home/sweet/project collecting ... collected 4 items / 3 deselected / 1 selected @@ -106,7 +106,7 @@ tests based on their module, class, method, or function name: $ pytest -v test_server.py::TestClass::test_method =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: /home/sweet/project collecting ... collected 1 item @@ -121,7 +121,7 @@ You can also select on the class: $ pytest -v test_server.py::TestClass =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: /home/sweet/project collecting ... collected 1 item @@ -136,7 +136,7 @@ Or select multiple nodes: $ pytest -v test_server.py::TestClass test_server.py::test_send_http =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: /home/sweet/project collecting ... collected 2 items @@ -167,9 +167,9 @@ Using ``-k expr`` to select tests based on their name .. versionadded:: 2.0/2.3.4 -You can use the ``-k`` command line option to specify an expression +You can use the :option:`-k` command line option to specify an expression which implements a substring match on the test names instead of the -exact match on markers that ``-m`` provides. This makes it easy to +exact match on markers that :option:`-m` provides. This makes it easy to select tests based on their names: .. versionchanged:: 5.4 @@ -180,7 +180,7 @@ The expression matching is now case-insensitive. $ pytest -v -k http # running with the above defined example module =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: /home/sweet/project collecting ... collected 4 items / 3 deselected / 1 selected @@ -195,7 +195,7 @@ And you can also run all tests except the ones that match the keyword: $ pytest -k "not send_http" -v =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: /home/sweet/project collecting ... collected 4 items / 1 deselected / 3 selected @@ -212,7 +212,7 @@ Or to select "http" and "quick" tests: $ pytest -k "http or quick" -v =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: /home/sweet/project collecting ... collected 4 items / 2 deselected / 2 selected @@ -225,7 +225,7 @@ Or to select "http" and "quick" tests: You can use ``and``, ``or``, ``not`` and parentheses. -In addition to the test's name, ``-k`` also matches the names of the test's parents (usually, the name of the file and class it's in), +In addition to the test's name, :option:`-k` also matches the names of the test's parents (usually, the name of the file and class it's in), attributes set on the test function, markers applied to it or its parents and any :attr:`extra keywords <_pytest.nodes.Node.extra_keyword_matches>` explicitly added to it or its parents. @@ -262,7 +262,7 @@ You can ask which markers exist for your test suite - the list includes our just @pytest.mark.skipif(condition, ..., *, reason=...): skip the given test function if any of the conditions evaluate to True. Example: skipif(sys.platform == 'win32') skips the test if we are on the win32 platform. See https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-skipif - @pytest.mark.xfail(condition, ..., *, reason=..., run=True, raises=None, strict=xfail_strict): mark the test function as an expected failure if any of the conditions evaluate to True. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-xfail + @pytest.mark.xfail(condition, ..., *, reason=..., run=True, raises=None, strict=strict_xfail): mark the test function as an expected failure if any of the conditions evaluate to True. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-xfail @pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see https://docs.pytest.org/en/stable/how-to/parametrize.html for more info and examples. @@ -411,14 +411,14 @@ A test file using this local plugin: def test_basic_db_operation(): pass -and an example invocations specifying a different environment than what +and an example invocation specifying a different environment than what the test needs: .. code-block:: pytest $ pytest -E stage2 =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 1 item @@ -432,7 +432,7 @@ and here is one that specifies exactly the environment needed: $ pytest -E stage1 =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 1 item @@ -440,7 +440,7 @@ and here is one that specifies exactly the environment needed: ============================ 1 passed in 0.12s ============================= -The ``--markers`` option always gives you a list of available markers: +The :option:`--markers` option always gives you a list of available markers: .. code-block:: pytest @@ -453,7 +453,7 @@ The ``--markers`` option always gives you a list of available markers: @pytest.mark.skipif(condition, ..., *, reason=...): skip the given test function if any of the conditions evaluate to True. Example: skipif(sys.platform == 'win32') skips the test if we are on the win32 platform. See https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-skipif - @pytest.mark.xfail(condition, ..., *, reason=..., run=True, raises=None, strict=xfail_strict): mark the test function as an expected failure if any of the conditions evaluate to True. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-xfail + @pytest.mark.xfail(condition, ..., *, reason=..., run=True, raises=None, strict=strict_xfail): mark the test function as an expected failure if any of the conditions evaluate to True. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-xfail @pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see https://docs.pytest.org/en/stable/how-to/parametrize.html for more info and examples. @@ -625,7 +625,7 @@ then you will see two tests skipped and two executed tests as expected: $ pytest -rs # this option reports skip reasons =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 4 items @@ -641,7 +641,7 @@ Note that if you specify a platform via the marker-command line option like this $ pytest -m linux =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 4 items / 3 deselected / 1 selected @@ -658,7 +658,7 @@ Automatically adding markers based on test names If you have a test suite where test function names indicate a certain type of test, you can implement a hook that automatically defines -markers so that you can use the ``-m`` option with it. Let's look +markers so that you can use the :option:`-m` option with it. Let's look at this test module: .. code-block:: python @@ -704,7 +704,7 @@ We can now use the ``-m option`` to select one set: $ pytest -m interface --tb=short =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 4 items / 2 deselected / 2 selected @@ -730,7 +730,7 @@ or to select both "event" and "interface" tests: $ pytest -m "interface or event" --tb=short =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 4 items / 1 deselected / 3 selected diff --git a/doc/en/example/nonpython.rst b/doc/en/example/nonpython.rst index aa463e2416b..54391d72fd4 100644 --- a/doc/en/example/nonpython.rst +++ b/doc/en/example/nonpython.rst @@ -28,7 +28,7 @@ now execute the test specification: nonpython $ pytest test_simple.yaml =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project/nonpython collected 2 items @@ -40,7 +40,7 @@ now execute the test specification: spec failed: 'some': 'other' no further details known at this point. ========================= short test summary info ========================== - FAILED test_simple.yaml::hello + FAILED test_simple.yaml::hello - usecase execution failed ======================= 1 failed, 1 passed in 0.12s ======================== .. regendoc:wipe @@ -58,13 +58,18 @@ your own domain specific testing language this way. will be reported as a (red) string. ``reportinfo()`` is used for representing the test location and is also -consulted when reporting in ``verbose`` mode: +consulted when reporting in ``verbose`` mode. It should return a tuple +``(path, lineno, description)``, where: + +* ``path`` is the path shown in reports (usually ``self.path`` or ``self.fspath``). +* ``lineno`` is a zero-based line number, or ``0`` when no specific line applies. +* ``description`` is a short label shown for the collected item: .. code-block:: pytest nonpython $ pytest -v =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: /home/sweet/project/nonpython collecting ... collected 2 items @@ -78,7 +83,7 @@ consulted when reporting in ``verbose`` mode: spec failed: 'some': 'other' no further details known at this point. ========================= short test summary info ========================== - FAILED test_simple.yaml::hello + FAILED test_simple.yaml::hello - usecase execution failed ======================= 1 failed, 1 passed in 0.12s ======================== .. regendoc:wipe @@ -90,7 +95,7 @@ interesting to just look at the collection tree: nonpython $ pytest --collect-only =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project/nonpython collected 2 items diff --git a/doc/en/example/parametrize.rst b/doc/en/example/parametrize.rst index 6374e0edb3d..de668def863 100644 --- a/doc/en/example/parametrize.rst +++ b/doc/en/example/parametrize.rst @@ -4,7 +4,7 @@ Parametrizing tests ================================================= -``pytest`` allows to easily parametrize test functions. +``pytest`` allows you to easily parametrize test functions. For basic docs, see :ref:`parametrize-basics`. In the following we provide some examples using @@ -83,9 +83,9 @@ Different options for test IDs ------------------------------------ pytest will build a string that is the test ID for each set of values in a -parametrized test. These IDs can be used with ``-k`` to select specific cases +parametrized test. These IDs can be used with :option:`-k` to select specific cases to run, and they will also identify the specific case when one is failing. -Running pytest with ``--collect-only`` will show the generated IDs. +Running pytest with :option:`--collect-only` will show the generated IDs. Numbers, strings, booleans and None will have their usual string representation used in the test ID. For other objects, pytest will make a string based on @@ -158,11 +158,11 @@ objects, they are still using the default pytest representation: $ pytest test_time.py --collect-only =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 8 items - + @@ -221,7 +221,7 @@ this is a fully self-contained example which you can run with: $ pytest test_scenarios.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 4 items @@ -235,11 +235,11 @@ If you just collect tests you'll also nicely see 'advanced' and 'basic' as varia $ pytest --collect-only test_scenarios.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 4 items - + @@ -314,11 +314,11 @@ Let's first see how it looks like at collection time: $ pytest test_backends.py --collect-only =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 2 items - + @@ -370,9 +370,19 @@ test: def test_indirect(fixt): assert len(fixt) == 3 + This can be used, for example, to do more expensive setup at test run time in the fixture, rather than having to run those setup steps at collection time. +.. note:: + + The ``request`` argument used by the fixture is pytest's built-in + :py:class:`FixtureRequest ` fixture. For indirect + parametrization, the value supplied to the test parameter is passed to the + fixture and made available as ``request.param``. + + For more information, see :ref:`fixture-parametrize`. + .. regendoc:wipe Apply indirect on particular arguments @@ -413,7 +423,7 @@ The result of this test will be successful: $ pytest -v test_indirect_list.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: /home/sweet/project collecting ... collected 1 item @@ -503,12 +513,10 @@ Running it results in some skips if we don't have all the python interpreters in .. code-block:: pytest . $ pytest -rs -q multipython.py - sssssssssssssssssssssssssss [100%] + ssssssssssss......sss...... [100%] ========================= short test summary info ========================== - SKIPPED [9] multipython.py:67: 'python3.9' not found - SKIPPED [9] multipython.py:67: 'python3.10' not found - SKIPPED [9] multipython.py:67: 'python3.11' not found - 27 skipped in 0.12s + SKIPPED [15] multipython.py:67: 'python3.11' not found + 12 passed, 15 skipped in 0.12s Parametrization of optional implementations/imports --------------------------------------------------- @@ -568,7 +576,7 @@ If you run this with reporting for skips enabled: $ pytest -rs test_module.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 2 items @@ -629,7 +637,7 @@ Then run ``pytest`` with verbose mode and with only the ``basic`` marker: $ pytest -v -m basic =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: /home/sweet/project collecting ... collected 24 items / 21 deselected / 3 selected @@ -644,7 +652,7 @@ As the result: - Four tests were collected - One test was deselected because it doesn't have the ``basic`` mark. -- Three tests with the ``basic`` mark was selected. +- Three tests with the ``basic`` mark were selected. - The test ``test_eval[1+7-8]`` passed, but the name is autogenerated and confusing. - The test ``test_eval[basic_2+4]`` passed. - The test ``test_eval[basic_6*9]`` was expected to fail and did fail. diff --git a/doc/en/example/pythoncollection.rst b/doc/en/example/pythoncollection.rst index 46d15557fa8..48ee2c8533f 100644 --- a/doc/en/example/pythoncollection.rst +++ b/doc/en/example/pythoncollection.rst @@ -5,7 +5,7 @@ Ignore paths during test collection ----------------------------------- You can easily ignore certain test directories and modules during collection -by passing the ``--ignore=path`` option on the cli. ``pytest`` allows multiple +by passing the :option:`--ignore=path` option on the cli. ``pytest`` allows multiple ``--ignore`` options. Example: .. code-block:: text @@ -43,16 +43,16 @@ you will see that ``pytest`` only collects test-modules, which do not match the ========================= 5 passed in 0.02 seconds ========================= -The ``--ignore-glob`` option allows to ignore test file paths based on Unix shell-style wildcards. -If you want to exclude test-modules that end with ``_01.py``, execute ``pytest`` with ``--ignore-glob='*_01.py'``. +The :option:`--ignore-glob` option allows to ignore test file paths based on Unix shell-style wildcards. +If you want to exclude test-modules that end with ``_01.py``, execute ``pytest`` with :option:`--ignore-glob='*_01.py'`. Deselect tests during test collection ------------------------------------- -Tests can individually be deselected during collection by passing the ``--deselect=item`` option. +Tests can individually be deselected during collection by passing the :option:`--deselect=item` option. For example, say ``tests/foobar/test_foobar_01.py`` contains ``test_a`` and ``test_b``. You can run all of the tests within ``tests/`` *except* for ``tests/foobar/test_foobar_01.py::test_a`` -by invoking ``pytest`` with ``--deselect tests/foobar/test_foobar_01.py::test_a``. +by invoking ``pytest`` with ``--deselect=tests/foobar/test_foobar_01.py::test_a``. ``pytest`` allows multiple ``--deselect`` options. .. _duplicate-paths: @@ -73,7 +73,7 @@ Example: Just collect tests once. -To collect duplicate tests, use the ``--keep-duplicates`` option on the cli. +To collect duplicate tests, use the :option:`--keep-duplicates` option on the cli. Example: .. code-block:: pytest @@ -137,12 +137,12 @@ The test collection would look like this: $ pytest --collect-only =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project - configfile: pytest.ini + configfile: pytest.toml collected 2 items - + @@ -161,14 +161,14 @@ You can check for multiple glob patterns by adding a space between the patterns: .. note:: - the ``python_functions`` and ``python_classes`` options has no effect + the ``python_functions`` and ``python_classes`` options have no effect for ``unittest.TestCase`` test discovery because pytest delegates discovery of test case methods to unittest code. Interpreting cmdline arguments as Python packages ----------------------------------------------------- -You can use the ``--pyargs`` option to make ``pytest`` try +You can use the :option:`--pyargs` option to make ``pytest`` try interpreting arguments as python package names, deriving their file system path and then running the test. For example if you have unittest2 installed you can type: @@ -200,12 +200,12 @@ You can always peek at the collection tree without running tests like this: . $ pytest --collect-only pythoncollection.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project - configfile: pytest.ini + configfile: pytest.toml collected 3 items - + @@ -231,7 +231,7 @@ You can easily instruct ``pytest`` to discover tests from every Python file: python_files = ["*.py"] However, many projects will have a ``setup.py`` which they don't want to be -imported. Moreover, there may files only importable by a specific python +imported. Moreover, there may be files only importable by a specific python version. For such cases you can dynamically define files to be ignored by listing them in a ``conftest.py`` file: @@ -284,9 +284,9 @@ file will be left out: $ pytest --collect-only =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project - configfile: pytest.ini + configfile: pytest.toml collected 0 items ======================= no tests collected in 0.12s ======================== diff --git a/doc/en/example/reportingdemo.rst b/doc/en/example/reportingdemo.rst index 8040ee9b957..29ba190b7e7 100644 --- a/doc/en/example/reportingdemo.rst +++ b/doc/en/example/reportingdemo.rst @@ -9,7 +9,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: assertion $ pytest failure_demo.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project/assertion collected 44 items diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst index 928dcddae74..a07927280ae 100644 --- a/doc/en/example/simple.rst +++ b/doc/en/example/simple.rst @@ -43,7 +43,7 @@ The actual command line executed is: pytest -ra -q -v -m slow Note that as usual for other command-line applications, in case of conflicting options the last one wins, so the example -above will show verbose output because ``-v`` overwrites ``-q``. +above will show verbose output because :option:`-v` overwrites :option:`-q`. .. _request example: @@ -166,6 +166,8 @@ Now we'll get feedback on a bad argument: $ pytest -q --cmdopt=type3 ERROR: usage: pytest [options] [file_or_dir] [file_or_dir] [...] pytest: error: argument --cmdopt: invalid choice: 'type3' (choose from type1, type2) + inifile: None + rootdir: /home/sweet/project If you need to provide more detailed error messages, you can use the @@ -233,7 +235,7 @@ directory with the above conftest.py: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 0 items @@ -297,7 +299,7 @@ and when running it will see a skipped "slow" test: $ pytest -rs # "-rs" means report details on the little 's' =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 2 items @@ -313,7 +315,7 @@ Or run it including the ``slow`` marked test: $ pytest --runslow =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 2 items @@ -351,7 +353,7 @@ Example: The ``__tracebackhide__`` setting influences ``pytest`` showing of tracebacks: the ``checkconfig`` function will not be shown -unless the ``--full-trace`` command line option is specified. +unless the :option:`--full-trace` command line option is specified. Let's run our little function: .. code-block:: pytest @@ -414,10 +416,10 @@ running from a test you can do this: if os.environ.get("PYTEST_VERSION") is not None: - # Things you want to to do if your code is called by pytest. + # Things you want to do if your code is called by pytest. ... else: - # Things you want to to do if your code is not called by pytest. + # Things you want to do if your code is not called by pytest. ... @@ -442,7 +444,7 @@ which will add the string to the test header accordingly: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y project deps: mylib-1.1 rootdir: /home/sweet/project collected 0 items @@ -470,7 +472,7 @@ which will add info only when run with "--v": $ pytest -v =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache info1: did you know that ... did you? @@ -485,7 +487,7 @@ and nothing when run plainly: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 0 items @@ -524,7 +526,7 @@ Now we can profile which test functions execute the slowest: $ pytest --durations=3 =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 3 items @@ -551,12 +553,10 @@ an ``incremental`` marker which is to be used on classes: # content of conftest.py - from typing import Dict, Tuple - import pytest # store history of failures per test class name and per index in parametrize (if parametrize used) - _test_failed_incremental: Dict[str, Dict[Tuple[int, ...], str]] = {} + _test_failed_incremental: dict[str, dict[tuple[int, ...], str]] = {} def pytest_runtest_makereport(item, call): @@ -630,7 +630,7 @@ If we run this: $ pytest -rx =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 4 items @@ -712,7 +712,7 @@ We can run this: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 7 items @@ -726,7 +726,7 @@ We can run this: file /home/sweet/project/b/test_error.py, line 1 def test_root(db): # no db here, will error out E fixture 'db' not found - > available fixtures: cache, capfd, capfdbinary, caplog, capsys, capsysbinary, capteesys, doctest_namespace, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory + > available fixtures: cache, capfd, capfdbinary, caplog, capsys, capsysbinary, capteesys, doctest_namespace, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, subtests, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory > use 'pytest --fixtures [testpath]' for help on them. /home/sweet/project/b/test_error.py:1 @@ -773,7 +773,7 @@ The two test modules in the ``a`` directory see the same ``db`` fixture instance while the one test in the sister-directory ``b`` doesn't see it. We could of course also define a ``db`` fixture in that sister directory's ``conftest.py`` file. Note that each fixture is only instantiated if there is a test actually needing -it (unless you use "autouse" fixture which are always executed ahead of the first test +it (unless you use "autouse" fixtures which are always executed ahead of the first test executing). @@ -834,7 +834,7 @@ and run them: $ pytest test_module.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 2 items @@ -881,11 +881,10 @@ here is a little example implemented via a local plugin: .. code-block:: python # content of conftest.py - from typing import Dict import pytest from pytest import StashKey, CollectReport - phase_report_key = StashKey[Dict[str, CollectReport]]() + phase_report_key = StashKey[dict[str, CollectReport]]() @pytest.hookimpl(wrapper=True, tryfirst=True) @@ -945,7 +944,7 @@ and run it: $ pytest -s test_module.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 3 items @@ -996,7 +995,7 @@ information. Sometimes a test session might get stuck and there might be no easy way to figure out -which test got stuck, for example if pytest was run in quiet mode (``-q``) or you don't have access to the console +which test got stuck, for example if pytest was run in quiet mode (:option:`-q`) or you don't have access to the console output. This is particularly a problem if the problem happens only sporadically, the famous "flaky" kind of tests. ``pytest`` sets the :envvar:`PYTEST_CURRENT_TEST` environment variable when running tests, which can be inspected diff --git a/doc/en/explanation/ci.rst b/doc/en/explanation/ci.rst index 6f6734f395b..1c03f840b43 100644 --- a/doc/en/explanation/ci.rst +++ b/doc/en/explanation/ci.rst @@ -8,7 +8,7 @@ Rationale The goal of testing in a CI pipeline is different from testing locally. Indeed, you can quickly edit some code and run your tests again on your computer, but -it is not possible with CI pipeline. They run on a separate server and are +it is not possible with CI pipelines. They run on a separate server and are triggered by specific actions. From that observation, pytest can detect when it is in a CI environment and @@ -17,10 +17,10 @@ adapt some of its behaviours. How CI is detected ------------------ -Pytest knows it is in a CI environment when either one of these environment variables are set to a non-empty value: +Pytest knows it is in a CI environment when either one of these environment variables is set to a non-empty value: -* `CI`: used by many CI systems. -* `BUILD_NUMBER`: used by Jenkins. +* :envvar:`CI`: used by many CI systems. +* :envvar:`BUILD_NUMBER`: used by Jenkins. Effects on CI ------------- @@ -50,7 +50,7 @@ Running this locally, without any extra options, will output: $ pytest test_ci.py ... ========================= short test summary info ========================== - FAILED test_backends.py::test_db_initialized[d2] - Failed: deliberately f... + FAILED test_ci.py::test_db_initialized - Failed: deliberately f... *(Note the truncated text)* @@ -63,7 +63,7 @@ While running this on CI will output: $ pytest test_ci.py ... ========================= short test summary info ========================== - FAILED test_backends.py::test_db_initialized[d2] - Failed: deliberately failing + FAILED test_ci.py::test_db_initialized - Failed: deliberately failing for demo purpose, Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras facilisis, massa in suscipit dignissim, mauris lacus molestie nisi, quis varius metus nulla ut ipsum. diff --git a/doc/en/explanation/flaky.rst b/doc/en/explanation/flaky.rst index 8369e1d9311..918d6f10b36 100644 --- a/doc/en/explanation/flaky.rst +++ b/doc/en/explanation/flaky.rst @@ -42,7 +42,7 @@ It is of course possible (and common) for tests and fixtures to spawn threads th * 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. +If your test suite uses threads and you are seeing flaky test results, do not discount the possibility that the test is implicitly using global state in pytest itself. Related features ^^^^^^^^^^^^^^^^ diff --git a/doc/en/explanation/goodpractices.rst b/doc/en/explanation/goodpractices.rst index d97dda06417..52474d148c6 100644 --- a/doc/en/explanation/goodpractices.rst +++ b/doc/en/explanation/goodpractices.rst @@ -143,13 +143,18 @@ which are better explained in this excellent `blog post`_ by Ionel Cristian Măr .. note:: - If you do not use an editable install and not use the ``src`` layout (``mypkg`` directly in the root + If you do not use an editable install and do not use the ``src`` layout (``mypkg`` directly in the root directory) you can rely on the fact that Python by default puts the current directory in ``sys.path`` to import your package and run ``python -m pytest`` to execute the tests against the local copy directly. See :ref:`pytest vs python -m pytest` for more information about the difference between calling ``pytest`` and ``python -m pytest``. +.. seealso:: + + :doc:`packaging:discussions/src-layout-vs-flat-layout` + The Python Packaging User Guide discusses the trade-offs between the ``src`` layout and ``flat`` layout. + Tests as part of application code ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -170,7 +175,7 @@ want to distribute them along with your application: test_view.py ... -In this scheme, it is easy to run your tests using the ``--pyargs`` option: +In this scheme, it is easy to run your tests using the :option:`--pyargs` option: .. code-block:: bash @@ -217,7 +222,7 @@ Note that this layout also works in conjunction with the ``src`` layout mentione from each other and thus deriving a canonical import name helps to avoid surprises such as a test module getting imported twice. - With ``--import-mode=importlib`` things are less convoluted because + With :option:`--import-mode=importlib` things are less convoluted because pytest doesn't need to change ``sys.path``, making things much less surprising. @@ -354,7 +359,7 @@ See the :confval:`strict` documentation for the options it enables and their eff If pytest adds new strictness options in the future, they will also be enabled in strict mode. Therefore, you should only enable strict mode if you use a pinned/locked version of pytest, or if you want to proactively adopt new strictness options as they are added. -If you don't want to automatically pick up new options, you can enable options individually:: +If you don't want to automatically pick up new options, you can enable options individually: .. tab:: toml @@ -376,7 +381,7 @@ If you don't want to automatically pick up new options, you can enable options i strict_parametrization_ids = true strict_xfail = true -If you want to use strict mode but having trouble with a specific option, you can turn it off individually:: +If you want to use strict mode but are having trouble with a specific option, you can turn it off individually: .. tab:: toml diff --git a/doc/en/explanation/pythonpath.rst b/doc/en/explanation/pythonpath.rst index ddcbd304f89..cb3ae67216a 100644 --- a/doc/en/explanation/pythonpath.rst +++ b/doc/en/explanation/pythonpath.rst @@ -11,7 +11,7 @@ Import modes pytest as a testing framework needs to import test modules and ``conftest.py`` files for execution. 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 +import process can be controlled through the :option:`--import-mode` command-line flag, which can assume these values: .. _`import-mode-prepend`: @@ -44,7 +44,7 @@ these values: pkg_under_test/ the tests will run against the installed version - of ``pkg_under_test`` when ``--import-mode=append`` is used whereas + of ``pkg_under_test`` when :option:`--import-mode=append` is used whereas with ``prepend``, they would pick up the local version. This kind of confusion is why we advocate for using :ref:`src-layouts `. diff --git a/doc/en/funcarg_compare.rst b/doc/en/funcarg_compare.rst index bc5e7d3c515..7cd4c0f1676 100644 --- a/doc/en/funcarg_compare.rst +++ b/doc/en/funcarg_compare.rst @@ -18,7 +18,7 @@ The pre pytest-2.3 funcarg mechanism calls a factory each time a funcarg for a test function is required. If a factory wants to reuse a resource across different scopes, it often used the ``request.cached_setup()`` helper to manage caching of -resources. Here is a basic example how we could implement +resources. Here is a basic example of how we could implement a per-session Database object: .. code-block:: python @@ -39,10 +39,10 @@ a per-session Database object: There are several limitations and difficulties with this approach: -1. Scoping funcarg resource creation is not straight forward, instead one must +1. Scoping funcarg resource creation is not straightforward, instead one must understand the intricate cached_setup() method mechanics. -2. parametrizing the "db" resource is not straight forward: +2. parametrizing the "db" resource is not straightforward: you need to apply a "parametrize" decorator or implement a :hook:`pytest_generate_tests` hook calling :py:func:`~pytest.Metafunc.parametrize` which @@ -55,7 +55,7 @@ There are several limitations and difficulties with this approach: at the same time, making it hard for them to affect global state of the application under test. -4. there is no way how you can make use of funcarg factories +4. there is no way you can make use of funcarg factories in xUnit setup methods. 5. A non-parametrized fixture function cannot use a parametrized diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index 24501f53a69..76a4428c163 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -20,7 +20,7 @@ Install ``pytest`` .. code-block:: bash $ pytest --version - pytest 8.4.2 + pytest 9.0.3 .. _`simpletest`: @@ -45,7 +45,7 @@ The test $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 1 item @@ -262,7 +262,7 @@ Find out what kind of builtin :ref:`pytest fixtures ` exist with the c pytest --fixtures # shows builtin and custom fixtures -Note that this command omits fixtures with leading ``_`` unless the ``-v`` option is added. +Note that this command omits fixtures with leading ``_`` unless the :option:`-v` option is added. Continue reading ------------------------------------- diff --git a/doc/en/historical-notes.rst b/doc/en/historical-notes.rst index be67036d6ca..d93c7b94793 100644 --- a/doc/en/historical-notes.rst +++ b/doc/en/historical-notes.rst @@ -263,20 +263,24 @@ configuration value which you might have added: @pytest.mark.skipif("not config.getvalue('db')") def test_function(): ... -The equivalent with "boolean conditions" is: +The equivalent with "boolean conditions" using ``request.config`` is: .. code-block:: python - @pytest.mark.skipif(not pytest.config.getvalue("db"), reason="--db was not specified") + @pytest.fixture(autouse=True) + def skip_if_no_db(request): + if not request.config.getoption("--db", default=False): + pytest.skip("--db was not specified") + + def test_function(): pass .. note:: - You cannot use ``pytest.config.getvalue()`` in code - imported before pytest's argument parsing takes place. For example, - ``conftest.py`` files are imported before command line parsing and thus - ``config.getvalue()`` will not execute correctly. + ``pytest.config`` was removed in pytest 5.0. Use ``request.config`` + (via the ``request`` fixture) or the ``pytestconfig`` fixture instead. + See :ref:`pytest.config global deprecated` for details. ``pytest.set_trace()`` ---------------------- @@ -304,7 +308,7 @@ For more details see :ref:`breakpoints`. -Access of ``Module``, ``Function``, ``Class``, ``Instance``, ``File`` and ``Item`` through ``Node`` instances have long +Access of ``Module``, ``Function``, ``Class``, ``Instance``, ``File`` and ``Item`` through ``Node`` instances has long been documented as deprecated, but started to emit warnings from pytest ``3.9`` and onward. Users should just ``import pytest`` and access those objects using the ``pytest`` module. diff --git a/doc/en/how-to/assert.rst b/doc/en/how-to/assert.rst index 4dfceda0fad..377b2a5a3c0 100644 --- a/doc/en/how-to/assert.rst +++ b/doc/en/how-to/assert.rst @@ -29,7 +29,7 @@ you will see the return value of the function call: $ pytest test_assert1.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 1 item @@ -64,8 +64,6 @@ it is printed alongside the assertion introspection in the traceback. See :ref:`assert-details` for more information on assertion introspection. -.. _`assertraises`: - Assertions about approximate equality ------------------------------------- @@ -93,6 +91,8 @@ It also supports comparisons involving NaNs. See :func:`pytest.approx` for details. +.. _`assertraises`: + Assertions about expected exceptions ------------------------------------------ @@ -218,7 +218,7 @@ To specify more details about the contained exception you can use :class:`pytest with pytest.RaisesGroup(pytest.RaisesExc(ValueError, match="foo")): raise ExceptionGroup("", (ValueError("foo"))) -They both supply a method :meth:`pytest.RaisesGroup.matches` :meth:`pytest.RaisesExc.matches` if you want to do matching outside of using it as a contextmanager. This can be helpful when checking ``.__context__`` or ``.__cause__``. +They both supply a method :meth:`pytest.RaisesGroup.matches` :meth:`pytest.RaisesExc.matches` if you want to do matching outside of using it as a :external+python:std:ref:`context manager `. This can be helpful when checking ``.__context__`` or ``.__cause__``. .. code-block:: python @@ -404,7 +404,7 @@ if you run this module: $ pytest test_assert2.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 1 item @@ -436,6 +436,10 @@ Special comparisons are done for a number of cases: * comparing long sequences: first failing indices * comparing dicts: different entries +In string context diffs, lines prefixed with ``-`` come from the left-hand side +of ``assert left == right``, while lines prefixed with ``+`` come from the +right-hand side. + See the :ref:`reporting demo ` for many more examples. Defining your own explanation for failed assertions @@ -599,4 +603,4 @@ If this is the case you have two options: * Disable rewriting for a specific module by adding the string ``PYTEST_DONT_REWRITE`` to its docstring. -* Disable rewriting for all modules by using ``--assert=plain``. +* Disable rewriting for all modules by using :option:`--assert=plain`. diff --git a/doc/en/how-to/cache.rst b/doc/en/how-to/cache.rst index e3209b79359..c030e487563 100644 --- a/doc/en/how-to/cache.rst +++ b/doc/en/how-to/cache.rst @@ -13,11 +13,11 @@ Usage The plugin provides two command line options to rerun failures from the last ``pytest`` invocation: -* ``--lf``, ``--last-failed`` - to only re-run the failures. -* ``--ff``, ``--failed-first`` - to run the failures first and then the rest of +* :option:`--lf, --last-failed <--lf>` - to only re-run the failures. +* :option:`--ff, --failed-first <--ff>` - to run the failures first and then the rest of the tests. -For cleanup (usually not needed), a ``--cache-clear`` option allows to remove +For cleanup (usually not needed), a :option:`--cache-clear` option allows to remove all cross-session cache contents ahead of a test run. Other plugins may access the `config.cache`_ object to set/get @@ -33,7 +33,7 @@ Other plugins may access the `config.cache`_ object to set/get Rerunning only failures or failures first ----------------------------------------------- -First, let's create 50 test invocation of which only 2 fail: +First, let's create 50 test invocations of which only 2 fail: .. code-block:: python @@ -80,13 +80,13 @@ If you run this for the first time you will see two failures: FAILED test_50.py::test_num[25] - Failed: bad luck 2 failed, 48 passed in 0.12s -If you then run it with ``--lf``: +If you then run it with :option:`--lf`: .. code-block:: pytest $ pytest --lf =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 2 items run-last-failure: rerun previous 2 failures @@ -124,7 +124,7 @@ If you then run it with ``--lf``: You have run only the two failing tests from the last run, while the 48 passing tests have not been run ("deselected"). -Now, if you run with the ``--ff`` option, all tests will be run but the first +Now, if you run with the :option:`--ff` option, all tests will be run but the first previous failures will be executed first (as can be seen from the series of ``FF`` and dots): @@ -132,7 +132,7 @@ of ``FF`` and dots): $ pytest --ff =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 50 items run-last-failure: rerun previous 2 failures first @@ -169,14 +169,14 @@ of ``FF`` and dots): .. _`config.cache`: -New ``--nf``, ``--new-first`` options: run new tests first followed by the rest +New :option:`--nf, --new-first <--nf>` option: run new tests first followed by the rest of the tests, in both cases tests are also sorted by the file modified time, with more recent files coming first. Behavior when no tests failed in the last run --------------------------------------------- -The ``--lfnf/--last-failed-no-failures`` option governs the behavior of ``--last-failed``. +The :option:`--lfnf, --last-failed-no-failures <--lfnf>` option governs the behavior of :option:`--last-failed`. Determines whether to execute tests when there are no previously (known) failures or when no cached ``lastfailed`` data was found. @@ -214,11 +214,18 @@ across pytest invocations: @pytest.fixture def mydata(pytestconfig): - val = pytestconfig.cache.get("example/value", None) + cache = getattr(pytestconfig, "cache", None) + if cache is None: + # pytestconfig not having the cache attribute means the + # cache plugin is disabled. + expensive_computation() + return 42 + + val = cache.get("example/value", None) if val is None: expensive_computation() val = 42 - pytestconfig.cache.set("example/value", val) + cache.set("example/value", val) return val @@ -275,13 +282,13 @@ Inspecting Cache content ------------------------ You can always peek at the content of the cache using the -``--cache-show`` command line option: +:option:`--cache-show` command line option: .. code-block:: pytest $ pytest --cache-show =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project cachedir: /home/sweet/project/.pytest_cache --------------------------- cache values for '*' --------------------------- @@ -294,14 +301,14 @@ You can always peek at the content of the cache using the ========================== no tests ran in 0.12s =========================== -``--cache-show`` takes an optional argument to specify a glob pattern for +:option:`--cache-show` takes an optional argument to specify a glob pattern for filtering: .. code-block:: pytest $ pytest --cache-show example/* =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project cachedir: /home/sweet/project/.pytest_cache ----------------------- cache values for 'example/*' ----------------------- @@ -314,7 +321,7 @@ Clearing Cache content ---------------------- You can instruct pytest to clear all cache files and values -by adding the ``--cache-clear`` option like this: +by adding the :option:`--cache-clear` option like this: .. code-block:: bash @@ -330,4 +337,4 @@ than speed. Stepwise -------- -As an alternative to ``--lf -x``, especially for cases where you expect a large part of the test suite will fail, ``--sw``, ``--stepwise`` allows you to fix them one at a time. The test suite will run until the first failure and then stop. At the next invocation, tests will continue from the last failing test and then run until the next failing test. You may use the ``--stepwise-skip`` option to ignore one failing test and stop the test execution on the second failing test instead. This is useful if you get stuck on a failing test and just want to ignore it until later. Providing ``--stepwise-skip`` will also enable ``--stepwise`` implicitly. +As an alternative to :option:`--lf` :option:`-x`, especially for cases where you expect a large part of the test suite will fail, :option:`--sw, --stepwise <--sw>` allows you to fix them one at a time. The test suite will run until the first failure and then stop. At the next invocation, tests will continue from the last failing test and then run until the next failing test. You may use the :option:`--stepwise-skip` option to ignore one failing test and stop the test execution on the second failing test instead. This is useful if you get stuck on a failing test and just want to ignore it until later. Providing ``--stepwise-skip`` will also enable ``--stepwise`` implicitly. diff --git a/doc/en/how-to/capture-stdout-stderr.rst b/doc/en/how-to/capture-stdout-stderr.rst index e6affd80ea1..5de89bc0e3f 100644 --- a/doc/en/how-to/capture-stdout-stderr.rst +++ b/doc/en/how-to/capture-stdout-stderr.rst @@ -4,26 +4,26 @@ How to capture stdout/stderr output ========================================================= -Pytest intercepts stdout and stderr as configured by the ``--capture=`` +Pytest intercepts stdout and stderr as configured by the :option:`--capture=` command-line argument or by using fixtures. The ``--capture=`` flag configures -reporting, whereas the fixtures offer more granular control and allows +reporting, whereas the fixtures offer more granular control and allow inspection of output during testing. The reports can be customized with the -`-r flag <../reference/reference.html#command-line-flags>`_. +:option:`-r` flag. Default stdout/stderr/stdin capturing behaviour --------------------------------------------------------- During test execution any output sent to ``stdout`` and ``stderr`` is captured. If a test or a setup method fails its according captured -output will usually be shown along with the failure traceback. (this -behavior can be configured by the ``--show-capture`` command-line option). +output will usually be shown along with the failure traceback. (This +behavior can be configured by the :option:`--show-capture` command-line option). In addition, ``stdin`` is set to a "null" object which will fail on attempts to read from it because it is rarely desired to wait for interactive input when running automated tests. By default capturing is done by intercepting writes to low level -file descriptors. This allows to capture output from simple +file descriptors. This allows capturing output from simple print statements as well as output from a subprocess started by a test. @@ -89,7 +89,7 @@ of the failing function and hide the other one: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 2 items @@ -109,6 +109,8 @@ of the failing function and hide the other one: FAILED test_module.py::test_func2 - assert False ======================= 1 failed, 1 passed in 0.12s ======================== +.. _accessing-captured-output: + Accessing captured output from a test function --------------------------------------------------- @@ -162,3 +164,13 @@ as a context manager, disabling capture inside the ``with`` block: with capsys.disabled(): print("output not captured, going directly to sys.stdout") print("this output is also captured") + +.. note:: + + When a capture fixture such as :fixture:`capsys` or :fixture:`capfd` is used, + it takes precedence over the global capturing configuration set via + command-line options such as ``-s`` or ``--capture=no``. + + This means that output produced within a test using a capture fixture will + still be captured and available via ``readouterr()``, even if global capturing + is disabled. diff --git a/doc/en/how-to/capture-warnings.rst b/doc/en/how-to/capture-warnings.rst index 8ed546bedf7..b0ff6a74892 100644 --- a/doc/en/how-to/capture-warnings.rst +++ b/doc/en/how-to/capture-warnings.rst @@ -28,7 +28,7 @@ Running pytest now produces this output: $ pytest test_show_warnings.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 1 item @@ -160,6 +160,15 @@ You can specify multiple filters with separate decorators: def test_one(): assert api_v1() == 1 +You can also pass multiple filters to a single mark by providing multiple arguments: + +.. code-block:: python + + # Later arguments take precedence, matching warnings.filterwarnings behavior. + @pytest.mark.filterwarnings("error", "ignore:api v1") + def test_one(): + assert api_v1() == 1 + .. important:: Regarding decorator order and filter precedence: @@ -198,7 +207,7 @@ decorator or to all tests in a module by setting the :globalvar:`pytestmark` var Disabling warnings summary -------------------------- -Although not recommended, you can use the ``--disable-warnings`` command-line option to suppress the +Although not recommended, you can use the :option:`--disable-warnings` command-line option to suppress the warning summary entirely from the test run output. Disabling warning capture entirely @@ -220,7 +229,7 @@ This plugin is enabled by default but can be disabled entirely in your configura [pytest] addopts = -p no:warnings -Or passing ``-p no:warnings`` in the command-line. This might be useful if your test suites handles warnings +Or passing ``-p no:warnings`` in the command-line. This might be useful if your test suite handles warnings using an external system. @@ -286,8 +295,8 @@ Ensuring code triggers a deprecation warning -------------------------------------------- You can also use :func:`pytest.deprecated_call` for checking -that a certain function call triggers a ``DeprecationWarning`` or -``PendingDeprecationWarning``: +that a certain function call triggers a ``DeprecationWarning``, ``PendingDeprecationWarning`` or +``FutureWarning``: .. code-block:: python diff --git a/doc/en/how-to/doctest.rst b/doc/en/how-to/doctest.rst index 601f5c0afd0..59d1033ed4f 100644 --- a/doc/en/how-to/doctest.rst +++ b/doc/en/how-to/doctest.rst @@ -11,7 +11,7 @@ can change the pattern by issuing: pytest --doctest-glob="*.rst" -on the command line. ``--doctest-glob`` can be given multiple times in the command-line. +on the command line. :option:`--doctest-glob` can be given multiple times in the command-line. If you then have a text file like this: @@ -30,7 +30,7 @@ then you can just invoke ``pytest`` directly: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 1 item @@ -39,7 +39,7 @@ then you can just invoke ``pytest`` directly: ============================ 1 passed in 0.12s ============================= By default, pytest will collect ``test*.txt`` files looking for doctest directives, but you -can pass additional globs using the ``--doctest-glob`` option (multi-allowed). +can pass additional globs using the :option:`--doctest-glob` option (multi-allowed). In addition to text files, you can also execute doctests directly from docstrings of your classes and functions, including from test modules: @@ -58,7 +58,7 @@ and functions, including from test modules: $ pytest --doctest-modules =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 2 items @@ -185,7 +185,7 @@ Output format ------------- You can change the diff output format on failure for your doctests -by using one of standard doctest modules format in options +by using one of the standard doctest module's format options (see :data:`python:doctest.REPORT_UDIFF`, :data:`python:doctest.REPORT_CDIFF`, :data:`python:doctest.REPORT_NDIFF`, :data:`python:doctest.REPORT_ONLY_FIRST_FAILURE`): diff --git a/doc/en/how-to/failures.rst b/doc/en/how-to/failures.rst index 0c45cd7b118..878c869d525 100644 --- a/doc/en/how-to/failures.rst +++ b/doc/en/how-to/failures.rst @@ -93,8 +93,8 @@ Pytest supports the use of ``breakpoint()`` with the following behaviours: - When ``breakpoint()`` is called and ``PYTHONBREAKPOINT`` is set to the default value, pytest will use the custom internal PDB trace UI instead of the system default ``Pdb``. - When tests are complete, the system will default back to the system ``Pdb`` trace UI. - - With ``--pdb`` passed to pytest, the custom internal Pdb trace UI is used with both ``breakpoint()`` and failed tests/unhandled exceptions. - - ``--pdbcls`` can be used to specify a custom debugger class. + - With :option:`--pdb` passed to pytest, the custom internal Pdb trace UI is used with both ``breakpoint()`` and failed tests/unhandled exceptions. + - :option:`--pdbcls` can be used to specify a custom debugger class. .. _faulthandler: diff --git a/doc/en/how-to/fixtures.rst b/doc/en/how-to/fixtures.rst index 73a3eeb15c1..2f554fb8c60 100644 --- a/doc/en/how-to/fixtures.rst +++ b/doc/en/how-to/fixtures.rst @@ -433,7 +433,7 @@ marked ``smtp_connection`` fixture function. Running the test looks like this: $ pytest test_module.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 2 items @@ -471,7 +471,7 @@ marked ``smtp_connection`` fixture function. Running the test looks like this: ============================ 2 failed in 0.12s ============================= You see the two ``assert 0`` failing and more importantly you can also see -that the **exactly same** ``smtp_connection`` object was passed into the +that the **exact same** ``smtp_connection`` object was passed into the two test functions because pytest shows the incoming argument values in the traceback. As a result, the two test functions using ``smtp_connection`` run as quick as a single one because they reuse the same instance. @@ -773,7 +773,7 @@ For yield fixtures, the first teardown code to run is from the right-most fixtur $ pytest -s test_finalizers.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 1 item @@ -807,7 +807,7 @@ For finalizers, the first fixture to run is last call to `request.addfinalizer`. $ pytest -s test_finalizers.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 1 item @@ -1368,9 +1368,9 @@ different server string is expected than what arrived. pytest will build a string that is the test ID for each fixture value in a parametrized fixture, e.g. ``test_ehlo[smtp.gmail.com]`` and ``test_ehlo[mail.python.org]`` in the above examples. These IDs can -be used with ``-k`` to select specific cases to run, and they will +be used with :option:`-k` to select specific cases to run, and they will also identify the specific case when one is failing. Running pytest -with ``--collect-only`` will show the generated IDs. +with :option:`--collect-only` will show the generated IDs. Numbers, strings, booleans and ``None`` will have their usual string representation used in the test ID. For other objects, pytest will @@ -1419,11 +1419,11 @@ Running the above tests results in the following test IDs being used: $ pytest --collect-only =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 12 items - + @@ -1474,7 +1474,7 @@ Running this test will *skip* the invocation of ``data_set`` with value ``2``: $ pytest test_fixture_marks.py -v =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: /home/sweet/project collecting ... collected 3 items @@ -1524,7 +1524,7 @@ Here we declare an ``app`` fixture which receives the previously defined $ pytest -v test_appsetup.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: /home/sweet/project collecting ... collected 2 items @@ -1604,7 +1604,7 @@ Let's run the tests in verbose mode and with looking at the print-output: $ pytest -v -s test_module.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: .pytest_cache rootdir: /home/sweet/project collecting ... collected 8 items @@ -1645,7 +1645,7 @@ Let's run the tests in verbose mode and with looking at the print-output: ============================ 8 passed in 0.12s ============================= You can see that the parametrized module-scoped ``modarg`` resource caused an -ordering of test execution that lead to the fewest possible "active" resources. +ordering of test execution that led to the fewest possible "active" resources. The finalizer for the ``mod1`` parametrized resource was executed before the ``mod2`` resource was setup. @@ -1654,7 +1654,7 @@ Then test_1 is executed with ``mod1``, then test_2 with ``mod1``, then test_1 with ``mod2`` and finally test_2 with ``mod2``. The ``otherarg`` parametrized resource (having function scope) was set up before -and teared down after every test that used it. +and torn down after every test that used it. .. _`usefixtures`: @@ -1762,8 +1762,8 @@ into a configuration file: Overriding fixtures on various levels ------------------------------------- -In relatively large test suite, you most likely need to ``override`` a ``global`` or ``root`` fixture with a ``locally`` -defined one, keeping the test code readable and maintainable. +In a relatively large test suite, you may want to *override* a fixture, to augment +or change its behavior inside of certain test modules or directories. Override a fixture on a folder (conftest) level ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1800,7 +1800,7 @@ Given the tests file structure is: def test_username(username): assert username == 'overridden-username' -As you can see, a fixture with the same name can be overridden for certain test folder level. +As you can see, a fixture with the same name can be overridden for a certain test directory level. Note that the ``base`` or ``super`` fixture can be accessed from the ``overriding`` fixture easily - used in the example above. @@ -1842,7 +1842,7 @@ Given the tests file structure is: def test_username(username): assert username == 'overridden-else-username' -In the example above, a fixture with the same name can be overridden for certain test module. +In the example above, a fixture with the same name can be overridden for a certain test module. Override a fixture with direct test parametrization diff --git a/doc/en/how-to/logging.rst b/doc/en/how-to/logging.rst index d6d87f03bbf..25b4e9017e2 100644 --- a/doc/en/how-to/logging.rst +++ b/doc/en/how-to/logging.rst @@ -65,7 +65,7 @@ These options can also be customized through a configuration file: log_format = %(asctime)s %(levelname)s %(message)s log_date_format = %Y-%m-%d %H:%M:%S -Specific loggers can be disabled via ``--log-disable={logger_name}``. +Specific loggers can be disabled via :option:`--log-disable={logger_name}`. This argument can be passed multiple times: .. code-block:: bash @@ -199,13 +199,13 @@ By setting the :confval:`log_cli` configuration option to ``true``, pytest will logging records as they are emitted directly into the console. You can specify the logging level for which log records with equal or higher -level are printed to the console by passing ``--log-cli-level``. This setting +level are printed to the console by passing :option:`--log-cli-level`. This setting accepts the logging level names or numeric values as seen in :ref:`logging's documentation `. -Additionally, you can also specify ``--log-cli-format`` and -``--log-cli-date-format`` which mirror and default to ``--log-format`` and -``--log-date-format`` if not provided, but are applied only to the console +Additionally, you can also specify :option:`--log-cli-format` and +:option:`--log-cli-date-format` which mirror and default to :option:`--log-format` and +:option:`--log-date-format` if not provided, but are applied only to the console logging handler. All of the CLI log options can also be set in the configuration file. The @@ -216,19 +216,19 @@ option names are: * :confval:`log_cli_date_format` If you need to record the whole test suite logging calls to a file, you can pass -``--log-file=/path/to/log/file``. This log file is opened in write mode by default which -means that it will be overwritten at each run tests session. -If you'd like the file opened in append mode instead, then you can pass ``--log-file-mode=a``. +:option:`--log-file=/path/to/log/file`. This log file is opened in write mode by default, which +means that it will be overwritten at each test session. +If you'd like the file opened in append mode instead, then you can pass :option:`--log-file-mode=a`. Note that relative paths for the log-file location, whether passed on the CLI or declared in a config file, are always resolved relative to the current working directory. You can also specify the logging level for the log file by passing -``--log-file-level``. This setting accepts the logging level names or numeric +:option:`--log-file-level`. This setting accepts the logging level names or numeric values as seen in :ref:`logging's documentation `. -Additionally, you can also specify ``--log-file-format`` and -``--log-file-date-format`` which are equal to ``--log-format`` and -``--log-date-format`` but are applied to the log file logging handler. +Additionally, you can also specify :option:`--log-file-format` and +:option:`--log-file-date-format` which are equal to ``--log-format`` and +:option:`--log-date-format` but are applied to the log file logging handler. All of the log file options can also be set in the configuration file. The option names are: @@ -302,17 +302,17 @@ This feature was introduced in ``3.3`` and some **incompatible changes** have be made in ``3.4`` after community feedback: * Log levels are no longer changed unless explicitly requested by the :confval:`log_level` configuration - or ``--log-level`` command-line options. This allows users to configure logger objects themselves. + or :option:`--log-level` command-line options. This allows users to configure logger objects themselves. Setting :confval:`log_level` will set the level that is captured globally so if a specific test requires a lower level than this, use the ``caplog.set_level()`` functionality otherwise that test will be prone to failure. * :ref:`Live Logs ` is now disabled by default and can be enabled setting the :confval:`log_cli` configuration option to ``true``. When enabled, the verbosity is increased so logging for each test is visible. -* :ref:`Live Logs ` are now sent to ``sys.stdout`` and no longer require the ``-s`` command-line option +* :ref:`Live Logs ` are now sent to ``sys.stdout`` and no longer require the :option:`-s` command-line option to work. -If you want to partially restore the logging behavior of version ``3.3``, you can add this options to your configuration +If you want to partially restore the logging behavior of version ``3.3``, you can add these options to your configuration file: .. tab:: toml @@ -331,4 +331,4 @@ file: log_cli = true log_level = NOTSET -More details about the discussion that lead to this changes can be read in :issue:`3013`. +More details about the discussion that led to these changes can be read in :issue:`3013`. diff --git a/doc/en/how-to/mark.rst b/doc/en/how-to/mark.rst index 575ce2f41c2..e22219414a0 100644 --- a/doc/en/how-to/mark.rst +++ b/doc/en/how-to/mark.rst @@ -21,7 +21,7 @@ Here are some of the builtin markers: It's easy to create custom markers or to apply markers to whole test classes or modules. Those markers can be used by plugins, and also -are commonly used to :ref:`select tests ` on the command-line with the ``-m`` option. +are commonly used to :ref:`select tests ` on the command-line with the :option:`-m` option. See :ref:`mark examples` for examples which also serve as documentation. diff --git a/doc/en/how-to/monkeypatch.rst b/doc/en/how-to/monkeypatch.rst index a9504dcb32a..7442a85c10e 100644 --- a/doc/en/how-to/monkeypatch.rst +++ b/doc/en/how-to/monkeypatch.rst @@ -235,7 +235,7 @@ so that any attempts within tests to create http requests will fail. Be advised that it is not recommended to patch builtin functions such as ``open``, ``compile``, etc., because it might break pytest's internals. If that's - unavoidable, passing ``--tb=native``, ``--assert=plain`` and ``--capture=no`` might + unavoidable, passing :option:`--tb=native`, :option:`--assert=plain` and :option:`--capture=no` might help although there's no guarantee. .. note:: @@ -382,7 +382,7 @@ You can use the :py:meth:`monkeypatch.delitem ` to remove v def test_missing_user(monkeypatch): - # patch the DEFAULT_CONFIG t be missing the 'user' key + # patch the DEFAULT_CONFIG to be missing the 'user' key monkeypatch.delitem(app.DEFAULT_CONFIG, "user", raising=False) # Key error expected because a config is not passed, and the diff --git a/doc/en/how-to/output.rst b/doc/en/how-to/output.rst index e03f477b22d..a594fcb3aab 100644 --- a/doc/en/how-to/output.rst +++ b/doc/en/how-to/output.rst @@ -30,8 +30,8 @@ Examples for modifying traceback printing: pytest --tb=native # Python standard library formatting pytest --tb=no # no traceback at all -The ``--full-trace`` causes very long traces to be printed on error (longer -than ``--tb=long``). It also ensures that a stack trace is printed on +The :option:`--full-trace` causes very long traces to be printed on error (longer +than :option:`--tb=long`). It also ensures that a stack trace is printed on **KeyboardInterrupt** (Ctrl+C). This is very useful if the tests are taking too long and you interrupt them with Ctrl+C to find out where the tests are *hanging*. By default no output @@ -52,8 +52,8 @@ Examples for modifying printing verbosity: pytest -vv # more verbose, display more details from the test output pytest -vvv # not a standard , but may be used for even more detail in certain setups -The ``-v`` flag controls the verbosity of pytest output in various aspects: test session progress, assertion -details when tests fail, fixtures details with ``--fixtures``, etc. +The :option:`-v` flag controls the verbosity of pytest output in various aspects: test session progress, assertion +details when tests fail, fixtures details with :option:`--fixtures`, etc. .. regendoc:wipe @@ -372,7 +372,7 @@ test inside the file gets its own line in the output. Producing a detailed summary report -------------------------------------------------- -The ``-r`` flag can be used to display a "short test summary info" at the end of the test session, +The :option:`-r` flag can be used to display a "short test summary info" at the end of the test session, making it easy in large test suites to get a clear picture of all failures, skips, xfails, etc. It defaults to ``fE`` to list failures and errors. @@ -421,7 +421,7 @@ Example: $ pytest -ra =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 6 items @@ -453,7 +453,7 @@ Example: FAILED test_example.py::test_fail - assert 0 == 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12s === -The ``-r`` options accepts a number of characters after it, with ``a`` used +The :option:`-r` options accepts a number of characters after it, with ``a`` used above meaning "all except passes". Here is the full list of available characters that can be used: @@ -478,7 +478,7 @@ More than one character can be used, so for example to only see failed and skipp $ pytest -rfs =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 6 items @@ -513,7 +513,7 @@ captured output: $ pytest -rpP =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 6 items @@ -547,7 +547,7 @@ captured output: .. note:: By default, parametrized variants of skipped tests are grouped together if - they share the same skip reason. You can use ``--no-fold-skipped`` to print each skipped test separately. + they share the same skip reason. You can use :option:`--no-fold-skipped` to print each skipped test separately. .. _truncation-params: @@ -771,7 +771,7 @@ record_testsuite_property .. versionadded:: 4.5 -If you want to add a properties node at the test-suite level, which may contains properties +If you want to add a properties node at the test-suite level, which may contain properties that are relevant to all tests, you can use the ``record_testsuite_property`` session-scoped fixture: The ``record_testsuite_property`` session-scoped fixture can be used to add properties relevant @@ -822,7 +822,7 @@ Sending test report to an online pastebin service This will submit test run information to a remote Paste service and provide a URL for each failure. You may select tests as usual or add -for example ``-x`` if you only want to send one particular failure. +for example :option:`-x` if you only want to send one particular failure. **Creating a URL for a whole test session log**: diff --git a/doc/en/how-to/parametrize.rst b/doc/en/how-to/parametrize.rst index dba2ac0b91e..5de28472705 100644 --- a/doc/en/how-to/parametrize.rst +++ b/doc/en/how-to/parametrize.rst @@ -57,7 +57,7 @@ them in turn: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 3 items @@ -177,7 +177,7 @@ Let's run this: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 3 items diff --git a/doc/en/how-to/plugins.rst b/doc/en/how-to/plugins.rst index 591c44dfa4d..c6641eb8484 100644 --- a/doc/en/how-to/plugins.rst +++ b/doc/en/how-to/plugins.rst @@ -148,7 +148,7 @@ Disabling plugins from autoloading ---------------------------------- If you want to disable plugins from loading automatically, instead of requiring you to -manually specify each plugin with ``-p`` or :envvar:`PYTEST_PLUGINS`, you can use ``--disable-plugin-autoload`` or :envvar:`PYTEST_DISABLE_PLUGIN_AUTOLOAD`. +manually specify each plugin with :option:`-p` or :envvar:`PYTEST_PLUGINS`, you can use :option:`--disable-plugin-autoload` or :envvar:`PYTEST_DISABLE_PLUGIN_AUTOLOAD`. .. code-block:: bash @@ -158,7 +158,7 @@ manually specify each plugin with ``-p`` or :envvar:`PYTEST_PLUGINS`, you can us .. code-block:: bash - pytest --disable-plugin-autoload -p NAME,NAME2 + pytest --disable-plugin-autoload -p NAME -p NAME2 .. tab:: toml @@ -179,4 +179,32 @@ manually specify each plugin with ``-p`` or :envvar:`PYTEST_PLUGINS`, you can us .. versionadded:: 8.4 - The ``--disable-plugin-autoload`` command-line flag. + The :option:`--disable-plugin-autoload` command-line flag. + +.. note:: + + :option:`-p` and :envvar:`PYTEST_PLUGINS` are both ways to explicitly control which + plugins are loaded, but they serve slightly different use-cases. + + * :option:`-p` loads (or disables with ``-p no:``) a plugin by name or entry point + for a specific pytest invocation, and is processed early during startup. + * :envvar:`PYTEST_PLUGINS` is a comma-separated list of Python modules that are imported + and registered as plugins during startup. This mechanism is commonly used by test + suites, for example when testing a plugin. + + When explicitly controlling plugin loading (especially with + :envvar:`PYTEST_DISABLE_PLUGIN_AUTOLOAD` or :option:`--disable-plugin-autoload`), + avoid specifying the same plugin via multiple mechanisms. Registering the same plugin + more than once can lead to errors during plugin registration. + +Examples: + +.. code-block:: bash + + # Disable auto-loading and load only specific plugins for this invocation + PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 pytest -p xdist + +.. code-block:: bash + + # Disable auto-loading and load plugin modules during startup + PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 PYTEST_PLUGINS=mymodule.plugin,xdist pytest diff --git a/doc/en/how-to/skipping.rst b/doc/en/how-to/skipping.rst index 1887fbd53ef..488f71b09f9 100644 --- a/doc/en/how-to/skipping.rst +++ b/doc/en/how-to/skipping.rst @@ -21,14 +21,14 @@ it's an **xpass** and will be reported in the test summary. ``pytest`` counts and lists *skip* and *xfail* tests separately. Detailed information about skipped/xfailed tests is not shown by default to avoid -cluttering the output. You can use the ``-r`` option to see details +cluttering the output. You can use the :option:`-r` option to see details corresponding to the "short" letters shown in the test progress: .. code-block:: bash pytest -rxXs # show extra info on xfailed, xpassed, and skipped tests -More details on the ``-r`` option can be found by running ``pytest -h``. +More details on the :option:`-r` option can be found by running ``pytest -h``. (See :ref:`how to change command line options defaults`) @@ -311,7 +311,7 @@ even executed, use the ``run`` parameter as ``False``: @pytest.mark.xfail(run=False) def test_function(): ... -This is specially useful for xfailing tests that are crashing the interpreter and should be +This is particularly useful for xfailing tests that are crashing the interpreter and should be investigated later. .. _`xfail strict tutorial`: diff --git a/doc/en/how-to/subtests.rst b/doc/en/how-to/subtests.rst index 956405bc7cf..c71f1bbe1ad 100644 --- a/doc/en/how-to/subtests.rst +++ b/doc/en/how-to/subtests.rst @@ -26,13 +26,45 @@ Subtests are an alternative to parametrization, particularly useful when the exa Each assertion failure or error is caught by the context manager and reported individually: -.. code-block:: pytest +.. code-block:: text $ pytest -q test_subtest.py - + uuuuuF [100%] + ================================= FAILURES ================================= + _______________________ test [custom message] (i=1) ________________________ + + subtests = <_pytest.subtests.Subtests object at 0xdeadbeef0001> + + def test(subtests): + for i in range(5): + with subtests.test(msg="custom message", i=i): + > assert i % 2 == 0 + E assert (1 % 2) == 0 + + test_subtest.py:6: AssertionError + _______________________ test [custom message] (i=3) ________________________ + + subtests = <_pytest.subtests.Subtests object at 0xdeadbeef0001> + + def test(subtests): + for i in range(5): + with subtests.test(msg="custom message", i=i): + > assert i % 2 == 0 + E assert (3 % 2) == 0 + + test_subtest.py:6: AssertionError + ___________________________________ test ___________________________________ + contains 2 failed subtests + ========================= short test summary info ========================== + SUBFAILED[custom message] (i=1) test_subtest.py::test - assert (1 % 2) == 0 + SUBFAILED[custom message] (i=3) test_subtest.py::test - assert (3 % 2) == 0 + FAILED test_subtest.py::test - contains 2 failed subtests + 3 failed, 3 subtests passed in 0.12s In the output above: +* The compact progress output uses ``u`` for both passed and failed subtests; + see the short test summary for each failed subtest. * Subtest failures are reported as ``SUBFAILED``. * Subtests are reported first and the "top-level" test is reported at the end on its own. @@ -60,7 +92,7 @@ outside the ``subtests.test`` block: Verbosity --------- -By default, only **subtest failures** are shown. Higher verbosity levels (``-v``) will also show progress output for **passed** subtests. +By default, only **subtest failures** are shown. Higher verbosity levels (:option:`-v`) will also show progress output for **passed** subtests. It is possible to control the verbosity of subtests by setting :confval:`verbosity_subtests`. @@ -88,7 +120,7 @@ Parametrization * Happens at collection time. * Generates individual tests. * Parametrized tests can be referenced from the command line. -* Plays well with plugins that handle test execution, such as ``--last-failed``. +* Plays well with plugins that handle test execution, such as :option:`--last-failed`. * Ideal for decision table testing. Subtests @@ -106,4 +138,4 @@ Subtests This feature was originally implemented as a separate plugin in `pytest-subtests `__, but since ``9.0`` has been merged into the core. - The core implementation should be compatible to the plugin implementation, except it does not contain custom command-line options to control subtest output. + The core implementation should be compatible with the plugin implementation, except it does not contain custom command-line options to control subtest output. diff --git a/doc/en/how-to/tmp_path.rst b/doc/en/how-to/tmp_path.rst index d19950431e5..e73c55878a6 100644 --- a/doc/en/how-to/tmp_path.rst +++ b/doc/en/how-to/tmp_path.rst @@ -35,7 +35,7 @@ Running this would result in a passed test except for the last $ pytest test_tmp_path.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 1 item @@ -136,9 +136,9 @@ Temporary directory location and retention The temporary directories, as returned by the :fixture:`tmp_path` and (now deprecated) :fixture:`tmpdir` fixtures, are automatically created under a base temporary directory, -in a structure that depends on the ``--basetemp`` option: +in a structure that depends on the :option:`--basetemp` option: -- By default (when the ``--basetemp`` option is not set), +- By default (when the :option:`--basetemp` option is not set), the temporary directories will follow this template: .. code-block:: text @@ -160,7 +160,7 @@ in a structure that depends on the ``--basetemp`` option: but this behavior can be configured with :confval:`tmp_path_retention_count` and :confval:`tmp_path_retention_policy`. -- When the ``--basetemp`` option is used (e.g. ``pytest --basetemp=mydir``), +- When the :option:`--basetemp` option is used (e.g. ``pytest --basetemp=mydir``), it will be used directly as base temporary directory: .. code-block:: text @@ -172,7 +172,7 @@ in a structure that depends on the ``--basetemp`` option: .. warning:: - The directory given to ``--basetemp`` will be cleared blindly before each test run, + The directory given to :option:`--basetemp` will be cleared blindly before each test run, so make sure to use a directory for that purpose only. When distributing tests on the local machine using ``pytest-xdist``, care is taken to diff --git a/doc/en/how-to/unittest.rst b/doc/en/how-to/unittest.rst index a8c56c266bd..0762e7d4cf8 100644 --- a/doc/en/how-to/unittest.rst +++ b/doc/en/how-to/unittest.rst @@ -42,7 +42,7 @@ in most cases without having to modify existing code: * Obtain :ref:`more informative tracebacks `; * :ref:`stdout and stderr ` capturing; -* :ref:`Test selection options ` using ``-k`` and ``-m`` flags; +* :ref:`Test selection options ` using :option:`-k` and :option:`-m` flags; * :ref:`maxfail`; * :ref:`--pdb ` command-line option for debugging on test failures (see :ref:`note ` below); @@ -137,7 +137,7 @@ the ``self.db`` values in the traceback: $ pytest test_unittest_db.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 2 items diff --git a/doc/en/how-to/usage.rst b/doc/en/how-to/usage.rst index 0e0a0310fd8..35b07bfe8c1 100644 --- a/doc/en/how-to/usage.rst +++ b/doc/en/how-to/usage.rst @@ -4,10 +4,10 @@ How to invoke pytest ========================================== -.. seealso:: :ref:`Complete pytest command-line flag reference ` +.. seealso:: :ref:`Complete pytest command-line flags reference ` In general, pytest is invoked with the command ``pytest`` (see below for :ref:`other ways to invoke pytest -`). This will execute all tests in all files whose names follow the form ``test_*.py`` or ``\*_test.py`` +`). This will execute all tests in all files whose names follow the form ``test_*.py`` or ``*_test.py`` in the current directory and its subdirectories. More generally, pytest follows :ref:`standard test discovery rules `. @@ -155,7 +155,7 @@ Managing loading of plugins Early loading plugins ~~~~~~~~~~~~~~~~~~~~~~~ -You can early-load plugins (internal and external) explicitly in the command-line with the ``-p`` option:: +You can early-load plugins (internal and external) explicitly in the command-line with the :option:`-p` option:: pytest -p mypluginmodule @@ -171,7 +171,7 @@ The option receives a ``name`` parameter, which can be: Disabling plugins ~~~~~~~~~~~~~~~~~~ -To disable loading specific plugins at invocation time, use the ``-p`` option +To disable loading specific plugins at invocation time, use the :option:`-p` option together with the prefix ``no:``. Example: to disable loading the plugin ``doctest``, which is responsible for diff --git a/doc/en/how-to/writing_hook_functions.rst b/doc/en/how-to/writing_hook_functions.rst index cd18301ce84..d5d6d2ae4f7 100644 --- a/doc/en/how-to/writing_hook_functions.rst +++ b/doc/en/how-to/writing_hook_functions.rst @@ -94,7 +94,7 @@ around the actual hook implementations, in which case it can return the result value of the ``yield``. The simplest (though useless) hook wrapper is ``return (yield)``. -In other cases, the wrapper wants the adjust or adapt the result, in which case +In other cases, the wrapper wants to adjust or adapt the result, in which case it can return a new value. If the result of the underlying hook is a mutable object, the wrapper may modify that result, but it's probably better to avoid it. diff --git a/doc/en/how-to/writing_plugins.rst b/doc/en/how-to/writing_plugins.rst index 3cf49ebb0c6..56043a14f97 100644 --- a/doc/en/how-to/writing_plugins.rst +++ b/doc/en/how-to/writing_plugins.rst @@ -48,7 +48,7 @@ Plugin discovery order at tool startup 5. by loading all plugins specified through the :envvar:`PYTEST_PLUGINS` environment variable. -6. by loading all "initial ":file:`conftest.py` files: +6. by loading all "initial" :file:`conftest.py` files: - determine the test paths: specified on the command line, otherwise in :confval:`testpaths` if defined and running from the rootdir, otherwise the @@ -295,7 +295,7 @@ the plugin manager like this: plugin = config.pluginmanager.get_plugin("name_of_plugin") If you want to look at the names of existing plugins, use -the ``--trace-config`` option. +the :option:`--trace-config` option. .. _registering-markers: @@ -446,9 +446,9 @@ in our configuration file to tell pytest where to look for example files. $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project - configfile: pytest.ini + configfile: pytest.toml collected 2 items test_example.py .. [100%] diff --git a/doc/en/index.rst b/doc/en/index.rst index 2d9e3bed42c..29d270084e9 100644 --- a/doc/en/index.rst +++ b/doc/en/index.rst @@ -2,7 +2,8 @@ .. sidebar:: **Next Open Trainings and Events** - - `Professional Testing with Python `_, via `Python Academy `_ (3 day in-depth training), **March 3th -- 5th 2026**, Leipzig (DE) / Remote + - `pytest development sprint `_, **July 20th -- 24th**, Klaus (AT), sign-up open until June 15th + - `Professional Testing with Python `_, via `Python Academy `_ (3 day in-depth training), **March 9th -- 11th 2027**, Leipzig (DE) / Remote Also see :doc:`previous talks and blogposts ` @@ -67,7 +68,7 @@ To execute it: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y + platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y rootdir: /home/sweet/project collected 1 item diff --git a/doc/en/reference/customize.rst b/doc/en/reference/customize.rst index 500e5519bdd..8903ceadf68 100644 --- a/doc/en/reference/customize.rst +++ b/doc/en/reference/customize.rst @@ -103,6 +103,10 @@ pyproject.toml "integration", ] + For projects that still run pytest versions older than 6.0, keep + ``minversion`` in ``pytest.ini`` or ``setup.cfg`` too. Those versions + do not read ``pyproject.toml``. + tox.ini ~~~~~~~ @@ -160,7 +164,7 @@ the command line arguments (specified test files, paths) and on the existence of configuration files. The determined ``rootdir`` and ``configfile`` are printed as part of the pytest header during startup. -Here's a summary what ``pytest`` uses ``rootdir`` for: +Here's a summary of what ``pytest`` uses ``rootdir`` for: * Construct *nodeids* during collection; each test is assigned a unique *nodeid* which is rooted at the ``rootdir`` and takes into account @@ -173,7 +177,7 @@ Here's a summary what ``pytest`` uses ``rootdir`` for: ``rootdir`` is **NOT** used to modify ``sys.path``/``PYTHONPATH`` or influence how modules are imported. See :ref:`pythonpath` for more details. -The ``--rootdir=path`` command-line option can be used to force a specific directory. +The :option:`--rootdir=path` command-line option can be used to force a specific directory. Note that contrary to other command-line options, ``--rootdir`` cannot be used with :confval:`addopts` inside a configuration file because the ``rootdir`` is used to *find* the configuration file already. @@ -183,7 +187,7 @@ Finding the ``rootdir`` Here is the algorithm which finds the rootdir from ``args``: -- If ``-c`` is passed in the command-line, use that as configuration file, and its directory as ``rootdir``. +- If :option:`-c` is passed in the command-line, use that as configuration file, and its directory as ``rootdir``. - Determine the common ancestor directory for the specified ``args`` that are recognised as paths that exist in the file system. If no such paths are @@ -204,7 +208,7 @@ Here is the algorithm which finds the rootdir from ``args``: directory. This allows the use of pytest in structures that are not part of a package and don't have any particular configuration file. -If no ``args`` are given, pytest collects test below the current working +If no ``args`` are given, pytest collects tests below the current working directory and also starts determining the ``rootdir`` from there. Files will only be matched for configuration if: @@ -274,7 +278,7 @@ check for configuration files as follows: ``pytest --log-output ../../test.log args``. Then ``args`` is mandatory, otherwise pytest uses the folder of test.log for rootdir determination (see also :issue:`1435`). - A dot ``.`` for referencing to the current working directory is also + A dot ``.`` for referencing the current working directory is also possible. diff --git a/doc/en/reference/exit-codes.rst b/doc/en/reference/exit-codes.rst index b695ca3702e..49aaca19121 100644 --- a/doc/en/reference/exit-codes.rst +++ b/doc/en/reference/exit-codes.rst @@ -20,7 +20,7 @@ They are represented by the :class:`pytest.ExitCode` enum. The exit codes being .. note:: - If you would like to customize the exit code in some scenarios, specially when + If you would like to customize the exit code in some scenarios, specifically when no tests are collected, consider using the `pytest-custom_exit_code `__ plugin. diff --git a/doc/en/reference/fixtures.rst b/doc/en/reference/fixtures.rst index 02e235ceb9e..c4a8d01ff0e 100644 --- a/doc/en/reference/fixtures.rst +++ b/doc/en/reference/fixtures.rst @@ -34,7 +34,7 @@ Built-in fixtures :fixture:`capteesys` Capture in the same manner as :fixture:`capsys`, but also pass text - through according to ``--capture=``. + through according to :option:`--capture`. :fixture:`capsysbinary` Capture, as bytes, output to ``sys.stdout`` and ``sys.stderr``. @@ -277,13 +277,13 @@ the test's search for fixtures would look like: pytest will only search for ``a_fix`` and ``b_fix`` in the plugins after searching for them first in the scopes inside ``tests/``. -.. note: +.. note:: pytest can tell you what fixtures are available for a given test if you call - ``pytests`` along with the test's name (or the scope it's in), and provide - the ``--fixtures`` flag, e.g. ``pytest --fixtures test_something.py`` + ``pytest`` along with the test's name (or the scope it's in), and provide + the :option:`--fixtures` flag, e.g. ``pytest --fixtures test_something.py`` (fixtures with names that start with ``_`` will only be shown if you also - provide the ``-v`` flag). + provide the :option:`-v` flag). .. _`fixture order`: @@ -354,7 +354,7 @@ an order of operations for a given test. If there's any ambiguity, and the order of operations can be interpreted more than one way, you should assume pytest could go with any one of those interpretations at any point. -For example, if ``d`` didn't request ``c``, i.e.the graph would look like this: +For example, if ``d`` didn't request ``c``, i.e. the graph would look like this: .. image:: /example/fixtures/test_fixtures_order_dependencies_unclear.* :align: center @@ -448,10 +448,10 @@ for the tests inside ``TestClassWithoutAutouse``, since they can reference can't see ``c3``. -.. note: +.. note:: pytest can tell you what order the fixtures will execute in for a given test - if you call ``pytests`` along with the test's name (or the scope it's in), - and provide the ``--setup-plan`` flag, e.g. + if you call ``pytest`` along with the test's name (or the scope it's in), + and provide the :option:`--setup-plan` flag, e.g. ``pytest --setup-plan test_something.py`` (fixtures with names that start - with ``_`` will only be shown if you also provide the ``-v`` flag). + with ``_`` will only be shown if you also provide the :option:`-v` flag). diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst index 108ad660af3..3760add53cf 100644 --- a/doc/en/reference/reference.rst +++ b/doc/en/reference/reference.rst @@ -18,7 +18,7 @@ The current pytest version, as a string:: >>> import pytest >>> pytest.__version__ - '7.0.0' + '9.0.2' .. _`hidden-param`: @@ -757,6 +757,7 @@ items, delete or otherwise amend the test items: If this hook is implemented in ``conftest.py`` files, it always receives all collected items, not only those under the ``conftest.py`` where it is implemented. +.. hook:: pytest_collection_finish .. autofunction:: pytest_collection_finish Test running (runtest) hooks @@ -1178,77 +1179,77 @@ Environment variables that can be used to change pytest's behavior. .. envvar:: CI -When set to a non-empty value, pytest acknowledges that is running in a CI process. See also :ref:`ci-pipelines`. + When set to a non-empty value, pytest acknowledges that it is running in a CI process. See also :ref:`ci-pipelines`. .. envvar:: BUILD_NUMBER -When set to a non-empty value, pytest acknowledges that is running in a CI process. Alternative to :envvar:`CI`. See also :ref:`ci-pipelines`. + When set to a non-empty value, pytest acknowledges that it is running in a CI process. Alternative to :envvar:`CI`. See also :ref:`ci-pipelines`. .. envvar:: PYTEST_ADDOPTS -This contains a command-line (parsed by the py:mod:`shlex` module) that will be **prepended** to the command line given -by the user, see :ref:`adding default options` for more information. + This contains a command-line (parsed by the py:mod:`shlex` module) that will be **prepended** to the command line given + by the user, see :ref:`adding default options` for more information. .. envvar:: PYTEST_VERSION -This environment variable is defined at the start of the pytest session and is undefined afterwards. -It contains the value of ``pytest.__version__``, and among other things can be used to easily check if a code is running from within a pytest run. + This environment variable is defined at the start of the pytest session and is undefined afterwards. + It contains the value of ``pytest.__version__``, and among other things can be used to easily check if a code is running from within a pytest run. .. envvar:: PYTEST_CURRENT_TEST -This is not meant to be set by users, but is set by pytest internally with the name of the current test so other -processes can inspect it, see :ref:`pytest current test env` for more information. + This is not meant to be set by users, but is set by pytest internally with the name of the current test so other + processes can inspect it, see :ref:`pytest current test env` for more information. .. envvar:: PYTEST_DEBUG -When set, pytest will print tracing and debug information. + When set, pytest will print tracing and debug information. .. envvar:: PYTEST_DEBUG_TEMPROOT -Root for temporary directories produced by fixtures like :fixture:`tmp_path` -as discussed in :ref:`temporary directory location and retention`. + Root for temporary directories produced by fixtures like :fixture:`tmp_path` + as discussed in :ref:`temporary directory location and retention`. .. envvar:: PYTEST_DISABLE_PLUGIN_AUTOLOAD -When set, disables plugin auto-loading through :std:doc:`entry point packaging -metadata `. Only plugins -explicitly specified in :envvar:`PYTEST_PLUGINS` or with ``-p`` will be loaded. -See also :ref:`--disable-plugin-autoload `. + When set, disables plugin auto-loading through :std:doc:`entry point packaging + metadata `. Only plugins + explicitly specified in :envvar:`PYTEST_PLUGINS` or with :option:`-p` will be loaded. + See also :ref:`--disable-plugin-autoload `. .. envvar:: PYTEST_PLUGINS -Contains comma-separated list of modules that should be loaded as plugins: + Contains comma-separated list of modules that should be loaded as plugins: -.. code-block:: bash + .. code-block:: bash - export PYTEST_PLUGINS=mymodule.plugin,xdist + export PYTEST_PLUGINS=mymodule.plugin,xdist -See also ``-p``. + See also :option:`-p`. .. envvar:: PYTEST_THEME -Sets a `pygment style `_ to use for the code output. + Sets a `pygment style `_ to use for the code output. .. envvar:: PYTEST_THEME_MODE -Sets the :envvar:`PYTEST_THEME` to be either *dark* or *light*. + Sets the :envvar:`PYTEST_THEME` to be either *dark* or *light*. .. envvar:: PY_COLORS -When set to ``1``, pytest will use color in terminal output. -When set to ``0``, pytest will not use color. -``PY_COLORS`` takes precedence over ``NO_COLOR`` and ``FORCE_COLOR``. + When set to ``1``, pytest will use color in terminal output. + When set to ``0``, pytest will not use color. + ``PY_COLORS`` takes precedence over ``NO_COLOR`` and ``FORCE_COLOR``. .. envvar:: NO_COLOR -When set to a non-empty string (regardless of value), pytest will not use color in terminal output. -``PY_COLORS`` takes precedence over ``NO_COLOR``, which takes precedence over ``FORCE_COLOR``. -See `no-color.org `__ for other libraries supporting this community standard. + When set to a non-empty string (regardless of value), pytest will not use color in terminal output. + ``PY_COLORS`` takes precedence over ``NO_COLOR``, which takes precedence over ``FORCE_COLOR``. + See `no-color.org `__ for other libraries supporting this community standard. .. envvar:: FORCE_COLOR -When set to a non-empty string (regardless of value), pytest will use color in terminal output. -``PY_COLORS`` and ``NO_COLOR`` take precedence over ``FORCE_COLOR``. + When set to a non-empty string (regardless of value), pytest will use color in terminal output. + ``PY_COLORS`` and ``NO_COLOR`` take precedence over ``FORCE_COLOR``. Exceptions ---------- @@ -1314,7 +1315,7 @@ Configuration Options Here is a list of builtin configuration options that may be written in a ``pytest.ini`` (or ``.pytest.ini``), ``pyproject.toml``, ``tox.ini``, or ``setup.cfg`` file, usually located at the root of your repository. -To see each file format in details, see :ref:`config file formats`. +To see each file format in detail, see :ref:`config file formats`. .. warning:: Usage of ``setup.cfg`` is not recommended except for very simple use cases. ``.cfg`` @@ -1406,7 +1407,7 @@ passed multiple times. The expected format is ``name=value``. For example:: when collecting Python modules. Default is ``False``. Set to ``True`` if the package you are testing is part of a namespace package. - Namespace packages are also supported as ``--pyargs`` target. + Namespace packages are also supported as :option:`--pyargs` target. Only `native namespace packages `__ are supported, with no plans to support `legacy namespace packages `__. @@ -1585,7 +1586,6 @@ passed multiple times. The expected format is ``name=value``. For example:: faulthandler_timeout = 5 For more information please refer to :ref:`faulthandler`. - For more information please refer to :ref:`faulthandler`. .. confval:: filterwarnings @@ -1739,7 +1739,7 @@ passed multiple times. The expected format is ``name=value``. For example:: Allow selective auto-indentation of multiline log messages. - Supports command line option ``--log-auto-indent [value]`` + Supports command line option :option:`--log-auto-indent=[value]` and config option ``log_auto_indent = [value]`` to set the auto-indentation behavior for all logging. @@ -1836,7 +1836,8 @@ passed multiple times. The expected format is ``name=value``. For example:: Sets the minimum log message level that should be captured for live logging. The integer value or - the names of the levels can be used. + the names of the levels can be used. Note in TOML the integer must be quoted, as there is no support + for config parameters of mixed type. .. tab:: toml @@ -1844,6 +1845,7 @@ passed multiple times. The expected format is ``name=value``. For example:: [pytest] log_cli_level = "INFO" + log_cli_level = "10" .. tab:: ini @@ -1851,6 +1853,7 @@ passed multiple times. The expected format is ``name=value``. For example:: [pytest] log_cli_level = INFO + log_cli_level = 10 For more information, see :ref:`live_logs`. @@ -1951,7 +1954,8 @@ passed multiple times. The expected format is ``name=value``. For example:: Sets the minimum log message level that should be captured for the logging file. The integer value or - the names of the levels can be used. + the names of the levels can be used. Note in TOML the integer must be quoted, as there is no support + for config parameters of mixed type. .. tab:: toml @@ -1959,6 +1963,7 @@ passed multiple times. The expected format is ``name=value``. For example:: [pytest] log_file_level = "INFO" + log_cli_level = "10" .. tab:: ini @@ -1966,6 +1971,7 @@ passed multiple times. The expected format is ``name=value``. For example:: [pytest] log_file_level = INFO + log_cli_level = 10 For more information, see :ref:`logging`. @@ -2020,7 +2026,8 @@ passed multiple times. The expected format is ``name=value``. For example:: Sets the minimum log message level that should be captured for logging capture. The integer value or - the names of the levels can be used. + the names of the levels can be used. Note in TOML the integer must be quoted, as there is no support + for config parameters of mixed type. .. tab:: toml @@ -2028,6 +2035,7 @@ passed multiple times. The expected format is ``name=value``. For example:: [pytest] log_level = "INFO" + log_cli_level = "10" .. tab:: ini @@ -2035,6 +2043,7 @@ passed multiple times. The expected format is ``name=value``. For example:: [pytest] log_level = INFO + log_cli_level = 10 For more information, see :ref:`logging`. @@ -2123,7 +2132,7 @@ passed multiple times. The expected format is ``name=value``. For example:: Additionally, ``pytest`` will attempt to intelligently identify and ignore a virtualenv. Any directory deemed to be the root of a virtual environment will not be considered during test collection unless - ``--collect-in-virtualenv`` is given. Note also that ``norecursedirs`` + :option:`--collect-in-virtualenv` is given. Note also that ``norecursedirs`` takes precedence over ``--collect-in-virtualenv``; e.g. if you intend to run tests in a virtualenv with a base directory that matches ``'.*'`` you *must* override ``norecursedirs`` in addition to using the @@ -2460,14 +2469,14 @@ passed multiple times. The expected format is ``name=value``. For example:: .. confval:: tmp_path_retention_count How many sessions should we keep the `tmp_path` directories, - according to `tmp_path_retention_policy`. + according to :confval:`tmp_path_retention_policy`. .. tab:: toml .. code-block:: toml [pytest] - tmp_path_retention_count = 3 + tmp_path_retention_count = "3" .. tab:: ini @@ -2538,7 +2547,7 @@ passed multiple times. The expected format is ``name=value``. For example:: .. confval:: truncation_limit_lines - Controls maximum number of linesto truncate assertion message contents. + Controls maximum number of lines to truncate assertion message contents. Setting value to ``0`` disables the lines limit for truncation. @@ -2596,7 +2605,7 @@ passed multiple times. The expected format is ``name=value``. For example:: .. code-block:: toml [pytest] - verbosity_assertions = 2 + verbosity_assertions = "2" .. tab:: ini @@ -2605,7 +2614,7 @@ passed multiple times. The expected format is ``name=value``. For example:: [pytest] verbosity_assertions = 2 - If not set, defaults to application wide verbosity level (via the ``-v`` command-line option). A special value of + If not set, defaults to application wide verbosity level (via the :option:`-v` command-line option). A special value of ``"auto"`` can be used to explicitly use the global verbosity level. @@ -2618,7 +2627,7 @@ passed multiple times. The expected format is ``name=value``. For example:: .. code-block:: toml [pytest] - verbosity_subtests = 1 + verbosity_subtests = "1" .. tab:: ini @@ -2628,9 +2637,9 @@ passed multiple times. The expected format is ``name=value``. For example:: verbosity_subtests = 1 A value of ``1`` or higher will show output for **passed** subtests (**failed** subtests are always reported). - Passed subtests output can be suppressed with the value ``0``, which overwrites the ``-v`` command-line option. + Passed subtests output can be suppressed with the value ``0``, which overwrites the :option:`-v` command-line option. - If not set, defaults to application wide verbosity level (via the ``-v`` command-line option). A special value of + If not set, defaults to application wide verbosity level (via the :option:`-v` command-line option). A special value of ``"auto"`` can be used to explicitly use the global verbosity level. See also: :ref:`subtests`. @@ -2645,7 +2654,7 @@ passed multiple times. The expected format is ``name=value``. For example:: .. code-block:: toml [pytest] - verbosity_test_cases = 2 + verbosity_test_cases = "2" .. tab:: ini @@ -2654,7 +2663,7 @@ passed multiple times. The expected format is ``name=value``. For example:: [pytest] verbosity_test_cases = 2 - If not set, defaults to application wide verbosity level (via the ``-v`` command-line option). A special value of + If not set, defaults to application wide verbosity level (via the :option:`-v` command-line option). A special value of ``"auto"`` can be used to explicitly use the global verbosity level. @@ -2663,7 +2672,576 @@ passed multiple times. The expected format is ``name=value``. For example:: Command-line Flags ------------------ -All the command-line flags can be obtained by running ``pytest --help``:: +This section documents all command-line options provided by pytest's core plugins. + +.. note:: + + External plugins can add their own command-line options. + This reference documents only the options from pytest's core plugins. + To see all available options including those from installed plugins, run ``pytest --help``. + +Test Selection +~~~~~~~~~~~~~~ + +.. option:: -k EXPRESSION + + Only run tests which match the given substring expression. + An expression is a Python evaluable expression where all names are substring-matched against test names and their parent classes. + + Examples:: + + pytest -k "test_method or test_other" # matches names containing 'test_method' OR 'test_other' + pytest -k "not test_method" # matches names NOT containing 'test_method' + pytest -k "not test_method and not test_other" # excludes both + + The matching is case-insensitive. + Keywords are also matched to classes and functions containing extra names in their ``extra_keyword_matches`` set. + + See :ref:`select-tests` for more information and examples. + +.. option:: -m MARKEXPR + + Only run tests matching given mark expression. + Supports ``and``, ``or``, and ``not`` operators. + + Examples:: + + pytest -m slow # run tests marked with @pytest.mark.slow + pytest -m "not slow" # run tests NOT marked slow + pytest -m "mark1 and not mark2" # run tests marked mark1 but not mark2 + + See :ref:`mark` for more information on markers. + +.. option:: --markers + + Show all available markers (builtin, plugin, and per-project markers defined in configuration). + +Test Execution Control +~~~~~~~~~~~~~~~~~~~~~~~ + +.. option:: -x, --exitfirst + + Exit instantly on first error or failed test. + +.. option:: --maxfail=NUM + + Exit after first ``num`` failures or errors. + Useful for CI environments where you want to fail fast but see a few failures. + +.. option:: --last-failed, --lf + + Rerun only the tests that failed at the last run. + If no tests failed (or no cached data exists), all tests are run. + See also :confval:`cache_dir` and :ref:`cache`. + +.. option:: --failed-first, --ff + + Run all tests, but run the last failures first. + This may re-order tests and thus lead to repeated fixture setup/teardown. + +.. option:: --new-first, --nf + + Run tests from new files first, then the rest of the tests sorted by file modification time. + +.. option:: --stepwise, --sw + + Exit on test failure and continue from last failing test next time. + Useful for fixing multiple test failures one at a time. + + See :ref:`cache stepwise` for more information. + +.. option:: --stepwise-skip, --sw-skip + + Ignore the first failing test but stop on the next failing test. + Implicitly enables :option:`--stepwise`. + +.. option:: --stepwise-reset, --sw-reset + + Resets stepwise state, restarting the stepwise workflow. + Implicitly enables :option:`--stepwise`. + +.. option:: --last-failed-no-failures, --lfnf + + With :option:`--last-failed`, determines whether to execute tests when there are no previously known failures or when no cached ``lastfailed`` data was found. + + * ``all`` (default): runs the full test suite again + * ``none``: just emits a message about no known failures and exits successfully + +.. option:: --runxfail + + Report the results of xfail tests as if they were not marked. + Useful for debugging xfailed tests. + See :ref:`xfail`. + +Collection +~~~~~~~~~~ + +.. option:: --collect-only, --co + + Only collect tests, don't execute them. + Shows which tests would be collected and run. + +.. option:: --pyargs + + Try to interpret all arguments as Python packages. + Useful for running tests of installed packages:: + + pytest --pyargs pkg.testing + +.. option:: --ignore=PATH + + Ignore path during collection (multi-allowed). + Can be specified multiple times. + +.. option:: --ignore-glob=PATTERN + + Ignore path pattern during collection (multi-allowed). + Supports glob patterns. + +.. option:: --deselect=NODEID_PREFIX + + Deselect item (via node id prefix) during collection (multi-allowed). + +.. option:: --confcutdir=DIR + + Only load ``conftest.py`` files relative to specified directory. + +.. option:: --noconftest + + Don't load any ``conftest.py`` files. + +.. option:: --keep-duplicates + + Keep duplicate tests. By default, pytest removes duplicate test items. + +.. option:: --collect-in-virtualenv + + Don't ignore tests in a local virtualenv directory. + By default, pytest skips tests in virtualenv directories. + +.. option:: --continue-on-collection-errors + + Force test execution even if collection errors occur. + +.. option:: --import-mode + + Prepend/append to sys.path when importing test modules and conftest files. + + * ``prepend`` (default): prepend to sys.path + * ``append``: append to sys.path + * ``importlib``: use importlib to import test modules + + See :ref:`pythonpath` for more information. + +Fixtures +~~~~~~~~ + +.. option:: --fixtures, --funcargs + + Show available fixtures, sorted by plugin appearance. + Fixtures with leading ``_`` are only shown with :option:`--verbose`. + +.. option:: --fixtures-per-test + + Show fixtures per test. + +.. option:: --setup-only + + Only setup fixtures, do not execute tests. + See :ref:`how-to-fixtures`. + +.. option:: --setup-show + + Show setup of fixtures while executing tests. + +.. option:: --setup-plan + + Show what fixtures and tests would be executed but don't execute anything. + +Debugging +~~~~~~~~~ + +.. option:: --pdb + + Start the interactive Python debugger on errors or KeyboardInterrupt. + See :ref:`pdb-option`. + +.. option:: --pdbcls=MODULENAME:CLASSNAME + + Specify a custom interactive Python debugger for use with :option:`--pdb`. + + Example:: + + pytest --pdbcls=IPython.terminal.debugger:TerminalPdb + +.. option:: --trace + + Immediately break when running each test. + + See :ref:`trace-option` for more information. + +.. option:: --full-trace + + Don't cut any tracebacks (default is to cut). + + See :ref:`how-to-modifying-python-tb-printing` for more information. + +.. option:: --debug, --debug=DEBUG_FILE_NAME + + Store internal tracing debug information in this log file. + This file is opened with ``'w'`` and truncated as a result, care advised. + Default file name if not specified: ``pytestdebug.log``. + +.. option:: --trace-config + + Trace considerations of conftest.py files. + +Output and Reporting +~~~~~~~~~~~~~~~~~~~~ + +.. option:: -v, --verbose + + Increase verbosity. + Can be specified multiple times (e.g., ``-vv``) for even more verbose output. + + See :ref:`pytest.fine_grained_verbosity` for fine-grained control over verbosity. + +.. option:: -q, --quiet + + Decrease verbosity. + +.. option:: --verbosity=NUM + + Set verbosity level explicitly. Default: 0. + +.. option:: -r CHARS + + Show extra test summary info as specified by chars: + + * ``f``: failed + * ``E``: error + * ``s``: skipped + * ``x``: xfailed + * ``X``: xpassed + * ``p``: passed + * ``P``: passed with output + * ``a``: all except passed (p/P) + * ``A``: all + * ``w``: warnings (enabled by default) + * ``N``: resets the list + + Default: ``'fE'`` + + Examples:: + + pytest -rA # show all outcomes + pytest -rfE # show only failed and errors (default) + pytest -rfs # show failed and skipped + + See :ref:`pytest.detailed_failed_tests_usage` for more information. + +.. option:: --no-header + + Disable header. + +.. option:: --no-summary + + Disable summary. + +.. option:: --no-fold-skipped + + Do not fold skipped tests in short summary. + +.. option:: --force-short-summary + + Force condensed summary output regardless of verbosity level. + +.. option:: -l, --showlocals + + Show locals in tracebacks (disabled by default). + +.. option:: --no-showlocals + + Hide locals in tracebacks (negate :option:`--showlocals` passed through addopts). + +.. option:: --tb=STYLE + + Traceback print mode: + + * ``auto``: intelligent traceback formatting (default) + * ``long``: exhaustive, informative traceback formatting + * ``short``: shorter traceback format + * ``line``: only the failing line + * ``native``: Python's standard traceback + * ``no``: no traceback + + See :ref:`how-to-modifying-python-tb-printing` for examples. + +.. option:: --xfail-tb + + Show tracebacks for xfail (as long as :option:`--tb` != ``no``). + +.. option:: --show-capture + + Controls how captured stdout/stderr/log is shown on failed tests. + + * ``no``: don't show captured output + * ``stdout``: show captured stdout + * ``stderr``: show captured stderr + * ``log``: show captured logging + * ``all`` (default): show all captured output + +.. option:: --color=WHEN + + Color terminal output: + + * ``yes``: always use color + * ``no``: never use color + * ``auto`` (default): use color if terminal supports it + +.. option:: --code-highlight={yes,no} + + Whether code should be highlighted (only if :option:`--color` is also enabled). + Default: ``yes``. + +.. option:: --pastebin=MODE + + Send failed|all info to bpaste.net pastebin service. + +.. option:: --durations=NUM + + Show N slowest setup/test durations (N=0 for all). + See :ref:`durations`. + +.. option:: --durations-min=NUM + + Minimal duration in seconds for inclusion in slowest list. + Default: 0.005 (or 0.0 if ``-vv`` is given). + +Output Capture +~~~~~~~~~~~~~~ + +.. option:: --capture=METHOD + + Per-test capturing method: + + * ``fd``: capture at file descriptor level (default) + * ``sys``: capture at sys level + * ``no``: don't capture output + * ``tee-sys``: capture but also show output on terminal + + See :ref:`captures`. + +.. option:: -s + + Shortcut for :option:`--capture=no`. + +JUnit XML +~~~~~~~~~ + +.. option:: --junit-xml=PATH, --junitxml=PATH + + Create junit-xml style report file at given path. + +.. option:: --junit-prefix=STR, --junitprefix=STR + + Prepend prefix to classnames in junit-xml output. + +Cache +~~~~~ + +.. option:: --cache-show[=PATTERN] + + Show cache contents, don't perform collection or tests. + Default glob pattern: ``'*'``. + +.. option:: --cache-clear + + Remove all cache contents at start of test run. + See :ref:`cache`. + +Warnings +~~~~~~~~ + +.. option:: --disable-pytest-warnings, --disable-warnings + + Disable warnings summary. + +.. option:: -W WARNING, --pythonwarnings=WARNING + + Set which warnings to report, see ``-W`` option of Python itself. + Can be specified multiple times. + +Doctest +~~~~~~~ + +.. option:: --doctest-modules + + Run doctests in all .py modules. + + See :ref:`doctest` for more information on using doctests with pytest. + +.. option:: --doctest-report + + Choose another output format for diffs on doctest failure: + + * ``none`` + * ``cdiff`` + * ``ndiff`` + * ``udiff`` + * ``only_first_failure`` + +.. option:: --doctest-glob=PATTERN + + Doctests file matching pattern. + Default: ``test*.txt``. + +.. option:: --doctest-ignore-import-errors + + Ignore doctest collection errors. + +.. option:: --doctest-continue-on-failure + + For a given doctest, continue to run after the first failure. + +Configuration +~~~~~~~~~~~~~ + +.. option:: -c FILE, --config-file=FILE + + Load configuration from ``FILE`` instead of trying to locate one of the implicit configuration files. + +.. option:: --rootdir=ROOTDIR + + Define root directory for tests. + Can be relative path: ``'root_dir'``, ``'./root_dir'``, ``'root_dir/another_dir/'``; absolute path: ``'/home/user/root_dir'``; path with variables: ``'$HOME/root_dir'``. + +.. option:: --basetemp=DIR + + Base temporary directory for this test run. + Warning: this directory is removed if it exists. + + See :ref:`temporary directory location and retention` for more information. + +.. option:: -o OPTION=VALUE, --override-ini=OPTION=VALUE + + Override configuration option with ``option=value`` style. + Can be specified multiple times. + + Example:: + + pytest -o strict_xfail=true -o cache_dir=cache + +.. option:: --strict-config + + Enables the :confval:`strict_config` option. + +.. option:: --strict-markers + + Enables the :confval:`strict_markers` option. + +.. option:: --strict + + Enables the :confval:`strict` option (which enables all strictness options). + +.. option:: --assert=MODE + + Control assertion debugging tools: + + * ``plain``: performs no assertion debugging + * ``rewrite`` (default): rewrites assert statements in test modules on import to provide assert expression information + +Logging +~~~~~~~ + +See :ref:`logging` for a guide on using these flags. + +.. option:: --log-level=LEVEL + + Level of messages to catch/display. + Not set by default, so it depends on the root/parent log handler's effective level, where it is ``WARNING`` by default. + +.. option:: --log-format=FORMAT + + Log format used by the logging module. + +.. option:: --log-date-format=FORMAT + + Log date format used by the logging module. + +.. option:: --log-cli-level=LEVEL + + CLI logging level. See :ref:`live_logs`. + +.. option:: --log-cli-format=FORMAT + + Log format used by the logging module for CLI output. + +.. option:: --log-cli-date-format=FORMAT + + Log date format used by the logging module for CLI output. + +.. option:: --log-file=PATH + + Path to a file logging will be written to. + +.. option:: --log-file-mode + + Log file open mode: + + * ``w`` (default): recreate the file + * ``a``: append to the file + +.. option:: --log-file-level=LEVEL + + Log file logging level. + +.. option:: --log-file-format=FORMAT + + Log format used by the logging module for the log file. + +.. option:: --log-file-date-format=FORMAT + + Log date format used by the logging module for the log file. + +.. option:: --log-auto-indent=VALUE + + Auto-indent multiline messages passed to the logging module. + Accepts ``true|on``, ``false|off`` or an integer. + +.. option:: --log-disable=LOGGER + + Disable a logger by name. Can be passed multiple times. + +Plugin and Extension Management +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. option:: -p NAME + + Early-load given plugin module name or entry point (multi-allowed). + To avoid loading of plugins, use the ``no:`` prefix, e.g. ``no:doctest``. + See also :option:`--disable-plugin-autoload`. + +.. option:: --disable-plugin-autoload + + Disable plugin auto-loading through entry point packaging metadata. + Only plugins explicitly specified in :option:`-p` or env var :envvar:`PYTEST_PLUGINS` will be loaded. + +Version and Help +~~~~~~~~~~~~~~~~ + +.. option:: -V, --version + + Display pytest version and information about plugins. When given twice, also display information about plugins. + +.. option:: -h, --help + + Show help message and configuration info. + +Complete Help Output +~~~~~~~~~~~~~~~~~~~~ + +All the command-line flags can also be obtained by running ``pytest --help``:: $ pytest --help usage: pytest [options] [file_or_dir] [file_or_dir] [...] @@ -2692,11 +3270,9 @@ All the command-line flags can be obtained by running ``pytest --help``:: --markers show markers (builtin, plugin and per-project ones). -x, --exitfirst Exit instantly on first error or failed test --maxfail=num Exit after first num failures or errors - --strict-config Any warnings encountered while parsing the `pytest` - section of the configuration file raise errors - --strict-markers Markers not registered in the `markers` section of - the configuration file raise errors - --strict (Deprecated) alias to --strict-markers + --strict-config Enables the strict_config option + --strict-markers Enables the strict_markers option + --strict Enables the strict option --fixtures, --funcargs Show available fixtures, sorted by plugin appearance (fixtures with leading '_' are only shown with '-v') @@ -2845,8 +3421,9 @@ All the command-line flags can be obtained by running ``pytest --help``:: file. This file is opened with 'w' and truncated as a result, care advised. Default: pytestdebug.log. -o, --override-ini OVERRIDE_INI - Override ini option with "option=value" style, e.g. - `-o xfail_strict=True -o cache_dir=cache`. + Override configuration option with "option=value" + style, e.g. `-o strict_xfail=True -o + cache_dir=cache`. --assert=MODE Control assertion debugging tools. 'plain' performs no assertion debugging. 'rewrite' (the default) rewrites assert statements @@ -2888,11 +3465,19 @@ All the command-line flags can be obtained by running ``pytest --help``:: Disable a logger by name. Can be passed multiple times. - [pytest] ini-options in the first pytest.ini|tox.ini|setup.cfg|pyproject.toml file found: + [pytest] configuration options in the first pytest.toml|pytest.ini|tox.ini|setup.cfg|pyproject.toml file found: markers (linelist): Register new markers for test functions empty_parameter_set_mark (string): Default marker for empty parametersets + strict_config (bool): Any warnings encountered while parsing the `pytest` + section of the configuration file raise errors + strict_markers (bool): + Markers not registered in the `markers` section of + the configuration file raise errors + strict (bool): Enables all strictness options, currently: + strict_config, strict_markers, strict_xfail, + strict_parametrization_ids filterwarnings (linelist): Each line specifies a pattern for warnings.filterwarnings. Processed after @@ -2919,6 +3504,9 @@ All the command-line flags can be obtained by running ``pytest --help``:: disable_test_id_escaping_and_forfeit_all_rights_to_community_support (bool): Disable string escape non-ASCII characters, might cause unwanted side effects(use at your own risk) + strict_parametrization_ids (bool): + Emit an error if non-unique parameter set IDs are + detected console_output_style (string): Console output: "classic", or with additional progress information ("progress" (percentage) | @@ -2929,8 +3517,9 @@ All the command-line flags can be obtained by running ``pytest --help``:: overriding the main level. Higher levels will provide more detailed information about each test case executed. - xfail_strict (bool): Default for the strict parameter of xfail markers - when not given explicitly (default: False) + strict_xfail (bool): Default for the strict parameter of xfail markers + when not given explicitly (default: False) (alias: + xfail_strict) tmp_path_retention_count (string): How many sessions should we keep the `tmp_path` directories, according to @@ -2998,6 +3587,10 @@ All the command-line flags can be obtained by running ``pytest --help``:: faulthandler_exit_on_timeout (bool): Exit the test process if a test takes more than faulthandler_timeout seconds to finish + verbosity_subtests (string): + Specify verbosity level for subtests. Higher levels + will generate output for passed subtests. Failed + subtests are always reported. addopts (args): Extra command line options minversion (string): Minimally required pytest version pythonpath (paths): Add paths to sys.path @@ -3011,6 +3604,9 @@ All the command-line flags can be obtained by running ``pytest --help``:: PYTEST_PLUGINS Comma-separated plugins to load during startup PYTEST_DISABLE_PLUGIN_AUTOLOAD Set to disable plugin auto-loading PYTEST_DEBUG Set to enable debug tracing of pytest's internals + PYTEST_DEBUG_TEMPROOT Override the system temporary directory + PYTEST_THEME The Pygments style to use for code output + PYTEST_THEME_MODE Set the PYTEST_THEME to be either 'dark' or 'light' to see available markers type: pytest --markers diff --git a/doc/en/requirements.txt b/doc/en/requirements.txt index 5483bb46063..d672a9d7e15 100644 --- a/doc/en/requirements.txt +++ b/doc/en/requirements.txt @@ -1,8 +1,9 @@ -c broken-dep-constraints.txt pluggy>=1.5.0 -pygments-pytest>=2.3.0 +pygments-pytest>=2.5.0 sphinx-removed-in>=0.2.0 -sphinx>=7 +# Pinning to <9.0 due to https://github.com/python-trio/sphinxcontrib-trio/issues/399. +sphinx>=7,<9.0 sphinxcontrib-trio sphinxcontrib-svg2pdfconverter furo diff --git a/doc/en/sponsor.rst b/doc/en/sponsor.rst index 8362a7f0a3a..6ad722be94c 100644 --- a/doc/en/sponsor.rst +++ b/doc/en/sponsor.rst @@ -2,7 +2,7 @@ Sponsor ======= pytest is maintained by a team of volunteers from all around the world in their free time. While -we work on pytest because we love the project and use it daily at our daily jobs, monetary +we work on pytest because we love the project and use it daily in our jobs, monetary compensation when possible is welcome to justify time away from friends, family and personal time. Money is also used to fund local sprints, merchandising (stickers to distribute in conferences for example) @@ -12,7 +12,7 @@ OpenCollective -------------- `Open Collective`_ is an online funding platform for open and transparent communities. -It provide tools to raise money and share your finances in full transparency. +It provides tools to raise money and share your finances in full transparency. It is the platform of choice for individuals and companies that want to make one-time or monthly donations directly to the project. diff --git a/doc/en/talks.rst b/doc/en/talks.rst index b9b153a792e..a45c05c6f2f 100644 --- a/doc/en/talks.rst +++ b/doc/en/talks.rst @@ -17,19 +17,19 @@ Books Talks and blog postings --------------------------------------------- -- Training: `pytest - simple, rapid and fun testing with Python `_, Florian Bruhin, PyConDE 2022 +- Training: `pytest - simple, rapid and fun testing with Python `_, Freya Bruhin, PyConDE 2022 -- `pytest: Simple, rapid and fun testing with Python, `_ (@ 4:22:32), Florian Bruhin, WeAreDevelopers World Congress 2021 +- `pytest: Simple, rapid and fun testing with Python, `_ (@ 4:22:32), Freya Bruhin, WeAreDevelopers World Congress 2021 -- Webinar: `pytest: Test Driven Development für Python (German) `_, Florian Bruhin, via mylearning.ch, 2020 +- Webinar: `pytest: Test Driven Development für Python (German) `_, Freya Bruhin, via mylearning.ch, 2020 - Webinar: `Simplify Your Tests with Fixtures `_, Oliver Bestwalter, via JetBrains, 2020 -- Training: `Introduction to pytest - simple, rapid and fun testing with Python `_, Florian Bruhin, PyConDE 2019 +- Training: `Introduction to pytest - simple, rapid and fun testing with Python `_, Freya Bruhin, PyConDE 2019 - Abridged metaprogramming classics - this episode: pytest, Oliver Bestwalter, PyConDE 2019 (`repository `__, `recording `__) -- Testing PySide/PyQt code easily using the pytest framework, Florian Bruhin, Qt World Summit 2019 (`slides `__, `recording `__) +- Testing PySide/PyQt code easily using the pytest framework, Freya Bruhin, Qt World Summit 2019 (`slides `__, `recording `__) - `pytest: recommendations, basic packages for testing in Python and Django, Andreu Vallbona, PyBCN June 2019 `_. @@ -41,7 +41,7 @@ Talks and blog postings - `Pythonic testing, Igor Starikov (Russian, PyNsk, November 2016) `_. -- `pytest - Rapid Simple Testing, Florian Bruhin, Swiss Python Summit 2016 +- `pytest - Rapid Simple Testing, Freya Bruhin, Swiss Python Summit 2016 `_. - `Improve your testing with Pytest and Mock, Gabe Hollombe, PyCon SG 2015 diff --git a/pyproject.toml b/pyproject.toml index f57d7e8e85b..31b8a029ec5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ authors = [ { name = "Ronny Pfannschmidt" }, { name = "Floris Bruynooghe" }, { name = "Brianna Laugher" }, - { name = "Florian Bruhin" }, + { name = "Freya Bruhin" }, { name = "Others (See AUTHORS)" }, ] requires-python = ">=3.10" diff --git a/scripts/update-plugin-list.py b/scripts/update-plugin-list.py index 61debb44043..be57d436966 100644 --- a/scripts/update-plugin-list.py +++ b/scripts/update-plugin-list.py @@ -30,7 +30,7 @@ Pytest Plugin List ================== -Below is an automated compilation of ``pytest``` plugins available on `PyPI `_. +Below is an automated compilation of ``pytest`` plugins available on `PyPI `_. It includes PyPI projects whose names begin with ``pytest-`` or ``pytest_`` and a handful of manually selected projects. Packages classified as inactive are excluded. diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index add2a493ca7..4fcce0427ef 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -121,8 +121,9 @@ def getargs(self, var: bool = False) -> tuple[str, ...]: raw = self.raw argcount = raw.co_argcount if var: - argcount += raw.co_flags & CO_VARARGS - argcount += raw.co_flags & CO_VARKEYWORDS + argcount += raw.co_kwonlyargcount + argcount += bool(raw.co_flags & CO_VARARGS) + argcount += bool(raw.co_flags & CO_VARKEYWORDS) return raw.co_varnames[:argcount] @@ -1193,9 +1194,15 @@ def repr_excinfo(self, excinfo: ExceptionInfo[BaseException]) -> ExceptionChainR format_exception( type(excinfo.value), excinfo.value, - traceback[0]._rawentry, + traceback[0]._rawentry if traceback else None, ) ) + if not traceback: + reprtraceback.extraline = ( + "All traceback entries are hidden. " + "Pass `--full-trace` to see hidden and internal frames." + ) + else: reprtraceback = self.repr_traceback(excinfo_) reprcrash = excinfo_._getreprcrash() diff --git a/src/_pytest/_py/path.py b/src/_pytest/_py/path.py index b7131b08a20..998a7819972 100644 --- a/src/_pytest/_py/path.py +++ b/src/_pytest/_py/path.py @@ -137,7 +137,7 @@ class NeverRaised(Exception): class Visitor: def __init__(self, fil, rec, ignore, bf, sort): - if isinstance(fil, str): + if isinstance(fil, (str, bytes)): fil = FNMatcher(fil) if isinstance(rec, str): self.rec: Callable[[LocalPath], bool] = FNMatcher(rec) diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index 2f5a4c863f9..72c3d0918fb 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -309,5 +309,6 @@ def __call__(self) -> bool: def running_on_ci() -> bool: """Check if we're currently running on a CI system.""" # Only enable CI mode if one of these env variables is defined and non-empty. + # Note: review `regendoc` tox env in case this list is changed. env_vars = ["CI", "BUILD_NUMBER"] return any(os.environ.get(var) for var in env_vars) diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 9b2afe3e8b4..67d9a0aa111 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -11,6 +11,7 @@ from collections.abc import Iterable from collections.abc import Iterator from collections.abc import Mapping +from collections.abc import MutableMapping from collections.abc import Sequence import contextlib import copy @@ -47,7 +48,9 @@ from .compat import PathAwareHookProxy from .exceptions import PrintHelp as PrintHelp from .exceptions import UsageError as UsageError +from .findpaths import ConfigValue from .findpaths import determine_setup +from .findpaths import parse_override_ini from _pytest import __version__ import _pytest._code from _pytest._code import ExceptionInfo @@ -178,9 +181,12 @@ def main( :returns: An exit code. """ - # Handle a single `--version` argument early to avoid starting up the entire pytest infrastructure. + # Handle a single `--version`/`-V` argument early to avoid starting up the entire pytest infrastructure. new_args = sys.argv[1:] if args is None else args - if isinstance(new_args, Sequence) and new_args.count("--version") == 1: + if ( + isinstance(new_args, Sequence) + and (new_args.count("--version") + new_args.count("-V")) == 1 + ): sys.stdout.write(f"pytest {__version__}\n") return ExitCode.OK @@ -297,6 +303,7 @@ def directory_arg(path: str, optname: str) -> str: *default_plugins, "pytester", "pytester_assertions", + "terminalprogress", } @@ -811,6 +818,12 @@ def consider_pluginarg(self, arg: str) -> None: if name in essential_plugins: raise UsageError(f"plugin {name} cannot be disabled") + if name.endswith("conftest.py"): + raise UsageError( + f"Blocking conftest files using -p is not supported: -p no:{name}\n" + "conftest.py files are not plugins and cannot be disabled via -p.\n" + ) + # PR #4304: remove stepwise if cacheprovider is blocked. if name == "cacheprovider": self.set_blocked("stepwise") @@ -980,6 +993,30 @@ def _iter_rewritable_modules(package_files: Iterable[str]) -> Iterator[str]: yield from _iter_rewritable_modules(new_package_files) +class _DeprecatedInicfgProxy(MutableMapping[str, Any]): + """Compatibility proxy for the deprecated Config.inicfg.""" + + __slots__ = ("_config",) + + def __init__(self, config: Config) -> None: + self._config = config + + def __getitem__(self, key: str) -> Any: + return self._config._inicfg[key].value + + def __setitem__(self, key: str, value: Any) -> None: + self._config._inicfg[key] = ConfigValue(value, origin="override", mode="toml") + + def __delitem__(self, key: str) -> None: + del self._config._inicfg[key] + + def __iter__(self) -> Iterator[str]: + return iter(self._config._inicfg) + + def __len__(self) -> int: + return len(self._config._inicfg) + + @final class Config: """Access to configuration values, pluginmanager and plugin hooks. @@ -1100,6 +1137,10 @@ def __init__( self.args_source = Config.ArgsSource.ARGS self.args: list[str] = [] + @property + def inicfg(self) -> _DeprecatedInicfgProxy: + return _DeprecatedInicfgProxy(self) + @property def rootpath(self) -> pathlib.Path: """The path to the :ref:`rootdir `. @@ -1376,7 +1417,7 @@ def pytest_collection(self) -> Generator[None, object, object]: def _checkversion(self) -> None: import pytest - minver_ini_value = self.inicfg.get("minversion", None) + minver_ini_value = self._inicfg.get("minversion", None) minver = minver_ini_value.value if minver_ini_value is not None else None if minver: # Imported lazily to improve start-up time. @@ -1440,7 +1481,7 @@ def _warn_or_fail_if_strict(self, message: str) -> None: def _get_unknown_ini_keys(self) -> set[str]: known_keys = self._parser._inidict.keys() | self._parser._ini_aliases.keys() - return self.inicfg.keys() - known_keys + return self._inicfg.keys() - known_keys def parse(self, args: list[str], addopts: bool = True) -> None: # Parse given cmdline arguments into this config object. @@ -1471,7 +1512,7 @@ def parse(self, args: list[str], addopts: bool = True) -> None: self._rootpath = rootpath self._inipath = inipath self._ignored_config_files = ignored_config_files - self.inicfg = inicfg + self._inicfg = inicfg self._parser.extra_info["rootdir"] = str(self.rootpath) self._parser.extra_info["inifile"] = str(self.inipath) @@ -1495,6 +1536,12 @@ def parse(self, args: list[str], addopts: bool = True) -> None: self.known_args_namespace = self._parser.parse_known_args( args, namespace=copy.copy(self.option) ) + if addopts: + # addopts may have added overrides (especially via OverrideIniAction). + # The thing can be endlessly circular but we only do one level (#14442). + if overrides := parse_override_ini(self.known_args_namespace.override_ini): + self._inicfg.update(overrides) + self._inicache.clear() self._checkversion() self._consider_importhook() self._configure_python_path() @@ -1648,14 +1695,14 @@ def _getini(self, name: str): except KeyError as e: raise ValueError(f"unknown configuration value: {name!r}") from e - # Collect all possible values (canonical name + aliases) from inicfg. + # Collect all possible values (canonical name + aliases) from _inicfg. # Each candidate is (ConfigValue, is_canonical). candidates = [] - if canonical_name in self.inicfg: - candidates.append((self.inicfg[canonical_name], True)) + if canonical_name in self._inicfg: + candidates.append((self._inicfg[canonical_name], True)) for alias, target in self._parser._ini_aliases.items(): - if target == canonical_name and alias in self.inicfg: - candidates.append((self.inicfg[alias], False)) + if target == canonical_name and alias in self._inicfg: + candidates.append((self._inicfg[alias], False)) if not candidates: return default diff --git a/src/_pytest/config/argparsing.py b/src/_pytest/config/argparsing.py index 995408800a8..8216ad8b226 100644 --- a/src/_pytest/config/argparsing.py +++ b/src/_pytest/config/argparsing.py @@ -170,7 +170,7 @@ def parse_known_and_unknown_args( arguments, and a list of unknown flag arguments. """ strargs = [os.fspath(x) for x in args] - if sys.version_info < (3, 12): + if sys.version_info < (3, 12, 8) or (3, 13) <= sys.version_info < (3, 13, 1): # Older argparse have a bugged parse_known_intermixed_args. namespace, unknown = self.optparser.parse_known_args(strargs, namespace) assert namespace is not None diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py index c5bcc36ad4b..dab3fb698a2 100644 --- a/src/_pytest/hookspec.py +++ b/src/_pytest/hookspec.py @@ -251,8 +251,8 @@ def pytest_collection(session: Session) -> object | None: 1. ``pytest_deselected(items)`` for any deselected items (may be called multiple times) - 3. ``pytest_collection_finish(session)`` - 4. Set ``session.items`` to the list of collected items + 3. Set ``session.items`` to the list of collected items + 4. ``pytest_collection_finish(session)`` 5. Set ``session.testscollected`` to the number of collected items You can implement this hook to only perform some action before collection, diff --git a/src/_pytest/junitxml.py b/src/_pytest/junitxml.py index ae8d2b94d36..30c93c88c07 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -55,9 +55,7 @@ def repl(matchobj: re.Match[str]) -> str: # The spec range of valid chars is: # Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] # For an unknown(?) reason, we disallow #x7F (DEL) as well. - illegal_xml_re = ( - "[^\u0009\u000a\u000d\u0020-\u007e\u0080-\ud7ff\ue000-\ufffd\u10000-\u10ffff]" - ) + illegal_xml_re = "[^\u0009\u000a\u000d\u0020-\u007e\u0080-\ud7ff\ue000-\ufffd\U00010000-\U0010ffff]" return re.sub(illegal_xml_re, repl, str(arg)) diff --git a/src/_pytest/mark/expression.py b/src/_pytest/mark/expression.py index 3bdbd03c2b5..4b4a68d8a74 100644 --- a/src/_pytest/mark/expression.py +++ b/src/_pytest/mark/expression.py @@ -102,10 +102,10 @@ def lex(self, input: str) -> Iterator[Token]: (FILE_NAME, 1, pos + 1, input), ) value = input[pos : end_quote_pos + 1] - if (backslash_pos := input.find("\\")) != -1: + if (backslash_pos := value.find("\\")) != -1: raise SyntaxError( r'escaping with "\" not supported in marker expression', - (FILE_NAME, 1, backslash_pos + 1, input), + (FILE_NAME, 1, pos + backslash_pos + 1, input), ) yield Token(TokenType.STRING, value, pos) pos += len(value) diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index 16bb6d81119..97842fc5704 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -496,7 +496,7 @@ def __call__(self, arg: Markable) -> Markable: ... @overload def __call__( self, - condition: str | bool = False, + condition: str | bool = True, *conditions: str | bool, reason: str = ..., run: bool = ..., diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index 1e389eb0663..1ff35e94fcf 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -242,7 +242,7 @@ def _repr_compare(self, other_side: Mapping[object, float]) -> list[str]: f"Lengths: {len(self.expected)} and {len(other_side)}", ] - if set(self.expected.keys()) != set(other_side.keys()): + if self.expected.keys() != other_side.keys(): return [ "comparison failed.", f"Mappings has different keys: expected {self.expected.keys()} but got {other_side.keys()}", @@ -256,9 +256,8 @@ def _repr_compare(self, other_side: Mapping[object, float]) -> list[str]: max_abs_diff = -math.inf max_rel_diff = -math.inf different_ids = [] - for (approx_key, approx_value), other_value in zip( - approx_side_as_map.items(), other_side.values(), strict=True - ): + for approx_key, approx_value in approx_side_as_map.items(): + other_value = other_side[approx_key] if approx_value != other_value: if approx_value.expected is not None and other_value is not None: try: @@ -815,6 +814,6 @@ def _as_numpy_array(obj: object) -> ndarray | None: return None elif isinstance(obj, np.ndarray): return obj - elif hasattr(obj, "__array__") or hasattr("obj", "__array_interface__"): + elif hasattr(obj, "__array__") or hasattr(obj, "__array_interface__"): return np.asarray(obj) return None diff --git a/src/_pytest/raises.py b/src/_pytest/raises.py index 7c246fde280..3ab109c0641 100644 --- a/src/_pytest/raises.py +++ b/src/_pytest/raises.py @@ -716,7 +716,7 @@ def __exit__( if not self.matches(exc_val): if self._just_propagate: return False - raise AssertionError(self._fail_reason) + raise AssertionError(self._fail_reason) from None # Cast to narrow the exception type now that it's verified.... # even though the TypeGuard in self.matches should be narrowing diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index 158558b4571..e66e4f48dd6 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -298,9 +298,10 @@ def mywriter(tags, args): config.trace.root.setprocessor("pytest:config", mywriter) - if reporter.isatty(): - plugin = TerminalProgressPlugin(reporter) - config.pluginmanager.register(plugin, "terminalprogress") + # See terminalprogress.py. + # On Windows it's safe to load by default. + if sys.platform == "win32": + config.pluginmanager.import_plugin("terminalprogress") def getreportopt(config: Config) -> str: diff --git a/src/_pytest/terminalprogress.py b/src/_pytest/terminalprogress.py new file mode 100644 index 00000000000..287f0d569ff --- /dev/null +++ b/src/_pytest/terminalprogress.py @@ -0,0 +1,30 @@ +# A plugin to register the TerminalProgressPlugin plugin. +# +# This plugin is not loaded by default due to compatibility issues (#13896), +# but can be enabled in one of these ways: +# - The terminal plugin enables it in a few cases where it's safe, and not +# blocked by the user (using e.g. `-p no:terminalprogress`). +# - The user explicitly requests it, e.g. using `-p terminalprogress`. +# +# In a few years, if it's safe, we can consider enabling it by default. Then, +# this file will become unnecessary and can be inlined into terminal.py. + +from __future__ import annotations + +import os + +from _pytest.config import Config +from _pytest.config import hookimpl +from _pytest.terminal import TerminalProgressPlugin +from _pytest.terminal import TerminalReporter + + +@hookimpl(trylast=True) +def pytest_configure(config: Config) -> None: + reporter: TerminalReporter | None = config.pluginmanager.get_plugin( + "terminalreporter" + ) + + if reporter is not None and reporter.isatty() and os.environ.get("TERM") != "dumb": + plugin = TerminalProgressPlugin(reporter) + config.pluginmanager.register(plugin, name="terminalprogress-plugin") diff --git a/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py index dcd5784f88f..66ca9f190e3 100644 --- a/src/_pytest/tmpdir.py +++ b/src/_pytest/tmpdir.py @@ -9,6 +9,7 @@ from pathlib import Path import re from shutil import rmtree +import stat import tempfile from typing import Any from typing import final @@ -170,16 +171,37 @@ def getbasetemp(self) -> Path: # Also, to keep things private, fixup any world-readable temp # rootdir's permissions. Historically 0o755 was used, so we can't # just error out on this, at least for a while. + # Don't follow symlinks, otherwise we're open to symlink-swapping + # TOCTOU vulnerability. + # This check makes us vulnerable to a DoS - a user can `mkdir + # /tmp/pytest-of-otheruser` and then `otheruser` will fail this + # check. For now we don't consider it a real problem. otheruser can + # change their TMPDIR or --basetemp, and maybe give the prankster a + # good scolding. uid = get_user_id() if uid is not None: - rootdir_stat = rootdir.stat() + stat_follow_symlinks = ( + False if os.stat in os.supports_follow_symlinks else True + ) + rootdir_stat = rootdir.stat(follow_symlinks=stat_follow_symlinks) + if stat.S_ISLNK(rootdir_stat.st_mode): + raise OSError( + f"The temporary directory {rootdir} is a symbolic link. " + "Fix this and try again." + ) if rootdir_stat.st_uid != uid: raise OSError( f"The temporary directory {rootdir} is not owned by the current user. " "Fix this and try again." ) if (rootdir_stat.st_mode & 0o077) != 0: - os.chmod(rootdir, rootdir_stat.st_mode & ~0o077) + chmod_follow_symlinks = ( + False if os.chmod in os.supports_follow_symlinks else True + ) + rootdir.chmod( + rootdir_stat.st_mode & ~0o077, + follow_symlinks=chmod_follow_symlinks, + ) keep = self._retention_count if self._retention_policy == "none": keep = 0 @@ -225,13 +247,16 @@ def pytest_addoption(parser: Parser) -> None: parser.addini( "tmp_path_retention_count", help="How many sessions should we keep the `tmp_path` directories, according to `tmp_path_retention_policy`.", - default=3, + default="3", + # NOTE: Would have been better as an `int` but can't change it now. + type="string", ) parser.addini( "tmp_path_retention_policy", help="Controls which directories created by the `tmp_path` fixture are kept around, based on test outcome. " "(all/failed/none)", + type="string", default="all", ) diff --git a/src/_pytest/unittest.py b/src/_pytest/unittest.py index 64321050853..31be8847821 100644 --- a/src/_pytest/unittest.py +++ b/src/_pytest/unittest.py @@ -225,6 +225,10 @@ def setup(self) -> None: # A bound method to be called during teardown() if set (see 'runtest()'). self._explicit_tearDown: Callable[[], None] | None = None super().setup() + if sys.version_info < (3, 11): + # A cache of the subTest errors and non-subtest skips in self._outcome. + # Compute and cache these lists once, instead of computing them again and again for each subtest (#13965). + self._cached_errors_and_skips: tuple[list[Any], list[Any]] | None = None def teardown(self) -> None: if self._explicit_tearDown is not None: @@ -313,11 +317,7 @@ def add_skip() -> None: # We also need to check if `self.instance._outcome` is `None` (this happens if the test # class/method is decorated with `unittest.skip`, see pytest-dev/pytest-subtests#173). if sys.version_info < (3, 11) and self.instance._outcome is not None: - subtest_errors = [ - x - for x, y in self.instance._outcome.errors - if isinstance(x, _SubTest) and y is not None - ] + subtest_errors, _ = self._obtain_errors_and_skips() if len(subtest_errors) == 0: add_skip() else: @@ -409,6 +409,10 @@ def addSubTest( | tuple[type[BaseException], BaseException, TracebackType] | None, ) -> None: + # Importing this private symbol locally in case this symbol is renamed/removed in the future; importing + # it globally would break pytest entirely, importing it locally only will break unittests using `addSubTest`. + from unittest.case import _subtest_msg_sentinel # type: ignore[attr-defined] + exception_info: ExceptionInfo[BaseException] | None match exc_info: case tuple(): @@ -427,7 +431,7 @@ def addSubTest( when="call", _ispytest=True, ) - msg = test._message if isinstance(test._message, str) else None # type: ignore[attr-defined] + msg = None if test._message is _subtest_msg_sentinel else str(test._message) # type: ignore[attr-defined] report = self.ihook.pytest_runtest_makereport(item=self, call=call_info) sub_report = SubtestReport._new( report, @@ -443,18 +447,8 @@ def addSubTest( # For python < 3.11: add non-subtest skips once all subtest failures are processed by # `_addSubTest`. if sys.version_info < (3, 11): - from unittest.case import _SubTest # type: ignore[attr-defined] - - non_subtest_skip = [ - (x, y) - for x, y in self.instance._outcome.skipped - if not isinstance(x, _SubTest) - ] - subtest_errors = [ - (x, y) - for x, y in self.instance._outcome.errors - if isinstance(x, _SubTest) and y is not None - ] + subtest_errors, non_subtest_skip = self._obtain_errors_and_skips() + # Check if we have non-subtest skips: if there are also sub failures, non-subtest skips are not treated in # `_addSubTest` and have to be added using `add_skip` after all subtest failures are processed. if len(non_subtest_skip) > 0 and len(subtest_errors) > 0: @@ -465,6 +459,30 @@ def addSubTest( for testcase, reason in non_subtest_skip: self.addSkip(testcase, reason, handle_subtests=False) + def _obtain_errors_and_skips(self) -> tuple[list[Any], list[Any]]: + """Compute or obtain the cached values for subtest errors and non-subtest skips.""" + from unittest.case import _SubTest # type: ignore[attr-defined] + + assert sys.version_info < (3, 11), ( + "This workaround only should be used in Python 3.10" + ) + if self._cached_errors_and_skips is not None: + return self._cached_errors_and_skips + + subtest_errors = [ + (x, y) + for x, y in self.instance._outcome.errors + if isinstance(x, _SubTest) and y is not None + ] + + non_subtest_skips = [ + (x, y) + for x, y in self.instance._outcome.skipped + if not isinstance(x, _SubTest) + ] + self._cached_errors_and_skips = (subtest_errors, non_subtest_skips) + return subtest_errors, non_subtest_skips + @hookimpl(tryfirst=True) def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> None: @@ -476,6 +494,14 @@ def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> None: except AttributeError: pass + # Convert unittest.SkipTest to pytest.skip. + # This covers explicit `raise unittest.SkipTest`. + unittest = sys.modules.get("unittest") + if unittest and call.excinfo and isinstance(call.excinfo.value, unittest.SkipTest): + excinfo = call.excinfo + call2 = CallInfo[None].from_call(lambda: skip(str(excinfo.value)), call.when) + call.excinfo = call2.excinfo + def _is_skipped(obj) -> bool: """Return True if the given object has been marked with @unittest.skip.""" diff --git a/testing/code/test_code.py b/testing/code/test_code.py index ae5e0e949cf..d21330429ca 100644 --- a/testing/code/test_code.py +++ b/testing/code/test_code.py @@ -117,6 +117,20 @@ def f4(x, *y, **z): c4 = Code.from_function(f4) assert c4.getargs(var=True) == ("x", "y", "z") + def f5(x, *y, **z): + a1 = a2 = a3 = a4 = a5 = a6 = 1 # noqa: F841 + + c5 = Code.from_function(f5) + f5(1, 2, 3, z=4) # cover function body + assert c5.getargs(var=True) == ("x", "y", "z") + + def f6(x, *y, kw=1, **z): + a1 = a2 = a3 = a4 = a5 = a6 = 1 # noqa: F841 + + c6 = Code.from_function(f6) + f6(1, 2, kw=3, z=4) # cover function body + assert c6.getargs(var=True) == ("x", "kw", "y", "z") + def test_frame_getargs() -> None: def f1(x) -> FrameType: diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index 476720f0bbe..70499fec893 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -1897,19 +1897,23 @@ def test_nested_multiple() -> None: @pytest.mark.parametrize("tbstyle", ("long", "short", "auto", "line", "native")) -def test_all_entries_hidden(pytester: Pytester, tbstyle: str) -> None: +@pytest.mark.parametrize("group", (True, False), ids=("group", "bare")) +def test_all_entries_hidden(pytester: Pytester, tbstyle: str, group: bool) -> None: """Regression test for #10903.""" pytester.makepyfile( - """ + f""" + import sys + if sys.version_info < (3, 11): + from exceptiongroup import ExceptionGroup def test(): __tracebackhide__ = True - 1 / 0 + raise {'ExceptionGroup("", [ValueError("bar")])' if group else 'ValueError("bar")'} """ ) result = pytester.runpytest("--tb", tbstyle) assert result.ret == 1 if tbstyle != "line": - result.stdout.fnmatch_lines(["*ZeroDivisionError: division by zero"]) + result.stdout.fnmatch_lines(["*ValueError: bar"]) if tbstyle not in ("line", "native"): result.stdout.fnmatch_lines(["All traceback entries are hidden.*"]) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 22d13f87b96..f33ac01f848 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -1,6 +1,6 @@ anyio[trio]==4.11.0 django==5.2.8 -pytest-asyncio==1.2.0 +pytest-asyncio==1.3.0 pytest-bdd==8.1.0 pytest-cov==7.0.0 pytest-django==4.11.1 diff --git a/testing/python/approx.py b/testing/python/approx.py index f870b9bd4d8..481df80565c 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -1062,6 +1062,46 @@ def test_approx_dicts_with_mismatch_on_keys(self) -> None: ): assert actual == approx(expected) + def test_approx_on_unordered_mapping_with_mismatch( + self, pytester: Pytester + ) -> None: + """https://github.com/pytest-dev/pytest/issues/12444""" + pytester.makepyfile( + """ + import pytest + + def test_approx_on_unordered_mapping_with_mismatch(): + expected = {"a": 1, "b": 2, "c": 3, "d": 4} + actual = {"d": 4, "c": 5, "a": 8, "b": 2} + assert actual == pytest.approx(expected) + """ + ) + result = pytester.runpytest() + result.assert_outcomes(failed=1) + result.stdout.fnmatch_lines( + [ + "*comparison failed.**Mismatched elements: 2 / 4:*", + "*Max absolute difference: 7*", + "*Index | Obtained | Expected *", + "* a * | 8 * | 1 *", + "* c * | 5 * | 3 *", + ] + ) + + def test_approx_on_unordered_mapping_matching(self, pytester: Pytester) -> None: + """https://github.com/pytest-dev/pytest/issues/12444""" + pytester.makepyfile( + """ + import pytest + def test_approx_on_unordered_mapping_matching(): + expected = {"a": 1, "b": 2, "c": 3, "d": 4} + actual = {"d": 4, "c": 3, "a": 1, "b": 2} + assert actual == pytest.approx(expected) + """ + ) + result = pytester.runpytest() + result.assert_outcomes(passed=1) + class MyVec3: # incomplete """sequence like""" diff --git a/testing/python/raises.py b/testing/python/raises.py index c9d57918a83..9fd43ecbdaf 100644 --- a/testing/python/raises.py +++ b/testing/python/raises.py @@ -177,6 +177,33 @@ def test_invalid_regex(): result.stdout.no_fnmatch_line("*File*") result.stdout.no_fnmatch_line("*line*") + def test_raises_match_failure_suppresses_exception_context( + self, pytester: Pytester + ) -> None: + pytester.makepyfile( + """ + import pytest + + def test_raises_match_failure(): + with pytest.raises(ValueError, match="expected"): + raise ValueError("actual") + """ + ) + result = pytester.runpytest("--tb=short") + assert result.ret == 1 + result.stdout.fnmatch_lines( + [ + "*E*AssertionError: Regex pattern did not match.*", + ] + ) + result.stdout.no_fnmatch_line("*ValueError: actual") + result.stdout.no_fnmatch_line( + "*The above exception was the direct cause of the following exception:*" + ) + result.stdout.no_fnmatch_line( + "*During handling of the above exception, another exception occurred:*" + ) + def test_noclass(self) -> None: with pytest.raises(TypeError): pytest.raises("wrong", lambda: None) # type: ignore[call-overload] diff --git a/testing/test_config.py b/testing/test_config.py index 98555e04452..4496e94d0f4 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -60,7 +60,7 @@ def test_getcfg_and_config( _, _, cfg, _ = locate_config(Path.cwd(), [sub]) assert cfg["name"] == ConfigValue("value", origin="file", mode="ini") config = pytester.parseconfigure(str(sub)) - assert config.inicfg["name"] == ConfigValue("value", origin="file", mode="ini") + assert config._inicfg["name"] == ConfigValue("value", origin="file", mode="ini") def test_setupcfg_uses_toolpytest_with_pytest(self, pytester: Pytester) -> None: p1 = pytester.makepyfile("def test(): pass") @@ -459,16 +459,21 @@ def test_silence_unknown_key_warning(self, pytester: Pytester) -> None: result = pytester.runpytest() result.stdout.no_fnmatch_line("*PytestConfigWarning*") - @pytest.mark.parametrize("option_name", ["strict_config", "strict"]) - def test_strict_config_ini_option( - self, pytester: Pytester, option_name: str - ) -> None: + @pytest.mark.parametrize( + "option", + [ + "strict_config = true", + "strict = true", + "addopts = --strict-config", + ], + ) + def test_strict_config_ini_option(self, pytester: Pytester, option: str) -> None: """Test that strict_config and strict ini options enable strict config checking.""" pytester.makeini( f""" [pytest] unknown_option = 1 - {option_name} = True + {option} """ ) result = pytester.runpytest() @@ -1434,10 +1439,10 @@ def test_inifilename(self, tmp_path: Path) -> None: # this indicates this is the file used for getting configuration values assert config.inipath == inipath - assert config.inicfg.get("name") == ConfigValue( + assert config._inicfg.get("name") == ConfigValue( "value", origin="file", mode="ini" ) - assert config.inicfg.get("should_not_be_set") is None + assert config._inicfg.get("should_not_be_set") is None def test_options_on_small_file_do_not_blow_up(pytester: Pytester) -> None: @@ -2277,7 +2282,7 @@ def test_addopts_before_initini( monkeypatch.setenv("PYTEST_ADDOPTS", f"-o cache_dir={cache_dir}") config = _config_for_test config.parse([], addopts=True) - assert config.inicfg.get("cache_dir") == ConfigValue( + assert config._inicfg.get("cache_dir") == ConfigValue( cache_dir, origin="override", mode="ini" ) @@ -2318,7 +2323,7 @@ def test_override_ini_does_not_contain_paths( """Check that -o no longer swallows all options after it (#3103)""" config = _config_for_test config.parse(["-o", "cache_dir=/cache", "/some/test/path"]) - assert config.inicfg.get("cache_dir") == ConfigValue( + assert config._inicfg.get("cache_dir") == ConfigValue( "/cache", origin="override", mode="ini" ) @@ -2356,7 +2361,16 @@ def test_override_ini_without_config_file(self, pytester: Pytester) -> None: } ) result = pytester.runpytest("--override-ini", "pythonpath=src") - assert result.parseoutcomes() == {"passed": 1} + result.assert_outcomes(passed=1) + + def test_override_ini_invalid_option(self, pytester: Pytester) -> None: + result = pytester.runpytest("--override-ini", "doesnotexist=true") + result.stdout.fnmatch_lines( + [ + "=*= warnings summary =*=", + "*PytestConfigWarning:*Unknown config option: doesnotexist", + ] + ) def test_help_via_addopts(pytester: Pytester) -> None: @@ -2451,6 +2465,10 @@ def test_config_does_not_load_blocked_plugin_from_args(pytester: Pytester) -> No result.stderr.fnmatch_lines(["*: error: unrecognized arguments: -s"]) assert result.ret == ExitCode.USAGE_ERROR + result = pytester.runpytest(str(p), "-p no:/path/to/conftest.py", "-s") + result.stderr.fnmatch_lines(["ERROR:*Blocking conftest files*"]) + assert result.ret == ExitCode.USAGE_ERROR + def test_invocation_args(pytester: Pytester) -> None: """Ensure that Config.invocation_* arguments are correctly defined""" @@ -2999,3 +3017,45 @@ def pytest_addoption(parser): with pytest.raises(TypeError, match=r"expects a string.*got int"): config.getini("string_not_string") + + +class TestInicfgDeprecation: + """Tests for the upcoming deprecation of config.inicfg.""" + + def test_inicfg_deprecated(self, pytester: Pytester) -> None: + """Test that accessing config.inicfg issues a deprecation warning (not yet).""" + pytester.makeini( + """ + [pytest] + minversion = 3.0 + """ + ) + config = pytester.parseconfig() + + inicfg = config.inicfg + + assert config.getini("minversion") == "3.0" + assert inicfg["minversion"] == "3.0" + assert inicfg.get("minversion") == "3.0" + del inicfg["minversion"] + inicfg["minversion"] = "4.0" + assert list(inicfg.keys()) == ["minversion"] + assert list(inicfg.items()) == [("minversion", "4.0")] + assert len(inicfg) == 1 + + def test_issue_13946_setting_bool_no_longer_crashes( + self, pytester: Pytester + ) -> None: + """Regression test for #13946 - setting inicfg doesn't cause a crash.""" + pytester.makepyfile( + """ + def pytest_configure(config): + config.inicfg["xfail_strict"] = True + + def test(): + pass + """ + ) + + result = pytester.runpytest() + assert result.ret == 0 diff --git a/testing/test_helpconfig.py b/testing/test_helpconfig.py index b01a6fa1559..7c2cb49d87e 100644 --- a/testing/test_helpconfig.py +++ b/testing/test_helpconfig.py @@ -16,10 +16,11 @@ def test_version_verbose(pytester: Pytester, pytestconfig, monkeypatch) -> None: result.stdout.fnmatch_lines(["*registered third-party plugins:", "*at*"]) -def test_version_less_verbose(pytester: Pytester) -> None: - """Single ``--version`` parameter should display only the pytest version, without loading plugins (#13574).""" +@pytest.mark.parametrize("flag", ["--version", "-V"]) +def test_version_less_verbose(pytester: Pytester, flag: str) -> None: + """Single ``--version`` or ``-V`` should display only the pytest version, without loading plugins (#13574).""" pytester.makeconftest("print('This should not be printed')") - result = pytester.runpytest_subprocess("--version") + result = pytester.runpytest_subprocess(flag) assert result.ret == ExitCode.OK assert result.stdout.str().strip() == f"pytest {pytest.__version__}" diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index 5a603c05bc8..2ddae7e0a39 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -1104,9 +1104,6 @@ def test_invalid_xml_escape() -> None: # Test some more invalid xml chars, the full range should be # tested really but let's just test the edges of the ranges # instead. - # XXX This only tests low unicode character points for now as - # there are some issues with the testing infrastructure for - # the higher ones. # XXX Testing 0xD (\r) is tricky as it overwrites the just written # line in the output, so we skip it too. invalid = ( @@ -1121,9 +1118,8 @@ def test_invalid_xml_escape() -> None: 0xDFFF, 0xFFFE, 0x0FFFF, - ) # , 0x110000) - valid = (0x9, 0xA, 0x20) - # 0xD, 0xD7FF, 0xE000, 0xFFFD, 0x10000, 0x10FFFF) + ) + valid = (0x9, 0xA, 0x20, 0xD, 0xD7FF, 0xE000, 0xFFFD, 0x10000, 0x10FFFF) for i in invalid: got = bin_xml_escape(chr(i)) diff --git a/testing/test_mark.py b/testing/test_mark.py index 8d76ea310eb..70df03be2fd 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -184,11 +184,16 @@ def test_hello(): @pytest.mark.parametrize( - "option_name", ["--strict-markers", "--strict", "strict_markers", "strict"] + "option", + [ + "--strict-markers", + "--strict", + "strict_markers = true", + "strict = true", + "addopts = --strict-markers", + ], ) -def test_strict_prohibits_unregistered_markers( - pytester: Pytester, option_name: str -) -> None: +def test_strict_prohibits_unregistered_markers(pytester: Pytester, option: str) -> None: pytester.makepyfile( """ import pytest @@ -197,16 +202,16 @@ def test_hello(): pass """ ) - if option_name in ("strict_markers", "strict"): + if option.startswith("-"): + result = pytester.runpytest(option) + else: pytester.makeini( f""" [pytest] - {option_name} = true + {option} """ ) result = pytester.runpytest() - else: - result = pytester.runpytest(option_name) assert result.ret != 0 result.stdout.fnmatch_lines( ["'unregisteredmark' not found in `markers` configuration option"] diff --git a/testing/test_mark_expression.py b/testing/test_mark_expression.py index 1e3c769347c..3a606bac17c 100644 --- a/testing/test_mark_expression.py +++ b/testing/test_mark_expression.py @@ -86,6 +86,20 @@ def matcher(name: str, /, **kwargs: str | int | bool | None) -> bool: evaluate("\nfoo\n", matcher) +def test_backslash_in_identifier_with_string_literal() -> None: + r"""Backslashes in identifiers should not cause false rejections when the + expression also contains string literals. Regression test for a bug where + the scanner searched the entire input for backslashes instead of only the + current string literal value.""" + + def matcher(name: str, /, **kwargs: str | int | bool | None) -> bool: + return {r"\nfoo\n", r"test\case", "mark"}.__contains__(name) + + assert evaluate(r'\nfoo\n and mark(x="y")', matcher) + assert evaluate(r'mark(x="y") and \nfoo\n', matcher) + assert evaluate(r'test\case and mark(x="y")', matcher) + + @pytest.mark.parametrize( ("expr", "column", "message"), ( diff --git a/testing/test_subtests.py b/testing/test_subtests.py index 6849df53622..06de9f009d8 100644 --- a/testing/test_subtests.py +++ b/testing/test_subtests.py @@ -370,6 +370,36 @@ def test_foo(subtests): ) +def test_msg_not_a_string( + pytester: pytest.Pytester, monkeypatch: pytest.MonkeyPatch +) -> None: + """ + Using a non-string in subtests.test() should still show it in the terminal (#14195). + + Note: this was not a problem originally with the subtests fixture, only with TestCase.subTest; this test + was added for symmetry. + """ + monkeypatch.setenv("COLUMNS", "120") + pytester.makepyfile( + """ + def test_int_msg(subtests): + with subtests.test(42): + assert False, "subtest failure" + + def test_no_msg(subtests): + with subtests.test(): + assert False, "subtest failure" + """ + ) + result = pytester.runpytest() + result.stdout.fnmatch_lines( + [ + "SUBFAILED[[]42[]] test_msg_not_a_string.py::test_int_msg - AssertionError: subtest failure", + "SUBFAILED() test_msg_not_a_string.py::test_no_msg - AssertionError: subtest failure", + ] + ) + + @pytest.mark.parametrize("flag", ["--last-failed", "--stepwise"]) def test_subtests_last_failed_step_wise(pytester: pytest.Pytester, flag: str) -> None: """Check that --last-failed and --step-wise correctly rerun tests with failed subtests.""" @@ -619,6 +649,33 @@ def test_foo(self): "SUBSKIPPED[[]subtest 1[]] [[]1[]] *.py:*: skip subtest 1" ) + def test_msg_not_a_string( + self, pytester: pytest.Pytester, monkeypatch: pytest.MonkeyPatch + ) -> None: + """Using a non-string in TestCase.subTest should still show it in the terminal (#14195).""" + monkeypatch.setenv("COLUMNS", "120") + pytester.makepyfile( + """ + from unittest import TestCase + + class T(TestCase): + def test_int_msg(self): + with self.subTest(42): + assert False, "subtest failure" + + def test_no_msg(self): + with self.subTest(): + assert False, "subtest failure" + """ + ) + result = pytester.runpytest() + result.stdout.fnmatch_lines( + [ + "SUBFAILED[[]42[]] test_msg_not_a_string.py::T::test_int_msg - AssertionError: subtest failure", + "SUBFAILED() test_msg_not_a_string.py::T::test_no_msg - AssertionError: subtest failure", + ] + ) + class TestCapture: def create_file(self, pytester: pytest.Pytester) -> None: diff --git a/testing/test_terminal.py b/testing/test_terminal.py index ee540b65135..3053f5ef9a1 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -12,8 +12,7 @@ from typing import cast from typing import Literal from typing import NamedTuple -from unittest.mock import Mock -from unittest.mock import patch +from unittest import mock import pluggy @@ -3419,29 +3418,57 @@ def mock_file(self) -> StringIO: @pytest.fixture def mock_tr(self, mock_file: StringIO) -> pytest.TerminalReporter: - tr = Mock(spec=pytest.TerminalReporter) + tr: pytest.TerminalReporter = mock.create_autospec(pytest.TerminalReporter) - def write_raw(s: str, *, flush: bool = False) -> None: - mock_file.write(s) + def write_raw(content: str, *, flush: bool = False) -> None: + mock_file.write(content) - tr.write_raw = write_raw + tr.write_raw = write_raw # type: ignore[method-assign] tr._progress_nodeids_reported = set() return tr - def test_plugin_registration(self, pytester: pytest.Pytester) -> None: - """Test that the plugin is registered correctly on TTY output.""" + @pytest.mark.skipif(sys.platform != "win32", reason="#13896") + def test_plugin_registration_enabled_by_default( + self, pytester: pytest.Pytester, monkeypatch: MonkeyPatch + ) -> None: + """Test that the plugin registration is enabled by default. + + Currently only on Windows (#13896). + """ + monkeypatch.setattr(sys.stdout, "isatty", lambda: True) # The plugin module should be registered as a default plugin. - with patch.object(sys.stdout, "isatty", return_value=True): - config = pytester.parseconfigure() - plugin = config.pluginmanager.get_plugin("terminalprogress") - assert plugin is not None + config = pytester.parseconfigure() + plugin = config.pluginmanager.get_plugin("terminalprogress") + assert plugin is not None - def test_disabled_for_non_tty(self, pytester: pytest.Pytester) -> None: + def test_plugin_registred_on_all_platforms_when_explicitly_requested( + self, pytester: pytest.Pytester, monkeypatch: MonkeyPatch + ) -> None: + """Test that the plugin is registered on any platform if explicitly requested.""" + monkeypatch.setattr(sys.stdout, "isatty", lambda: True) + # The plugin module should be registered as a default plugin. + config = pytester.parseconfigure("-p", "terminalprogress") + plugin = config.pluginmanager.get_plugin("terminalprogress") + assert plugin is not None + + def test_disabled_for_non_tty( + self, pytester: pytest.Pytester, monkeypatch: MonkeyPatch + ) -> None: """Test that plugin is disabled for non-TTY output.""" - with patch.object(sys.stdout, "isatty", return_value=False): - config = pytester.parseconfigure() - plugin = config.pluginmanager.get_plugin("terminalprogress") - assert plugin is None + monkeypatch.setattr(sys.stdout, "isatty", lambda: False) + config = pytester.parseconfigure("-p", "terminalprogress") + plugin = config.pluginmanager.get_plugin("terminalprogress-plugin") + assert plugin is None + + def test_disabled_for_dumb_terminal( + self, pytester: pytest.Pytester, monkeypatch: MonkeyPatch + ) -> None: + """Test that plugin is disabled when TERM=dumb.""" + monkeypatch.setenv("TERM", "dumb") + monkeypatch.setattr(sys.stdout, "isatty", lambda: True) + config = pytester.parseconfigure("-p", "terminalprogress") + plugin = config.pluginmanager.get_plugin("terminalprogress-plugin") + assert plugin is None @pytest.mark.parametrize( ["state", "progress", "expected"], @@ -3473,7 +3500,7 @@ def test_session_lifecycle( """Test progress updates during session lifecycle.""" plugin = TerminalProgressPlugin(mock_tr) - session = Mock(spec=pytest.Session) + session = mock.create_autospec(pytest.Session) session.testscollected = 3 # Session start - should emit indeterminate progress. diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index 363172110d3..789e8005184 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -5,6 +5,7 @@ import dataclasses import os from pathlib import Path +import shutil import stat import sys from typing import cast @@ -619,3 +620,33 @@ def test_tmp_path_factory_fixes_up_world_readable_permissions( # After - fixed. assert (basetemp.parent.stat().st_mode & 0o077) == 0 + + +@pytest.mark.skipif( + not hasattr(os, "getuid") or os.stat not in os.supports_follow_symlinks, + reason="checks unix permissions and symlinks", +) +def test_tmp_path_factory_doesnt_follow_symlinks( + tmp_path: Path, monkeypatch: MonkeyPatch +) -> None: + """Verify that if a /tmp/pytest-of-foo directory is a symbolic link, + it is rejected (#13669, CVE-2025-71176).""" + attacker_controlled = tmp_path / "attacker_controlled" + attacker_controlled.mkdir() + + # Use the test's tmp_path as the system temproot (/tmp). + monkeypatch.setenv("PYTEST_DEBUG_TEMPROOT", str(tmp_path)) + + # First just get the pytest-of-user path. + tmp_factory = TempPathFactory(None, 3, "all", lambda *args: None, _ispytest=True) + pytest_of_user = tmp_factory.getbasetemp().parent + # Just for safety in the test, before we nuke it. + assert "pytest-of-" in str(pytest_of_user) + shutil.rmtree(pytest_of_user) + + pytest_of_user.symlink_to(attacker_controlled) + + # This now tries to use the directory when it's a symlink. + tmp_factory = TempPathFactory(None, 3, "all", lambda *args: None, _ispytest=True) + with pytest.raises(OSError, match=r"temporary directory .* is a symbolic link"): + tmp_factory.getbasetemp() diff --git a/testing/test_unittest.py b/testing/test_unittest.py index d6757077847..395c9fe647e 100644 --- a/testing/test_unittest.py +++ b/testing/test_unittest.py @@ -1094,6 +1094,49 @@ def test_two(self): result.assert_outcomes(passed=2) +def test_skip_setup_class(pytester: Pytester) -> None: + """Skipping tests in a class by raising unittest.SkipTest in `setUpClass` (#13985).""" + pytester.makepyfile( + """ + import unittest + + class Test(unittest.TestCase): + + @classmethod + def setUpClass(cls): + raise unittest.SkipTest('Skipping setupclass') + + def test_foo(self): + assert False + + def test_bar(self): + assert False + """ + ) + result = pytester.runpytest() + result.assert_outcomes(skipped=2) + + +def test_unittest_skip_function(pytester: Pytester) -> None: + """ + Ensure raising an explicit unittest.SkipTest skips standard pytest functions. + + Support for this is debatable -- technically we only support unittest.SkipTest in TestCase subclasses, + but stating this support here in this test because users currently expect this to work, + so if we ever break it we at least know we are breaking this use case (#13985). + """ + pytester.makepyfile( + """ + import unittest + + def test_foo(): + raise unittest.SkipTest('Skipping test_foo') + """ + ) + result = pytester.runpytest() + result.assert_outcomes(skipped=1) + + def test_testcase_handles_init_exceptions(pytester: Pytester) -> None: """ Regression test to make sure exceptions in the __init__ method are bubbled up correctly. diff --git a/tox.ini b/tox.ini index b6fcecc886a..e2e09fa8b7f 100644 --- a/tox.ini +++ b/tox.ini @@ -23,6 +23,26 @@ envlist = +[pkgenv] +# NOTE: This section tweaks how Tox manages the PEP 517 build +# NOTE: environment where it assembles wheels (editable and regular) +# NOTE: for further installing them into regular testenvs. +# +# NOTE: `[testenv:.pkg]` does not work due to a regression in tox v4.14.1 +# NOTE: so `[pkgenv]` is being used in place of it. +# Refs: +# * https://github.com/tox-dev/tox/pull/3237 +# * https://github.com/tox-dev/tox/issues/3238 +# * https://github.com/tox-dev/tox/issues/3292 +# * https://hynek.me/articles/turbo-charge-tox/ +# +# NOTE: The `SETUPTOOLS_SCM_PRETEND_VERSION_FOR_PYTEST` environment +# NOTE: variable allows enforcing a pre-determined version for use in +# NOTE: the wheel being installed into usual testenvs. +pass_env = + SETUPTOOLS_SCM_PRETEND_VERSION_FOR_PYTEST + + [testenv] description = run the tests @@ -42,17 +62,16 @@ description = doctesting: including doctests commands = {env:_PYTEST_TOX_COVERAGE_RUN:} pytest {posargs:{env:_PYTEST_TOX_DEFAULT_POSARGS:}} - doctesting: {env:_PYTEST_TOX_COVERAGE_RUN:} pytest --doctest-modules --pyargs _pytest + doctesting: {env:_PYTEST_TOX_COVERAGE_RUN:} pytest --doctest-modules {env:_PYTEST_TOX_POSARGS_JUNIT:} --pyargs _pytest coverage: coverage combine coverage: coverage report -m passenv = COVERAGE_* PYTEST_ADDOPTS TERM - SETUPTOOLS_SCM_PRETEND_VERSION_FOR_PYTEST CI setenv = - _PYTEST_TOX_DEFAULT_POSARGS={env:_PYTEST_TOX_POSARGS_DOCTESTING:} {env:_PYTEST_TOX_POSARGS_LSOF:} {env:_PYTEST_TOX_POSARGS_XDIST:} {env:_PYTEST_FILES:} + _PYTEST_TOX_DEFAULT_POSARGS={env:_PYTEST_TOX_POSARGS_DOCTESTING:} {env:_PYTEST_TOX_POSARGS_JUNIT:} {env:_PYTEST_TOX_POSARGS_LSOF:} {env:_PYTEST_TOX_POSARGS_XDIST:} {env:_PYTEST_FILES:} # See https://docs.python.org/3/library/io.html#io-encoding-warning # If we don't enable this, neither can any of our downstream users! @@ -111,7 +130,7 @@ setenv = description = build the documentation site under \ `{toxinidir}{/}doc{/}en{/}_build{/}html` with `{basepython}` -basepython = python3.12 # sync with rtd to get errors +basepython = python3.13 # Sync with .readthedocs.yaml to get errors. usedevelop = True deps = -r{toxinidir}/doc/en/requirements.txt @@ -141,8 +160,6 @@ setenv = description = regenerate documentation examples under `{basepython}` changedir = doc/en -passenv = - SETUPTOOLS_SCM_PRETEND_VERSION_FOR_PYTEST deps = PyYAML regendoc>=0.8.1 @@ -154,6 +171,10 @@ commands = setenv = # We don't want this warning to reach regen output. PYTHONWARNDEFAULTENCODING= + # Remove CI markers: pytest auto-detects those and uses more verbose output, which is undesirable + # for the example documentation. + CI= + BUILD_NUMBER= [testenv:plugins] description =