diff --git a/.coveragerc b/.coveragerc
index d39d3d5f02b..4eab3e33381 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -25,6 +25,7 @@ exclude_lines =
^\s*raise NotImplementedError\b
^\s*return NotImplemented\b
^\s*assert False(,|$)
+ ^\s*case unreachable:
^\s*assert_never\(
^\s*if TYPE_CHECKING:
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 9e63bc68cfd..176daa1b4d7 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -25,41 +25,85 @@ jobs:
attestations: write
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
with:
fetch-depth: 0
persist-credentials: false
- name: Build and Check Package
- uses: hynek/build-and-inspect-python-package@b5076c307dc91924a82ad150cdd1533b444d3310
+ uses: hynek/build-and-inspect-python-package@efb823f52190ad02594531168b7a2d5790e66516
with:
attest-build-provenance-github: 'true'
- deploy:
- if: github.repository == 'pytest-dev/pytest'
+ generate-gh-release-notes:
needs: [package]
runs-on: ubuntu-latest
- environment: deploy
timeout-minutes: 30
permissions:
- id-token: write
- contents: write
+ contents: read
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
with:
- persist-credentials: true
+ fetch-depth: 0
+ persist-credentials: false
+
+ - name: Set up Python
+ uses: actions/setup-python@v6
+ with:
+ python-version: "3.13"
+
+ - name: Install tox
+ run: |
+ python -m pip install --upgrade pip
+ pip install --upgrade tox
+
+ - name: Generate release notes
+ env:
+ VERSION: ${{ github.event.inputs.version }}
+ run: |
+ tox -e generate-gh-release-notes -- "$VERSION" gh-release-notes.md
+
+ - name: Upload release notes
+ uses: actions/upload-artifact@v4
+ with:
+ name: release-notes
+ path: gh-release-notes.md
+ retention-days: 1
+ publish-to-pypi:
+ if: github.repository == 'pytest-dev/pytest'
+ # Need generate-gh-release-notes only for ordering.
+ # Don't want to release to PyPI if generating GitHub release notes fails.
+ needs: [package, generate-gh-release-notes]
+ runs-on: ubuntu-latest
+ environment: deploy
+ timeout-minutes: 30
+ permissions:
+ id-token: write
+ steps:
- name: Download Package
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@v6
with:
name: Packages
path: dist
- name: Publish package to PyPI
- uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc
+ uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e
with:
attestations: true
+ push-tag:
+ needs: [publish-to-pypi]
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ permissions:
+ contents: write
+ steps:
+ - uses: actions/checkout@v5
+ with:
+ fetch-depth: 0
+ persist-credentials: true
+
- name: Push tag
env:
VERSION: ${{ github.event.inputs.version }}
@@ -69,48 +113,28 @@ jobs:
git tag --annotate --message=v"$VERSION" "$VERSION" ${{ github.sha }}
git push origin "$VERSION"
- release-notes:
-
- # todo: generate the content in the build job
- # the goal being of using a github action script to push the release data
- # after success instead of creating a complete python/tox env
- needs: [deploy]
+ create-github-release:
+ needs: [push-tag, generate-gh-release-notes]
runs-on: ubuntu-latest
- timeout-minutes: 30
+ timeout-minutes: 10
permissions:
contents: write
steps:
- - uses: actions/checkout@v4
- with:
- fetch-depth: 0
- persist-credentials: false
-
- name: Download Package
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@v6
with:
name: Packages
path: dist
- - name: Set up Python
- uses: actions/setup-python@v5
+ - name: Download release notes
+ uses: actions/download-artifact@v6
with:
- python-version: "3.11"
-
- - name: Install tox
- run: |
- python -m pip install --upgrade pip
- pip install --upgrade tox
+ name: release-notes
+ path: .
- - name: Generate release notes
+ - name: Publish GitHub Release
env:
VERSION: ${{ github.event.inputs.version }}
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
- sudo apt-get install pandoc
- tox -e generate-gh-release-notes -- "$VERSION" scripts/latest-release-notes.md
-
- - name: Publish GitHub Release
- uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631
- with:
- body_path: scripts/latest-release-notes.md
- files: dist/*
- tag_name: ${{ github.event.inputs.version }}
+ gh release create --notes-file gh-release-notes.md --verify-tag "$VERSION" dist/*
diff --git a/.github/workflows/doc-check-links.yml b/.github/workflows/doc-check-links.yml
new file mode 100644
index 00000000000..497ec73500a
--- /dev/null
+++ b/.github/workflows/doc-check-links.yml
@@ -0,0 +1,37 @@
+name: Doc Check Links
+
+on:
+ schedule:
+ # At 00:00 on Sunday.
+ # https://crontab.guru
+ - cron: '0 0 * * 0'
+ workflow_dispatch:
+
+# Set permissions at the job level.
+permissions: {}
+
+jobs:
+ doc-check-links:
+ if: github.repository_owner == 'pytest-dev'
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v5
+ with:
+ fetch-depth: 0
+ persist-credentials: false
+
+ - name: Setup Python
+ uses: actions/setup-python@v6
+ with:
+ python-version: "3.13"
+ cache: pip
+
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install tox
+
+ - name: Run sphinx linkcheck via tox
+ run: tox -e docs-checklinks
diff --git a/.github/workflows/prepare-release-pr.yml b/.github/workflows/prepare-release-pr.yml
index b21ca70cb46..9dcfea7bae5 100644
--- a/.github/workflows/prepare-release-pr.yml
+++ b/.github/workflows/prepare-release-pr.yml
@@ -27,16 +27,16 @@ jobs:
pull-requests: write
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
with:
fetch-depth: 0
# persist-credentials is needed in order for us to push the release branch.
persist-credentials: true
- name: Set up Python
- uses: actions/setup-python@v5
+ uses: actions/setup-python@v6
with:
- python-version: "3.x"
+ python-version: "3.13"
- name: Install dependencies
run: |
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
index 82f9a1f2579..aeac36cea60 100644
--- a/.github/workflows/stale.yml
+++ b/.github/workflows/stale.yml
@@ -10,7 +10,7 @@ jobs:
permissions:
issues: write
steps:
- - uses: actions/stale@v9
+ - uses: actions/stale@v10
with:
debug-only: false
days-before-issue-stale: 14
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 3ecc133878f..fbee593f7c2 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -20,6 +20,8 @@ on:
- reopened # default
- ready_for_review # used in PRs created from the release workflow
+ workflow_dispatch: # allows manual triggering of the workflow
+
env:
PYTEST_ADDOPTS: "--color=yes"
@@ -35,12 +37,12 @@ jobs:
package:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
with:
fetch-depth: 0
persist-credentials: false
- name: Build and Check Package
- uses: hynek/build-and-inspect-python-package@b5076c307dc91924a82ad150cdd1533b444d3310
+ uses: hynek/build-and-inspect-python-package@efb823f52190ad02594531168b7a2d5790e66516
build:
needs: [package]
@@ -54,19 +56,22 @@ jobs:
fail-fast: false
matrix:
name: [
- "windows-py39-unittestextras",
- "windows-py39-pluggy",
- "windows-py39-xdist",
- "windows-py310",
+ "windows-py310-unittest-asynctest",
+ "windows-py310-unittest-twisted24",
+ "windows-py310-unittest-twisted25",
+ "windows-py310-pluggy",
+ "windows-py310-xdist",
"windows-py311",
"windows-py312",
"windows-py313",
"windows-py314",
- "ubuntu-py39-lsof-numpy-pexpect",
- "ubuntu-py39-pluggy",
- "ubuntu-py39-freeze",
- "ubuntu-py39-xdist",
+ "ubuntu-py310-unittest-asynctest",
+ "ubuntu-py310-unittest-twisted24",
+ "ubuntu-py310-unittest-twisted25",
+ "ubuntu-py310-lsof-numpy-pexpect",
+ "ubuntu-py310-pluggy",
+ "ubuntu-py310-freeze",
"ubuntu-py310-xdist",
"ubuntu-py311",
"ubuntu-py312",
@@ -74,7 +79,6 @@ jobs:
"ubuntu-py314",
"ubuntu-pypy3-xdist",
- "macos-py39",
"macos-py310",
"macos-py312",
"macos-py313",
@@ -85,23 +89,32 @@ jobs:
]
include:
- - name: "windows-py39-unittestextras"
- python: "3.9"
+ # Use separate jobs for different unittest flavors (twisted, asynctest) to ensure proper coverage.
+ - name: "windows-py310-unittest-asynctest"
+ python: "3.10"
os: windows-latest
- tox_env: "py39-unittestextras"
+ tox_env: "py310-asynctest"
use_coverage: true
- - name: "windows-py39-pluggy"
- python: "3.9"
+ - name: "windows-py310-unittest-twisted24"
+ python: "3.10"
os: windows-latest
- tox_env: "py39-pluggymain-pylib-xdist"
+ tox_env: "py310-twisted24"
+ use_coverage: true
- - name: "windows-py39-xdist"
- python: "3.9"
+ - name: "windows-py310-unittest-twisted25"
+ python: "3.10"
os: windows-latest
- tox_env: "py39-xdist"
+ tox_env: "py310-twisted25"
+ use_coverage: true
- - name: "windows-py310"
+ - name: "windows-py310-pluggy"
+ python: "3.10"
+ os: windows-latest
+ tox_env: "py310-pluggymain-pylib-xdist"
+ xfail: true
+
+ - name: "windows-py310-xdist"
python: "3.10"
os: windows-latest
tox_env: "py310-xdist"
@@ -120,32 +133,49 @@ jobs:
python: "3.13"
os: windows-latest
tox_env: "py313"
+ xfail: true
- name: "windows-py314"
python: "3.14"
os: windows-latest
tox_env: "py314"
- - name: "ubuntu-py39-lsof-numpy-pexpect"
- python: "3.9"
+ # Use separate jobs for different unittest flavors (twisted, asynctest) to ensure proper coverage.
+ - name: "ubuntu-py310-unittest-asynctest"
+ python: "3.10"
+ os: ubuntu-latest
+ tox_env: "py310-asynctest"
+ use_coverage: true
+
+ - name: "ubuntu-py310-unittest-twisted24"
+ python: "3.10"
os: ubuntu-latest
- tox_env: "py39-lsof-numpy-pexpect"
+ tox_env: "py310-twisted24"
use_coverage: true
- - name: "ubuntu-py39-pluggy"
- python: "3.9"
+ - name: "ubuntu-py310-unittest-twisted25"
+ python: "3.10"
os: ubuntu-latest
- tox_env: "py39-pluggymain-pylib-xdist"
+ tox_env: "py310-twisted25"
+ use_coverage: true
- - name: "ubuntu-py39-freeze"
- python: "3.9"
+ - name: "ubuntu-py310-lsof-numpy-pexpect"
+ python: "3.10"
os: ubuntu-latest
- tox_env: "py39-freeze"
+ tox_env: "py310-lsof-numpy-pexpect"
+ use_coverage: true
- - name: "ubuntu-py39-xdist"
- python: "3.9"
+ - name: "ubuntu-py310-pluggy"
+ python: "3.10"
os: ubuntu-latest
- tox_env: "py39-xdist"
+ tox_env: "py310-pluggymain-pylib-xdist"
+ xfail: true
+
+ - name: "ubuntu-py310-freeze"
+ python: "3.10"
+ os: ubuntu-latest
+ tox_env: "py310-freeze"
+ xfail: true
- name: "ubuntu-py310-xdist"
python: "3.10"
@@ -169,6 +199,7 @@ jobs:
os: ubuntu-latest
tox_env: "py313-pexpect"
use_coverage: true
+ xfail: true
- name: "ubuntu-py314"
python: "3.14"
@@ -177,21 +208,16 @@ jobs:
use_coverage: true
- name: "ubuntu-pypy3-xdist"
- python: "pypy-3.9"
+ python: "pypy-3.10"
os: ubuntu-latest
tox_env: "pypy3-xdist"
- - name: "macos-py39"
- python: "3.9"
- os: macos-latest
- tox_env: "py39-xdist"
- use_coverage: true
-
- name: "macos-py310"
python: "3.10"
os: macos-latest
tox_env: "py310-xdist"
+ xfail: true
- name: "macos-py312"
python: "3.12"
@@ -202,6 +228,7 @@ jobs:
python: "3.13"
os: macos-latest
tox_env: "py313-xdist"
+ xfail: true
- name: "macos-py314"
python: "3.14"
@@ -215,45 +242,27 @@ jobs:
- name: "doctesting"
- python: "3.9"
+ python: "3.10"
os: ubuntu-latest
tox_env: "doctesting"
use_coverage: true
- continue-on-error: >-
- ${{
- contains(
- fromJSON(
- '[
- "windows-py39-pluggy",
- "windows-py313",
- "ubuntu-py39-pluggy",
- "ubuntu-py39-freeze",
- "ubuntu-py313",
- "macos-py39",
- "macos-py313"
- ]'
- ),
- matrix.name
- )
- && true
- || false
- }}
+ continue-on-error: ${{ matrix.xfail && true || false }}
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
with:
fetch-depth: 0
persist-credentials: false
- name: Download Package
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@v6
with:
name: Packages
path: dist
- name: Set up Python ${{ matrix.python }}
- uses: actions/setup-python@v5
+ uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python }}
check-latest: true
@@ -280,7 +289,7 @@ jobs:
- name: Upload coverage to Codecov
if: "matrix.use_coverage"
- uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24
+ uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7
with:
fail_ci_if_error: false
files: ./coverage.xml
@@ -296,6 +305,6 @@ jobs:
steps:
- name: Decide whether the needed jobs succeeded or failed
- uses: re-actors/alls-green@223e4bb7a751b91f43eda76992bcfbf23b8b0302
+ uses: re-actors/alls-green@2765efec08f0fd63e83ad900f5fd75646be69ff6
with:
jobs: ${{ toJSON(needs) }}
diff --git a/.github/workflows/update-plugin-list.yml b/.github/workflows/update-plugin-list.yml
index c10aefa3a55..b396d6e19d4 100644
--- a/.github/workflows/update-plugin-list.yml
+++ b/.github/workflows/update-plugin-list.yml
@@ -20,16 +20,15 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
with:
fetch-depth: 0
persist-credentials: false
- name: Setup Python
- uses: actions/setup-python@v5
+ uses: actions/setup-python@v6
with:
- python-version: "3.11"
- cache: pip
+ python-version: "3.13"
- name: requests-cache
uses: actions/cache@v4
@@ -38,13 +37,13 @@ jobs:
key: plugins-http-cache-${{ github.run_id }} # Can use time based key as well
restore-keys: plugins-http-cache-
- - name: Install dependencies
+ - name: Install tox
run: |
python -m pip install --upgrade pip
- pip install packaging requests tabulate[widechars] tqdm requests-cache platformdirs
+ pip install --upgrade tox
- name: Update Plugin List
- run: python scripts/update-plugin-list.py
+ run: tox -e update-plugin-list
- name: Create Pull Request
id: pr
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 149f7e6af59..a4af51ee0f2 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,22 +1,22 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: "v0.11.11"
+ rev: "v0.14.3"
hooks:
- - id: ruff
+ - id: ruff-check
args: ["--fix"]
- id: ruff-format
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v5.0.0
+ rev: v6.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- repo: https://github.com/woodruffw/zizmor-pre-commit
- rev: v1.8.0
+ rev: v1.16.2
hooks:
- id: zizmor
- repo: https://github.com/adamchainz/blacken-docs
- rev: 1.19.1
+ rev: 1.20.0
hooks:
- id: blacken-docs
additional_dependencies: [black==24.1.1]
@@ -32,7 +32,7 @@ repos:
hooks:
- id: python-use-type-annotations
- repo: https://github.com/pre-commit/mirrors-mypy
- rev: v1.15.0
+ rev: v1.18.2
hooks:
- id: mypy
files: ^(src/|testing/|scripts/)
@@ -48,7 +48,7 @@ repos:
# on <3.11
- exceptiongroup>=1.0.0rc8
- repo: https://github.com/RobertCraigie/pyright-python
- rev: v1.1.401
+ rev: v1.1.407
hooks:
- id: pyright
files: ^(src/|scripts/)
@@ -66,17 +66,17 @@ repos:
# Manual because passing pyright is a work in progress.
stages: [manual]
- repo: https://github.com/tox-dev/pyproject-fmt
- rev: "v2.6.0"
+ rev: "v2.11.0"
hooks:
- id: pyproject-fmt
# https://pyproject-fmt.readthedocs.io/en/latest/#calculating-max-supported-python-version
additional_dependencies: ["tox>=4.9"]
- repo: https://github.com/asottile/pyupgrade
- rev: v3.20.0
+ rev: v3.21.0
hooks:
- id: pyupgrade
args:
- - "--py39-plus"
+ - "--py310-plus"
# Manual because ruff does what pyupgrade does and the two are not out of sync
# often enough to make launching pyupgrade everytime worth it
stages: [manual]
diff --git a/AUTHORS b/AUTHORS
index e5b863e71f1..83509281035 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -39,6 +39,7 @@ Andrzej Klajnert
Andrzej Ostrowski
Andy Freeland
Anita Hammer
+Anna Tasiopoulou
Anthon van der Neut
Anthony Shaw
Anthony Sottile
@@ -82,6 +83,7 @@ Carlos Jenkins
Ceridwen
Charles Cloud
Charles Machalow
+Charles-Meldhine Madi Mnemoi (cmnemoi)
Charnjit SiNGH (CCSJ)
Cheuk Ting Ho
Chris Mahoney
@@ -134,6 +136,7 @@ Deysha Rivera
Dheeraj C K
Dhiren Serai
Diego Russo
+Dima Gerasimov
Dmitry Dygalo
Dmitry Pribysh
Dominic Mortlock
@@ -200,6 +203,7 @@ Ilya Konstantinov
Ionuț Turturică
Isaac Virshup
Israel Fruchter
+Israël Hallé
Itxaso Aizpurua
Iwan Briquemont
Jaap Broekhuizen
@@ -225,6 +229,7 @@ Jon Parise
Jon Sonesen
Jonas Obrist
Jordan Guymon
+Jordan Macdonald
Jordan Moldow
Jordan Speicher
Joseph Hunkeler
@@ -250,6 +255,7 @@ Kevin Hierro Carrasco
Kevin J. Foley
Kian Eliasi
Kian-Meng Ang
+Kim Soo
Kodi B. Arfer
Kojo Idrissa
Kostis Anagnostopoulos
@@ -261,6 +267,7 @@ Leonardus Chen
Lev Maximov
Levon Saldamli
Lewis Cowles
+Liam DeVoe
Llandy Riveron Del Risco
Loic Esteve
lovetheguitar
@@ -279,6 +286,7 @@ Marcin Augustynów
Marcin Bachry
Marc Bresson
Marco Gorelli
+Marcos Boger
Mark Abramowitz
Mark Dickinson
Mark Vong
@@ -313,6 +321,7 @@ Mike Hoyle (hoylemd)
Mike Lundy
Milan Lesnek
Miro Hrončok
+Mulat Mekonen
mrbean-bremen
Nathan Goldbaum
Nathan Rousseau
@@ -335,8 +344,10 @@ Oleg Sushchenko
Oleksandr Zavertniev
Olga Matoula
Oliver Bestwalter
+Olivier Grisel
Omar Kohl
Omer Hadari
+Omri Golan
Ondřej Súkup
Oscar Benjamin
Parth Patel
@@ -375,6 +386,7 @@ Reza Mousavi
Raquel Alegre
Ravi Chandra
Reagan Lee
+Reilly Brogan
Rob Arrow
Robert Holt
Roberto Aldera
@@ -393,6 +405,7 @@ Sadra Barikbin
Saiprasad Kale
Samuel Colvin
Samuel Dion-Girardeau
+Samuel Gaist
Samuel Jirovec
Samuel Searles-Bryant
Samuel Therrien (Avasam)
@@ -488,6 +501,7 @@ Yusuke Kadowaki
Yutian Li
Yuval Shimon
Zac Hatfield-Dodds
+Zac Palmer Laporte
Zach Snicker
Zachary Kneupper
Zachary OBrien
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index b79955e1c01..f31c14aec49 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -197,13 +197,13 @@ Short version
#. Follow `PEP-8 `_ for naming.
#. Tests are run using ``tox``::
- tox -e linting,py39
+ tox -e linting,py313
The test environments above are usually enough to cover most cases locally.
#. Write a ``changelog`` entry: ``changelog/2574.bugfix.rst``, use issue id number
and one of ``feature``, ``improvement``, ``bugfix``, ``doc``, ``deprecation``,
- ``breaking``, ``vendor`` or ``trivial`` for the issue type.
+ ``breaking``, ``vendor``, ``packaging``, ``contrib``, or ``misc`` for the issue type.
#. Unless your change is a trivial or a documentation fix (e.g., a typo or reword of a small section) please
@@ -269,24 +269,24 @@ Here is a simple overview, with pytest-specific bits:
#. Run all the tests
- You need to have Python 3.9 or later available in your system. Now
+ You need to have a supported Python version available in your system. Now
running tests is as simple as issuing this command::
- $ tox -e linting,py39
+ $ tox -e linting,py
- This command will run tests via the "tox" tool against Python 3.9
- and also perform "lint" coding-style checks.
+ This command will run tests via the "tox" tool against your default Python
+ version and also perform "lint" coding-style checks.
#. 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.9 and pass options to pytest
+ 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::
- $ tox -e py39 -- --pdb
+ $ tox -e py313 -- --pdb
- Or to only run tests in a particular test module on Python 3.9::
+ Or to only run tests in a particular test module on Python 3.12::
- $ tox -e py39 -- testing/test_config.py
+ $ tox -e py312 -- testing/test_config.py
When committing, ``pre-commit`` will re-format the files if necessary.
@@ -305,8 +305,9 @@ Here is a simple overview, with pytest-specific bits:
#. Create a new changelog entry in ``changelog``. The file should be named ``..rst``,
where *issueid* is the number of the issue related to the change and *type* is one of
- ``feature``, ``improvement``, ``bugfix``, ``doc``, ``deprecation``, ``breaking``, ``vendor``
- or ``trivial``. You may skip creating the changelog entry if the change doesn't affect the
+ ``feature``, ``improvement``, ``bugfix``, ``doc``, ``deprecation``, ``breaking``, ``vendor``,
+ ``packaging``, ``contrib``, or ``misc``.
+ You may skip creating the changelog entry if the change doesn't affect the
documented behaviour of pytest.
#. Add yourself to ``AUTHORS`` file if not there yet, in alphabetical order.
diff --git a/README.rst b/README.rst
index 091afc363da..3bc5f06fc81 100644
--- a/README.rst
+++ b/README.rst
@@ -79,7 +79,7 @@ To execute it::
========================== 1 failed in 0.04 seconds ===========================
-Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` statements are used. See `getting-started `_ for more examples.
+Thanks to ``pytest``'s detailed assertion introspection, you can simply use plain ``assert`` statements. See `getting-started `_ for more examples.
Features
@@ -97,7 +97,7 @@ Features
- Can run `unittest `_ (or trial)
test suites out of the box
-- Python 3.9+ or PyPy3
+- Python 3.10+ or PyPy3
- Rich plugin architecture, with over 1300+ `external plugins `_ and thriving community
diff --git a/RELEASING.rst b/RELEASING.rst
index 0ca63ee4fbf..79b4e2f764d 100644
--- a/RELEASING.rst
+++ b/RELEASING.rst
@@ -133,7 +133,7 @@ Releasing
Both automatic and manual processes described above follow the same steps from this point onward.
-#. After all tests pass and the PR has been approved, trigger the ``deploy`` job
+#. After all tests pass and the PR has been approved, trigger the ``deploy`` workflow
in https://github.com/pytest-dev/pytest/actions/workflows/deploy.yml, using the ``release-MAJOR.MINOR.PATCH`` branch
as source.
@@ -168,9 +168,7 @@ Both automatic and manual processes described above follow the same steps from t
To the following mailing lists:
- * pytest-dev@python.org (all releases)
- * python-announce-list@python.org (all releases)
- * testing-in-python@lists.idyll.org (only major/minor releases)
+ * python-announce-list@python.org
And announce it with the ``#pytest`` hashtag on:
diff --git a/changelog/10224.improvement.rst b/changelog/10224.improvement.rst
deleted file mode 100644
index 93afe9e2c1e..00000000000
--- a/changelog/10224.improvement.rst
+++ /dev/null
@@ -1,18 +0,0 @@
-pytest's ``short`` and ``long`` traceback styles (:ref:`how-to-modifying-python-tb-printing`)
-now have partial :pep:`657` support and will show specific code segments in the
-traceback.
-
-.. code-block:: pytest
-
- ================================= FAILURES =================================
- _______________________ test_gets_correct_tracebacks _______________________
-
- test_tracebacks.py:12: in test_gets_correct_tracebacks
- assert manhattan_distance(p1, p2) == 1
- ^^^^^^^^^^^^^^^^^^^^^^^^^^
- test_tracebacks.py:6: in manhattan_distance
- return abs(point_1.x - point_2.x) + abs(point_1.y - point_2.y)
- ^^^^^^^^^
- E AttributeError: 'NoneType' object has no attribute 'x'
-
--- by :user:`ammaraskar`
diff --git a/changelog/10404.bugfix.rst b/changelog/10404.bugfix.rst
deleted file mode 100644
index 4c98ea03d64..00000000000
--- a/changelog/10404.bugfix.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-Apply filterwarnings from config/cli as soon as possible, and revert them as late as possible
-so that warnings as errors are collected throughout the pytest run and before the
-unraisable and threadexcept hooks are removed.
-
-This allows very late warnings and unraisable/threadexcept exceptions to fail the test suite.
-
-This also changes the warning that the lsof plugin issues from PytestWarning to the new warning PytestFDWarning so it can be more easily filtered.
diff --git a/changelog/10839.deprecation.rst b/changelog/10839.deprecation.rst
deleted file mode 100644
index a3e2cbf51d0..00000000000
--- a/changelog/10839.deprecation.rst
+++ /dev/null
@@ -1 +0,0 @@
-Requesting an asynchronous fixture without a `pytest_fixture_setup` hook that resolves it will now give a DeprecationWarning. This most commonly happens if a sync test requests an async fixture. This should have no effect on a majority of users with async tests or fixtures using async pytest plugins, but may affect non-standard hook setups or ``autouse=True``. For guidance on how to work around this warning see :ref:`sync-test-async-fixture`.
diff --git a/changelog/11067.bugfix.rst b/changelog/11067.bugfix.rst
deleted file mode 100644
index 4e3cb8e7dd7..00000000000
--- a/changelog/11067.bugfix.rst
+++ /dev/null
@@ -1,3 +0,0 @@
-The test report is now consistent regardless if the test xfailed via :ref:`pytest.mark.xfail ` or :func:`pytest.fail`.
-
-Previously, *xfailed* tests via the marker would have the string ``"reason: "`` prefixed to the message, while those *xfailed* via the function did not. The prefix has been removed.
diff --git a/changelog/11118.improvement.rst b/changelog/11118.improvement.rst
deleted file mode 100644
index 4760dbe9d64..00000000000
--- a/changelog/11118.improvement.rst
+++ /dev/null
@@ -1,3 +0,0 @@
-Now :confval:`pythonpath` configures `$PYTHONPATH` earlier than before during the initialization process, which now also affects plugins loaded via the `-p` command-line option.
-
--- by :user:`millerdev`
diff --git a/changelog/11372.breaking.rst b/changelog/11372.breaking.rst
deleted file mode 100644
index f4b5c3c6f6b..00000000000
--- a/changelog/11372.breaking.rst
+++ /dev/null
@@ -1 +0,0 @@
-Async tests will now fail, instead of warning+skipping, if you don't have any suitable plugin installed.
diff --git a/changelog/11381.improvement.rst b/changelog/11381.improvement.rst
deleted file mode 100644
index 74c080cc188..00000000000
--- a/changelog/11381.improvement.rst
+++ /dev/null
@@ -1,17 +0,0 @@
-The ``type`` parameter of the ``parser.addini`` method now accepts `"int"` and ``"float"`` parameters, facilitating the parsing of configuration values in the configuration file.
-
-Example:
-
-.. code-block:: python
-
- def pytest_addoption(parser):
- parser.addini("int_value", type="int", default=2, help="my int value")
- parser.addini("float_value", type="float", default=4.2, help="my float value")
-
-The `pytest.ini` file:
-
-.. code-block:: ini
-
- [pytest]
- int_value = 3
- float_value = 5.4
diff --git a/changelog/11525.improvement.rst b/changelog/11525.improvement.rst
deleted file mode 100644
index 1935ce59343..00000000000
--- a/changelog/11525.improvement.rst
+++ /dev/null
@@ -1,3 +0,0 @@
-Fixtures are now clearly represented in the output as a "fixture object", not as a normal function as before, making it easy for beginners to catch mistakes such as referencing a fixture declared in the same module but not requested in the test function.
-
--- by :user:`the-compiler` and :user:`glyphack`
diff --git a/changelog/11538.feature.rst b/changelog/11538.feature.rst
deleted file mode 100644
index d6473b8fe73..00000000000
--- a/changelog/11538.feature.rst
+++ /dev/null
@@ -1 +0,0 @@
-Added :class:`pytest.RaisesGroup` as an equivalent to :func:`pytest.raises` for expecting :exc:`ExceptionGroup`. Also adds :class:`pytest.RaisesExc` which is now the logic behind :func:`pytest.raises` and used as parameter to :class:`pytest.RaisesGroup`. ``RaisesGroup`` includes the ability to specify multiple different expected exceptions, the structure of nested exception groups, and flags for emulating :ref:`except* `. See :ref:`assert-matching-exception-groups` and docstrings for more information.
diff --git a/changelog/12008.bugfix.rst b/changelog/12008.bugfix.rst
deleted file mode 100644
index b9680b89236..00000000000
--- a/changelog/12008.bugfix.rst
+++ /dev/null
@@ -1 +0,0 @@
-In :pr:`11220`, an unintended change in reordering was introduced by changing the way indices were assigned to direct params. More specifically, before that change, the indices of direct params to metafunc's callspecs were assigned after all parametrizations took place. Now, that change is reverted.
diff --git a/changelog/12017.contrib.rst b/changelog/12017.contrib.rst
deleted file mode 100644
index ec1861893b3..00000000000
--- a/changelog/12017.contrib.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-Mixed internal improvements:
-
-* Migrate formatting to f-strings in some tests.
-* Use type-safe constructs in JUnitXML tests.
-* Moved`` MockTiming`` into ``_pytest.timing``.
-
--- by :user:`RonnyPfannschmidt`
diff --git a/changelog/12081.feature.rst b/changelog/12081.feature.rst
deleted file mode 100644
index 6538fbf30f8..00000000000
--- a/changelog/12081.feature.rst
+++ /dev/null
@@ -1 +0,0 @@
-Added :fixture:`capteesys` to capture AND pass output to next handler set by ``--capture=``.
diff --git a/changelog/12346.breaking.rst b/changelog/12346.breaking.rst
deleted file mode 100644
index 7013cf734c8..00000000000
--- a/changelog/12346.breaking.rst
+++ /dev/null
@@ -1 +0,0 @@
-Tests will now fail, instead of raising a warning, if they return any value other than None.
diff --git a/changelog/12426.improvement.rst b/changelog/12426.improvement.rst
deleted file mode 100644
index 0da1f838aea..00000000000
--- a/changelog/12426.improvement.rst
+++ /dev/null
@@ -1 +0,0 @@
-A warning is now issued when :ref:`pytest.mark.usefixtures ref` is used without specifying any fixtures. Previously, empty usefixtures markers were silently ignored.
diff --git a/changelog/12504.feature.rst b/changelog/12504.feature.rst
deleted file mode 100644
index d72b97958c2..00000000000
--- a/changelog/12504.feature.rst
+++ /dev/null
@@ -1 +0,0 @@
-:func:`pytest.mark.xfail` now accepts :class:`pytest.RaisesGroup` for the ``raises`` parameter when you expect an exception group. You can also pass a :class:`pytest.RaisesExc` if you e.g. want to make use of the ``check`` parameter.
diff --git a/changelog/12535.doc.rst b/changelog/12535.doc.rst
deleted file mode 100644
index d43c1c822ea..00000000000
--- a/changelog/12535.doc.rst
+++ /dev/null
@@ -1,4 +0,0 @@
-`This
-example`
-showed ``print`` statements that do not exactly reflect what the
-different branches actually do. The fix makes the example more precise.
diff --git a/changelog/12647.contrib.rst b/changelog/12647.contrib.rst
deleted file mode 100644
index 1d7a3181778..00000000000
--- a/changelog/12647.contrib.rst
+++ /dev/null
@@ -1 +0,0 @@
-Fixed running the test suite with the ``hypothesis`` pytest plugin.
diff --git a/changelog/12707.improvement.rst b/changelog/12707.improvement.rst
deleted file mode 100644
index 4684b6561c8..00000000000
--- a/changelog/12707.improvement.rst
+++ /dev/null
@@ -1 +0,0 @@
-Exception chains can be navigated when dropped into Pdb in Python 3.13+.
diff --git a/changelog/12713.feature.rst b/changelog/12713.feature.rst
deleted file mode 100644
index 90867b87eae..00000000000
--- a/changelog/12713.feature.rst
+++ /dev/null
@@ -1,3 +0,0 @@
-New `--force-short-summary` option to force condensed summary output regardless of verbosity level.
-
-This lets users still see condensed summary output of failures for quick reference in log files from job outputs, being especially useful if non-condensed output is very verbose.
diff --git a/changelog/12736.improvement.rst b/changelog/12736.improvement.rst
deleted file mode 100644
index 5fdb14e2ef5..00000000000
--- a/changelog/12736.improvement.rst
+++ /dev/null
@@ -1,3 +0,0 @@
-Added a new attribute `name` with the fixed value `"pytest tests"` to the root tag `testsuites` of the junit-xml generated by pytest.
-
-This attribute is part of many junit-xml specifications and is even part of the `junit-10.xsd` specification that pytest's implementation is based on.
diff --git a/changelog/12749.feature.rst b/changelog/12749.feature.rst
deleted file mode 100644
index c3b7ca5d321..00000000000
--- a/changelog/12749.feature.rst
+++ /dev/null
@@ -1,21 +0,0 @@
-pytest traditionally collects classes/functions in the test module namespace even if they are imported from another file.
-
-For example:
-
-.. code-block:: python
-
- # contents of src/domain.py
- class Testament: ...
-
-
- # contents of tests/test_testament.py
- from domain import Testament
-
-
- def test_testament(): ...
-
-In this scenario with the default options, pytest will collect the class `Testament` from `tests/test_testament.py` because it starts with `Test`, even though in this case it is a production class being imported in the test module namespace.
-
-This behavior can now be prevented by setting the new :confval:`collect_imported_tests` configuration option to ``false``, which will make pytest collect classes/functions from test files **only** if they are defined in that file.
-
--- by :user:`FreerGit`
diff --git a/changelog/12765.feature.rst b/changelog/12765.feature.rst
deleted file mode 100644
index 193c75621f7..00000000000
--- a/changelog/12765.feature.rst
+++ /dev/null
@@ -1,3 +0,0 @@
-Thresholds to trigger snippet truncation can now be set with :confval:`truncation_limit_lines` and :confval:`truncation_limit_chars`.
-
-See :ref:`truncation-params` for more information.
diff --git a/changelog/12863.bugfix.rst b/changelog/12863.bugfix.rst
deleted file mode 100644
index 0b1c397a08e..00000000000
--- a/changelog/12863.bugfix.rst
+++ /dev/null
@@ -1 +0,0 @@
-Fix applying markers, including :ref:`pytest.mark.parametrize ` when placed above `@staticmethod` or `@classmethod`.
diff --git a/changelog/12874.breaking.rst b/changelog/12874.breaking.rst
deleted file mode 100644
index a442586eeb5..00000000000
--- a/changelog/12874.breaking.rst
+++ /dev/null
@@ -1 +0,0 @@
-We dropped support for Python 3.8 following its end of life (2024-10-07).
diff --git a/changelog/12929.bugfix.rst b/changelog/12929.bugfix.rst
deleted file mode 100644
index fcf490d83e2..00000000000
--- a/changelog/12929.bugfix.rst
+++ /dev/null
@@ -1 +0,0 @@
-Handle StopIteration from test cases, setup and teardown correctly.
diff --git a/changelog/12938.bugfix.rst b/changelog/12938.bugfix.rst
deleted file mode 100644
index d54d73bdbf5..00000000000
--- a/changelog/12938.bugfix.rst
+++ /dev/null
@@ -1 +0,0 @@
-Fixed ``--durations-min`` argument not respected if ``-vv`` is used.
diff --git a/changelog/12943.improvement.rst b/changelog/12943.improvement.rst
deleted file mode 100644
index eb8ac63650a..00000000000
--- a/changelog/12943.improvement.rst
+++ /dev/null
@@ -1 +0,0 @@
-If a test fails with an exceptiongroup with a single exception, the contained exception will now be displayed in the short test summary info.
diff --git a/changelog/12946.bugfix.rst b/changelog/12946.bugfix.rst
deleted file mode 100644
index b11da09e7ae..00000000000
--- a/changelog/12946.bugfix.rst
+++ /dev/null
@@ -1 +0,0 @@
-Fixed missing help for :mod:`pdb` commands wrapped by pytest -- by :user:`adamchainz`.
diff --git a/changelog/12958.improvement.rst b/changelog/12958.improvement.rst
deleted file mode 100644
index ee8dc8c0710..00000000000
--- a/changelog/12958.improvement.rst
+++ /dev/null
@@ -1,9 +0,0 @@
-A number of :ref:`unraisable ` enhancements:
-
-* Set the unraisable hook as early as possible and unset it as late as possible, to collect the most possible number of unraisable exceptions.
-* Call the garbage collector just before unsetting the unraisable hook, to collect any straggling exceptions.
-* Collect multiple unraisable exceptions per test phase.
-* Report the :mod:`tracemalloc` allocation traceback (if available).
-* Avoid using a generator based hook to allow handling :class:`StopIteration` in test failures.
-* Report the unraisable exception as the cause of the :class:`pytest.PytestUnraisableExceptionWarning` exception if raised.
-* Compute the ``repr`` of the unraisable object in the unraisable hook so you get the latest information if available, and should help with resurrection of the object.
diff --git a/changelog/12960.breaking.rst b/changelog/12960.breaking.rst
deleted file mode 100644
index 3ab87e6fe23..00000000000
--- a/changelog/12960.breaking.rst
+++ /dev/null
@@ -1,3 +0,0 @@
-Test functions containing a yield now cause an explicit error. They have not been run since pytest 4.0, and were previously marked as an expected failure and deprecation warning.
-
-See :ref:`the docs ` for more information.
diff --git a/changelog/12981.bugfix.rst b/changelog/12981.bugfix.rst
deleted file mode 100644
index 5fc8e29656f..00000000000
--- a/changelog/12981.bugfix.rst
+++ /dev/null
@@ -1 +0,0 @@
-Prevent exceptions in :func:`pytest.Config.add_cleanup` callbacks preventing further cleanups.
diff --git a/changelog/13010.improvement.rst b/changelog/13010.improvement.rst
deleted file mode 100644
index d6b814f090e..00000000000
--- a/changelog/13010.improvement.rst
+++ /dev/null
@@ -1 +0,0 @@
-:func:`pytest.approx` now can compare collections that contain numbers and non-numbers mixed.
diff --git a/changelog/13016.improvement.rst b/changelog/13016.improvement.rst
deleted file mode 100644
index 634672ab69b..00000000000
--- a/changelog/13016.improvement.rst
+++ /dev/null
@@ -1,8 +0,0 @@
-A number of :ref:`threadexception ` enhancements:
-
-* Set the excepthook as early as possible and unset it as late as possible, to collect the most possible number of unhandled exceptions from threads.
-* Collect multiple thread exceptions per test phase.
-* Report the :mod:`tracemalloc` allocation traceback (if available).
-* Avoid using a generator based hook to allow handling :class:`StopIteration` in test failures.
-* Report the thread exception as the cause of the :class:`pytest.PytestUnhandledThreadExceptionWarning` exception if raised.
-* Extract the ``name`` of the thread object in the excepthook which should help with resurrection of the thread.
diff --git a/changelog/13031.improvement.rst b/changelog/13031.improvement.rst
deleted file mode 100644
index c6c64c4673a..00000000000
--- a/changelog/13031.improvement.rst
+++ /dev/null
@@ -1 +0,0 @@
-An empty parameter set as in ``pytest.mark.parametrize([], ids=idfunc)`` will no longer trigger a call to ``idfunc`` with internal objects.
diff --git a/changelog/13047.bugfix.rst b/changelog/13047.bugfix.rst
deleted file mode 100644
index 399e860505c..00000000000
--- a/changelog/13047.bugfix.rst
+++ /dev/null
@@ -1,17 +0,0 @@
-Restore :func:`pytest.approx` handling of equality checks between `bool` and `numpy.bool_` types.
-
-Comparing `bool` and `numpy.bool_` using :func:`pytest.approx` accidentally changed in version `8.3.4` and `8.3.5` to no longer match:
-
-.. code-block:: pycon
-
- >>> import numpy as np
- >>> from pytest import approx
- >>> [np.True_, np.True_] == pytest.approx([True, True])
- False
-
-This has now been fixed:
-
-.. code-block:: pycon
-
- >>> [np.True_, np.True_] == pytest.approx([True, True])
- True
diff --git a/changelog/13115.improvement.rst b/changelog/13115.improvement.rst
deleted file mode 100644
index 9ac45820917..00000000000
--- a/changelog/13115.improvement.rst
+++ /dev/null
@@ -1,8 +0,0 @@
-Allows supplying ``ExceptionGroup[Exception]`` and ``BaseExceptionGroup[BaseException]`` to ``pytest.raises`` to keep full typing on :class:`ExceptionInfo `:
-
-.. code-block:: python
-
- with pytest.raises(ExceptionGroup[Exception]) as exc_info:
- some_function()
-
-Parametrizing with other exception types remains an error - we do not check the types of child exceptions and thus do not permit code that might look like we do.
diff --git a/changelog/13119.bugfix.rst b/changelog/13119.bugfix.rst
deleted file mode 100644
index b7e56af9bb8..00000000000
--- a/changelog/13119.bugfix.rst
+++ /dev/null
@@ -1 +0,0 @@
-Improved handling of invalid regex patterns for filter warnings by providing a clear error message.
diff --git a/changelog/13122.improvement.rst b/changelog/13122.improvement.rst
deleted file mode 100644
index c302713b320..00000000000
--- a/changelog/13122.improvement.rst
+++ /dev/null
@@ -1,15 +0,0 @@
-The ``--stepwise`` mode received a number of improvements:
-
-* It no longer forgets the last failed test in case pytest is executed later without the flag.
-
- This enables the following workflow:
-
- 1. Execute pytest with ``--stepwise``, pytest then stops at the first failing test;
- 2. Iteratively update the code and run the test in isolation, without the ``--stepwise`` flag (for example in an IDE), until it is fixed.
- 3. Execute pytest with ``--stepwise`` again and pytest will continue from the previously failed test, and if it passes, continue on to the next tests.
-
- Previously, at step 3, pytest would start from the beginning, forgetting the previously failed test.
-
- This change however might cause issues if the ``--stepwise`` mode is used far apart in time, as the state might get stale, so the internal state will be reset automatically in case the test suite changes (for now only the number of tests are considered for this, we might change/improve this on the future).
-
-* New ``--stepwise-reset``/``--sw-reset`` flag, allowing the user to explicitly reset the stepwise state and restart the workflow from the beginning.
diff --git a/changelog/13125.feature.rst b/changelog/13125.feature.rst
deleted file mode 100644
index 0c7d66c1169..00000000000
--- a/changelog/13125.feature.rst
+++ /dev/null
@@ -1 +0,0 @@
-:confval:`console_output_style` now supports ``times`` to show execution time of each test.
diff --git a/changelog/13175.bugfix.rst b/changelog/13175.bugfix.rst
deleted file mode 100644
index bdbb72b41e1..00000000000
--- a/changelog/13175.bugfix.rst
+++ /dev/null
@@ -1 +0,0 @@
-The diff is now also highlighted correctly when comparing two strings.
diff --git a/changelog/13192.feature.1.rst b/changelog/13192.feature.1.rst
deleted file mode 100644
index 71fb06f7d70..00000000000
--- a/changelog/13192.feature.1.rst
+++ /dev/null
@@ -1 +0,0 @@
-:func:`pytest.raises` will now print a helpful string diff if matching fails and the match parameter has ``^`` and ``$`` and is otherwise escaped.
diff --git a/changelog/13192.feature.2.rst b/changelog/13192.feature.2.rst
deleted file mode 100644
index 0ffa0e1496a..00000000000
--- a/changelog/13192.feature.2.rst
+++ /dev/null
@@ -1 +0,0 @@
-You can now pass :func:`with pytest.raises(check=fn): `, where ``fn`` is a function which takes a raised exception and returns a boolean. The ``raises`` fails if no exception was raised (as usual), passes if an exception is raised and ``fn`` returns ``True`` (as well as ``match`` and the type matching, if specified, which are checked before), and propagates the exception if ``fn`` returns ``False`` (which likely also fails the test).
diff --git a/changelog/13192.feature.rst b/changelog/13192.feature.rst
deleted file mode 100644
index 97f31ce233c..00000000000
--- a/changelog/13192.feature.rst
+++ /dev/null
@@ -1 +0,0 @@
-:func:`pytest.raises` will now raise a warning when passing an empty string to ``match``, as this will match against any value. Use ``match="^$"`` if you want to check that an exception has no message.
diff --git a/changelog/13218.doc.rst b/changelog/13218.doc.rst
deleted file mode 100644
index 907a817e895..00000000000
--- a/changelog/13218.doc.rst
+++ /dev/null
@@ -1 +0,0 @@
-Pointed out in the :func:`pytest.approx` documentation that it considers booleans unequal to numeric zero or one.
diff --git a/changelog/13221.doc.rst b/changelog/13221.doc.rst
deleted file mode 100644
index cfd35f821b4..00000000000
--- a/changelog/13221.doc.rst
+++ /dev/null
@@ -1 +0,0 @@
-Improved grouping of CLI options in the ``--help`` output.
diff --git a/changelog/13228.feature.rst b/changelog/13228.feature.rst
deleted file mode 100644
index c5d84182313..00000000000
--- a/changelog/13228.feature.rst
+++ /dev/null
@@ -1,3 +0,0 @@
-:ref:`hidden-param` can now be used in ``id`` of :func:`pytest.param` or in
-``ids`` of :py:func:`Metafunc.parametrize `.
-It hides the parameter set from the test name.
diff --git a/changelog/13248.bugfix.rst b/changelog/13248.bugfix.rst
deleted file mode 100644
index 2ebb102fd07..00000000000
--- a/changelog/13248.bugfix.rst
+++ /dev/null
@@ -1,2 +0,0 @@
-Fixed an issue where passing a ``scope`` in :py:func:`Metafunc.parametrize ` with ``indirect=True``
-could result in other fixtures being unable to depend on the parametrized fixture.
diff --git a/changelog/13253.feature.rst b/changelog/13253.feature.rst
deleted file mode 100644
index e497c207223..00000000000
--- a/changelog/13253.feature.rst
+++ /dev/null
@@ -1 +0,0 @@
-New flag: :ref:`--disable-plugin-autoload ` which works as an alternative to :envvar:`PYTEST_DISABLE_PLUGIN_AUTOLOAD` when setting environment variables is inconvenient; and allows setting it in config files with :confval:`addopts`.
diff --git a/changelog/13291.bugfix.rst b/changelog/13291.bugfix.rst
deleted file mode 100644
index 03ce06b697a..00000000000
--- a/changelog/13291.bugfix.rst
+++ /dev/null
@@ -1 +0,0 @@
-Fixed ``repr`` of ``attrs`` objects in assertion failure messages when using ``attrs>=25.2``.
diff --git a/changelog/13308.improvement.rst b/changelog/13308.improvement.rst
deleted file mode 100644
index 70018c66d59..00000000000
--- a/changelog/13308.improvement.rst
+++ /dev/null
@@ -1 +0,0 @@
-Added official support for Python 3.14.
diff --git a/changelog/13312.bugfix.rst b/changelog/13312.bugfix.rst
deleted file mode 100644
index 62ad36879f5..00000000000
--- a/changelog/13312.bugfix.rst
+++ /dev/null
@@ -1 +0,0 @@
-Fixed a possible ``KeyError`` crash on PyPy during collection of tests involving higher-scoped parameters.
diff --git a/changelog/13317.packaging.rst b/changelog/13317.packaging.rst
deleted file mode 100644
index 94171cb1ef3..00000000000
--- a/changelog/13317.packaging.rst
+++ /dev/null
@@ -1,4 +0,0 @@
-Specified minimum allowed versions of ``colorama``, ``iniconfig``,
-and ``packaging``; and bumped the minimum allowed version
-of ``exceptiongroup`` for ``python_version<'3.11'`` from a release
-candidate to a full release.
diff --git a/changelog/13345.bugfix.rst b/changelog/13345.bugfix.rst
deleted file mode 100644
index 5010888aa08..00000000000
--- a/changelog/13345.bugfix.rst
+++ /dev/null
@@ -1 +0,0 @@
-Fix type hints for :attr:`pytest.TestReport.when` and :attr:`pytest.TestReport.location`.
diff --git a/changelog/13377.bugfix.rst b/changelog/13377.bugfix.rst
deleted file mode 100644
index 15755481f7f..00000000000
--- a/changelog/13377.bugfix.rst
+++ /dev/null
@@ -1,12 +0,0 @@
-Fixed handling of test methods with positional-only parameter syntax.
-
-Now, methods are supported that formally define ``self`` as positional-only
-and/or fixture parameters as keyword-only, e.g.:
-
-.. code-block:: python
-
- class TestClass:
-
- def test_method(self, /, *, fixture): ...
-
-Before, this caused an internal error in pytest.
diff --git a/changelog/13380.improvement.rst b/changelog/13380.improvement.rst
deleted file mode 100644
index 51f374fbf01..00000000000
--- a/changelog/13380.improvement.rst
+++ /dev/null
@@ -1 +0,0 @@
-Fix :class:`ExceptionGroup` traceback filtering to exclude pytest internals.
diff --git a/changelog/13384.bugfix.rst b/changelog/13384.bugfix.rst
deleted file mode 100644
index e93d01dcab0..00000000000
--- a/changelog/13384.bugfix.rst
+++ /dev/null
@@ -1 +0,0 @@
-Fixed an issue where pytest could report negative durations.
diff --git a/changelog/13415.improvement.rst b/changelog/13415.improvement.rst
deleted file mode 100644
index 61667f15c7b..00000000000
--- a/changelog/13415.improvement.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-The author metadata of the BibTex example is now correctly formatted with last names following first names.
-An example of BibLaTex has been added.
-BibTex and BibLaTex examples now clearly indicate that what is cited is software.
-
--- by :user:`willynilly`
diff --git a/changelog/13420.bugfix.rst b/changelog/13420.bugfix.rst
deleted file mode 100644
index 02f7372a759..00000000000
--- a/changelog/13420.bugfix.rst
+++ /dev/null
@@ -1 +0,0 @@
-Added ``lru_cache`` to ``nodes._check_initialpaths_for_relpath``.
diff --git a/changelog/13420.improvement.rst b/changelog/13420.improvement.rst
deleted file mode 100644
index 54fe50a72b0..00000000000
--- a/changelog/13420.improvement.rst
+++ /dev/null
@@ -1 +0,0 @@
-Improved test collection performance by optimizing path resolution used in ``FSCollector``.
diff --git a/changelog/13457.improvement.rst b/changelog/13457.improvement.rst
deleted file mode 100644
index 3937384b322..00000000000
--- a/changelog/13457.improvement.rst
+++ /dev/null
@@ -1 +0,0 @@
-The error message about duplicate parametrization no longer displays an internal stack trace.
diff --git a/changelog/4112.improvement.rst b/changelog/4112.improvement.rst
deleted file mode 100644
index 426b87ffa19..00000000000
--- a/changelog/4112.improvement.rst
+++ /dev/null
@@ -1 +0,0 @@
-Using :ref:`pytest.mark.usefixtures ` on :func:`pytest.param` now produces an error instead of silently doing nothing.
diff --git a/changelog/5473.improvement.rst b/changelog/5473.improvement.rst
deleted file mode 100644
index 1b9ab006d49..00000000000
--- a/changelog/5473.improvement.rst
+++ /dev/null
@@ -1 +0,0 @@
-Replace `:` with `;` in the assertion rewrite warning message so it can be filtered using standard Python warning filters before calling :func:`pytest.main`.
diff --git a/changelog/6649.doc.rst b/changelog/6649.doc.rst
deleted file mode 100644
index cf5bb781b87..00000000000
--- a/changelog/6649.doc.rst
+++ /dev/null
@@ -1 +0,0 @@
-Added :class:`~pytest.TerminalReporter` to the :ref:`api-reference` documentation page.
diff --git a/changelog/6649.misc.rst b/changelog/6649.misc.rst
deleted file mode 100644
index cec8c3f4506..00000000000
--- a/changelog/6649.misc.rst
+++ /dev/null
@@ -1 +0,0 @@
-Added :class:`~pytest.TerminalReporter` to the public pytest API, as it is part of the signature of the :hook:`pytest_terminal_summary` hook.
diff --git a/changelog/6985.improvement.rst b/changelog/6985.improvement.rst
deleted file mode 100644
index 34ee8edc77d..00000000000
--- a/changelog/6985.improvement.rst
+++ /dev/null
@@ -1,21 +0,0 @@
-Improved :func:`pytest.approx` to enhance the readability of value ranges and tolerances between 0.001 and 1000.
- * The `repr` method now provides clearer output for values within those ranges, making it easier to interpret the results.
- * Previously, the output for those ranges of values and tolerances was displayed in scientific notation (e.g., `42 ± 1.0e+00`). The updated method now presents the tolerance as a decimal for better readability (e.g., `42 ± 1`).
-
- Example:
-
- **Previous Output:**
-
- .. code-block:: console
-
- >>> pytest.approx(42, abs=1)
- 42 ± 1.0e+00
-
- **Current Output:**
-
- .. code-block:: console
-
- >>> pytest.approx(42, abs=1)
- 42 ± 1
-
- -- by :user:`fazeelghafoor`
diff --git a/changelog/7683.improvement.rst b/changelog/7683.improvement.rst
deleted file mode 100644
index 311abe4df93..00000000000
--- a/changelog/7683.improvement.rst
+++ /dev/null
@@ -1 +0,0 @@
-The formerly optional ``pygments`` dependency is now required, causing output always to be source-highlighted (unless disabled via the ``--code-highlight=no`` CLI option).
diff --git a/changelog/8612.doc.rst b/changelog/8612.doc.rst
deleted file mode 100644
index 6ab4102ace4..00000000000
--- a/changelog/8612.doc.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-Add a recipe for handling abstract test classes in the documentation.
-
-A new example has been added to the documentation to demonstrate how to use a mixin class to handle abstract
-test classes without manually setting the ``__test__`` attribute for subclasses.
-This ensures that subclasses of abstract test classes are automatically collected by pytest.
diff --git a/changelog/9037.bugfix.rst b/changelog/9037.bugfix.rst
deleted file mode 100644
index 5367452337e..00000000000
--- a/changelog/9037.bugfix.rst
+++ /dev/null
@@ -1 +0,0 @@
-Honor :confval:`disable_test_id_escaping_and_forfeit_all_rights_to_community_support` when escaping ids in parametrized tests.
diff --git a/doc/en/announce/index.rst b/doc/en/announce/index.rst
index 51edc964a0c..f5e00d34cdb 100644
--- a/doc/en/announce/index.rst
+++ b/doc/en/announce/index.rst
@@ -6,6 +6,10 @@ Release announcements
:maxdepth: 2
+ release-9.0.0
+ release-8.4.2
+ release-8.4.1
+ release-8.4.0
release-8.3.5
release-8.3.4
release-8.3.3
diff --git a/doc/en/announce/release-8.4.0.rst b/doc/en/announce/release-8.4.0.rst
new file mode 100644
index 00000000000..65e80a55919
--- /dev/null
+++ b/doc/en/announce/release-8.4.0.rst
@@ -0,0 +1,106 @@
+pytest-8.4.0
+=======================================
+
+The pytest team is proud to announce the 8.4.0 release!
+
+This release contains new features, improvements, and bug fixes,
+the full list of changes is available in the changelog:
+
+ https://docs.pytest.org/en/stable/changelog.html
+
+For complete documentation, please visit:
+
+ https://docs.pytest.org/en/stable/
+
+As usual, you can upgrade from PyPI via:
+
+ pip install -U pytest
+
+Thanks to all of the contributors to this release:
+
+* Adam Johnson
+* Ammar Askar
+* Andrew Pikul
+* Andy Freeland
+* Anthony Sottile
+* Anton Zhilin
+* Arpit Gupta
+* Ashley Whetter
+* Avasam
+* Bahram Farahmand
+* Brigitta Sipőcz
+* Bruno Oliveira
+* Callum Scott
+* Christian Clauss
+* Christopher Head
+* Daara
+* Daniel Miller
+* Deysha Rivera
+* Emil Hjelm
+* Eugene Mwangi
+* Florian Bruhin
+* Frank Hoffmann
+* GTowers1
+* Guillaume Gauvrit
+* Gupta Arpit
+* Harmin Parra Rueda
+* Jakob van Santen
+* Jason N. White
+* Jiajun Xu
+* John Litborn
+* Julian Valentin
+* JulianJvn
+* Kenny Y
+* Leonardus Chen
+* Marcelo Duarte Trevisani
+* Marcin Augustynów
+* Natalia Mokeeva
+* Nathan Rousseau
+* Nauman Ahmed
+* Nick Murphy
+* Oleksandr Zavertniev
+* Pavel Zhukov
+* Peter Gessler
+* Pierre Sassoulas
+* Pradeep Kumar
+* Ran Benita
+* Reagan Lee
+* Rob Arrow
+* Ronny Pfannschmidt
+* Sadra Barikbin
+* Sam Bull
+* Samuel Bronson
+* Sashko
+* Serge Smertin
+* Shaygan Hooshyari
+* Stefaan Lippens
+* Stefan Zimmermann
+* Stephen McDowell
+* Sviatoslav Sydorenko
+* Sviatoslav Sydorenko (Святослав Сидоренко)
+* Thomas Grainger
+* TobiMcNamobi
+* Tobias Alex-Petersen
+* Tony Narlock
+* Vincent (Wen Yu) Ge
+* Virendra Patil
+* Will Riley
+* Yann Dirson
+* Zac Hatfield-Dodds
+* delta87
+* dongfangtianyu
+* eitanwass
+* fazeelghafoor
+* ikappaki
+* jakkdl
+* maugu
+* moajo
+* mwychung
+* polkapolka
+* suspe
+* sven
+* 🇺🇦 Sviatoslav Sydorenko (Святослав Сидоренко)
+
+
+Happy testing,
+The pytest Development Team
diff --git a/doc/en/announce/release-8.4.1.rst b/doc/en/announce/release-8.4.1.rst
new file mode 100644
index 00000000000..07ee26187a7
--- /dev/null
+++ b/doc/en/announce/release-8.4.1.rst
@@ -0,0 +1,21 @@
+pytest-8.4.1
+=======================================
+
+pytest 8.4.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
+* Iwithyou2025
+* John Litborn
+* Martin Fischer
+* Ran Benita
+* SarahPythonista
+
+
+Happy testing,
+The pytest Development Team
diff --git a/doc/en/announce/release-8.4.2.rst b/doc/en/announce/release-8.4.2.rst
new file mode 100644
index 00000000000..58a842c4d4b
--- /dev/null
+++ b/doc/en/announce/release-8.4.2.rst
@@ -0,0 +1,27 @@
+pytest-8.4.2
+=======================================
+
+pytest 8.4.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:
+
+* AD
+* Aditi De
+* Bruno Oliveira
+* Florian Bruhin
+* John Litborn
+* Liam DeVoe
+* Marc Mueller
+* NayeemJohn
+* Olivier Grisel
+* Ran Benita
+* bengartner
+* 🇺🇦 Sviatoslav Sydorenko (Святослав Сидоренко)
+
+
+Happy testing,
+The pytest Development Team
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..86dd828f045
--- /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
+* Florian 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/builtin.rst b/doc/en/builtin.rst
index 8aa6fef681c..a7b0ff2e5c8 100644
--- a/doc/en/builtin.rst
+++ b/doc/en/builtin.rst
@@ -22,7 +22,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
cachedir: .pytest_cache
rootdir: /home/sweet/project
collected 0 items
- cache -- .../_pytest/cacheprovider.py:556
+ cache -- .../_pytest/cacheprovider.py:566
Return a cache object that can persist state between testing sessions.
cache.get(key, default)
@@ -33,7 +33,48 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
Values can be any object handled by the json stdlib module.
- capsysbinary -- .../_pytest/capture.py:1024
+ capsys -- .../_pytest/capture.py:1000
+ Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``.
+
+ The captured output is made available via ``capsys.readouterr()`` method
+ calls, which return a ``(out, err)`` namedtuple.
+ ``out`` and ``err`` will be ``text`` objects.
+
+ Returns an instance of :class:`CaptureFixture[str] `.
+
+ Example:
+
+ .. code-block:: python
+
+ def test_output(capsys):
+ print("hello")
+ captured = capsys.readouterr()
+ assert captured.out == "hello\n"
+
+ capteesys -- .../_pytest/capture.py:1028
+ Enable simultaneous text capturing and pass-through of writes
+ to ``sys.stdout`` and ``sys.stderr`` as defined by ``--capture=``.
+
+
+ The captured output is made available via ``capteesys.readouterr()`` method
+ calls, which return a ``(out, err)`` namedtuple.
+ ``out`` and ``err`` will be ``text`` objects.
+
+ The output is also passed-through, allowing it to be "live-printed",
+ reported, or both as defined by ``--capture=``.
+
+ Returns an instance of :class:`CaptureFixture[str] `.
+
+ Example:
+
+ .. code-block:: python
+
+ def test_output(capteesys):
+ print("hello")
+ captured = capteesys.readouterr()
+ assert captured.out == "hello\n"
+
+ capsysbinary -- .../_pytest/capture.py:1063
Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``.
The captured output is made available via ``capsysbinary.readouterr()``
@@ -51,7 +92,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
captured = capsysbinary.readouterr()
assert captured.out == b"hello\n"
- capfd -- .../_pytest/capture.py:1052
+ capfd -- .../_pytest/capture.py:1091
Enable text capturing of writes to file descriptors ``1`` and ``2``.
The captured output is made available via ``capfd.readouterr()`` method
@@ -69,7 +110,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
captured = capfd.readouterr()
assert captured.out == "hello\n"
- capfdbinary -- .../_pytest/capture.py:1080
+ capfdbinary -- .../_pytest/capture.py:1119
Enable bytes capturing of writes to file descriptors ``1`` and ``2``.
The captured output is made available via ``capfd.readouterr()`` method
@@ -87,25 +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"
- capsys -- .../_pytest/capture.py:996
- Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``.
-
- The captured output is made available via ``capsys.readouterr()`` method
- calls, which return a ``(out, err)`` namedtuple.
- ``out`` and ``err`` will be ``text`` objects.
-
- Returns an instance of :class:`CaptureFixture[str] `.
-
- Example:
-
- .. code-block:: python
-
- def test_output(capsys):
- print("hello")
- captured = capsys.readouterr()
- assert captured.out == "hello\n"
-
- doctest_namespace [session scope] -- .../_pytest/doctest.py:741
+ doctest_namespace [session scope] -- .../_pytest/doctest.py:722
Fixture that returns a :py:class:`dict` that will be injected into the
namespace of doctests.
@@ -119,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:1345
+ pytestconfig [session scope] -- .../_pytest/fixtures.py:1431
Session-scoped fixture that returns the session's :class:`pytest.Config`
object.
@@ -129,7 +152,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
if pytestconfig.get_verbosity() > 0:
...
- record_property -- .../_pytest/junitxml.py:280
+ record_property -- .../_pytest/junitxml.py:277
Add extra properties to the calling test.
User properties become part of the test report and are available to the
@@ -143,13 +166,13 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
def test_function(record_property):
record_property("example_key", 1)
- record_xml_attribute -- .../_pytest/junitxml.py:303
+ record_xml_attribute -- .../_pytest/junitxml.py:300
Add extra xml attributes to the tag for the calling test.
The fixture is callable with ``name, value``. The value is
automatically XML-encoded.
- record_testsuite_property [session scope] -- .../_pytest/junitxml.py:341
+ record_testsuite_property [session scope] -- .../_pytest/junitxml.py:338
Record a new ```` tag as child of the root ````.
This is suitable to writing global information regarding the entire test
@@ -191,7 +214,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
.. _legacy_path: https://py.readthedocs.io/en/latest/path.html
- caplog -- .../_pytest/logging.py:598
+ caplog -- .../_pytest/logging.py:596
Access and control log capturing.
Captured logs are available through the following properties/methods::
@@ -202,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
@@ -226,15 +249,18 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
To undo modifications done by the fixture in a contained scope,
use :meth:`context() `.
- recwarn -- .../_pytest/recwarn.py:35
+ recwarn -- .../_pytest/recwarn.py:34
Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions.
See :ref:`warnings` for information on warning categories.
- tmp_path_factory [session scope] -- .../_pytest/tmpdir.py:241
+ subtests -- .../_pytest/subtests.py:129
+ Provides subtests functionality.
+
+ tmp_path_factory [session scope] -- .../_pytest/tmpdir.py:240
Return a :class:`pytest.TempPathFactory` instance for the test session.
- tmp_path -- .../_pytest/tmpdir.py:256
+ tmp_path -- .../_pytest/tmpdir.py:255
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 c92cd7d4263..5220afc46e3 100644
--- a/doc/en/changelog.rst
+++ b/doc/en/changelog.rst
@@ -31,6 +31,799 @@ with advance notice in the **Deprecations** section of releases.
.. towncrier release notes start
+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.
+ 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.
+
+
+- `#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)
+=========================
+
+Bug fixes
+---------
+
+- `#13478 `_: Fixed a crash when using :confval:`console_output_style` with ``times`` and a module is skipped.
+
+
+- `#13530 `_: Fixed a crash when using :func:`pytest.approx` and :class:`decimal.Decimal` instances with the :class:`decimal.FloatOperation` trap set.
+
+
+- `#13549 `_: No longer evaluate type annotations in Python ``3.14`` when inspecting function signatures.
+
+ This prevents crashes during module collection when modules do not explicitly use ``from __future__ import annotations`` and import types for annotations within a ``if TYPE_CHECKING:`` block.
+
+
+- `#13559 `_: Added missing `int` and `float` variants to the `Literal` type annotation of the `type` parameter in :meth:`pytest.Parser.addini`.
+
+
+- `#13563 `_: :func:`pytest.approx` now only imports ``numpy`` if NumPy is already in ``sys.modules``. This fixes unconditional import behavior introduced in `8.4.0`.
+
+
+
+Improved documentation
+----------------------
+
+- `#13577 `_: Clarify that ``pytest_generate_tests`` is discovered in test modules/classes; other hooks must be in ``conftest.py`` or plugins.
+
+
+
+Contributor-facing changes
+--------------------------
+
+- `#13480 `_: Self-testing: fixed a few test failures when run with ``-Wdefault`` or a similar override.
+
+
+- `#13547 `_: Self-testing: corrected expected message for ``test_doctest_unexpected_exception`` in Python ``3.14``.
+
+
+- `#13684 `_: Make pytest's own testsuite insensitive to the presence of the ``CI`` environment variable -- by :user:`ogrisel`.
+
+
+pytest 8.4.1 (2025-06-17)
+=========================
+
+Bug fixes
+---------
+
+- `#13461 `_: Corrected ``_pytest.terminal.TerminalReporter.isatty`` to support
+ being called as a method. Before it was just a boolean which could
+ break correct code when using ``-o log_cli=true``).
+
+
+- `#13477 `_: Reintroduced :class:`pytest.PytestReturnNotNoneWarning` which was removed by accident in pytest `8.4`.
+
+ This warning is raised when a test functions returns a value other than ``None``, which is often a mistake made by beginners.
+
+ See :ref:`return-not-none` for more information.
+
+
+- `#13497 `_: Fixed compatibility with ``Twisted 25+``.
+
+
+
+Improved documentation
+----------------------
+
+- `#13492 `_: Fixed outdated warning about ``faulthandler`` not working on Windows.
+
+
+pytest 8.4.0 (2025-06-02)
+=========================
+
+Removals and backward incompatible breaking changes
+---------------------------------------------------
+
+- `#11372 `_: Async tests will now fail, instead of warning+skipping, if you don't have any suitable plugin installed.
+
+
+- `#12346 `_: Tests will now fail, instead of raising a warning, if they return any value other than None.
+
+
+- `#12874 `_: We dropped support for Python 3.8 following its end of life (2024-10-07).
+
+
+- `#12960 `_: Test functions containing a yield now cause an explicit error. They have not been run since pytest 4.0, and were previously marked as an expected failure and deprecation warning.
+
+ See :ref:`the docs ` for more information.
+
+
+
+Deprecations (removal in next major release)
+--------------------------------------------
+
+- `#10839 `_: Requesting an asynchronous fixture without a `pytest_fixture_setup` hook that resolves it will now give a DeprecationWarning. This most commonly happens if a sync test requests an async fixture. This should have no effect on a majority of users with async tests or fixtures using async pytest plugins, but may affect non-standard hook setups or ``autouse=True``. For guidance on how to work around this warning see :ref:`sync-test-async-fixture`.
+
+
+
+New features
+------------
+
+- `#11538 `_: Added :class:`pytest.RaisesGroup` as an equivalent to :func:`pytest.raises` for expecting :exc:`ExceptionGroup`. Also adds :class:`pytest.RaisesExc` which is now the logic behind :func:`pytest.raises` and used as parameter to :class:`pytest.RaisesGroup`. ``RaisesGroup`` includes the ability to specify multiple different expected exceptions, the structure of nested exception groups, and flags for emulating :ref:`except* `. See :ref:`assert-matching-exception-groups` and docstrings for more information.
+
+
+- `#12081 `_: Added :fixture:`capteesys` to capture AND pass output to next handler set by ``--capture=``.
+
+
+- `#12504 `_: :func:`pytest.mark.xfail` now accepts :class:`pytest.RaisesGroup` for the ``raises`` parameter when you expect an exception group. You can also pass a :class:`pytest.RaisesExc` if you e.g. want to make use of the ``check`` parameter.
+
+
+- `#12713 `_: New `--force-short-summary` option to force condensed summary output regardless of verbosity level.
+
+ This lets users still see condensed summary output of failures for quick reference in log files from job outputs, being especially useful if non-condensed output is very verbose.
+
+
+- `#12749 `_: pytest traditionally collects classes/functions in the test module namespace even if they are imported from another file.
+
+ For example:
+
+ .. code-block:: python
+
+ # contents of src/domain.py
+ class Testament: ...
+
+
+ # contents of tests/test_testament.py
+ from domain import Testament
+
+
+ def test_testament(): ...
+
+ In this scenario with the default options, pytest will collect the class `Testament` from `tests/test_testament.py` because it starts with `Test`, even though in this case it is a production class being imported in the test module namespace.
+
+ This behavior can now be prevented by setting the new :confval:`collect_imported_tests` configuration option to ``false``, which will make pytest collect classes/functions from test files **only** if they are defined in that file.
+
+ -- by :user:`FreerGit`
+
+
+- `#12765 `_: Thresholds to trigger snippet truncation can now be set with :confval:`truncation_limit_lines` and :confval:`truncation_limit_chars`.
+
+ See :ref:`truncation-params` for more information.
+
+
+- `#13125 `_: :confval:`console_output_style` now supports ``times`` to show execution time of each test.
+
+
+- `#13192 `_: :func:`pytest.raises` will now raise a warning when passing an empty string to ``match``, as this will match against any value. Use ``match="^$"`` if you want to check that an exception has no message.
+
+
+- `#13192 `_: :func:`pytest.raises` will now print a helpful string diff if matching fails and the match parameter has ``^`` and ``$`` and is otherwise escaped.
+
+
+- `#13192 `_: You can now pass :func:`with pytest.raises(check=fn): `, where ``fn`` is a function which takes a raised exception and returns a boolean. The ``raises`` fails if no exception was raised (as usual), passes if an exception is raised and ``fn`` returns ``True`` (as well as ``match`` and the type matching, if specified, which are checked before), and propagates the exception if ``fn`` returns ``False`` (which likely also fails the test).
+
+
+- `#13228 `_: :ref:`hidden-param` can now be used in ``id`` of :func:`pytest.param` or in
+ ``ids`` of :py:func:`Metafunc.parametrize `.
+ It hides the parameter set from the test name.
+
+
+- `#13253 `_: New flag: :ref:`--disable-plugin-autoload ` which works as an alternative to :envvar:`PYTEST_DISABLE_PLUGIN_AUTOLOAD` when setting environment variables is inconvenient; and allows setting it in config files with :confval:`addopts`.
+
+
+
+Improvements in existing functionality
+--------------------------------------
+
+- `#10224 `_: pytest's ``short`` and ``long`` traceback styles (:ref:`how-to-modifying-python-tb-printing`)
+ now have partial :pep:`657` support and will show specific code segments in the
+ traceback.
+
+ .. code-block:: pytest
+
+ ================================= FAILURES =================================
+ _______________________ test_gets_correct_tracebacks _______________________
+
+ test_tracebacks.py:12: in test_gets_correct_tracebacks
+ assert manhattan_distance(p1, p2) == 1
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ test_tracebacks.py:6: in manhattan_distance
+ return abs(point_1.x - point_2.x) + abs(point_1.y - point_2.y)
+ ^^^^^^^^^
+ E AttributeError: 'NoneType' object has no attribute 'x'
+
+ -- by :user:`ammaraskar`
+
+
+- `#11118 `_: Now :confval:`pythonpath` configures `$PYTHONPATH` earlier than before during the initialization process, which now also affects plugins loaded via the `-p` command-line option.
+
+ -- by :user:`millerdev`
+
+
+- `#11381 `_: The ``type`` parameter of the ``parser.addini`` method now accepts `"int"` and ``"float"`` parameters, facilitating the parsing of configuration values in the configuration file.
+
+ Example:
+
+ .. code-block:: python
+
+ def pytest_addoption(parser):
+ parser.addini("int_value", type="int", default=2, help="my int value")
+ parser.addini("float_value", type="float", default=4.2, help="my float value")
+
+ The `pytest.ini` file:
+
+ .. code-block:: ini
+
+ [pytest]
+ int_value = 3
+ float_value = 5.4
+
+
+- `#11525 `_: Fixtures are now clearly represented in the output as a "fixture object", not as a normal function as before, making it easy for beginners to catch mistakes such as referencing a fixture declared in the same module but not requested in the test function.
+
+ -- by :user:`the-compiler` and :user:`glyphack`
+
+
+- `#12426 `_: A warning is now issued when :ref:`pytest.mark.usefixtures ref` is used without specifying any fixtures. Previously, empty usefixtures markers were silently ignored.
+
+
+- `#12707 `_: Exception chains can be navigated when dropped into Pdb in Python 3.13+.
+
+
+- `#12736 `_: Added a new attribute `name` with the fixed value `"pytest tests"` to the root tag `testsuites` of the junit-xml generated by pytest.
+
+ This attribute is part of many junit-xml specifications and is even part of the `junit-10.xsd` specification that pytest's implementation is based on.
+
+
+- `#12943 `_: If a test fails with an exceptiongroup with a single exception, the contained exception will now be displayed in the short test summary info.
+
+
+- `#12958 `_: A number of :ref:`unraisable ` enhancements:
+
+ * Set the unraisable hook as early as possible and unset it as late as possible, to collect the most possible number of unraisable exceptions.
+ * Call the garbage collector just before unsetting the unraisable hook, to collect any straggling exceptions.
+ * Collect multiple unraisable exceptions per test phase.
+ * Report the :mod:`tracemalloc` allocation traceback (if available).
+ * Avoid using a generator based hook to allow handling :class:`StopIteration` in test failures.
+ * Report the unraisable exception as the cause of the :class:`pytest.PytestUnraisableExceptionWarning` exception if raised.
+ * Compute the ``repr`` of the unraisable object in the unraisable hook so you get the latest information if available, and should help with resurrection of the object.
+
+
+- `#13010 `_: :func:`pytest.approx` now can compare collections that contain numbers and non-numbers mixed.
+
+
+- `#13016 `_: A number of :ref:`threadexception ` enhancements:
+
+ * Set the excepthook as early as possible and unset it as late as possible, to collect the most possible number of unhandled exceptions from threads.
+ * Collect multiple thread exceptions per test phase.
+ * Report the :mod:`tracemalloc` allocation traceback (if available).
+ * Avoid using a generator based hook to allow handling :class:`StopIteration` in test failures.
+ * Report the thread exception as the cause of the :class:`pytest.PytestUnhandledThreadExceptionWarning` exception if raised.
+ * Extract the ``name`` of the thread object in the excepthook which should help with resurrection of the thread.
+
+
+- `#13031 `_: An empty parameter set as in ``pytest.mark.parametrize([], ids=idfunc)`` will no longer trigger a call to ``idfunc`` with internal objects.
+
+
+- `#13115 `_: Allows supplying ``ExceptionGroup[Exception]`` and ``BaseExceptionGroup[BaseException]`` to ``pytest.raises`` to keep full typing on :class:`ExceptionInfo `:
+
+ .. code-block:: python
+
+ with pytest.raises(ExceptionGroup[Exception]) as exc_info:
+ some_function()
+
+ Parametrizing with other exception types remains an error - we do not check the types of child exceptions and thus do not permit code that might look like we do.
+
+
+- `#13122 `_: The ``--stepwise`` mode received a number of improvements:
+
+ * It no longer forgets the last failed test in case pytest is executed later without the flag.
+
+ This enables the following workflow:
+
+ 1. Execute pytest with ``--stepwise``, pytest then stops at the first failing test;
+ 2. Iteratively update the code and run the test in isolation, without the ``--stepwise`` flag (for example in an IDE), until it is fixed.
+ 3. Execute pytest with ``--stepwise`` again and pytest will continue from the previously failed test, and if it passes, continue on to the next tests.
+
+ Previously, at step 3, pytest would start from the beginning, forgetting the previously failed test.
+
+ This change however might cause issues if the ``--stepwise`` mode is used far apart in time, as the state might get stale, so the internal state will be reset automatically in case the test suite changes (for now only the number of tests are considered for this, we might change/improve this on the future).
+
+ * New ``--stepwise-reset``/``--sw-reset`` flag, allowing the user to explicitly reset the stepwise state and restart the workflow from the beginning.
+
+
+- `#13308 `_: Added official support for Python 3.14.
+
+
+- `#13380 `_: Fix :class:`ExceptionGroup` traceback filtering to exclude pytest internals.
+
+
+- `#13415 `_: The author metadata of the BibTex example is now correctly formatted with last names following first names.
+ An example of BibLaTex has been added.
+ BibTex and BibLaTex examples now clearly indicate that what is cited is software.
+
+ -- by :user:`willynilly`
+
+
+- `#13420 `_: Improved test collection performance by optimizing path resolution used in ``FSCollector``.
+
+
+- `#13457 `_: The error message about duplicate parametrization no longer displays an internal stack trace.
+
+
+- `#4112 `_: Using :ref:`pytest.mark.usefixtures ` on :func:`pytest.param` now produces an error instead of silently doing nothing.
+
+
+- `#5473 `_: Replace `:` with `;` in the assertion rewrite warning message so it can be filtered using standard Python warning filters before calling :func:`pytest.main`.
+
+
+- `#6985 `_: Improved :func:`pytest.approx` to enhance the readability of value ranges and tolerances between 0.001 and 1000.
+ * The `repr` method now provides clearer output for values within those ranges, making it easier to interpret the results.
+ * Previously, the output for those ranges of values and tolerances was displayed in scientific notation (e.g., `42 ± 1.0e+00`). The updated method now presents the tolerance as a decimal for better readability (e.g., `42 ± 1`).
+
+ Example:
+
+ **Previous Output:**
+
+ .. code-block:: console
+
+ >>> pytest.approx(42, abs=1)
+ 42 ± 1.0e+00
+
+ **Current Output:**
+
+ .. code-block:: console
+
+ >>> pytest.approx(42, abs=1)
+ 42 ± 1
+
+ -- by :user:`fazeelghafoor`
+
+
+- `#7683 `_: The formerly optional ``pygments`` dependency is now required, causing output always to be source-highlighted (unless disabled via the ``--code-highlight=no`` CLI option).
+
+
+
+Bug fixes
+---------
+
+- `#10404 `_: Apply filterwarnings from config/cli as soon as possible, and revert them as late as possible
+ so that warnings as errors are collected throughout the pytest run and before the
+ unraisable and threadexcept hooks are removed.
+
+ This allows very late warnings and unraisable/threadexcept exceptions to fail the test suite.
+
+ This also changes the warning that the lsof plugin issues from PytestWarning to the new warning PytestFDWarning so it can be more easily filtered.
+
+
+- `#11067 `_: The test report is now consistent regardless if the test xfailed via :ref:`pytest.mark.xfail ` or :func:`pytest.fail`.
+
+ Previously, *xfailed* tests via the marker would have the string ``"reason: "`` prefixed to the message, while those *xfailed* via the function did not. The prefix has been removed.
+
+
+- `#12008 `_: In :pr:`11220`, an unintended change in reordering was introduced by changing the way indices were assigned to direct params. More specifically, before that change, the indices of direct params to metafunc's callspecs were assigned after all parametrizations took place. Now, that change is reverted.
+
+
+- `#12863 `_: Fix applying markers, including :ref:`pytest.mark.parametrize ` when placed above `@staticmethod` or `@classmethod`.
+
+
+- `#12929 `_: Handle StopIteration from test cases, setup and teardown correctly.
+
+
+- `#12938 `_: Fixed ``--durations-min`` argument not respected if ``-vv`` is used.
+
+
+- `#12946 `_: Fixed missing help for :mod:`pdb` commands wrapped by pytest -- by :user:`adamchainz`.
+
+
+- `#12981 `_: Prevent exceptions in :func:`pytest.Config.add_cleanup` callbacks preventing further cleanups.
+
+
+- `#13047 `_: Restore :func:`pytest.approx` handling of equality checks between `bool` and `numpy.bool_` types.
+
+ Comparing `bool` and `numpy.bool_` using :func:`pytest.approx` accidentally changed in version `8.3.4` and `8.3.5` to no longer match:
+
+ .. code-block:: pycon
+
+ >>> import numpy as np
+ >>> from pytest import approx
+ >>> [np.True_, np.True_] == pytest.approx([True, True])
+ False
+
+ This has now been fixed:
+
+ .. code-block:: pycon
+
+ >>> [np.True_, np.True_] == pytest.approx([True, True])
+ True
+
+
+- `#13119 `_: Improved handling of invalid regex patterns for filter warnings by providing a clear error message.
+
+
+- `#13175 `_: The diff is now also highlighted correctly when comparing two strings.
+
+
+- `#13248 `_: Fixed an issue where passing a ``scope`` in :py:func:`Metafunc.parametrize ` with ``indirect=True``
+ could result in other fixtures being unable to depend on the parametrized fixture.
+
+
+- `#13291 `_: Fixed ``repr`` of ``attrs`` objects in assertion failure messages when using ``attrs>=25.2``.
+
+
+- `#13312 `_: Fixed a possible ``KeyError`` crash on PyPy during collection of tests involving higher-scoped parameters.
+
+
+- `#13345 `_: Fix type hints for :attr:`pytest.TestReport.when` and :attr:`pytest.TestReport.location`.
+
+
+- `#13377 `_: Fixed handling of test methods with positional-only parameter syntax.
+
+ Now, methods are supported that formally define ``self`` as positional-only
+ and/or fixture parameters as keyword-only, e.g.:
+
+ .. code-block:: python
+
+ class TestClass:
+
+ def test_method(self, /, *, fixture): ...
+
+ Before, this caused an internal error in pytest.
+
+
+- `#13384 `_: Fixed an issue where pytest could report negative durations.
+
+
+- `#13420 `_: Added ``lru_cache`` to ``nodes._check_initialpaths_for_relpath``.
+
+
+- `#9037 `_: Honor :confval:`disable_test_id_escaping_and_forfeit_all_rights_to_community_support` when escaping ids in parametrized tests.
+
+
+
+Improved documentation
+----------------------
+
+- `#12535 `_: `This
+ example`
+ showed ``print`` statements that do not exactly reflect what the
+ different branches actually do. The fix makes the example more precise.
+
+
+- `#13218 `_: Pointed out in the :func:`pytest.approx` documentation that it considers booleans unequal to numeric zero or one.
+
+
+- `#13221 `_: Improved grouping of CLI options in the ``--help`` output.
+
+
+- `#6649 `_: Added :class:`~pytest.TerminalReporter` to the :ref:`api-reference` documentation page.
+
+
+- `#8612 `_: Add a recipe for handling abstract test classes in the documentation.
+
+ A new example has been added to the documentation to demonstrate how to use a mixin class to handle abstract
+ test classes without manually setting the ``__test__`` attribute for subclasses.
+ This ensures that subclasses of abstract test classes are automatically collected by pytest.
+
+
+
+Packaging updates and notes for downstreams
+-------------------------------------------
+
+- `#13317 `_: Specified minimum allowed versions of ``colorama``, ``iniconfig``,
+ and ``packaging``; and bumped the minimum allowed version
+ of ``exceptiongroup`` for ``python_version<'3.11'`` from a release
+ candidate to a full release.
+
+
+
+Contributor-facing changes
+--------------------------
+
+- `#12017 `_: Mixed internal improvements:
+
+ * Migrate formatting to f-strings in some tests.
+ * Use type-safe constructs in JUnitXML tests.
+ * Moved`` MockTiming`` into ``_pytest.timing``.
+
+ -- by :user:`RonnyPfannschmidt`
+
+
+- `#12647 `_: Fixed running the test suite with the ``hypothesis`` pytest plugin.
+
+
+
+Miscellaneous internal changes
+------------------------------
+
+- `#6649 `_: Added :class:`~pytest.TerminalReporter` to the public pytest API, as it is part of the signature of the :hook:`pytest_terminal_summary` hook.
+
+
pytest 8.3.5 (2025-03-02)
=========================
diff --git a/doc/en/conf.py b/doc/en/conf.py
index c89e14d07fa..81156493131 100644
--- a/doc/en/conf.py
+++ b/doc/en/conf.py
@@ -34,6 +34,7 @@
"sphinx.ext.todo",
"sphinx.ext.viewcode",
"sphinx_removed_in",
+ "sphinx_inline_tabs",
"sphinxcontrib_trio",
"sphinxcontrib.towncrier.ext", # provides `towncrier-draft-entries` directive
"sphinx_issues", # implements `:issue:`, `:pr:` and other GH-related roles
diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst
index 18df64c9204..65a05823517 100644
--- a/doc/en/deprecations.rst
+++ b/doc/en/deprecations.rst
@@ -15,6 +15,41 @@ Below is a complete list of all pytest features which are considered deprecated.
:class:`~pytest.PytestWarning` or subclasses, which can be filtered using :ref:`standard warning filters `.
+.. _monkeypatch-fixup-namespace-packages:
+
+``monkeypatch.syspath_prepend`` with legacy namespace packages
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. deprecated:: 9.0
+
+When using :meth:`monkeypatch.syspath_prepend() `,
+pytest automatically calls ``pkg_resources.fixup_namespace_packages()`` if ``pkg_resources`` is imported.
+This is only needed for legacy namespace packages that use ``pkg_resources.declare_namespace()``.
+
+Legacy namespace packages are deprecated in favor of native namespace packages (:pep:`420`).
+If you are using ``pkg_resources.declare_namespace()`` in your ``__init__.py`` files,
+you should migrate to native namespace packages by removing the ``__init__.py`` files from your namespace packages.
+
+This deprecation warning will only be issued when:
+
+1. ``pkg_resources`` is imported, and
+2. The specific path being prepended contains a declared namespace package (via ``pkg_resources.declare_namespace()``)
+
+To fix this warning, convert your legacy namespace packages to native namespace packages:
+
+**Legacy namespace package** (deprecated):
+
+.. code-block:: python
+
+ # mypkg/__init__.py
+ __import__("pkg_resources").declare_namespace(__name__)
+
+**Native namespace package** (recommended):
+
+Simply remove the ``__init__.py`` file entirely.
+Python 3.3+ natively supports namespace packages without ``__init__.py``.
+
+
.. _sync-test-async-fixture:
sync test depending on async fixture
@@ -316,46 +351,6 @@ Users expected in this case that the ``usefixtures`` mark would have its intende
Now pytest will issue a warning when it encounters this problem, and will raise an error in the future versions.
-Returning non-None value in test functions
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. deprecated:: 7.2
-
-A ``pytest.PytestReturnNotNoneWarning`` is now emitted if a test function returns something other than `None`.
-
-This prevents a common mistake among beginners that expect that returning a `bool` would cause a test to pass or fail, for example:
-
-.. code-block:: python
-
- @pytest.mark.parametrize(
- ["a", "b", "result"],
- [
- [1, 2, 5],
- [2, 3, 8],
- [5, 3, 18],
- ],
- )
- def test_foo(a, b, result):
- return foo(a, b) == result
-
-Given that pytest ignores the return value, this might be surprising that it will never fail.
-
-The proper fix is to change the `return` to an `assert`:
-
-.. code-block:: python
-
- @pytest.mark.parametrize(
- ["a", "b", "result"],
- [
- [1, 2, 5],
- [2, 3, 8],
- [5, 3, 18],
- ],
- )
- def test_foo(a, b, result):
- assert foo(a, b) == result
-
-
The ``yield_fixture`` function/decorator
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -594,18 +589,20 @@ removed in pytest 8 (deprecated since pytest 2.4.0):
- ``parser.addoption(..., type="int/string/float/complex")`` - use ``type=int`` etc. instead.
-The ``--strict`` command-line option
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The ``--strict`` command-line option (reintroduced)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 6.2
-.. versionremoved:: 8.0
+.. versionchanged:: 9.0
-The ``--strict`` command-line option has been deprecated in favor of ``--strict-markers``, which
+The ``--strict`` command-line option had been deprecated in favor of ``--strict-markers``, which
better conveys what the option does.
-We have plans to maybe in the future to reintroduce ``--strict`` and make it an encompassing
-flag for all strictness related options (``--strict-markers`` and ``--strict-config``
-at the moment, more might be introduced in the future).
+In version 8.1, we accidentally un-deprecated ``--strict``.
+
+In version 9.0, we changed ``--strict`` to make it set the new :confval:`strict`
+configuration option. It now enables all strictness related options (including
+:confval:`strict_markers`).
.. _cmdline-preparse-deprecated:
@@ -848,20 +845,38 @@ that manipulate this type of file (for example, Jenkins, Azure Pipelines, etc.).
Users are recommended to try the new ``xunit2`` format and see if their tooling that consumes the JUnit
XML file supports it.
-To use the new format, update your ``pytest.ini``:
+To use the new format, update your configuration file:
+
+.. tab:: toml
+
+ .. code-block:: toml
-.. code-block:: ini
+ [pytest]
+ junit_family = "xunit2"
- [pytest]
- junit_family=xunit2
+.. tab:: ini
+
+ .. code-block:: ini
+
+ [pytest]
+ junit_family = xunit2
If you discover that your tooling does not support the new format, and want to keep using the
legacy version, set the option to ``legacy`` instead:
-.. code-block:: ini
+.. tab:: toml
+
+ .. code-block:: toml
+
+ [pytest]
+ junit_family = "legacy"
+
+.. tab:: ini
+
+ .. code-block:: ini
- [pytest]
- junit_family=legacy
+ [pytest]
+ junit_family = legacy
By using ``legacy`` you will keep using the legacy/xunit1 format when upgrading to
pytest 6.0, where the default format will be ``xunit2``.
diff --git a/doc/en/example/.ruff.toml b/doc/en/example/.ruff.toml
new file mode 100644
index 00000000000..feddc5c0654
--- /dev/null
+++ b/doc/en/example/.ruff.toml
@@ -0,0 +1 @@
+lint.ignore = ["RUF059"]
diff --git a/doc/en/example/markers.rst b/doc/en/example/markers.rst
index babcd9e2f3a..afb1ece0fe8 100644
--- a/doc/en/example/markers.rst
+++ b/doc/en/example/markers.rst
@@ -239,13 +239,11 @@ Registering markers
Registering markers for your test suite is simple:
-.. code-block:: ini
+.. code-block:: toml
- # content of pytest.ini
+ # content of pytest.toml
[pytest]
- markers =
- webtest: mark a test as a webtest.
- slow: mark test as slow.
+ markers = ["webtest: mark a test as a webtest.", "slow: mark test as slow."]
Multiple custom markers can be registered, by defining each one in its own line, as shown in above example.
@@ -264,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.
@@ -286,8 +284,7 @@ For an example on how to add and work with markers from a plugin, see
* Asking for existing markers via ``pytest --markers`` gives good output
- * Typos in function markers are treated as an error if you use
- the ``--strict-markers`` option.
+ * Typos in function markers are treated as an error if you use the :confval:`strict_markers` configuration option.
.. _`scoped-marking`:
@@ -456,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.
diff --git a/doc/en/example/multipython.py b/doc/en/example/multipython.py
index f54524213bc..c04a2868812 100644
--- a/doc/en/example/multipython.py
+++ b/doc/en/example/multipython.py
@@ -10,7 +10,7 @@
import pytest
-pythonlist = ["python3.9", "python3.10", "python3.11"]
+pythonlist = ["python3.11", "python3.12", "python3.13"]
@pytest.fixture(params=pythonlist)
diff --git a/doc/en/example/nonpython.rst b/doc/en/example/nonpython.rst
index aa463e2416b..a8d172937c5 100644
--- a/doc/en/example/nonpython.rst
+++ b/doc/en/example/nonpython.rst
@@ -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
@@ -78,7 +78,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
diff --git a/doc/en/example/parametrize.rst b/doc/en/example/parametrize.rst
index 69e715c9db1..b27dae18c32 100644
--- a/doc/en/example/parametrize.rst
+++ b/doc/en/example/parametrize.rst
@@ -162,7 +162,7 @@ objects, they are still using the default pytest representation:
rootdir: /home/sweet/project
collected 8 items
-
+
@@ -239,7 +239,7 @@ If you just collect tests you'll also nicely see 'advanced' and 'basic' as varia
rootdir: /home/sweet/project
collected 4 items
-
+
@@ -318,7 +318,7 @@ Let's first see how it looks like at collection time:
rootdir: /home/sweet/project
collected 2 items
-
+
@@ -352,7 +352,7 @@ The first invocation with ``db == "DB1"`` passed while the second with ``db == "
Indirect parametrization
---------------------------------------------------
-Using the ``indirect=True`` parameter when parametrizing a test allows to
+Using the ``indirect=True`` parameter when parametrizing a test allows one to
parametrize a test with a fixture receiving the values before passing them to a
test:
@@ -503,12 +503,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
---------------------------------------------------
diff --git a/doc/en/example/pythoncollection.rst b/doc/en/example/pythoncollection.rst
index 6a3b143d580..09489418773 100644
--- a/doc/en/example/pythoncollection.rst
+++ b/doc/en/example/pythoncollection.rst
@@ -55,6 +55,8 @@ You can run all of the tests within ``tests/`` *except* for ``tests/foobar/test_
by invoking ``pytest`` with ``--deselect tests/foobar/test_foobar_01.py::test_a``.
``pytest`` allows multiple ``--deselect`` options.
+.. _duplicate-paths:
+
Keeping duplicate paths specified from command line
----------------------------------------------------
@@ -82,29 +84,17 @@ Example:
collected 2 items
...
-As the collector just works on directories, if you specify twice a single test file, ``pytest`` will
-still collect it twice, no matter if the ``--keep-duplicates`` is not specified.
-Example:
-
-.. code-block:: pytest
-
- pytest test_a.py test_a.py
-
- ...
- collected 2 items
- ...
-
Changing directory recursion
-----------------------------------------------------
-You can set the :confval:`norecursedirs` option in an ini-file, for example your ``pytest.ini`` in the project root directory:
+You can set the :confval:`norecursedirs` option in a configuration file:
-.. code-block:: ini
+.. code-block:: toml
- # content of pytest.ini
+ # content of pytest.toml
[pytest]
- norecursedirs = .svn _build tmp*
+ norecursedirs = [".svn", "_build", "tmp*"]
This would tell ``pytest`` to not recurse into typical subversion or sphinx-build directories or into any ``tmp`` prefixed directory.
@@ -118,14 +108,14 @@ the :confval:`python_files`, :confval:`python_classes` and
:confval:`python_functions` in your :ref:`configuration file `.
Here is an example:
-.. code-block:: ini
+.. code-block:: toml
- # content of pytest.ini
+ # content of pytest.toml
# Example 1: have pytest look for "check" instead of "test"
[pytest]
- python_files = check_*.py
- python_classes = Check
- python_functions = *_check
+ python_files = ["check_*.py"]
+ python_classes = ["Check"]
+ python_functions = ["*_check"]
This would make ``pytest`` look for tests in files that match the ``check_*
.py`` glob-pattern, ``Check`` prefixes in classes, and functions and methods
@@ -149,10 +139,10 @@ The test collection would look like this:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
- configfile: pytest.ini
+ configfile: pytest.toml
collected 2 items
-
+
@@ -162,12 +152,12 @@ The test collection would look like this:
You can check for multiple glob patterns by adding a space between the patterns:
-.. code-block:: ini
+.. code-block:: toml
+ # content of pytest.toml
# Example 2: have pytest look for files with "test" and "example"
- # content of pytest.ini
[pytest]
- python_files = test_*.py example_*.py
+ python_files = ["test_*.py", "example_*.py"]
.. note::
@@ -188,14 +178,14 @@ example if you have unittest2 installed you can type:
pytest --pyargs unittest2.test.test_skipping -q
which would run the respective test module. Like with
-other options, through an ini-file and the :confval:`addopts` option you
+other options, through a configuration file and the :confval:`addopts` option you
can make this change more permanently:
-.. code-block:: ini
+.. code-block:: toml
- # content of pytest.ini
+ # content of pytest.toml
[pytest]
- addopts = --pyargs
+ addopts = ["--pyargs"]
Now a simple invocation of ``pytest NAME`` will check
if NAME exists as an importable package/module and otherwise
@@ -212,10 +202,10 @@ You can always peek at the collection tree without running tests like this:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
- configfile: pytest.ini
+ configfile: pytest.toml
collected 3 items
-
+
@@ -234,11 +224,11 @@ Customizing test collection
You can easily instruct ``pytest`` to discover tests from every Python file:
-.. code-block:: ini
+.. code-block:: toml
- # content of pytest.ini
+ # content of pytest.toml
[pytest]
- python_files = *.py
+ 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
@@ -296,7 +286,7 @@ file will be left out:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.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 5e48815bbc9..8040ee9b957 100644
--- a/doc/en/example/reportingdemo.rst
+++ b/doc/en/example/reportingdemo.rst
@@ -384,6 +384,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
i = Foo()
> assert i.b == 2
+ ^^^
failure_demo.py:148:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
@@ -446,6 +447,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
def test_tupleerror(self):
> a, b = [1] # noqa: F841
+ ^^^^
E ValueError: not enough values to unpack (expected 2, got 1)
failure_demo.py:177: ValueError
@@ -457,6 +459,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
items = [1, 2, 3]
print(f"items is {items!r}")
> a, b = items.pop()
+ ^^^^
E TypeError: cannot unpack non-iterable int object
failure_demo.py:182: TypeError
@@ -468,6 +471,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
def test_some_error(self):
> if namenotexi: # noqa: F821
+ ^^^^^^^^^^
E NameError: name 'namenotexi' is not defined
failure_demo.py:185: NameError
@@ -526,6 +530,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
def test_z1_unpack_error(self):
items = []
> a, b = items
+ ^^^^
E ValueError: not enough values to unpack (expected 2, got 0)
failure_demo.py:219: ValueError
@@ -536,6 +541,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
def test_z2_type_error(self):
items = 3
> a, b = items
+ ^^^^
E TypeError: cannot unpack non-iterable int object
failure_demo.py:223: TypeError
@@ -568,12 +574,12 @@ Here is a nice run of several failures and how ``pytest`` presents things:
E + where False = ('456')
E + where = '123'.startswith
E + where '123' = .f at 0xdeadbeef0029>()
- E + and '456' = .g at 0xdeadbeef0003>()
+ E + and '456' = .g at 0xdeadbeef002a>()
failure_demo.py:237: AssertionError
_____________________ TestMoreErrors.test_global_func ______________________
- self =
+ self =
def test_global_func(self):
> assert isinstance(globf(42), float)
@@ -584,18 +590,18 @@ Here is a nice run of several failures and how ``pytest`` presents things:
failure_demo.py:240: AssertionError
_______________________ TestMoreErrors.test_instance _______________________
- self =
+ self =
def test_instance(self):
self.x = 6 * 7
> assert self.x != 42
E assert 42 != 42
- E + where 42 = .x
+ E + where 42 = .x
failure_demo.py:244: AssertionError
_______________________ TestMoreErrors.test_compare ________________________
- self =
+ self =
def test_compare(self):
> assert globf(10) < 5
@@ -605,7 +611,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
failure_demo.py:247: AssertionError
_____________________ TestMoreErrors.test_try_finally ______________________
- self =
+ self =
def test_try_finally(self):
x = 1
@@ -616,7 +622,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
failure_demo.py:252: AssertionError
___________________ TestCustomAssertMsg.test_single_line ___________________
- self =
+ self =
def test_single_line(self):
class A:
@@ -631,16 +637,16 @@ Here is a nice run of several failures and how ``pytest`` presents things:
failure_demo.py:263: AssertionError
____________________ TestCustomAssertMsg.test_multiline ____________________
- self =
+ self =
def test_multiline(self):
class A:
a = 1
b = 2
- > assert (
- A.a == b
- ), "A.a appears not to be b\nor does not appear to be b\none of those"
+ > assert A.a == b, (
+ "A.a appears not to be b\nor does not appear to be b\none of those"
+ )
E AssertionError: A.a appears not to be b
E or does not appear to be b
E one of those
@@ -650,7 +656,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
failure_demo.py:270: AssertionError
___________________ TestCustomAssertMsg.test_custom_repr ___________________
- self =
+ self =
def test_custom_repr(self):
class JSON:
diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst
index 69d973e51d0..e150e7ca00b 100644
--- a/doc/en/example/simple.rst
+++ b/doc/en/example/simple.rst
@@ -11,12 +11,11 @@ every time you use ``pytest``. For example, if you always want to see
detailed info on skipped and xfailed tests, as well as have terser "dot"
progress output, you can write it into a configuration file:
-.. code-block:: ini
+.. code-block:: toml
- # content of pytest.ini
+ # content of pytest.toml
[pytest]
- addopts = -ra -q
-
+ addopts = ["-ra", "-q"]
Alternatively, you can set a ``PYTEST_ADDOPTS`` environment variable to add command
line options while the environment is in use:
@@ -29,7 +28,7 @@ Here's how the command-line is built in the presence of ``addopts`` or the envir
.. code-block:: text
- $PYTEST_ADDOPTS
+ $PYTEST_ADDOPTS
So if the user executes in the command-line:
@@ -104,6 +103,7 @@ Let's run this without supplying our new option:
elif cmdopt == "type2":
print("second")
> assert 0 # to see what was printed
+ ^^^^^^^^
E assert 0
test_sample.py:6: AssertionError
@@ -130,6 +130,7 @@ And now with supplying a command line option:
elif cmdopt == "type2":
print("second")
> assert 0 # to see what was printed
+ ^^^^^^^^
E assert 0
test_sample.py:6: AssertionError
@@ -165,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
@@ -646,7 +649,7 @@ If we run this:
test_step.py:11: AssertionError
========================= short test summary info ==========================
- XFAIL test_step.py::TestUserHandling::test_deletion - reason: previous test failed (test_modification)
+ XFAIL test_step.py::TestUserHandling::test_deletion - previous test failed (test_modification)
================== 1 failed, 2 passed, 1 xfailed in 0.12s ==================
We'll see that ``test_deletion`` was not executed because ``test_modification``
@@ -725,7 +728,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, 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
@@ -736,6 +739,7 @@ We can run this:
def test_a1(db):
> assert 0, db # to show value
+ ^^^^^^^^^^^^
E AssertionError:
E assert 0
@@ -746,6 +750,7 @@ We can run this:
def test_a2(db):
> assert 0, db # to show value
+ ^^^^^^^^^^^^
E AssertionError:
E assert 0
@@ -946,7 +951,7 @@ and run it:
rootdir: /home/sweet/project
collected 3 items
- test_module.py Esetting up a test failed or skipped test_module.py::test_setup_fails
+ test_module.py Esetting up a test failed test_module.py::test_setup_fails
Fexecuting test failed or skipped test_module.py::test_call_fails
F
diff --git a/doc/en/explanation/ci.rst b/doc/en/explanation/ci.rst
index 45fe658d14f..6f6734f395b 100644
--- a/doc/en/explanation/ci.rst
+++ b/doc/en/explanation/ci.rst
@@ -17,8 +17,7 @@ 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,
-regardless of their value:
+Pytest knows it is in a CI environment when either one of these environment variables are set to a non-empty value:
* `CI`: used by many CI systems.
* `BUILD_NUMBER`: used by Jenkins.
diff --git a/doc/en/explanation/goodpractices.rst b/doc/en/explanation/goodpractices.rst
index 51c0b960aed..d97dda06417 100644
--- a/doc/en/explanation/goodpractices.rst
+++ b/doc/en/explanation/goodpractices.rst
@@ -94,14 +94,13 @@ This has the following benefits:
For new projects, we recommend to use ``importlib`` :ref:`import mode `
(see which-import-mode_ for a detailed explanation).
-To this end, add the following to your ``pyproject.toml``:
+To this end, add the following to your configuration file:
.. code-block:: toml
- [tool.pytest.ini_options]
- addopts = [
- "--import-mode=importlib",
- ]
+ # content of pytest.toml
+ [pytest]
+ addopts = ["--import-mode=importlib"]
.. _src-layout:
@@ -126,12 +125,21 @@ which are better explained in this excellent `blog post`_ by Ionel Cristian Măr
PYTHONPATH=src pytest
or in a permanent manner by using the :confval:`pythonpath` configuration variable and adding the
- following to your ``pyproject.toml``:
+ following to your configuration file:
- .. code-block:: toml
+ .. tab:: toml
+
+ .. code-block:: toml
+
+ [pytest]
+ pythonpath = ["src"]
+
+ .. tab:: ini
+
+ .. code-block:: ini
- [tool.pytest.ini_options]
- pythonpath = "src"
+ [pytest]
+ pythonpath = src
.. note::
@@ -313,3 +321,75 @@ A list of the lints detected by flake8-pytest-style can be found on its `PyPI pa
.. note::
flake8-pytest-style is not an official pytest project. Some of the rules enforce certain style choices, such as using `@pytest.fixture()` over `@pytest.fixture`, but you can configure the plugin to fit your preferred style.
+
+.. _`strict mode`:
+
+Using pytest's strict mode
+--------------------------
+
+.. versionadded:: 9.0
+
+Pytest contains a set of configuration options that make it more strict.
+The options are off by default for compatibility or other reasons,
+but you should enable them if you can.
+
+You can enable all of the strictness options at once by setting the :confval:`strict` configuration option:
+
+.. tab:: toml
+
+ .. code-block:: toml
+
+ [pytest]
+ strict = true
+
+.. tab:: ini
+
+ .. code-block:: ini
+
+ [pytest]
+ strict = true
+
+See the :confval:`strict` documentation for the options it enables and their effect.
+
+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::
+
+.. tab:: toml
+
+ .. code-block:: toml
+
+ [pytest]
+ strict_config = true
+ strict_markers = true
+ strict_parametrization_ids = true
+ strict_xfail = true
+
+.. tab:: ini
+
+ .. code-block:: ini
+
+ [pytest]
+ strict_config = true
+ strict_markers = true
+ 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::
+
+.. tab:: toml
+
+ .. code-block:: toml
+
+ [pytest]
+ strict = true
+ strict_parametrization_ids = false
+
+.. tab:: ini
+
+ .. code-block:: ini
+
+ [pytest]
+ strict = true
+ strict_parametrization_ids = false
diff --git a/doc/en/explanation/pythonpath.rst b/doc/en/explanation/pythonpath.rst
index e68f455cedf..ddcbd304f89 100644
--- a/doc/en/explanation/pythonpath.rst
+++ b/doc/en/explanation/pythonpath.rst
@@ -49,7 +49,7 @@ these values:
we advocate for using :ref:`src-layouts `.
Same as ``prepend``, requires test module names to be unique when the test directory tree is
- not arranged in packages, because the modules will put in :py:data:`sys.modules` after importing.
+ not arranged in packages, because the modules will be put in :py:data:`sys.modules` after importing.
.. _`import-mode-importlib`:
@@ -64,7 +64,7 @@ these values:
* Test modules can't import each other.
* Testing utility modules in the tests directories (for example a ``tests.helpers`` module containing test-related functions/classes)
- are not importable. The recommendation in this case it to place testing utility modules together with the application/library
+ are not importable. The recommendation in this case is to place testing utility modules together with the application/library
code, for example ``app.testing.helpers``.
Important: by "test utility modules", we mean functions/classes which are imported by
@@ -152,7 +152,7 @@ this case ``foo/``). To load the module, it will insert ``root/`` to the front
The same logic applies to the ``conftest.py`` file: it will be imported as ``foo.conftest`` module.
Preserving the full package name is important when tests live in a package to avoid problems
-and allow test modules to have duplicated names. This is also discussed in details in
+and allow test modules to have duplicated names. This is also discussed in detail in
:ref:`test discovery`.
Standalone test modules / ``conftest.py`` files
@@ -182,7 +182,7 @@ with the ``conftest.py`` file by adding ``root/foo`` to :py:data:`sys.path` to i
For this reason this layout cannot have test modules with the same name, as they all will be
imported in the global import namespace.
-This is also discussed in details in :ref:`test discovery`.
+This is also discussed in detail in :ref:`test discovery`.
.. _`pytest vs python -m pytest`:
diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst
index 41469de3864..ec1ef60a605 100644
--- a/doc/en/getting-started.rst
+++ b/doc/en/getting-started.rst
@@ -9,8 +9,6 @@ Get Started
Install ``pytest``
----------------------------------------
-``pytest`` requires: Python 3.8+ or PyPy3.
-
1. Run the following command in your command line:
.. code-block:: bash
@@ -22,7 +20,7 @@ Install ``pytest``
.. code-block:: bash
$ pytest --version
- pytest 8.3.5
+ pytest 9.0.0
.. _`simpletest`:
@@ -75,7 +73,7 @@ The ``[100%]`` refers to the overall progress of running all test cases. After i
Run multiple tests
----------------------------------------------------------
-``pytest`` will run all files of the form test_*.py or \*_test.py in the current directory and its subdirectories. More generally, it follows :ref:`standard test discovery rules `.
+``pytest`` will run all files of the form ``test_*.py`` or ``*_test.py`` in the current directory and its subdirectories. More generally, it follows :ref:`standard test discovery rules `.
Assert that a certain exception is raised
@@ -201,6 +199,26 @@ This is outlined below:
Note that attributes added at class level are *class attributes*, so they will be shared between tests.
+Compare floating-point values with pytest.approx
+--------------------------------------------------------------
+
+``pytest`` also provides a number of utilities to make writing tests easier.
+For example, you can use :func:`pytest.approx` to compare floating-point
+values that may have small rounding errors:
+
+.. code-block:: python
+
+ # content of test_approx.py
+ import pytest
+
+
+ def test_sum():
+ assert (0.1 + 0.2) == pytest.approx(0.3)
+
+This avoids the need for manual tolerance checks or using
+``math.isclose`` and works with scalars, lists, and NumPy arrays.
+
+
Request a unique temporary directory for functional tests
--------------------------------------------------------------
diff --git a/doc/en/how-to/assert.rst b/doc/en/how-to/assert.rst
index 6bc8f6fed33..4dfceda0fad 100644
--- a/doc/en/how-to/assert.rst
+++ b/doc/en/how-to/assert.rst
@@ -66,6 +66,33 @@ See :ref:`assert-details` for more information on assertion introspection.
.. _`assertraises`:
+Assertions about approximate equality
+-------------------------------------
+
+When comparing floating point values (or arrays of floats), small rounding
+errors are common. Instead of using ``assert abs(a - b) < tol`` or
+``numpy.isclose``, you can use :func:`pytest.approx`:
+
+.. code-block:: python
+
+ import pytest
+ import numpy as np
+
+
+ def test_floats():
+ assert (0.1 + 0.2) == pytest.approx(0.3)
+
+
+ def test_arrays():
+ a = np.array([1.0, 2.0, 3.0])
+ b = np.array([0.9999, 2.0001, 3.0])
+ assert a == pytest.approx(b)
+
+``pytest.approx`` works with scalars, lists, dictionaries, and NumPy arrays.
+It also supports comparisons involving NaNs.
+
+See :func:`pytest.approx` for details.
+
Assertions about expected exceptions
------------------------------------------
@@ -476,6 +503,50 @@ the conftest file:
FAILED test_foocompare.py::test_compare - assert Comparing Foo instances:
1 failed in 0.12s
+.. _`return-not-none`:
+
+Returning non-None value in test functions
+------------------------------------------
+
+A :class:`pytest.PytestReturnNotNoneWarning` is emitted when a test function returns a value other than ``None``.
+
+This helps prevent a common mistake made by beginners who assume that returning a ``bool`` (e.g., ``True`` or ``False``) will determine whether a test passes or fails.
+
+Example:
+
+.. code-block:: python
+
+ @pytest.mark.parametrize(
+ ["a", "b", "result"],
+ [
+ [1, 2, 5],
+ [2, 3, 8],
+ [5, 3, 18],
+ ],
+ )
+ def test_foo(a, b, result):
+ return foo(a, b) == result # Incorrect usage, do not do this.
+
+Since pytest ignores return values, it might be surprising that the test will never fail based on the returned value.
+
+The correct fix is to replace the ``return`` statement with an ``assert``:
+
+.. code-block:: python
+
+ @pytest.mark.parametrize(
+ ["a", "b", "result"],
+ [
+ [1, 2, 5],
+ [2, 3, 8],
+ [5, 3, 18],
+ ],
+ )
+ def test_foo(a, b, result):
+ assert foo(a, b) == result
+
+
+
+
.. _assert-details:
.. _`assert introspection`:
@@ -512,7 +583,7 @@ Note that you still get the benefits of assertion introspection, the only change
the ``.pyc`` files won't be cached on disk.
Additionally, rewriting will silently skip caching if it cannot write new ``.pyc`` files,
-i.e. in a read-only filesystem or a zipfile.
+e.g. in a read-only filesystem or a zipfile.
Disabling assert rewriting
diff --git a/doc/en/how-to/cache.rst b/doc/en/how-to/cache.rst
index a3b2a862534..e3209b79359 100644
--- a/doc/en/how-to/cache.rst
+++ b/doc/en/how-to/cache.rst
@@ -289,8 +289,6 @@ You can always peek at the content of the cache using the
{'test_caching.py::test_function': True}
cache/nodeids contains:
['test_caching.py::test_function']
- cache/stepwise contains:
- []
example/value contains:
42
diff --git a/doc/en/how-to/capture-warnings.rst b/doc/en/how-to/capture-warnings.rst
index 4b1de6f3704..8ed546bedf7 100644
--- a/doc/en/how-to/capture-warnings.rst
+++ b/doc/en/how-to/capture-warnings.rst
@@ -66,6 +66,7 @@ as an error:
def test_one():
> assert api_v1() == 1
+ ^^^^^^^^
test_show_warnings.py:10:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
@@ -79,30 +80,32 @@ as an error:
FAILED test_show_warnings.py::test_one - UserWarning: api v1, should use ...
1 failed in 0.12s
-The same option can be set in the ``pytest.ini`` or ``pyproject.toml`` file using the
-``filterwarnings`` ini option. For example, the configuration below will ignore all
+The same option can be set in the configuration file using the
+:confval:`filterwarnings` configuration option. For example, the configuration below will ignore all
user warnings and specific deprecation warnings matching a regex, but will transform
all other warnings into errors.
-.. code-block:: ini
+.. tab:: toml
- # pytest.ini
- [pytest]
- filterwarnings =
- error
- ignore::UserWarning
- ignore:function ham\(\) is deprecated:DeprecationWarning
+ .. code-block:: toml
-.. code-block:: toml
+ [pytest]
+ filterwarnings = [
+ "error",
+ "ignore::UserWarning",
+ # note the use of single quote below to denote "raw" strings in TOML
+ 'ignore:function ham\(\) is deprecated:DeprecationWarning',
+ ]
+
+.. tab:: ini
- # pyproject.toml
- [tool.pytest.ini_options]
- filterwarnings = [
- "error",
- "ignore::UserWarning",
- # note the use of single quote below to denote "raw" strings in TOML
- 'ignore:function ham\(\) is deprecated:DeprecationWarning',
- ]
+ .. code-block:: ini
+
+ [pytest]
+ filterwarnings =
+ error
+ ignore::UserWarning
+ ignore:function ham\(\) is deprecated:DeprecationWarning
When a warning matches more than one option in the list, the action for the last matching option
@@ -111,7 +114,7 @@ is performed.
.. note::
- The ``-W`` flag and the ``filterwarnings`` ini option use warning filters that are
+ The ``-W`` flag and the :confval:`filterwarnings` configuration option use warning filters that are
similar in structure, but each configuration option interprets its filter
differently. For example, *message* in ``filterwarnings`` is a string containing a
regular expression that the start of the warning message must match,
@@ -168,7 +171,7 @@ You can specify multiple filters with separate decorators:
Filters applied using a mark take precedence over filters passed on the command line or configured
-by the :confval:`filterwarnings` ini option.
+by the :confval:`filterwarnings` configuration option.
You may apply a filter to all tests of a class by using the :ref:`filterwarnings ` mark as a class
decorator or to all tests in a module by setting the :globalvar:`pytestmark` variable:
@@ -201,7 +204,16 @@ warning summary entirely from the test run output.
Disabling warning capture entirely
----------------------------------
-This plugin is enabled by default but can be disabled entirely in your ``pytest.ini`` file with:
+This plugin is enabled by default but can be disabled entirely in your configuration file with:
+
+.. tab:: toml
+
+ .. code-block:: toml
+
+ [pytest]
+ addopts = ["-p", "no:warnings"]
+
+.. tab:: ini
.. code-block:: ini
@@ -226,16 +238,27 @@ However, in the specific case where users capture any type of warnings in their
no warning will be displayed at all.
Sometimes it is useful to hide some specific deprecation warnings that happen in code that you have no control over
-(such as third-party libraries), in which case you might use the warning filters options (ini or marks) to ignore
+(such as third-party libraries), in which case you might use the warning filters options (configuration or marks) to ignore
those warnings.
For example:
-.. code-block:: ini
+.. tab:: toml
+
+ .. code-block:: toml
+
+ [pytest]
+ filterwarnings = [
+ 'ignore:.*U.*mode is deprecated:DeprecationWarning',
+ ]
+
+.. tab:: ini
+
+ .. code-block:: ini
- [pytest]
- filterwarnings =
- ignore:.*U.*mode is deprecated:DeprecationWarning
+ [pytest]
+ filterwarnings =
+ ignore:.*U.*mode is deprecated:DeprecationWarning
This will ignore all warnings of type ``DeprecationWarning`` where the start of the message matches
diff --git a/doc/en/how-to/doctest.rst b/doc/en/how-to/doctest.rst
index c2a6cc8e958..601f5c0afd0 100644
--- a/doc/en/how-to/doctest.rst
+++ b/doc/en/how-to/doctest.rst
@@ -68,27 +68,34 @@ and functions, including from test modules:
============================ 2 passed in 0.12s =============================
You can make these changes permanent in your project by
-putting them into a pytest.ini file like this:
+putting them into a configuration file like this:
-.. code-block:: ini
+.. code-block:: toml
- # content of pytest.ini
+ # content of pytest.toml
[pytest]
- addopts = --doctest-modules
-
+ addopts = ["--doctest-modules"]
Encoding
--------
The default encoding is **UTF-8**, but you can specify the encoding
that will be used for those doctest files using the
-``doctest_encoding`` ini option:
+:confval:`doctest_encoding` configuration option:
-.. code-block:: ini
+.. tab:: toml
- # content of pytest.ini
- [pytest]
- doctest_encoding = latin1
+ .. code-block:: toml
+
+ [pytest]
+ doctest_encoding = "latin1"
+
+.. tab:: ini
+
+ .. code-block:: ini
+
+ [pytest]
+ doctest_encoding = latin1
.. _using doctest options:
@@ -102,10 +109,19 @@ configuration file.
For example, to make pytest ignore trailing whitespaces and ignore
lengthy exception stack traces you can just write:
-.. code-block:: ini
+.. tab:: toml
- [pytest]
- doctest_optionflags = NORMALIZE_WHITESPACE IGNORE_EXCEPTION_DETAIL
+ .. code-block:: toml
+
+ [pytest]
+ doctest_optionflags = ["NORMALIZE_WHITESPACE", "IGNORE_EXCEPTION_DETAIL"]
+
+.. tab:: ini
+
+ .. code-block:: ini
+
+ [pytest]
+ doctest_optionflags = NORMALIZE_WHITESPACE IGNORE_EXCEPTION_DETAIL
Alternatively, options can be enabled by an inline comment in the doc test
itself:
@@ -307,7 +323,7 @@ While the built-in pytest support provides a good set of functionalities for usi
doctests, if you use them extensively you might be interested in those external packages
which add many more features, and include pytest integration:
-* `pytest-doctestplus `__: provides
+* `pytest-doctestplus `__: provides
advanced doctest support and enables the testing of reStructuredText (".rst") files.
* `Sybil `__: provides a way to test examples in
diff --git a/doc/en/how-to/failures.rst b/doc/en/how-to/failures.rst
index b3d0c155b48..0c45cd7b118 100644
--- a/doc/en/how-to/failures.rst
+++ b/doc/en/how-to/failures.rst
@@ -112,7 +112,7 @@ on the command-line.
Also the :confval:`faulthandler_timeout=X` configuration option can be used
to dump the traceback of all threads if a test takes longer than ``X``
-seconds to finish (not available on Windows).
+seconds to finish.
.. note::
diff --git a/doc/en/how-to/fixtures.rst b/doc/en/how-to/fixtures.rst
index 8f84e4867a6..0c4ddb8b4dc 100644
--- a/doc/en/how-to/fixtures.rst
+++ b/doc/en/how-to/fixtures.rst
@@ -449,6 +449,7 @@ marked ``smtp_connection`` fixture function. Running the test looks like this:
assert response == 250
assert b"smtp.gmail.com" in msg
> assert 0 # for demo purposes
+ ^^^^^^^^
E assert 0
test_module.py:7: AssertionError
@@ -460,6 +461,7 @@ marked ``smtp_connection`` fixture function. Running the test looks like this:
response, msg = smtp_connection.noop()
assert response == 250
> assert 0 # for demo purposes
+ ^^^^^^^^
E assert 0
test_module.py:13: AssertionError
@@ -1308,6 +1310,7 @@ So let's just do another run:
assert response == 250
assert b"smtp.gmail.com" in msg
> assert 0 # for demo purposes
+ ^^^^^^^^
E assert 0
test_module.py:7: AssertionError
@@ -1319,6 +1322,7 @@ So let's just do another run:
response, msg = smtp_connection.noop()
assert response == 250
> assert 0 # for demo purposes
+ ^^^^^^^^
E assert 0
test_module.py:13: AssertionError
@@ -1343,6 +1347,7 @@ So let's just do another run:
response, msg = smtp_connection.noop()
assert response == 250
> assert 0 # for demo purposes
+ ^^^^^^^^
E assert 0
test_module.py:13: AssertionError
@@ -1418,7 +1423,7 @@ Running the above tests results in the following test IDs being used:
rootdir: /home/sweet/project
collected 12 items
-
+
@@ -1731,14 +1736,13 @@ and you may specify fixture usage at the test module level using :globalvar:`pyt
It is also possible to put fixtures required by all tests in your project
-into an ini-file:
+into a configuration file:
-.. code-block:: ini
+.. code-block:: toml
- # content of pytest.ini
+ # content of pytest.toml
[pytest]
- usefixtures = cleandir
-
+ usefixtures = ["cleandir"]
.. warning::
diff --git a/doc/en/how-to/index.rst b/doc/en/how-to/index.rst
index 225f289651e..9796f1f8090 100644
--- a/doc/en/how-to/index.rst
+++ b/doc/en/how-to/index.rst
@@ -16,6 +16,7 @@ Core pytest functionality
fixtures
mark
parametrize
+ subtests
tmp_path
monkeypatch
doctest
diff --git a/doc/en/how-to/logging.rst b/doc/en/how-to/logging.rst
index 300e9f6e6c2..d6d87f03bbf 100644
--- a/doc/en/how-to/logging.rst
+++ b/doc/en/how-to/logging.rst
@@ -47,13 +47,23 @@ Shows failed tests like so:
text going to stderr
==================== 2 failed in 0.02 seconds =====================
-These options can also be customized through ``pytest.ini`` file:
+These options can also be customized through a configuration file:
-.. code-block:: ini
+.. tab:: toml
- [pytest]
- log_format = %(asctime)s %(levelname)s %(message)s
- log_date_format = %Y-%m-%d %H:%M:%S
+ .. code-block:: toml
+
+ [pytest]
+ log_format = "%(asctime)s %(levelname)s %(message)s"
+ log_date_format = "%Y-%m-%d %H:%M:%S"
+
+.. tab:: ini
+
+ .. code-block:: ini
+
+ [pytest]
+ 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}``.
This argument can be passed multiple times:
@@ -198,12 +208,12 @@ Additionally, you can also specify ``--log-cli-format`` and
``--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 INI file. The
+All of the CLI log options can also be set in the configuration file. The
option names are:
-* ``log_cli_level``
-* ``log_cli_format``
-* ``log_cli_date_format``
+* :confval:`log_cli_level`
+* :confval:`log_cli_format`
+* :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
@@ -220,17 +230,17 @@ 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.
-All of the log file options can also be set in the configuration INI file. The
+All of the log file options can also be set in the configuration file. The
option names are:
-* ``log_file``
-* ``log_file_mode``
-* ``log_file_level``
-* ``log_file_format``
-* ``log_file_date_format``
+* :confval:`log_file`
+* :confval:`log_file_mode`
+* :confval:`log_file_level`
+* :confval:`log_file_format`
+* :confval:`log_file_date_format`
You can call ``set_log_path()`` to customize the log_file path dynamically. This functionality
-is considered **experimental**. Note that ``set_log_path()`` respects the ``log_file_mode`` option.
+is considered **experimental**. Note that ``set_log_path()`` respects the :confval:`log_file_mode` option.
.. _log_colors:
@@ -266,12 +276,21 @@ This feature was introduced as a drop-in replacement for the
with each other. The backward compatibility API with ``pytest-capturelog``
has been dropped when this feature was introduced, so if for that reason you
still need ``pytest-catchlog`` you can disable the internal feature by
-adding to your ``pytest.ini``:
+adding to your configuration file:
+
+.. tab:: toml
+
+ .. code-block:: toml
-.. code-block:: ini
+ [pytest]
+ addopts = ["-p", "no:logging"]
- [pytest]
- addopts=-p no:logging
+.. tab:: ini
+
+ .. code-block:: ini
+
+ [pytest]
+ addopts = -p no:logging
.. _log_changes_3_4:
@@ -293,13 +312,23 @@ made in ``3.4`` after community feedback:
* :ref:`Live Logs ` are now sent to ``sys.stdout`` and no longer require the ``-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 ``ini``
+If you want to partially restore the logging behavior of version ``3.3``, you can add this options to your configuration
file:
-.. code-block:: ini
+.. tab:: toml
+
+ .. code-block:: toml
+
+ [pytest]
+ log_cli = true
+ log_level = "NOTSET"
+
+.. tab:: ini
+
+ .. code-block:: ini
- [pytest]
- log_cli=true
- log_level=NOTSET
+ [pytest]
+ log_cli = true
+ log_level = NOTSET
More details about the discussion that lead to this changes can be read in :issue:`3013`.
diff --git a/doc/en/how-to/mark.rst b/doc/en/how-to/mark.rst
index 33f9d18bfe3..575ce2f41c2 100644
--- a/doc/en/how-to/mark.rst
+++ b/doc/en/how-to/mark.rst
@@ -34,24 +34,26 @@ See :ref:`mark examples` for examples which also serve as documentation.
Registering marks
-----------------
-You can register custom marks in your ``pytest.ini`` file like this:
+You can register custom marks in your configuration file like this:
-.. code-block:: ini
+.. tab:: toml
- [pytest]
- markers =
- slow: marks tests as slow (deselect with '-m "not slow"')
- serial
+ .. code-block:: toml
-or in your ``pyproject.toml`` file like this:
+ [pytest]
+ markers = [
+ "slow: marks tests as slow (deselect with '-m \"not slow\"')",
+ "serial",
+ ]
-.. code-block:: toml
+.. tab:: ini
- [tool.pytest.ini_options]
- markers = [
- "slow: marks tests as slow (deselect with '-m \"not slow\"')",
- "serial",
- ]
+ .. code-block:: ini
+
+ [pytest]
+ markers =
+ slow: marks tests as slow (deselect with '-m "not slow"')
+ serial
Note that everything past the ``:`` after the mark name is an optional description.
@@ -77,17 +79,30 @@ Raising errors on unknown marks
Unregistered marks applied with the ``@pytest.mark.name_of_the_mark`` decorator
will always emit a warning in order to avoid silently doing something
surprising due to mistyped names. As described in the previous section, you can disable
-the warning for custom marks by registering them in your ``pytest.ini`` file or
+the warning for custom marks by registering them in your configuration file or
using a custom ``pytest_configure`` hook.
-When the ``--strict-markers`` command-line flag is passed, any unknown marks applied
+When the :confval:`strict_markers` configuration option is set, any unknown marks applied
with the ``@pytest.mark.name_of_the_mark`` decorator will trigger an error. You can
-enforce this validation in your project by adding ``--strict-markers`` to ``addopts``:
+enforce this validation in your project by setting :confval:`strict_markers` in your configuration:
+
+.. tab:: toml
+
+ .. code-block:: toml
+
+ [pytest]
+ addopts = ["--strict-markers"]
+ markers = [
+ "slow: marks tests as slow (deselect with '-m \"not slow\"')",
+ "serial",
+ ]
+
+.. tab:: ini
-.. code-block:: ini
+ .. code-block:: ini
- [pytest]
- addopts = --strict-markers
- markers =
- slow: marks tests as slow (deselect with '-m "not slow"')
- serial
+ [pytest]
+ strict_markers = true
+ markers =
+ slow: marks tests as slow (deselect with '-m "not slow"')
+ serial
diff --git a/doc/en/how-to/output.rst b/doc/en/how-to/output.rst
index cb9276c7ea0..e03f477b22d 100644
--- a/doc/en/how-to/output.rst
+++ b/doc/en/how-to/output.rst
@@ -447,7 +447,7 @@ Example:
================================= XPASSES ==================================
========================= short test summary info ==========================
SKIPPED [1] test_example.py:22: skipping this test
- XFAIL test_example.py::test_xfail - reason: xfailing this test
+ XFAIL test_example.py::test_xfail - xfailing this test
XPASS test_example.py::test_xpass - always xfail
ERROR test_example.py::test_error - assert 0
FAILED test_example.py::test_fail - assert 0
@@ -558,13 +558,23 @@ Modifying truncation limits
.. versionadded: 8.4
Default truncation limits are 8 lines or 640 characters, whichever comes first.
-To set custom truncation limits you can use following ``pytest.ini`` file options:
+To set custom truncation limits you can use the following configuration file options:
-.. code-block:: ini
+.. tab:: toml
- [pytest]
- truncation_limit_lines = 10
- truncation_limit_chars = 90
+ .. code-block:: toml
+
+ [pytest]
+ truncation_limit_lines = 10
+ truncation_limit_chars = 90
+
+.. tab:: ini
+
+ .. code-block:: ini
+
+ [pytest]
+ truncation_limit_lines = 10
+ truncation_limit_chars = 90
That will cause pytest to truncate the assertions to 10 lines or 90 characters, whichever comes first.
@@ -588,10 +598,19 @@ to create an XML file at ``path``.
To set the name of the root test suite xml item, you can configure the ``junit_suite_name`` option in your config file:
-.. code-block:: ini
+.. tab:: toml
+
+ .. code-block:: toml
+
+ [pytest]
+ junit_suite_name = "my_suite"
- [pytest]
- junit_suite_name = my_suite
+.. tab:: ini
+
+ .. code-block:: ini
+
+ [pytest]
+ junit_suite_name = my_suite
.. versionadded:: 4.0
@@ -602,10 +621,19 @@ should report total test execution times, including setup and teardown
It is the default pytest behavior. To report just call durations
instead, configure the ``junit_duration_report`` option like this:
-.. code-block:: ini
+.. tab:: toml
+
+ .. code-block:: toml
+
+ [pytest]
+ junit_duration_report = "call"
+
+.. tab:: ini
+
+ .. code-block:: ini
- [pytest]
- junit_duration_report = call
+ [pytest]
+ junit_duration_report = call
.. _record_property example:
diff --git a/doc/en/how-to/parametrize.rst b/doc/en/how-to/parametrize.rst
index 5a16684eb96..dba2ac0b91e 100644
--- a/doc/en/how-to/parametrize.rst
+++ b/doc/en/how-to/parametrize.rst
@@ -20,6 +20,11 @@ pytest enables test parametrization at several levels:
* `pytest_generate_tests`_ allows one to define custom parametrization
schemes or extensions.
+
+.. note::
+
+ See :ref:`subtests` for an alternative to parametrization.
+
.. _parametrizemark:
.. _`@pytest.mark.parametrize`:
@@ -88,12 +93,21 @@ them in turn:
for the parametrization because it has several downsides.
If however you would like to use unicode strings in parametrization
and see them in the terminal as is (non-escaped), use this option
- in your ``pytest.ini``:
+ in your configuration file:
+
+ .. tab:: toml
+
+ .. code-block:: toml
- .. code-block:: ini
+ [pytest]
+ disable_test_id_escaping_and_forfeit_all_rights_to_community_support = true
- [pytest]
- disable_test_id_escaping_and_forfeit_all_rights_to_community_support = True
+ .. tab:: ini
+
+ .. code-block:: ini
+
+ [pytest]
+ disable_test_id_escaping_and_forfeit_all_rights_to_community_support = true
Keep in mind however that this might cause unwanted side effects and
even bugs depending on the OS used and plugins currently installed,
@@ -194,6 +208,7 @@ To get all combinations of multiple parametrized arguments you can stack
This will run the test with the arguments set to ``x=0/y=2``, ``x=1/y=2``,
``x=0/y=3``, and ``x=1/y=3`` exhausting parameters in the order of the decorators.
+
.. _`pytest_generate_tests`:
Basic ``pytest_generate_tests`` example
@@ -240,6 +255,13 @@ command line option and the parametrization of our test function:
if "stringinput" in metafunc.fixturenames:
metafunc.parametrize("stringinput", metafunc.config.getoption("stringinput"))
+.. note::
+
+ The :hook:`pytest_generate_tests` hook can also be implemented directly in a test
+ module or inside a test class; unlike other hooks, pytest will discover it there
+ as well. Other hooks must live in a :ref:`conftest.py ` or a plugin.
+ See :ref:`writinghooks`.
+
If we now pass two stringinput values, our test will run twice:
.. code-block:: pytest
@@ -281,7 +303,7 @@ list:
$ pytest -q -rs test_strings.py
s [100%]
========================= short test summary info ==========================
- SKIPPED [1] test_strings.py: got empty parameter set ['stringinput'], function test_valid_string at /home/sweet/project/test_strings.py:2
+ SKIPPED [1] test_strings.py: got empty parameter set for (stringinput)
1 skipped in 0.12s
Note that when calling ``metafunc.parametrize`` multiple times with different parameter sets, all parameter names across
diff --git a/doc/en/how-to/plugins.rst b/doc/en/how-to/plugins.rst
index fca8ab54e63..591c44dfa4d 100644
--- a/doc/en/how-to/plugins.rst
+++ b/doc/en/how-to/plugins.rst
@@ -120,12 +120,21 @@ This means that any subsequent try to activate/load the named
plugin will not work.
If you want to unconditionally disable a plugin for a project, you can add
-this option to your ``pytest.ini`` file:
+this option to your configuration file:
-.. code-block:: ini
+.. tab:: toml
- [pytest]
- addopts = -p no:NAME
+ .. code-block:: toml
+
+ [pytest]
+ addopts = ["-p", "no:NAME"]
+
+.. tab:: ini
+
+ .. code-block:: ini
+
+ [pytest]
+ addopts = -p no:NAME
Alternatively to disable it only in certain environments (for example in a
CI server), you can set ``PYTEST_ADDOPTS`` environment variable to
@@ -151,10 +160,22 @@ manually specify each plugin with ``-p`` or :envvar:`PYTEST_PLUGINS`, you can us
pytest --disable-plugin-autoload -p NAME,NAME2
-.. code-block:: ini
+.. tab:: toml
+
+ .. code-block:: toml
+
+ [pytest]
+ addopts = ["--disable-plugin-autoload", "-p", "NAME", "-p", "NAME2"]
+
+.. tab:: ini
+
+ .. code-block:: ini
- [pytest]
- addopts = --disable-plugin-autoload -p NAME,NAME2
+ [pytest]
+ addopts =
+ --disable-plugin-autoload
+ -p NAME
+ -p NAME2
.. versionadded:: 8.4
diff --git a/doc/en/how-to/skipping.rst b/doc/en/how-to/skipping.rst
index 09a19766f99..1887fbd53ef 100644
--- a/doc/en/how-to/skipping.rst
+++ b/doc/en/how-to/skipping.rst
@@ -84,14 +84,14 @@ It is also possible to skip the whole module using
If you wish to skip something conditionally then you can use ``skipif`` instead.
Here is an example of marking a test function to be skipped
-when run on an interpreter earlier than Python3.10:
+when run on an interpreter earlier than Python3.13:
.. code-block:: python
import sys
- @pytest.mark.skipif(sys.version_info < (3, 10), reason="requires python3.10 or higher")
+ @pytest.mark.skipif(sys.version_info < (3, 13), reason="requires python3.13 or higher")
def test_function(): ...
If the condition evaluates to ``True`` during collection, the test function will be skipped,
@@ -331,12 +331,21 @@ You can change this by setting the ``strict`` keyword-only parameter to ``True``
This will make ``XPASS`` ("unexpectedly passing") results from this test to fail the test suite.
You can change the default value of the ``strict`` parameter using the
-``xfail_strict`` ini option:
+``strict_xfail`` ini option:
-.. code-block:: ini
+.. tab:: toml
- [pytest]
- xfail_strict=true
+ .. code-block:: toml
+
+ [pytest]
+ xfail_strict = true
+
+.. tab:: ini
+
+ .. code-block:: ini
+
+ [pytest]
+ strict_xfail = true
Ignoring xfail
diff --git a/doc/en/how-to/subtests.rst b/doc/en/how-to/subtests.rst
new file mode 100644
index 00000000000..c8d9461c983
--- /dev/null
+++ b/doc/en/how-to/subtests.rst
@@ -0,0 +1,139 @@
+.. _subtests:
+
+How to use subtests
+===================
+
+.. versionadded:: 9.0
+
+.. note::
+
+ This feature is experimental. Its behavior, particularly how failures are reported, may evolve in future releases. However, the core functionality and usage are considered stable.
+
+pytest allows for grouping assertions within a normal test, known as *subtests*.
+
+Subtests are an alternative to parametrization, particularly useful when the exact parametrization values are not known at collection time.
+
+
+.. code-block:: python
+
+ # content of test_subtest.py
+
+
+ def test(subtests):
+ for i in range(5):
+ with subtests.test(msg="custom message", i=i):
+ assert i % 2 == 0
+
+Each assertion failure or error is caught by the context manager and reported individually:
+
+.. code-block:: pytest
+
+ $ 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:
+
+* Subtest failures are reported as ``SUBFAILED``.
+* Subtests are reported first and the "top-level" test is reported at the end on its own.
+
+Note that it is possible to use ``subtests`` multiple times in the same test, or even mix and match with normal assertions
+outside the ``subtests.test`` block:
+
+.. code-block:: python
+
+ def test(subtests):
+ for i in range(5):
+ with subtests.test("stage 1", i=i):
+ assert i % 2 == 0
+
+ assert func() == 10
+
+ for i in range(10, 20):
+ with subtests.test("stage 2", i=i):
+ assert i % 2 == 0
+
+.. note::
+
+ See :ref:`parametrize` for an alternative to subtests.
+
+
+Verbosity
+---------
+
+By default, only **subtest failures** are shown. Higher verbosity levels (``-v``) will also show progress output for **passed** subtests.
+
+It is possible to control the verbosity of subtests by setting :confval:`verbosity_subtests`.
+
+
+Typing
+------
+
+:class:`pytest.Subtests` is exported so it can be used in type annotations:
+
+.. code-block:: python
+
+ def test(subtests: pytest.Subtests) -> None: ...
+
+.. _parametrize_vs_subtests:
+
+Parametrization vs Subtests
+---------------------------
+
+While :ref:`traditional pytest parametrization ` and ``subtests`` are similar, they have important differences and use cases.
+
+
+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``.
+* Ideal for decision table testing.
+
+Subtests
+~~~~~~~~
+
+* Happen during test execution.
+* Are not known at collection time.
+* Can be generated dynamically.
+* Cannot be referenced individually from the command line.
+* Plugins that handle test execution cannot target individual subtests.
+* An assertion failure inside a subtest does not interrupt the test, letting users see all failures in the same report.
+
+
+.. note::
+
+ 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.
diff --git a/doc/en/how-to/unittest.rst b/doc/en/how-to/unittest.rst
index 62e32b6d28f..a8c56c266bd 100644
--- a/doc/en/how-to/unittest.rst
+++ b/doc/en/how-to/unittest.rst
@@ -22,17 +22,14 @@ their ``test`` methods in ``test_*.py`` or ``*_test.py`` files.
Almost all ``unittest`` features are supported:
-* ``@unittest.skip`` style decorators;
-* ``setUp/tearDown``;
-* ``setUpClass/tearDownClass``;
-* ``setUpModule/tearDownModule``;
+* :func:`unittest.skip`/:func:`unittest.skipIf` style decorators
+* :meth:`unittest.TestCase.setUp`/:meth:`unittest.TestCase.tearDown`
+* :meth:`unittest.TestCase.setUpClass`/:meth:`unittest.TestCase.tearDownClass`
+* :func:`unittest.setUpModule`/:func:`unittest.tearDownModule`
+* :meth:`unittest.TestCase.subTest` (since version ``9.0``)
-.. _`pytest-subtests`: https://github.com/pytest-dev/pytest-subtests
.. _`load_tests protocol`: https://docs.python.org/3/library/unittest.html#load-tests-protocol
-Additionally, :ref:`subtests ` are supported by the
-`pytest-subtests`_ plugin.
-
Up to this point pytest does not have support for the following features:
* `load_tests protocol`_;
@@ -154,6 +151,7 @@ the ``self.db`` values in the traceback:
def test_method1(self):
assert hasattr(self, "db")
> assert 0, self.db # fail for demo purposes
+ ^^^^^^^^^^^^^^^^^
E AssertionError: .DummyDB object at 0xdeadbeef0001>
E assert 0
@@ -164,6 +162,7 @@ the ``self.db`` values in the traceback:
def test_method2(self):
> assert 0, self.db # fail for demo purposes
+ ^^^^^^^^^^^^^^^^^
E AssertionError: .DummyDB object at 0xdeadbeef0001>
E assert 0
diff --git a/doc/en/how-to/writing_hook_functions.rst b/doc/en/how-to/writing_hook_functions.rst
index f4c00d04fda..cd18301ce84 100644
--- a/doc/en/how-to/writing_hook_functions.rst
+++ b/doc/en/how-to/writing_hook_functions.rst
@@ -235,6 +235,12 @@ Example:
"""
print(config.hook)
+.. note::
+
+ Unlike other hooks, the :hook:`pytest_generate_tests` hook is also discovered when
+ defined inside a test module or test class. Other hooks must live in
+ :ref:`conftest.py plugins ` or external plugins.
+ See :ref:`parametrize-basics` and the :ref:`hook-reference`.
.. _`addoptionhooks`:
diff --git a/doc/en/how-to/writing_plugins.rst b/doc/en/how-to/writing_plugins.rst
index 1bba9644649..6382edc4797 100644
--- a/doc/en/how-to/writing_plugins.rst
+++ b/doc/en/how-to/writing_plugins.rst
@@ -420,13 +420,13 @@ before running pytest on it. This way we can abstract the tested logic to separa
which is especially useful for longer tests and/or longer ``conftest.py`` files.
Note that for ``pytester.copy_example`` to work we need to set `pytester_example_dir`
-in our ``pytest.ini`` to tell pytest where to look for example files.
+in our configuration file to tell pytest where to look for example files.
-.. code-block:: ini
+.. code-block:: toml
- # content of pytest.ini
- [pytest]
- pytester_example_dir = .
+ # content of pytest.toml
+ [pytest]
+ pytester_example_dir = "."
.. code-block:: python
@@ -448,7 +448,7 @@ in our ``pytest.ini`` to tell pytest where to look for example files.
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.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 fb5d0482c0d..2d9e3bed42c 100644
--- a/doc/en/index.rst
+++ b/doc/en/index.rst
@@ -2,7 +2,6 @@
.. sidebar:: **Next Open Trainings and Events**
- - `pytest - simple, rapid and fun testing with Python `_, at `EuroPython 2025 `_, **July 14th** (3h), Prague, Czech Republic
- `Professional Testing with Python `_, via `Python Academy `_ (3 day in-depth training), **March 3th -- 5th 2026**, Leipzig (DE) / Remote
Also see :doc:`previous talks and blogposts `
@@ -46,8 +45,6 @@ The ``pytest`` framework makes it easy to write small, readable tests, and can
scale to support complex functional testing for applications and libraries.
-``pytest`` requires: Python 3.8+ or PyPy3.
-
**PyPI package name**: :pypi:`pytest`
A quick example
@@ -104,7 +101,7 @@ Features
- Can run :ref:`unittest ` (including trial) test suites out of the box
-- Python 3.8+ or PyPy 3
+- Python 3.10+ or PyPy 3
- Rich plugin architecture, with over 1300+ :ref:`external plugins ` and thriving community
diff --git a/doc/en/reference/customize.rst b/doc/en/reference/customize.rst
index 373223ec913..500e5519bdd 100644
--- a/doc/en/reference/customize.rst
+++ b/doc/en/reference/customize.rst
@@ -4,8 +4,7 @@ Configuration
Command line options and configuration file settings
-----------------------------------------------------------------
-You can get help on command line options and values in INI-style
-configurations files by using the general help option:
+You can get help on command line and configuration options by using the general help option:
.. code-block:: bash
@@ -24,51 +23,85 @@ by convention resides in the root directory of your repository.
A quick example of the configuration files supported by pytest:
+pytest.toml
+~~~~~~~~~~~
+
+.. versionadded:: 9.0
+
+``pytest.toml`` files take precedence over other files, even when empty.
+
+Alternatively, the hidden version ``.pytest.toml`` can be used.
+
+.. tab:: toml
+
+ .. code-block:: toml
+
+ # pytest.toml or .pytest.toml
+ [pytest]
+ minversion = "9.0"
+ addopts = ["-ra", "-q"]
+ testpaths = [
+ "tests",
+ "integration",
+ ]
+
pytest.ini
~~~~~~~~~~
-``pytest.ini`` files take precedence over other files, even when empty.
+``pytest.ini`` files take precedence over other files (except ``pytest.toml`` and ``.pytest.toml``), even when empty.
Alternatively, the hidden version ``.pytest.ini`` can be used.
-.. code-block:: ini
+.. tab:: ini
- # pytest.ini or .pytest.ini
- [pytest]
- minversion = 6.0
- addopts = -ra -q
- testpaths =
- tests
- integration
+ .. code-block:: ini
+
+ # pytest.ini or .pytest.ini
+ [pytest]
+ minversion = 6.0
+ addopts = -ra -q
+ testpaths =
+ tests
+ integration
pyproject.toml
~~~~~~~~~~~~~~
.. versionadded:: 6.0
+.. versionchanged:: 9.0
+
+``pyproject.toml`` files are supported for configuration.
+
+.. tab:: toml
-``pyproject.toml`` are considered for configuration when they contain a ``tool.pytest.ini_options`` table.
+ Use ``[tool.pytest]`` to leverage native TOML types (supported since pytest 9.0):
-.. code-block:: toml
+ .. code-block:: toml
- # pyproject.toml
- [tool.pytest.ini_options]
- minversion = "6.0"
- addopts = "-ra -q"
- testpaths = [
- "tests",
- "integration",
- ]
+ # pyproject.toml
+ [tool.pytest]
+ minversion = "9.0"
+ addopts = ["-ra", "-q"]
+ testpaths = [
+ "tests",
+ "integration",
+ ]
-.. note::
+.. tab:: ini
- One might wonder why ``[tool.pytest.ini_options]`` instead of ``[tool.pytest]`` as is the
- case with other tools.
+ Use ``[tool.pytest.ini_options]`` for INI-style configuration (supported since pytest 6.0):
- The reason is that the pytest team intends to fully utilize the rich TOML data format
- for configuration in the future, reserving the ``[tool.pytest]`` table for that.
- The ``ini_options`` table is being used, for now, as a bridge between the existing
- ``.ini`` configuration system and the future configuration format.
+ .. code-block:: toml
+
+ # pyproject.toml
+ [tool.pytest.ini_options]
+ minversion = "6.0"
+ addopts = "-ra -q"
+ testpaths = [
+ "tests",
+ "integration",
+ ]
tox.ini
~~~~~~~
@@ -76,15 +109,17 @@ tox.ini
``tox.ini`` files are the configuration files of the `tox