Compare commits

..

1 Commits

Author SHA1 Message Date
pytest bot bf3bcb2bf1 [automated] Update plugin list 2023-06-11 00:27:03 +00:00
200 changed files with 2891 additions and 5912 deletions
+1 -1
View File
@@ -22,7 +22,7 @@ jobs:
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
with:
fetch-depth: 0
persist-credentials: true
+18 -48
View File
@@ -1,44 +1,35 @@
name: deploy
on:
workflow_dispatch:
inputs:
version:
description: 'Release version'
required: true
default: '1.2.3'
push:
tags:
# These tags are protected, see:
# https://github.com/pytest-dev/pytest/settings/tag_protection
- "[0-9]+.[0-9]+.[0-9]+"
- "[0-9]+.[0-9]+.[0-9]+rc[0-9]+"
# Set permissions at the job level.
permissions: {}
jobs:
package:
deploy:
if: github.repository == 'pytest-dev/pytest'
runs-on: ubuntu-latest
env:
SETUPTOOLS_SCM_PRETEND_VERSION: ${{ github.event.inputs.version }}
timeout-minutes: 10
timeout-minutes: 30
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
with:
fetch-depth: 0
persist-credentials: false
- name: Build and Check Package
uses: hynek/build-and-inspect-python-package@v1.5.3
deploy:
if: github.repository == 'pytest-dev/pytest'
needs: [package]
runs-on: ubuntu-latest
environment: deploy
timeout-minutes: 30
permissions:
id-token: write
contents: write
steps:
- uses: actions/checkout@v4
uses: hynek/build-and-inspect-python-package@v1.5
- name: Download Package
uses: actions/download-artifact@v3
@@ -47,35 +38,14 @@ jobs:
path: dist
- name: Publish package to PyPI
uses: pypa/gh-action-pypi-publish@v1.8.10
- name: Push tag
run: |
git config user.name "pytest bot"
git config user.email "pytestbot@gmail.com"
git tag --annotate --message=v${{ github.event.inputs.version }} ${{ github.event.inputs.version }} ${{ github.sha }}
git push origin ${{ github.event.inputs.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]
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: write
steps:
- uses: actions/checkout@v4
uses: pypa/gh-action-pypi-publish@release/v1
with:
fetch-depth: 0
persist-credentials: false
password: ${{ secrets.pypi_token }}
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.11"
python-version: "3.7"
- name: Install tox
run: |
+1 -1
View File
@@ -27,7 +27,7 @@ jobs:
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
with:
fetch-depth: 0
+48 -46
View File
@@ -27,19 +27,7 @@ concurrency:
permissions: {}
jobs:
package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Build and Check Package
uses: hynek/build-and-inspect-python-package@v1.5.3
build:
needs: [package]
runs-on: ${{ matrix.os }}
timeout-minutes: 45
permissions:
@@ -49,41 +37,48 @@ jobs:
fail-fast: false
matrix:
name: [
"windows-py37",
"windows-py37-pluggy",
"windows-py38",
"windows-py38-pluggy",
"windows-py39",
"windows-py310",
"windows-py311",
"windows-py312",
"ubuntu-py37",
"ubuntu-py37-pluggy",
"ubuntu-py37-freeze",
"ubuntu-py38",
"ubuntu-py38-pluggy",
"ubuntu-py38-freeze",
"ubuntu-py39",
"ubuntu-py310",
"ubuntu-py311",
"ubuntu-py312",
"ubuntu-pypy3",
"macos-py38",
"macos-py37",
"macos-py39",
"macos-py310",
"macos-py312",
"docs",
"doctesting",
"plugins",
]
include:
- name: "windows-py37"
python: "3.7"
os: windows-latest
tox_env: "py37-numpy"
- name: "windows-py37-pluggy"
python: "3.7"
os: windows-latest
tox_env: "py37-pluggymain-pylib-xdist"
- name: "windows-py38"
python: "3.8"
os: windows-latest
tox_env: "py38-unittestextras"
use_coverage: true
- name: "windows-py38-pluggy"
python: "3.8"
os: windows-latest
tox_env: "py38-pluggymain-pylib-xdist"
- name: "windows-py39"
python: "3.9"
os: windows-latest
@@ -101,19 +96,23 @@ jobs:
os: windows-latest
tox_env: "py312"
- name: "ubuntu-py37"
python: "3.7"
os: ubuntu-latest
tox_env: "py37-lsof-numpy-pexpect"
use_coverage: true
- name: "ubuntu-py37-pluggy"
python: "3.7"
os: ubuntu-latest
tox_env: "py37-pluggymain-pylib-xdist"
- name: "ubuntu-py37-freeze"
python: "3.7"
os: ubuntu-latest
tox_env: "py37-freeze"
- name: "ubuntu-py38"
python: "3.8"
os: ubuntu-latest
tox_env: "py38-lsof-numpy-pexpect"
use_coverage: true
- name: "ubuntu-py38-pluggy"
python: "3.8"
os: ubuntu-latest
tox_env: "py38-pluggymain-pylib-xdist"
- name: "ubuntu-py38-freeze"
python: "3.8"
os: ubuntu-latest
tox_env: "py38-freeze"
tox_env: "py38-xdist"
- name: "ubuntu-py39"
python: "3.9"
os: ubuntu-latest
@@ -133,14 +132,14 @@ jobs:
tox_env: "py312"
use_coverage: true
- name: "ubuntu-pypy3"
python: "pypy-3.8"
python: "pypy-3.7"
os: ubuntu-latest
tox_env: "pypy3-xdist"
- name: "macos-py38"
python: "3.8"
- name: "macos-py37"
python: "3.7"
os: macos-latest
tox_env: "py38-xdist"
tox_env: "py37-xdist"
- name: "macos-py39"
python: "3.9"
os: macos-latest
@@ -160,24 +159,22 @@ jobs:
os: ubuntu-latest
tox_env: "plugins"
- name: "docs"
python: "3.7"
os: ubuntu-latest
tox_env: "docs"
- name: "doctesting"
python: "3.8"
python: "3.7"
os: ubuntu-latest
tox_env: "doctesting"
use_coverage: true
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
with:
fetch-depth: 0
persist-credentials: false
- name: Download Package
uses: actions/download-artifact@v3
with:
name: Packages
path: dist
- name: Set up Python ${{ matrix.python }}
uses: actions/setup-python@v4
with:
@@ -191,13 +188,11 @@ jobs:
- name: Test without coverage
if: "! matrix.use_coverage"
shell: bash
run: tox run -e ${{ matrix.tox_env }} --installpkg `find dist/*.tar.gz`
run: "tox -e ${{ matrix.tox_env }}"
- name: Test with coverage
if: "matrix.use_coverage"
shell: bash
run: tox run -e ${{ matrix.tox_env }}-coverage --installpkg `find dist/*.tar.gz`
run: "tox -e ${{ matrix.tox_env }}-coverage"
- name: Generate coverage report
if: "matrix.use_coverage"
@@ -211,3 +206,10 @@ jobs:
fail_ci_if_error: true
files: ./coverage.xml
verbose: true
check-package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build and Check Package
uses: hynek/build-and-inspect-python-package@v1.5
+4 -12
View File
@@ -20,33 +20,25 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: "3.11"
cache: pip
- name: requests-cache
uses: actions/cache@v3
with:
path: ~/.cache/pytest-plugin-list/
key: plugins-http-cache-${{ github.run_id }} # Can use time based key as well
restore-keys: plugins-http-cache-
python-version: 3.8
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install packaging requests tabulate[widechars] tqdm requests-cache platformdirs
pip install packaging requests tabulate[widechars] tqdm
- name: Update Plugin List
run: python scripts/update-plugin-list.py
- name: Create Pull Request
uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38
uses: peter-evans/create-pull-request@5b4a9f6a9e2af26e5f02351490b90d01eb8ec1e5
with:
commit-message: '[automated] Update plugin list'
author: 'pytest bot <pytestbot@users.noreply.github.com>'
+12 -12
View File
@@ -1,16 +1,16 @@
repos:
- repo: https://github.com/psf/black
rev: 23.10.1
rev: 23.3.0
hooks:
- id: black
args: [--safe, --quiet]
- repo: https://github.com/asottile/blacken-docs
rev: 1.16.0
rev: 1.13.0
hooks:
- id: blacken-docs
additional_dependencies: [black==23.7.0]
additional_dependencies: [black==23.1.0]
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
@@ -21,7 +21,7 @@ repos:
exclude: _pytest/(debugging|hookspec).py
language_version: python3
- repo: https://github.com/PyCQA/autoflake
rev: v2.2.1
rev: v2.1.1
hooks:
- id: autoflake
name: autoflake
@@ -29,7 +29,7 @@ repos:
language: python
files: \.py$
- repo: https://github.com/PyCQA/flake8
rev: 6.1.0
rev: 6.0.0
hooks:
- id: flake8
language_version: python3
@@ -37,17 +37,17 @@ repos:
- flake8-typing-imports==1.12.0
- flake8-docstrings==1.5.0
- repo: https://github.com/asottile/reorder-python-imports
rev: v3.12.0
rev: v3.9.0
hooks:
- id: reorder-python-imports
args: ['--application-directories=.:src', --py38-plus]
args: ['--application-directories=.:src', --py37-plus]
- repo: https://github.com/asottile/pyupgrade
rev: v3.15.0
rev: v3.4.0
hooks:
- id: pyupgrade
args: [--py38-plus]
args: [--py37-plus]
- repo: https://github.com/asottile/setup-cfg-fmt
rev: v2.5.0
rev: v2.3.0
hooks:
- id: setup-cfg-fmt
args: ["--max-py-version=3.12", "--include-version-classifiers"]
@@ -56,7 +56,7 @@ repos:
hooks:
- id: python-use-type-annotations
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.6.1
rev: v1.3.0
hooks:
- id: mypy
files: ^(src/|testing/)
-4
View File
@@ -9,10 +9,6 @@ python:
path: .
- requirements: doc/en/requirements.txt
sphinx:
configuration: doc/en/conf.py
fail_on_warning: true
build:
os: ubuntu-20.04
tools:
-21
View File
@@ -11,7 +11,6 @@ Adam Johnson
Adam Stewart
Adam Uhlir
Ahn Ki-Wook
Akhilesh Ramakrishnan
Akiomi Kamakura
Alan Velasco
Alessio Izzo
@@ -56,9 +55,7 @@ Barney Gale
Ben Gartner
Ben Webb
Benjamin Peterson
Benjamin Schubert
Bernard Pratz
Bo Wu
Bob Ippolito
Brian Dorsey
Brian Larsen
@@ -132,7 +129,6 @@ Eric Hunsberger
Eric Liu
Eric Siegerman
Erik Aronesty
Erik Hasse
Erik M. Bray
Evan Kepner
Evgeny Seliverstov
@@ -144,7 +140,6 @@ Feng Ma
Florian Bruhin
Florian Dahlitz
Floris Bruynooghe
Fraser Stark
Gabriel Landau
Gabriel Reis
Garvit Shubham
@@ -171,8 +166,6 @@ Ian Bicking
Ian Lesperance
Ilya Konstantinov
Ionuț Turturică
Isaac Virshup
Israel Fruchter
Itxaso Aizpurua
Iwan Briquemont
Jaap Broekhuizen
@@ -236,7 +229,6 @@ Maho
Maik Figura
Mandeep Bhutani
Manuel Krebber
Marc Mueller
Marc Schlaich
Marcelo Duarte Trevisani
Marcin Bachry
@@ -267,10 +259,8 @@ Michal Wajszczuk
Michał Zięba
Mickey Pashov
Mihai Capotă
Mihail Milushev
Mike Hoyle (hoylemd)
Mike Lundy
Milan Lesnek
Miro Hrončok
Nathaniel Compton
Nathaniel Waisbrot
@@ -320,7 +310,6 @@ Raphael Pierzina
Rafal Semik
Raquel Alegre
Ravi Chandra
Reagan Lee
Robert Holt
Roberto Aldera
Roberto Polli
@@ -331,9 +320,7 @@ Ronny Pfannschmidt
Ross Lawley
Ruaridh Williamson
Russel Winder
Ryan Puddephatt
Ryan Wooden
Sadra Barikbin
Saiprasad Kale
Samuel Colvin
Samuel Dion-Girardeau
@@ -342,20 +329,16 @@ Samuele Pedroni
Sanket Duthade
Sankt Petersbug
Saravanan Padmanaban
Sean Malloy
Segev Finer
Serhii Mozghovyi
Seth Junot
Shantanu Jain
Sharad Nair
Shubham Adep
Simon Blanchard
Simon Gomizelj
Simon Holesch
Simon Kerr
Skylar Downes
Srinivas Reddy Thatiparthy
Stefaan Lippens
Stefan Farmbauer
Stefan Scherfke
Stefan Zimmermann
@@ -369,7 +352,6 @@ Tadek Teleżyński
Takafumi Arakaki
Taneli Hukkinen
Tanvi Mehta
Tanya Agarwal
Tarcisio Fischer
Tareq Alayan
Tatiana Ovary
@@ -388,16 +370,13 @@ Tomer Keren
Tony Narlock
Tor Colvin
Trevor Bekolay
Tushar Sadhwani
Tyler Goodlet
Tyler Smart
Tzu-ping Chung
Vasily Kuznetsov
Victor Maryama
Victor Rodriguez
Victor Uriarte
Vidar T. Fauske
Vijay Arora
Virgil Dupras
Vitaly Lashmanov
Vivaan Verma
+9 -11
View File
@@ -50,7 +50,7 @@ Fix bugs
--------
Look through the `GitHub issues for bugs <https://github.com/pytest-dev/pytest/labels/type:%20bug>`_.
See also the `"good first issue" issues <https://github.com/pytest-dev/pytest/labels/good%20first%20issue>`_
See also the `"status: easy" issues <https://github.com/pytest-dev/pytest/labels/status%3A%20easy>`_
that are friendly to new contributors.
:ref:`Talk <contact>` to developers to find out how you can fix specific bugs. To indicate that you are going
@@ -197,12 +197,11 @@ Short version
~~~~~~~~~~~~~
#. Fork the repository.
#. Fetch tags from upstream if necessary (if you cloned only main `git fetch --tags https://github.com/pytest-dev/pytest`).
#. Enable and install `pre-commit <https://pre-commit.com>`_ to ensure style-guides and code checks are followed.
#. Follow **PEP-8** for naming and `black <https://github.com/psf/black>`_ for formatting.
#. Tests are run using ``tox``::
tox -e linting,py39
tox -e linting,py37
The test environments above are usually enough to cover most cases locally.
@@ -237,7 +236,6 @@ Here is a simple overview, with pytest-specific bits:
$ git clone git@github.com:YOUR_GITHUB_USERNAME/pytest.git
$ cd pytest
$ git fetch --tags https://github.com/pytest-dev/pytest
# now, create your own branch off "main":
$ git checkout -b your-bugfix-branch-name main
@@ -274,24 +272,24 @@ Here is a simple overview, with pytest-specific bits:
#. Run all the tests
You need to have Python 3.8 or later available in your system. Now
You need to have Python 3.7 available in your system. Now
running tests is as simple as issuing this command::
$ tox -e linting,py39
$ tox -e linting,py37
This command will run tests via the "tox" tool against Python 3.9
This command will run tests via the "tox" tool against Python 3.7
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.7 and pass options to pytest
(e.g. enter pdb on failure) to pytest you can do::
$ tox -e py39 -- --pdb
$ tox -e py37 -- --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.7::
$ tox -e py39 -- testing/test_config.py
$ tox -e py37 -- testing/test_config.py
When committing, ``pre-commit`` will re-format the files if necessary.
+1 -1
View File
@@ -100,7 +100,7 @@ Features
- Can run `unittest <https://docs.pytest.org/en/stable/how-to/unittest.html>`_ (or trial),
`nose <https://docs.pytest.org/en/stable/how-to/nose.html>`_ test suites out of the box
- Python 3.8+ or PyPy3
- Python 3.7+ or PyPy3
- Rich plugin architecture, with over 850+ `external plugins <https://docs.pytest.org/en/latest/reference/plugin_list.html>`_ and thriving community
+7 -5
View File
@@ -133,12 +133,14 @@ 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
in https://github.com/pytest-dev/pytest/actions/workflows/deploy.yml, using the ``release-MAJOR.MINOR.PATCH`` branch
as source.
#. After all tests pass and the PR has been approved, tag the release commit
in the ``release-MAJOR.MINOR.PATCH`` branch and push it. This will publish to PyPI::
This job will require approval from ``pytest-dev/core``, after which it will publish to PyPI
and tag the repository.
git fetch upstream
git tag MAJOR.MINOR.PATCH upstream/release-MAJOR.MINOR.PATCH
git push upstream MAJOR.MINOR.PATCH
Wait for the deploy to complete, then make sure it is `available on PyPI <https://pypi.org/project/pytest>`_.
#. Merge the PR. **Make sure it's not squash-merged**, so that the tagged commit ends up in the main branch.
-2
View File
@@ -1,2 +0,0 @@
Added :func:`ExceptionInfo.group_contains() <pytest.ExceptionInfo.group_contains>`, an assertion
helper that tests if an `ExceptionGroup` contains a matching exception.
-1
View File
@@ -1 +0,0 @@
Test functions returning a value other than None will now issue a :class:`pytest.PytestWarning` instead of :class:`pytest.PytestRemovedIn8Warning`, meaning this will stay a warning instead of becoming an error in the future.
-2
View File
@@ -1,2 +0,0 @@
Added more comprehensive set assertion rewrites for comparisons other than equality ``==``, with
the following operations now providing better failure messages: ``!=``, ``<=``, ``>=``, ``<``, and ``>``.
-2
View File
@@ -1,2 +0,0 @@
:meth:`pytest.WarningsRecorder.pop` will return the most-closely-matched warning in the list,
rather than the first warning which is an instance of the requested type.
+1
View File
@@ -0,0 +1 @@
Terminal Reporting: Fixed bug when running in ``--tb=line`` mode where ``pytest.fail(pytrace=False)`` tests report ``None``.
+1
View File
@@ -0,0 +1 @@
Update test log report annotation to named tuple and fixed inconsistency in docs for :hook:`pytest_report_teststatus` hook.
+2
View File
@@ -0,0 +1,2 @@
Added :func:`ExceptionInfo.from_exception() <pytest.ExceptionInfo.from_exception>`, a simpler way to create an :class:`~pytest.ExceptionInfo` from an exception.
This can replace :func:`ExceptionInfo.from_exc_info() <pytest.ExceptionInfo.from_exc_info()>` for most uses.
+5
View File
@@ -0,0 +1,5 @@
When an exception traceback to be displayed is completely filtered out (by mechanisms such as ``__tracebackhide__``, internal frames, and similar), now only the exception string and the following message are shown:
"All traceback entries are hidden. Pass `--full-trace` to see hidden and internal frames.".
Previously, the last frame of the traceback was shown, even though it was hidden.
+3
View File
@@ -0,0 +1,3 @@
Improved verbose output (``-vv``) of ``skip`` and ``xfail`` reasons by performing text wrapping while leaving a clear margin for progress output.
Added :func:`TerminalReporter.wrap_write() <pytest.TerminalReporter.wrap_write>` as a helper for that.
+1
View File
@@ -0,0 +1 @@
Added handling of ``%f`` directive to print microseconds in log format options, such as ``log-date-format``.
+1
View File
@@ -0,0 +1 @@
Added underlying exception to cache provider path creation and write warning messages.
-1
View File
@@ -1 +0,0 @@
Added a warning about modifying the root logger during tests when using ``caplog``.
+1
View File
@@ -0,0 +1 @@
Added warning when :confval:`testpaths` is set, but paths are not found by glob. In this case, pytest will fall back to searching from the current directory.
+1
View File
@@ -0,0 +1 @@
Enhanced the CLI flag for ``-c`` to now include ``--config-file`` to make it clear that this flag applies to the usage of a custom config file.
+3
View File
@@ -0,0 +1,3 @@
When `--confcutdir` is not specified, and there is no config file present, the conftest cutoff directory (`--confcutdir`) is now set to the :ref:`rootdir`.
Previously in such cases, `conftest.py` files would be probed all the way to the root directory of the filesystem.
If you are badly affected by this change, consider adding an empty config file to your desired cutoff directory, or explicitly set `--confcutdir`.
-3
View File
@@ -1,3 +0,0 @@
Use pytestconfig instead of request.config in cache example
to be consistent with the API documentation.
+1
View File
@@ -0,0 +1 @@
Fixed the ``--last-failed`` whole-file skipping functionality ("skipped N files") for :ref:`non-python test files <non-python tests>`.
+7
View File
@@ -0,0 +1,7 @@
The :confval:`norecursedir` check is now performed in a :hook:`pytest_ignore_collect` implementation, so plugins can affect it.
If after updating to this version you see that your `norecursedir` setting is not being respected,
it means that a conftest or a plugin you use has a bad `pytest_ignore_collect` implementation.
Most likely, your hook returns `False` for paths it does not want to ignore,
which ends the processing and doesn't allow other plugins, including pytest itself, to ignore the path.
The fix is to return `None` instead of `False` for paths your hook doesn't want to ignore.
-1
View File
@@ -1 +0,0 @@
Updated documentation and tests to refer to hyphonated options: replaced ``--junitxml`` with ``--junit-xml`` and ``--collectonly`` with ``--collect-only``.
-6
View File
@@ -1,6 +0,0 @@
``pluggy>=1.2.0`` is now required.
pytest now uses "new-style" hook wrappers internally, available since pluggy 1.2.0.
See `pluggy's 1.2.0 changelog <https://pluggy.readthedocs.io/en/latest/changelog.html#pluggy-1-2-0-2023-06-21>`_ and the :ref:`updated docs <hookwrapper>` for details.
Plugins which want to use new-style wrappers can do so if they require this version of pytest or later.
-11
View File
@@ -1,11 +0,0 @@
:class:`pytest.Package` is no longer a :class:`pytest.Module` or :class:`pytest.File`.
The ``Package`` collector node designates a Python package, that is, a directory with an `__init__.py` file.
Previously ``Package`` was a subtype of ``pytest.Module`` (which represents a single Python module),
the module being the `__init__.py` file.
This has been deemed a design mistake (see :issue:`11137` and :issue:`7777` for details).
The ``path`` property of ``Package`` nodes now points to the package directory instead of the ``__init__.py`` file.
Note that a ``Module`` node for ``__init__.py`` (which is not a ``Package``) may still exist,
if it is picked up during collection (e.g. if you configured :confval:`python_files` to include ``__init__.py`` files).
-1
View File
@@ -1 +0,0 @@
- Prevent constants at the top of file from being detected as docstrings.
-1
View File
@@ -1 +0,0 @@
Dropped support for Python 3.7, which `reached end-of-life on 2023-06-27 <https://devguide.python.org/versions/>`__.
-2
View File
@@ -1,2 +0,0 @@
The (internal) ``FixtureDef.cached_result`` type has changed.
Now the third item ``cached_result[2]``, when set, is an exception instance instead of an exception triplet.
-1
View File
@@ -1 +0,0 @@
If a test is skipped from inside an :ref:`xunit setup fixture <classic xunit>`, the test summary now shows the test location instead of the fixture location.
-5
View File
@@ -1,5 +0,0 @@
(This entry is meant to assist plugins which access private pytest internals to instantiate ``FixtureRequest`` objects.)
:class:`~pytest.FixtureRequest` is now an abstract class which can't be instantiated directly.
A new concrete ``TopRequest`` subclass of ``FixtureRequest`` has been added for the ``request`` fixture in test functions,
as counterpart to the existing ``SubRequest`` subclass for the ``request`` fixture in fixture functions.
-1
View File
@@ -1 +0,0 @@
Allow :func:`pytest.raises` ``match`` argument to match against `PEP-678 <https://peps.python.org/pep-0678/>` ``__notes__``.
-1
View File
@@ -1 +0,0 @@
Fixed crash on `parametrize(..., scope="package")` without a package present.
-2
View File
@@ -1,2 +0,0 @@
Fixed a bug that when there are multiple fixtures for an indirect parameter,
the scope of the highest-scope fixture is picked for the parameter set, instead of that of the one with the narrowest scope.
-2
View File
@@ -1,2 +0,0 @@
Logging to a file using the ``--log-file`` option will use ``--log-level``, ``--log-format`` and ``--log-date-format`` as fallback
if ``--log-file-level``, ``--log-file-format`` and ``--log-file-date-format`` are not provided respectively.
-3
View File
@@ -1,3 +0,0 @@
The :fixture:`pytester` fixture now uses the :fixture:`monkeypatch` fixture to manage the current working directory.
If you use ``pytester`` in combination with :func:`monkeypatch.undo() <pytest.MonkeyPatch.undo>`, the CWD might get restored.
Use :func:`monkeypatch.context() <pytest.MonkeyPatch.context>` instead.
-2
View File
@@ -1,2 +0,0 @@
Corrected the spelling of ``Config.ArgsSource.INVOCATION_DIR``.
The previous spelling ``INCOVATION_DIR`` remains as an alias.
-1
View File
@@ -1 +0,0 @@
pluggy>=1.3.0 is now required. This adds typing to :class:`~pytest.PytestPluginManager`.
-1
View File
@@ -1 +0,0 @@
:func:`pytest.deprecated_call` now also considers warnings of type :class:`FutureWarning`.
-4
View File
@@ -1,4 +0,0 @@
Parametrized tests now *really do* ensure that the ids given to each input are unique - for
example, ``a, a, a0`` now results in ``a1, a2, a0`` instead of the previous (buggy) ``a0, a1, a0``.
This necessarily means changing nodeids where these were previously colliding, and for
readability adds an underscore when non-unique ids end in a number.
-1
View File
@@ -1 +0,0 @@
Improved very verbose diff output to color it as a diff instead of only red.
-1
View File
@@ -1 +0,0 @@
Fixed crash when using an empty string for the same parametrized value more than once.
+1
View File
@@ -0,0 +1 @@
Fixed traceback entries hidden with ``__tracebackhide__ = True`` still being shown for chained exceptions (parts after "... the above exception ..." message).
-3
View File
@@ -1,3 +0,0 @@
Applying a mark to a fixture function now issues a warning: marks in fixtures never had any effect, but it is a common user error to apply a mark to a fixture (for example ``usefixtures``) and expect it to work.
This will become an error in the future.
-22
View File
@@ -1,22 +0,0 @@
**PytestRemovedIn8Warning deprecation warnings are now errors by default.**
Following our plan to remove deprecated features with as little disruption as
possible, all warnings of type ``PytestRemovedIn8Warning`` now generate errors
instead of warning messages by default.
**The affected features will be effectively removed in pytest 8.1**, so please consult the
:ref:`deprecations` section in the docs for directions on how to update existing code.
In the pytest ``8.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.PytestRemovedIn8Warning
But this will stop working when pytest ``8.1`` is released.
**If you have concerns** about the removal of a specific feature, please add a
comment to :issue:`7363`.
-1
View File
@@ -1 +0,0 @@
:class:`~pytest.FixtureDef` is now exported as ``pytest.FixtureDef`` for typing purposes.
-1
View File
@@ -1 +0,0 @@
Removes unhelpful error message from assertion rewrite mechanism when exceptions raised in __iter__ methods, and instead treats them as un-iterable.
+3
View File
@@ -0,0 +1,3 @@
:func:`_pytest.logging.LogCaptureFixture.set_level` and :func:`_pytest.logging.LogCaptureFixture.at_level`
will temporarily enable the requested ``level`` if ``level`` was disabled globally via
``logging.disable(LEVEL)``.
-5
View File
@@ -1,5 +0,0 @@
Running `pytest pkg/__init__.py` now collects the `pkg/__init__.py` file (module) only.
Previously, it collected the entire `pkg` package, including other test files in the directory, but excluding tests in the `__init__.py` file itself
(unless :confval:`python_files` was changed to allow `__init__.py` file).
To collect the entire package, specify just the directory: `pytest pkg`.
-1
View File
@@ -1 +0,0 @@
``pytest.warns`` and similar functions now capture warnings when an exception is raised inside a ``with`` block.
-7
View File
@@ -1,7 +0,0 @@
:func:`pytest.warns <warns>` now re-emits unmatched warnings when the context
closes -- previously it would consume all warnings, hiding those that were not
matched by the function.
While this is a new feature, we decided to announce this as a breaking change
because many test suites are configured to error-out on warnings, and will
therefore fail on the newly-re-emitted warnings.
+1 -1
View File
@@ -14,7 +14,7 @@ Each file should be named like ``<ISSUE>.<TYPE>.rst``, where
``<ISSUE>`` is an issue number, and ``<TYPE>`` is one of:
* ``feature``: new user facing features, like new command-line options and new behavior.
* ``improvement``: improvement of existing functionality, usually without requiring user intervention (for example, new fields being written in ``--junit-xml``, improved colors in terminal, etc).
* ``improvement``: improvement of existing functionality, usually without requiring user intervention (for example, new fields being written in ``--junitxml``, improved colors in terminal, etc).
* ``bugfix``: fixes a bug.
* ``doc``: documentation improvement, like rewording an entire session or adding missing docs.
* ``deprecation``: feature deprecation.
-4
View File
@@ -6,10 +6,6 @@ Release announcements
:maxdepth: 2
release-7.4.3
release-7.4.2
release-7.4.1
release-7.4.0
release-7.3.2
release-7.3.1
release-7.3.0
-49
View File
@@ -1,49 +0,0 @@
pytest-7.4.0
=======================================
The pytest team is proud to announce the 7.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 J. Stewart
* Alessio Izzo
* Alex
* Alex Lambson
* Brian Larsen
* Bruno Oliveira
* Bryan Ricker
* Chris Mahoney
* Facundo Batista
* Florian Bruhin
* Jarrett Keifer
* Kenny Y
* Miro Hrončok
* Ran Benita
* Roberto Aldera
* Ronny Pfannschmidt
* Sergey Kim
* Stefanie Molin
* Vijay Arora
* Ville Skyttä
* Zac Hatfield-Dodds
* bzoracler
* leeyueh
* nondescryptid
* theirix
Happy testing,
The pytest Development Team
-20
View File
@@ -1,20 +0,0 @@
pytest-7.4.1
=======================================
pytest 7.4.1 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
Thanks to all of the contributors to this release:
* Bruno Oliveira
* Florian Bruhin
* Ran Benita
Happy testing,
The pytest Development Team
-18
View File
@@ -1,18 +0,0 @@
pytest-7.4.2
=======================================
pytest 7.4.2 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
Thanks to all of the contributors to this release:
* Bruno Oliveira
Happy testing,
The pytest Development Team
-19
View File
@@ -1,19 +0,0 @@
pytest-7.4.3
=======================================
pytest 7.4.3 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
Thanks to all of the contributors to this release:
* Bruno Oliveira
* Marc Mueller
Happy testing,
The pytest Development Team
-1
View File
@@ -87,7 +87,6 @@ Released pytest versions support all Python versions that are actively maintaine
============== ===================
pytest version min. Python version
============== ===================
8.0+ 3.8+
7.1+ 3.7+
6.2 - 7.0 3.6+
5.0 - 6.1 3.5+
+4 -4
View File
@@ -22,7 +22,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
cachedir: .pytest_cache
rootdir: /home/sweet/project
collected 0 items
cache -- .../_pytest/cacheprovider.py:532
cache -- .../_pytest/cacheprovider.py:510
Return a cache object that can persist state between testing sessions.
cache.get(key, default)
@@ -105,7 +105,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
captured = capsys.readouterr()
assert captured.out == "hello\n"
doctest_namespace [session scope] -- .../_pytest/doctest.py:757
doctest_namespace [session scope] -- .../_pytest/doctest.py:737
Fixture that returns a :py:class:`dict` that will be injected into the
namespace of doctests.
@@ -119,7 +119,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:1353
pytestconfig [session scope] -- .../_pytest/fixtures.py:1360
Session-scoped fixture that returns the session's :class:`pytest.Config`
object.
@@ -196,7 +196,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:570
caplog -- .../_pytest/logging.py:498
Access and control log capturing.
Captured logs are available through the following properties/methods::
-149
View File
@@ -28,155 +28,6 @@ with advance notice in the **Deprecations** section of releases.
.. towncrier release notes start
pytest 7.4.3 (2023-10-24)
=========================
Bug Fixes
---------
- `#10447 <https://github.com/pytest-dev/pytest/issues/10447>`_: Markers are now considered in the reverse mro order to ensure base class markers are considered first -- this resolves a regression.
- `#11239 <https://github.com/pytest-dev/pytest/issues/11239>`_: Fixed ``:=`` in asserts impacting unrelated test cases.
- `#11439 <https://github.com/pytest-dev/pytest/issues/11439>`_: Handled an edge case where :data:`sys.stderr` might already be closed when :ref:`faulthandler` is tearing down.
pytest 7.4.2 (2023-09-07)
=========================
Bug Fixes
---------
- `#11237 <https://github.com/pytest-dev/pytest/issues/11237>`_: Fix doctest collection of `functools.cached_property` objects.
- `#11306 <https://github.com/pytest-dev/pytest/issues/11306>`_: Fixed bug using ``--importmode=importlib`` which would cause package ``__init__.py`` files to be imported more than once in some cases.
- `#11367 <https://github.com/pytest-dev/pytest/issues/11367>`_: Fixed bug where `user_properties` where not being saved in the JUnit XML file if a fixture failed during teardown.
- `#11394 <https://github.com/pytest-dev/pytest/issues/11394>`_: Fixed crash when parsing long command line arguments that might be interpreted as files.
Improved Documentation
----------------------
- `#11391 <https://github.com/pytest-dev/pytest/issues/11391>`_: Improved disclaimer on pytest plugin reference page to better indicate this is an automated, non-curated listing.
pytest 7.4.1 (2023-09-02)
=========================
Bug Fixes
---------
- `#10337 <https://github.com/pytest-dev/pytest/issues/10337>`_: Fixed bug where fake intermediate modules generated by ``--import-mode=importlib`` would not include the
child modules as attributes of the parent modules.
- `#10702 <https://github.com/pytest-dev/pytest/issues/10702>`_: Fixed error assertion handling in :func:`pytest.approx` when ``None`` is an expected or received value when comparing dictionaries.
- `#10811 <https://github.com/pytest-dev/pytest/issues/10811>`_: Fixed issue when using ``--import-mode=importlib`` together with ``--doctest-modules`` that caused modules
to be imported more than once, causing problems with modules that have import side effects.
pytest 7.4.0 (2023-06-23)
=========================
Features
--------
- `#10901 <https://github.com/pytest-dev/pytest/issues/10901>`_: Added :func:`ExceptionInfo.from_exception() <pytest.ExceptionInfo.from_exception>`, a simpler way to create an :class:`~pytest.ExceptionInfo` from an exception.
This can replace :func:`ExceptionInfo.from_exc_info() <pytest.ExceptionInfo.from_exc_info()>` for most uses.
Improvements
------------
- `#10872 <https://github.com/pytest-dev/pytest/issues/10872>`_: Update test log report annotation to named tuple and fixed inconsistency in docs for :hook:`pytest_report_teststatus` hook.
- `#10907 <https://github.com/pytest-dev/pytest/issues/10907>`_: When an exception traceback to be displayed is completely filtered out (by mechanisms such as ``__tracebackhide__``, internal frames, and similar), now only the exception string and the following message are shown:
"All traceback entries are hidden. Pass `--full-trace` to see hidden and internal frames.".
Previously, the last frame of the traceback was shown, even though it was hidden.
- `#10940 <https://github.com/pytest-dev/pytest/issues/10940>`_: Improved verbose output (``-vv``) of ``skip`` and ``xfail`` reasons by performing text wrapping while leaving a clear margin for progress output.
Added ``TerminalReporter.wrap_write()`` as a helper for that.
- `#10991 <https://github.com/pytest-dev/pytest/issues/10991>`_: Added handling of ``%f`` directive to print microseconds in log format options, such as ``log-date-format``.
- `#11005 <https://github.com/pytest-dev/pytest/issues/11005>`_: Added the underlying exception to the cache provider's path creation and write warning messages.
- `#11013 <https://github.com/pytest-dev/pytest/issues/11013>`_: Added warning when :confval:`testpaths` is set, but paths are not found by glob. In this case, pytest will fall back to searching from the current directory.
- `#11043 <https://github.com/pytest-dev/pytest/issues/11043>`_: When `--confcutdir` is not specified, and there is no config file present, the conftest cutoff directory (`--confcutdir`) is now set to the :ref:`rootdir <rootdir>`.
Previously in such cases, `conftest.py` files would be probed all the way to the root directory of the filesystem.
If you are badly affected by this change, consider adding an empty config file to your desired cutoff directory, or explicitly set `--confcutdir`.
- `#11081 <https://github.com/pytest-dev/pytest/issues/11081>`_: The :confval:`norecursedirs` check is now performed in a :hook:`pytest_ignore_collect` implementation, so plugins can affect it.
If after updating to this version you see that your `norecursedirs` setting is not being respected,
it means that a conftest or a plugin you use has a bad `pytest_ignore_collect` implementation.
Most likely, your hook returns `False` for paths it does not want to ignore,
which ends the processing and doesn't allow other plugins, including pytest itself, to ignore the path.
The fix is to return `None` instead of `False` for paths your hook doesn't want to ignore.
- `#8711 <https://github.com/pytest-dev/pytest/issues/8711>`_: :func:`caplog.set_level() <pytest.LogCaptureFixture.set_level>` and :func:`caplog.at_level() <pytest.LogCaptureFixture.at_level>`
will temporarily enable the requested ``level`` if ``level`` was disabled globally via
``logging.disable(LEVEL)``.
Bug Fixes
---------
- `#10831 <https://github.com/pytest-dev/pytest/issues/10831>`_: Terminal Reporting: Fixed bug when running in ``--tb=line`` mode where ``pytest.fail(pytrace=False)`` tests report ``None``.
- `#11068 <https://github.com/pytest-dev/pytest/issues/11068>`_: Fixed the ``--last-failed`` whole-file skipping functionality ("skipped N files") for :ref:`non-python test files <non-python tests>`.
- `#11104 <https://github.com/pytest-dev/pytest/issues/11104>`_: Fixed a regression in pytest 7.3.2 which caused to :confval:`testpaths` to be considered for loading initial conftests,
even when it was not utilized (e.g. when explicit paths were given on the command line).
Now the ``testpaths`` are only considered when they are in use.
- `#1904 <https://github.com/pytest-dev/pytest/issues/1904>`_: Fixed traceback entries hidden with ``__tracebackhide__ = True`` still being shown for chained exceptions (parts after "... the above exception ..." message).
- `#7781 <https://github.com/pytest-dev/pytest/issues/7781>`_: Fix writing non-encodable text to log file when using ``--debug``.
Improved Documentation
----------------------
- `#9146 <https://github.com/pytest-dev/pytest/issues/9146>`_: Improved documentation for :func:`caplog.set_level() <pytest.LogCaptureFixture.set_level>`.
Trivial/Internal Changes
------------------------
- `#11031 <https://github.com/pytest-dev/pytest/issues/11031>`_: Enhanced the CLI flag for ``-c`` to now include ``--config-file`` to make it clear that this flag applies to the usage of a custom config file.
pytest 7.3.2 (2023-06-10)
=========================
+21
View File
@@ -15,10 +15,12 @@
#
# The full version, including alpha/beta/rc tags.
# The short X.Y version.
import ast
import os
import shutil
import sys
from textwrap import dedent
from typing import List
from typing import TYPE_CHECKING
from _pytest import __version__ as version
@@ -449,6 +451,25 @@ def setup(app: "sphinx.application.Sphinx") -> None:
configure_logging(app)
# Make Sphinx mark classes with "final" when decorated with @final.
# We need this because we import final from pytest._compat, not from
# typing (for Python < 3.8 compat), so Sphinx doesn't detect it.
# To keep things simple we accept any `@final` decorator.
# Ref: https://github.com/pytest-dev/pytest/pull/7780
import sphinx.pycode.ast
import sphinx.pycode.parser
original_is_final = sphinx.pycode.parser.VariableCommentPicker.is_final
def patched_is_final(self, decorators: List[ast.expr]) -> bool:
if original_is_final(self, decorators):
return True
return any(
sphinx.pycode.ast.unparse(decorator) == "final" for decorator in decorators
)
sphinx.pycode.parser.VariableCommentPicker.is_final = patched_is_final
# legacypath.py monkey-patches pytest.Testdir in. Import the file so
# that autodoc can discover references to it.
import _pytest.legacypath # noqa: F401
+3 -52
View File
@@ -380,25 +380,6 @@ conflicts (such as :class:`pytest.File` now taking ``path`` instead of
``fspath``, as :ref:`outlined above <node-ctor-fspath-deprecation>`), a
deprecation warning is now raised.
Applying a mark to a fixture function
-------------------------------------
.. deprecated:: 7.4
Applying a mark to a fixture function never had any effect, but it is a common user error.
.. code-block:: python
@pytest.mark.usefixtures("clean_database")
@pytest.fixture
def user() -> User:
...
Users expected in this case that the ``usefixtures`` mark would have its intended effect of using the ``clean_database`` fixture when ``user`` was invoked, when in fact it has no effect at all.
Now pytest will issue a warning when it encounters this problem, and will raise an error in the future versions.
Backward compatibilities in ``Parser.addoption``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -486,42 +467,12 @@ The ``yield_fixture`` function/decorator
It has been so for a very long time, so can be search/replaced safely.
Removed Features and Breaking Changes
-------------------------------------
Removed Features
----------------
As stated in our :ref:`backwards-compatibility` policy, deprecated features are removed only in major releases after
an appropriate period of deprecation has passed.
Some breaking changes which could not be deprecated are also listed.
:class:`pytest.Package` is no longer a :class:`pytest.Module` or :class:`pytest.File`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. versionchanged:: 8.0
The ``Package`` collector node designates a Python package, that is, a directory with an `__init__.py` file.
Previously ``Package`` was a subtype of ``pytest.Module`` (which represents a single Python module),
the module being the `__init__.py` file.
This has been deemed a design mistake (see :issue:`11137` and :issue:`7777` for details).
The ``path`` property of ``Package`` nodes now points to the package directory instead of the ``__init__.py`` file.
Note that a ``Module`` node for ``__init__.py`` (which is not a ``Package``) may still exist,
if it is picked up during collection (e.g. if you configured :confval:`python_files` to include ``__init__.py`` files).
Collecting ``__init__.py`` files no longer collects package
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. versionremoved:: 8.0
Running `pytest pkg/__init__.py` now collects the `pkg/__init__.py` file (module) only.
Previously, it collected the entire `pkg` package, including other test files in the directory, but excluding tests in the `__init__.py` file itself
(unless :confval:`python_files` was changed to allow `__init__.py` file).
To collect the entire package, specify just the directory: `pytest pkg`.
The ``pytest.collect`` module
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -645,7 +596,7 @@ By using ``legacy`` you will keep using the legacy/xunit1 format when upgrading
pytest 6.0, where the default format will be ``xunit2``.
In order to let users know about the transition, pytest will issue a warning in case
the ``--junit-xml`` option is given in the command line but ``junit_family`` is not explicitly
the ``--junitxml`` option is given in the command line but ``junit_family`` is not explicitly
configured in ``pytest.ini``.
Services known to support the ``xunit2`` format:
+1 -1
View File
@@ -136,7 +136,7 @@ Or select multiple nodes:
Node IDs for failing tests are displayed in the test summary info
when running pytest with the ``-rf`` option. You can also
construct Node IDs from the output of ``pytest --collect-only``.
construct Node IDs from the output of ``pytest --collectonly``.
Using ``-k expr`` to select tests based on their name
-------------------------------------------------------
+7 -6
View File
@@ -1,13 +1,14 @@
"""Module containing a parametrized tests testing cross-python serialization
via the pickle module."""
"""
module containing a parametrized tests testing cross-python
serialization via the pickle module.
"""
import shutil
import subprocess
import textwrap
import pytest
pythonlist = ["python3.9", "python3.10", "python3.11"]
pythonlist = ["python3.5", "python3.6", "python3.7"]
@pytest.fixture(params=pythonlist)
@@ -42,7 +43,7 @@ class Python:
)
)
)
subprocess.run((self.pythonpath, str(dumpfile)), check=True)
subprocess.check_call((self.pythonpath, str(dumpfile)))
def load_and_is_true(self, expression):
loadfile = self.picklefile.with_name("load.py")
@@ -62,7 +63,7 @@ class Python:
)
)
print(loadfile)
subprocess.run((self.pythonpath, str(loadfile)), check=True)
subprocess.check_call((self.pythonpath, str(loadfile)))
@pytest.mark.parametrize("obj", [42, {}, {1: 3}])
+1 -1
View File
@@ -12,7 +12,7 @@ class YamlFile(pytest.File):
# We need a yaml parser, e.g. PyYAML.
import yaml
raw = yaml.safe_load(self.path.open(encoding="utf-8"))
raw = yaml.safe_load(self.path.open())
for name, spec in sorted(raw.items()):
yield YamlItem.from_parent(self, name=name, spec=spec)
+13 -17
View File
@@ -483,8 +483,8 @@ argument sets to use for each test function. Let's run it:
FAILED test_parametrize.py::TestClass::test_equals[1-2] - assert 1 == 2
1 failed, 2 passed in 0.12s
Parametrization with multiple fixtures
--------------------------------------
Indirect parametrization with multiple fixtures
--------------------------------------------------------------
Here is a stripped down real-life example of using parametrized
testing for testing serialization of objects between different python
@@ -509,8 +509,8 @@ Running it results in some skips if we don't have all the python interpreters in
SKIPPED [9] multipython.py:69: 'python3.7' not found
27 skipped in 0.12s
Parametrization of optional implementations/imports
---------------------------------------------------
Indirect parametrization of optional implementations/imports
--------------------------------------------------------------------
If you want to compare the outcomes of several implementations of a given
API, you can write test functions that receive the already imported implementations
@@ -657,16 +657,13 @@ Use :func:`pytest.raises` with the
:ref:`pytest.mark.parametrize ref` decorator to write parametrized tests
in which some tests raise exceptions and others do not.
``contextlib.nullcontext`` can be used to test cases that are not expected to
raise exceptions but that should result in some value. The value is given as the
``enter_result`` parameter, which will be available as the ``with`` statements
target (``e`` in the example below).
It may be helpful to use ``nullcontext`` as a complement to ``raises``.
For example:
.. code-block:: python
from contextlib import nullcontext
from contextlib import nullcontext as does_not_raise
import pytest
@@ -674,17 +671,16 @@ For example:
@pytest.mark.parametrize(
"example_input,expectation",
[
(3, nullcontext(2)),
(2, nullcontext(3)),
(1, nullcontext(6)),
(3, does_not_raise()),
(2, does_not_raise()),
(1, does_not_raise()),
(0, pytest.raises(ZeroDivisionError)),
],
)
def test_division(example_input, expectation):
"""Test how much I know division."""
with expectation as e:
assert (6 / example_input) == e
with expectation:
assert (6 / example_input) is not None
In the example above, the first three test cases should run without any
exceptions, while the fourth should raise a``ZeroDivisionError`` exception,
which is expected by pytest.
In the example above, the first three test cases should run unexceptionally,
while the fourth should raise ``ZeroDivisionError``.
+8 -10
View File
@@ -808,15 +808,16 @@ case we just write some information out to a ``failures`` file:
import pytest
@pytest.hookimpl(wrapper=True, tryfirst=True)
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
# execute all other hooks to obtain the report object
rep = yield
outcome = yield
rep = outcome.get_result()
# we only look at actual failing test calls, not setup/teardown
if rep.when == "call" and rep.failed:
mode = "a" if os.path.exists("failures") else "w"
with open("failures", mode, encoding="utf-8") as f:
with open("failures", mode) as f:
# let's also access a fixture for the fun of it
if "tmp_path" in item.fixturenames:
extra = " ({})".format(item.funcargs["tmp_path"])
@@ -825,8 +826,6 @@ case we just write some information out to a ``failures`` file:
f.write(rep.nodeid + extra + "\n")
return rep
if you then have failing tests:
@@ -900,17 +899,16 @@ here is a little example implemented via a local plugin:
phase_report_key = StashKey[Dict[str, CollectReport]]()
@pytest.hookimpl(wrapper=True, tryfirst=True)
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
# execute all other hooks to obtain the report object
rep = yield
outcome = yield
rep = outcome.get_result()
# store test results for each phase of a call, which can
# be "setup", "call", "teardown"
item.stash.setdefault(phase_report_key, {})[rep.when] = rep
return rep
@pytest.fixture
def something(request):
@@ -1090,4 +1088,4 @@ application with standard ``pytest`` command-line options:
.. code-block:: bash
./app_main --pytest --verbose --tb=long --junit=xml=results.xml test-suite/
./app_main --pytest --verbose --tb=long --junitxml=results.xml test-suite/
+1 -1
View File
@@ -34,7 +34,7 @@ a function/method call.
**Assert** is where we look at that resulting state and check if it looks how
we'd expect after the dust has settled. It's where we gather evidence to say the
behavior does or does not align with what we expect. The ``assert`` in our test
behavior does or does not aligns with what we expect. The ``assert`` in our test
is where we take that measurement/observation and apply our judgement to it. If
something should be green, we'd say ``assert thing == "green"``.
+2 -26
View File
@@ -9,7 +9,7 @@ Get Started
Install ``pytest``
----------------------------------------
``pytest`` requires: Python 3.8+ or PyPy3.
``pytest`` requires: Python 3.7+ or PyPy3.
1. Run the following command in your command line:
@@ -22,7 +22,7 @@ Install ``pytest``
.. code-block:: bash
$ pytest --version
pytest 7.4.3
pytest 7.3.2
.. _`simpletest`:
@@ -97,30 +97,6 @@ Use the :ref:`raises <assertraises>` helper to assert that some code raises an e
with pytest.raises(SystemExit):
f()
You can also use the context provided by :ref:`raises <assertraises>` to
assert that an expected exception is part of a raised ``ExceptionGroup``:
.. code-block:: python
# content of test_exceptiongroup.py
import pytest
def f():
raise ExceptionGroup(
"Group message",
[
RuntimeError(),
],
)
def test_exception_in_group():
with pytest.raises(ExceptionGroup) as excinfo:
f()
assert excinfo.group_contains(RuntimeError)
assert not excinfo.group_contains(TypeError)
Execute the test function with “quiet” reporting mode:
.. code-block:: pytest
+4 -49
View File
@@ -54,13 +54,14 @@ operators. (See :ref:`tbreportdemo`). This allows you to use the
idiomatic python constructs without boilerplate code while not losing
introspection information.
If a message is specified with the assertion like this:
However, if you specify a message with the assertion like this:
.. code-block:: python
assert a % 2 == 0, "value was odd, should be even"
it is printed alongside the assertion introspection in the traceback.
then no assertion introspection takes places at all and the message
will be simply shown in the traceback.
See :ref:`assert-details` for more information on assertion introspection.
@@ -115,56 +116,10 @@ that a regular expression matches on the string representation of an exception
with pytest.raises(ValueError, match=r".* 123 .*"):
myfunc()
The regexp parameter of the ``match`` parameter is matched with the ``re.search``
The regexp parameter of the ``match`` method is matched with the ``re.search``
function, so in the above example ``match='123'`` would have worked as
well.
You can also use the :func:`excinfo.group_contains() <pytest.ExceptionInfo.group_contains>`
method to test for exceptions returned as part of an ``ExceptionGroup``:
.. code-block:: python
def test_exception_in_group():
with pytest.raises(RuntimeError) as excinfo:
raise ExceptionGroup(
"Group message",
[
RuntimeError("Exception 123 raised"),
],
)
assert excinfo.group_contains(RuntimeError, match=r".* 123 .*")
assert not excinfo.group_contains(TypeError)
The optional ``match`` keyword parameter works the same way as for
:func:`pytest.raises`.
By default ``group_contains()`` will recursively search for a matching
exception at any level of nested ``ExceptionGroup`` instances. You can
specify a ``depth`` keyword parameter if you only want to match an
exception at a specific level; exceptions contained directly in the top
``ExceptionGroup`` would match ``depth=1``.
.. code-block:: python
def test_exception_in_group_at_given_depth():
with pytest.raises(RuntimeError) as excinfo:
raise ExceptionGroup(
"Group message",
[
RuntimeError(),
ExceptionGroup(
"Nested group",
[
TypeError(),
],
),
],
)
assert excinfo.group_contains(RuntimeError, depth=1)
assert excinfo.group_contains(TypeError, depth=2)
assert not excinfo.group_contains(RuntimeError, depth=2)
assert not excinfo.group_contains(TypeError, depth=1)
There's an alternate form of the :func:`pytest.raises` function where you pass
a function that will be executed with the given ``*args`` and ``**kwargs`` and
assert that the given exception is raised:
+8 -15
View File
@@ -176,21 +176,14 @@ with more recent files coming first.
Behavior when no tests failed in the last run
---------------------------------------------
The ``--lfnf/--last-failed-no-failures`` option governs the behavior of ``--last-failed``.
Determines whether to execute tests when there are no previously (known)
failures or when no cached ``lastfailed`` data was found.
There are two options:
* ``all``: when there are no known test failures, runs all tests (the full test suite). This is the default.
* ``none``: when there are no known test failures, just emits a message stating this and exit successfully.
Example:
When no tests failed in the last run, or when no cached ``lastfailed`` data was
found, ``pytest`` can be configured either to run all of the tests or no tests,
using the ``--last-failed-no-failures`` option, which takes one of the following values:
.. code-block:: bash
pytest --last-failed --last-failed-no-failures all # runs the full test suite (default behavior)
pytest --last-failed --last-failed-no-failures none # runs no tests and exits successfully
pytest --last-failed --last-failed-no-failures all # run all tests (default behavior)
pytest --last-failed --last-failed-no-failures none # run no tests and exit
The new config.cache object
--------------------------------
@@ -213,12 +206,12 @@ across pytest invocations:
@pytest.fixture
def mydata(pytestconfig):
val = pytestconfig.cache.get("example/value", None)
def mydata(request):
val = request.config.cache.get("example/value", None)
if val is None:
expensive_computation()
val = 42
pytestconfig.cache.set("example/value", val)
request.config.cache.set("example/value", val)
return val
+4
View File
@@ -135,6 +135,10 @@ Warning about unraisable exceptions and unhandled thread exceptions
.. versionadded:: 6.2
.. note::
These features only work on Python>=3.8.
Unhandled exceptions are exceptions that are raised in a situation in which
they cannot propagate to a caller. The most common case is an exception raised
in a :meth:`__del__ <object.__del__>` implementation.
+3 -2
View File
@@ -1698,7 +1698,7 @@ and declare its use in a test module via a ``usefixtures`` marker:
class TestDirectoryInit:
def test_cwd_starts_empty(self):
assert os.listdir(os.getcwd()) == []
with open("myfile", "w", encoding="utf-8") as f:
with open("myfile", "w") as f:
f.write("hello")
def test_cwd_again_starts_empty(self):
@@ -1752,7 +1752,8 @@ into an ini-file:
def my_fixture_that_sadly_wont_use_my_other_fixture():
...
This generates a deprecation warning, and will become an error in Pytest 8.
Currently this will not generate any error or warning, but this is intended
to be handled by :issue:`3664`.
.. _`override fixtures`:
-7
View File
@@ -172,13 +172,6 @@ the records for the ``setup`` and ``call`` stages during teardown like so:
The full API is available at :class:`pytest.LogCaptureFixture`.
.. warning::
The ``caplog`` fixture adds a handler to the root logger to capture logs. If the root logger is
modified during a test, for example with ``logging.config.dictConfig``, this handler may be
removed and cause no logs to be captured. To avoid this, ensure that any root logger configuration
only adds to the existing handlers.
.. _live_logs:
+4 -21
View File
@@ -47,7 +47,8 @@ Unsupported idioms / known issues
- nose imports test modules with the same import path (e.g.
``tests.test_mode``) but different file system paths
(e.g. ``tests/test_mode.py`` and ``other/tests/test_mode.py``)
by extending sys.path/import semantics. pytest does not do that. Note that
by extending sys.path/import semantics. pytest does not do that
but there is discussion in :issue:`268` for adding some support. Note that
`nose2 choose to avoid this sys.path/import hackery <https://nose2.readthedocs.io/en/latest/differences.html#test-discovery-and-loading>`_.
If you place a conftest.py file in the root directory of your project
@@ -65,34 +66,16 @@ Unsupported idioms / known issues
- no nose-configuration is recognized.
- ``yield``-based methods are
- ``yield``-based methods are unsupported as of pytest 4.1.0. They are
fundamentally incompatible with pytest because they don't support fixtures
properly since collection and test execution are separated.
Here is a table comparing the default supported naming conventions for both
nose and pytest.
========= ========================== ======= =====
what default naming convention pytest nose
========= ========================== ======= =====
module ``test*.py``
module ``test_*.py`` ✅ ✅
module ``*_test.py``
module ``*_tests.py``
class ``*(unittest.TestCase)`` ✅ ✅
method ``test_*`` ✅ ✅
class ``Test*``
method ``test_*``
function ``test_*``
========= ========================== ======= =====
Migrating from nose to pytest
------------------------------
`nose2pytest <https://github.com/pytest-dev/nose2pytest>`_ is a Python script
and pytest plugin to help convert Nose-based tests into pytest-based tests.
Specifically, the script transforms ``nose.tools.assert_*`` function calls into
Specifically, the script transforms nose.tools.assert_* function calls into
raw assert statements, while preserving format of original arguments
as much as possible.
+1 -17
View File
@@ -16,12 +16,6 @@ Examples for modifying traceback printing:
pytest -l # show local variables (shortcut)
pytest --no-showlocals # hide local variables (if addopts enables them)
pytest --capture=fd # default, capture at the file descriptor level
pytest --capture=sys # capture at the sys level
pytest --capture=no # don't capture
pytest -s # don't capture (shortcut)
pytest --capture=tee-sys # capture to logs but also output to sys level streams
pytest --tb=auto # (default) 'long' tracebacks for the first and last
# entry, but 'short' style for the other entries
pytest --tb=long # exhaustive, informative traceback formatting
@@ -42,16 +36,6 @@ option you make sure a trace is shown.
Verbosity
--------------------------------------------------
Examples for modifying printing verbosity:
.. code-block:: bash
pytest --quiet # quiet - less verbose - mode
pytest -q # quiet - less verbose - mode (shortcut)
pytest -v # increase verbosity, display individual test names
pytest -vv # more verbose, display more details from the test output
pytest -vvv # not a standard , but may be used for even more detail in certain setups
The ``-v`` flag controls the verbosity of pytest output in various aspects: test session progress, assertion
details when tests fail, fixtures details with ``--fixtures``, etc.
@@ -494,7 +478,7 @@ integration servers, use this invocation:
.. code-block:: bash
pytest --junit-xml=path
pytest --junitxml=path
to create an XML file at ``path``.
+4 -4
View File
@@ -24,8 +24,8 @@ created in the `base temporary directory`_.
d = tmp_path / "sub"
d.mkdir()
p = d / "hello.txt"
p.write_text(CONTENT, encoding="utf-8")
assert p.read_text(encoding="utf-8") == CONTENT
p.write_text(CONTENT)
assert p.read_text() == CONTENT
assert len(list(tmp_path.iterdir())) == 1
assert 0
@@ -51,8 +51,8 @@ Running this would result in a passed test except for the last
d = tmp_path / "sub"
d.mkdir()
p = d / "hello.txt"
p.write_text(CONTENT, encoding="utf-8")
assert p.read_text(encoding="utf-8") == CONTENT
p.write_text(CONTENT)
assert p.read_text() == CONTENT
assert len(list(tmp_path.iterdir())) == 1
> assert 0
E assert 0
+2 -2
View File
@@ -207,10 +207,10 @@ creation of a per-test temporary directory:
@pytest.fixture(autouse=True)
def initdir(self, tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path) # change to pytest-provided temporary directory
tmp_path.joinpath("samplefile.ini").write_text("# testdata", encoding="utf-8")
tmp_path.joinpath("samplefile.ini").write_text("# testdata")
def test_method(self):
with open("samplefile.ini", encoding="utf-8") as f:
with open("samplefile.ini") as f:
s = f.read()
assert "testdata" in s
+8 -20
View File
@@ -44,34 +44,23 @@ Use ``""`` instead of ``''`` in expression when running this on Windows
.. _nodeids:
**Run tests by collection arguments**
**Run tests by node ids**
Pass the module filename relative to the working directory, followed by specifiers like the class name and function name
separated by ``::`` characters, and parameters from parameterization enclosed in ``[]``.
Each collected test is assigned a unique ``nodeid`` which consist of the module filename followed
by specifiers like class names, function names and parameters from parametrization, separated by ``::`` characters.
To run a specific test within a module:
.. code-block:: bash
pytest tests/test_mod.py::test_func
pytest test_mod.py::test_func
To run all tests in a class:
Another example specifying a test method in the command line:
.. code-block:: bash
pytest tests/test_mod.py::TestClass
Specifying a specific test method:
.. code-block:: bash
pytest tests/test_mod.py::TestClass::test_method
Specifying a specific parametrization of a test:
.. code-block:: bash
pytest tests/test_mod.py::test_func[x1,y2]
pytest test_mod.py::TestClass::test_method
**Run tests by marker expressions**
@@ -184,8 +173,7 @@ You can invoke ``pytest`` from Python code directly:
this acts as if you would call "pytest" from the command line.
It will not raise :class:`SystemExit` but return the :ref:`exit code <exit-codes>` instead.
If you don't pass it any arguments, ``main`` reads the arguments from the command line arguments of the process (:data:`sys.argv`), which may be undesirable.
You can pass in options and arguments explicitly:
You can pass in options and arguments:
.. code-block:: python
+24 -33
View File
@@ -56,7 +56,7 @@ The remaining hook functions will not be called in this case.
.. _`hookwrapper`:
hook wrappers: executing around other hooks
hookwrapper: executing around other hooks
-------------------------------------------------
.. currentmodule:: _pytest.core
@@ -69,8 +69,10 @@ which yields exactly once. When pytest invokes hooks it first executes
hook wrappers and passes the same arguments as to the regular hooks.
At the yield point of the hook wrapper pytest will execute the next hook
implementations and return their result to the yield point, or will
propagate an exception if they raised.
implementations and return their result to the yield point in the form of
a :py:class:`Result <pluggy._Result>` instance which encapsulates a result or
exception info. The yield point itself will thus typically not raise
exceptions (unless there are bugs).
Here is an example definition of a hook wrapper:
@@ -79,35 +81,26 @@ Here is an example definition of a hook wrapper:
import pytest
@pytest.hookimpl(wrapper=True)
@pytest.hookimpl(hookwrapper=True)
def pytest_pyfunc_call(pyfuncitem):
do_something_before_next_hook_executes()
# If the outcome is an exception, will raise the exception.
res = yield
outcome = yield
# outcome.excinfo may be None or a (cls, val, tb) tuple
new_res = post_process_result(res)
res = outcome.get_result() # will raise if outcome was exception
# Override the return value to the plugin system.
return new_res
post_process_result(res)
The hook wrapper needs to return a result for the hook, or raise an exception.
outcome.force_result(new_res) # to override the return value to the plugin system
In many cases, the wrapper only needs to perform tracing or other side effects
around the actual hook implementations, in which case it can return the result
value of the ``yield``. The simplest (though useless) hook wrapper is
``return (yield)``.
In other cases, the wrapper wants the adjust or adapt the result, in which case
it can return a new value. If the result of the underlying hook is a mutable
object, the wrapper may modify that result, but it's probably better to avoid it.
If the hook implementation failed with an exception, the wrapper can handle that
exception using a ``try-catch-finally`` around the ``yield``, by propagating it,
supressing it, or raising a different exception entirely.
Note that hook wrappers don't return results themselves, they merely
perform tracing or other side effects around the actual hook implementations.
If the result of the underlying hook is a mutable object, they may modify
that result but it's probably better to avoid it.
For more information, consult the
:ref:`pluggy documentation about hook wrappers <pluggy:hookwrappers>`.
:ref:`pluggy documentation about hookwrappers <pluggy:hookwrappers>`.
.. _plugin-hookorder:
@@ -137,14 +130,11 @@ after others, i.e. the position in the ``N``-sized list of functions:
# Plugin 3
@pytest.hookimpl(wrapper=True)
@pytest.hookimpl(hookwrapper=True)
def pytest_collection_modifyitems(items):
# will execute even before the tryfirst one above!
try:
return (yield)
finally:
# will execute after all non-wrappers executed
...
outcome = yield
# will execute after all non-hookwrappers executed
Here is the order of execution:
@@ -159,11 +149,12 @@ Here is the order of execution:
Plugin1).
4. Plugin3's pytest_collection_modifyitems then executing the code after the yield
point. The yield receives the result from calling the non-wrappers, or raises
an exception if the non-wrappers raised.
point. The yield receives a :py:class:`Result <pluggy._Result>` instance which encapsulates
the result from calling the non-wrappers. Wrappers shall not modify the result.
It's possible to use ``tryfirst`` and ``trylast`` also on hook wrappers
in which case it will influence the ordering of hook wrappers among each other.
It's possible to use ``tryfirst`` and ``trylast`` also in conjunction with
``hookwrapper=True`` in which case it will influence the ordering of hookwrappers
among each other.
Declaring new hooks
+3 -3
View File
@@ -2,7 +2,7 @@
.. sidebar:: Next Open Trainings
- `Professional Testing with Python <https://python-academy.com/courses/python_course_testing.html>`_, via `Python Academy <https://www.python-academy.com/>`_, **March 5th to 7th 2024** (3 day in-depth training), **Leipzig, Germany / Remote**
- `Professional Testing with Python <https://python-academy.com/courses/python_course_testing.html>`_, via `Python Academy <https://www.python-academy.com/>`_, March 5th to 7th 2024 (3 day in-depth training), Leipzig/Remote
Also see :doc:`previous talks and blogposts <talks>`.
@@ -17,7 +17,7 @@ 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.
``pytest`` requires: Python 3.7+ or PyPy3.
**PyPI package name**: :pypi:`pytest`
@@ -76,7 +76,7 @@ Features
- Can run :ref:`unittest <unittest>` (including trial) and :ref:`nose <noseintegration>` test suites out of the box
- Python 3.8+ or PyPy 3
- Python 3.7+ or PyPy 3
- Rich plugin architecture, with over 800+ :ref:`external plugins <plugin-list>` and thriving community
+1 -1
View File
@@ -90,7 +90,7 @@ and can also be used to hold pytest configuration if they have a ``[pytest]`` se
setup.cfg
~~~~~~~~~
``setup.cfg`` files are general purpose configuration files, used originally by ``distutils`` (now deprecated) and `setuptools <https://setuptools.pypa.io/en/latest/userguide/declarative_config.html>`__, and can also be used to hold pytest configuration
``setup.cfg`` files are general purpose configuration files, used originally by :doc:`distutils <python:distutils/configfile>`, and can also be used to hold pytest configuration
if they have a ``[tool:pytest]`` section.
.. code-block:: ini
File diff suppressed because it is too large Load Diff
+78 -110
View File
@@ -1,5 +1,3 @@
:tocdepth: 3
.. _`api-reference`:
API Reference
@@ -84,8 +82,6 @@ pytest.exit
pytest.main
~~~~~~~~~~~
**Tutorial**: :ref:`pytest.main-usage`
.. autofunction:: pytest.main
pytest.param
@@ -239,7 +235,7 @@ pytest.mark.xfail
Marks a test function as *expected to fail*.
.. py:function:: pytest.mark.xfail(condition=None, *, reason=None, raises=None, run=True, strict=xfail_strict)
.. py:function:: pytest.mark.xfail(condition=None, *, reason=None, raises=None, run=True, strict=False)
:type condition: bool or str
:param condition:
@@ -251,10 +247,10 @@ Marks a test function as *expected to fail*.
:keyword Type[Exception] raises:
Exception subclass (or tuple of subclasses) expected to be raised by the test function; other exceptions will fail the test.
:keyword bool run:
Whether the test function should actually be executed. If ``False``, the function will always xfail and will
If the test function should actually be executed. If ``False``, the function will always xfail and will
not be executed (useful if a function is segfaulting).
:keyword bool strict:
* If ``False`` the function will be shown in the terminal output as ``xfailed`` if it fails
* If ``False`` (the default) the function will be shown in the terminal output as ``xfailed`` if it fails
and as ``xpass`` if it passes. In both cases this will not cause the test suite to fail as a whole. This
is particularly useful to mark *flaky* tests (tests that fail at random) to be tackled later.
* If ``True``, the function will be shown in the terminal output as ``xfailed`` if it fails, but if it
@@ -262,8 +258,6 @@ Marks a test function as *expected to fail*.
that are always failing and there should be a clear indication if they unexpectedly start to pass (for example
a new release of a library fixes a known bug).
Defaults to :confval:`xfail_strict`, which is ``False`` by default.
Custom marks
~~~~~~~~~~~~
@@ -789,66 +783,18 @@ reporting or interaction with exceptions:
.. autofunction:: pytest_leave_pdb
Collection tree objects
-----------------------
Objects
-------
These are the collector and item classes (collectively called "nodes") which
make up the collection tree.
Full reference to objects accessible from :ref:`fixtures <fixture>` or :ref:`hooks <hook-reference>`.
Node
~~~~
.. autoclass:: _pytest.nodes.Node()
CallInfo
~~~~~~~~
.. autoclass:: pytest.CallInfo()
:members:
Collector
~~~~~~~~~
.. autoclass:: pytest.Collector()
:members:
:show-inheritance:
Item
~~~~
.. autoclass:: pytest.Item()
:members:
:show-inheritance:
File
~~~~
.. autoclass:: pytest.File()
:members:
:show-inheritance:
FSCollector
~~~~~~~~~~~
.. autoclass:: _pytest.nodes.FSCollector()
:members:
:show-inheritance:
Session
~~~~~~~
.. autoclass:: pytest.Session()
:members:
:show-inheritance:
Package
~~~~~~~
.. autoclass:: pytest.Package()
:members:
:show-inheritance:
Module
~~~~~~
.. autoclass:: pytest.Module()
:members:
:show-inheritance:
Class
~~~~~
@@ -857,34 +803,13 @@ Class
:members:
:show-inheritance:
Function
~~~~~~~~
Collector
~~~~~~~~~
.. autoclass:: pytest.Function()
.. autoclass:: pytest.Collector()
:members:
:show-inheritance:
FunctionDefinition
~~~~~~~~~~~~~~~~~~
.. autoclass:: _pytest.python.FunctionDefinition()
:members:
:show-inheritance:
Objects
-------
Objects accessible from :ref:`fixtures <fixture>` or :ref:`hooks <hook-reference>`
or importable from ``pytest``.
CallInfo
~~~~~~~~
.. autoclass:: pytest.CallInfo()
:members:
CollectReport
~~~~~~~~~~~~~
@@ -912,11 +837,46 @@ ExitCode
.. autoclass:: pytest.ExitCode
:members:
File
~~~~
.. autoclass:: pytest.File()
:members:
:show-inheritance:
FixtureDef
~~~~~~~~~~
.. autoclass:: pytest.FixtureDef()
.. autoclass:: _pytest.fixtures.FixtureDef()
:members:
:show-inheritance:
FSCollector
~~~~~~~~~~~
.. autoclass:: _pytest.nodes.FSCollector()
:members:
:show-inheritance:
Function
~~~~~~~~
.. autoclass:: pytest.Function()
:members:
:show-inheritance:
FunctionDefinition
~~~~~~~~~~~~~~~~~~
.. autoclass:: _pytest.python.FunctionDefinition()
:members:
:show-inheritance:
Item
~~~~
.. autoclass:: pytest.Item()
:members:
:show-inheritance:
@@ -947,6 +907,19 @@ Metafunc
.. autoclass:: pytest.Metafunc()
:members:
Module
~~~~~~
.. autoclass:: pytest.Module()
:members:
:show-inheritance:
Node
~~~~
.. autoclass:: _pytest.nodes.Node()
:members:
Parser
~~~~~~
@@ -968,6 +941,13 @@ PytestPluginManager
:inherited-members:
:show-inheritance:
Session
~~~~~~~
.. autoclass:: pytest.Session()
:members:
:show-inheritance:
TestReport
~~~~~~~~~~
@@ -982,10 +962,10 @@ TestShortLogReport
.. autoclass:: pytest.TestShortLogReport()
:members:
Result
_Result
~~~~~~~
Result object used within :ref:`hook wrappers <hookwrapper>`, see :py:class:`Result in the pluggy documentation <pluggy.Result>` for more information.
Result object used within :ref:`hook wrappers <hookwrapper>`, see :py:class:`_Result in the pluggy documentation <pluggy._callers._Result>` for more information.
Stash
~~~~~
@@ -1173,9 +1153,6 @@ Custom warnings generated in some situations such as improper usage or deprecate
.. autoclass:: pytest.PytestRemovedIn8Warning
:show-inheritance:
.. autoclass:: pytest.PytestRemovedIn9Warning
:show-inheritance:
.. autoclass:: pytest.PytestUnhandledCoroutineWarning
:show-inheritance:
@@ -1642,11 +1619,11 @@ passed multiple times. The expected format is ``name=value``. For example::
Additionally, ``pytest`` will attempt to intelligently identify and ignore a
virtualenv by the presence of an activation script. Any directory deemed to
be the root of a virtual environment will not be considered during test
collection unless ``--collect-in-virtualenv`` is given. Note also that
``norecursedirs`` takes precedence over ``--collect-in-virtualenv``; e.g. if
collection unless ``‑‑collectinvirtualenv`` is given. Note also that
``norecursedirs`` takes precedence over ``‑‑collectinvirtualenv``; e.g. if
you intend to run tests in a virtualenv with a base directory that matches
``'.*'`` you *must* override ``norecursedirs`` in addition to using the
``--collect-in-virtualenv`` flag.
``‑‑collectinvirtualenv`` flag.
.. confval:: python_classes
@@ -1726,11 +1703,6 @@ passed multiple times. The expected format is ``name=value``. For example::
[pytest]
pythonpath = src1 src2
.. note::
``pythonpath`` does not affect some imports that happen very early,
most notably plugins loaded using the ``-p`` command line option.
.. confval:: required_plugins
@@ -1894,12 +1866,8 @@ All the command-line flags can be obtained by running ``pytest --help``::
tests. Optional argument: glob (default: '*').
--cache-clear Remove all cache contents at start of test run
--lfnf={all,none}, --last-failed-no-failures={all,none}
With ``--lf``, determines whether to execute tests
when there are no previously (known) failures or
when no cached ``lastfailed`` data was found.
``all`` (the default) runs the full test suite
again. ``none`` just emits a message about no known
failures and exits successfully.
Which tests to run with no previously (known)
failures
--sw, --stepwise Exit on test failure and continue from last failing
test next time
--sw-skip, --stepwise-skip
@@ -1950,9 +1918,9 @@ All the command-line flags can be obtained by running ``pytest --help``::
--strict-markers Markers not registered in the `markers` section of
the configuration file raise errors
--strict (Deprecated) alias to --strict-markers
-c FILE, --config-file=FILE
-c, --config-file FILE
Load configuration from `FILE` instead of trying to
locate one of the implicit configuration files.
locate one of the implicit configuration files
--continue-on-collection-errors
Force test execution even if collection errors occur
--rootdir=ROOTDIR Define root directory for tests. Can be relative
+2 -2
View File
@@ -1,8 +1,8 @@
pallets-sphinx-themes
pluggy>=1.2.0
pluggy>=1.0
pygments-pytest>=2.3.0
sphinx-removed-in>=0.2.0
sphinx>=5,<8
sphinx>=5,<6
sphinxcontrib-trio
sphinxcontrib-svg2pdfconverter
# Pin packaging because it no longer handles 'latest' version, which
+2 -7
View File
@@ -17,12 +17,7 @@ python_classes = ["Test", "Acceptance"]
python_functions = ["test"]
# NOTE: "doc" is not included here, but gets tested explicitly via "doctesting".
testpaths = ["testing"]
norecursedirs = [
"testing/example_scripts",
".*",
"build",
"dist",
]
norecursedirs = ["testing/example_scripts"]
xfail_strict = true
filterwarnings = [
"error",
@@ -118,7 +113,7 @@ template = "changelog/_template.rst"
showcontent = true
[tool.black]
target-version = ['py38']
target-version = ['py37']
# check-wheel-contents is executed by the build-and-inspect-python-package action.
[tool.check-wheel-contents]
+3 -15
View File
@@ -31,22 +31,10 @@ class InvalidFeatureRelease(Exception):
SLUG = "pytest-dev/pytest"
PR_BODY = """\
Created by the [prepare release pr]\
(https://github.com/pytest-dev/pytest/actions/workflows/prepare-release-pr.yml) workflow.
Created automatically from manual trigger.
Once all builds pass and it has been **approved** by one or more maintainers, start the \
[deploy](https://github.com/pytest-dev/pytest/actions/workflows/deploy.yml) workflow, using these parameters:
* `Use workflow from`: `release-{version}`.
* `Release version`: `{version}`.
Or execute on the command line:
```console
gh workflow run deploy.yml -r release-{version} -f version={version}
```
After the workflow has been approved by a core maintainer, the package will be uploaded to PyPI automatically.
Once all builds pass and it has been **approved** by one or more maintainers, the build
can be released by pushing a tag `{version}` to this repository.
"""
+1 -3
View File
@@ -7,9 +7,7 @@ def main():
Platform agnostic wrapper script for towncrier.
Fixes the issue (#7251) where windows users are unable to natively run tox -e docs to build pytest docs.
"""
with open(
"doc/en/_changelog_towncrier_draft.rst", "w", encoding="utf-8"
) as draft_file:
with open("doc/en/_changelog_towncrier_draft.rst", "w") as draft_file:
return call(("towncrier", "--draft"), stdout=draft_file)
+18 -67
View File
@@ -5,41 +5,22 @@ from textwrap import dedent
from textwrap import indent
import packaging.version
import platformdirs
import requests
import tabulate
import wcwidth
from requests_cache import CachedResponse
from requests_cache import CachedSession
from requests_cache import OriginalResponse
from requests_cache import SQLiteCache
from tqdm import tqdm
FILE_HEAD = r"""
.. Note this file is autogenerated by scripts/update-plugin-list.py - usually weekly via github action
.. _plugin-list:
Pytest Plugin List
==================
Plugin List
===========
Below is an automated compilation of ``pytest``` plugins available on `PyPI <https://pypi.org>`_.
It includes PyPI projects whose names begin with "pytest-" and a handful of manually selected projects.
PyPI projects that match "pytest-\*" are considered plugins and are listed
automatically together with a manually-maintained list in `the source
code <https://github.com/pytest-dev/pytest/blob/main/scripts/update-plugin-list.py>`_.
Packages classified as inactive are excluded.
For detailed insights into how this list is generated,
please refer to `the update script <https://github.com/pytest-dev/pytest/blob/main/scripts/update-plugin-list.py>`_.
.. warning::
Please be aware that this list is not a curated collection of projects
and does not undergo a systematic review process.
It serves purely as an informational resource to aid in the discovery of ``pytest`` plugins.
Do not presume any endorsement from the ``pytest`` project or its developers,
and always conduct your own quality assessment before incorporating any of these plugins into your own projects.
.. The following conditional uses a different format for this list when
creating a PDF, because otherwise the table gets far too wide for the
page.
@@ -56,8 +37,6 @@ DEVELOPMENT_STATUS_CLASSIFIERS = (
)
ADDITIONAL_PROJECTS = { # set of additional projects to consider as plugins
"logassert",
"nuts",
"flask_fixture",
}
@@ -74,47 +53,19 @@ def escape_rst(text: str) -> str:
return text
def project_response_with_refresh(
session: CachedSession, name: str, last_serial: int
) -> OriginalResponse | CachedResponse:
"""Get a http cached pypi project
force refresh in case of last serial mismatch
"""
response = session.get(f"https://pypi.org/pypi/{name}/json")
if int(response.headers.get("X-PyPI-Last-Serial", -1)) != last_serial:
response = session.get(f"https://pypi.org/pypi/{name}/json", refresh=True)
return response
def get_session() -> CachedSession:
"""Configures the requests-cache session"""
cache_path = platformdirs.user_cache_path("pytest-plugin-list")
cache_path.mkdir(exist_ok=True, parents=True)
cache_file = cache_path.joinpath("http_cache.sqlite3")
return CachedSession(backend=SQLiteCache(cache_file))
def pytest_plugin_projects_from_pypi(session: CachedSession) -> dict[str, int]:
response = session.get(
"https://pypi.org/simple",
headers={"Accept": "application/vnd.pypi.simple.v1+json"},
refresh=True,
)
return {
name: p["_last-serial"]
for p in response.json()["projects"]
if (name := p["name"]).startswith("pytest-") or name in ADDITIONAL_PROJECTS
}
def iter_plugins():
session = get_session()
name_2_serial = pytest_plugin_projects_from_pypi(session)
regex = r">([\d\w-]*)</a>"
response = requests.get("https://pypi.org/simple")
for name, last_serial in tqdm(name_2_serial.items(), smoothing=0):
response = project_response_with_refresh(session, name, last_serial)
match_names = (match.groups()[0] for match in re.finditer(regex, response.text))
plugin_names = [
name
for name in match_names
if name.startswith("pytest-") or name in ADDITIONAL_PROJECTS
]
for name in tqdm(plugin_names, smoothing=0):
response = requests.get(f"https://pypi.org/pypi/{name}/json")
if response.status_code == 404:
# Some packages, like pytest-azurepipelines42, are included in https://pypi.org/simple
# but return 404 on the JSON API. Skip.
@@ -185,7 +136,7 @@ def plugin_definitions(plugins):
def main():
plugins = [*iter_plugins()]
plugins = list(iter_plugins())
reference_dir = pathlib.Path("doc", "en", "reference")
+4 -2
View File
@@ -17,6 +17,7 @@ classifiers =
Operating System :: POSIX
Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
@@ -46,11 +47,12 @@ py_modules = py
install_requires =
iniconfig
packaging
pluggy>=1.3.0,<2.0
pluggy>=0.12,<2.0
colorama;sys_platform=="win32"
exceptiongroup>=1.0.0rc8;python_version<"3.11"
importlib-metadata>=0.12;python_version<"3.8"
tomli>=1.0.0;python_version<"3.11"
python_requires = >=3.8
python_requires = >=3.7
package_dir =
=src
setup_requires =
+20 -88
View File
@@ -17,21 +17,18 @@ from typing import Any
from typing import Callable
from typing import ClassVar
from typing import Dict
from typing import Final
from typing import final
from typing import Generic
from typing import Iterable
from typing import List
from typing import Literal
from typing import Mapping
from typing import Optional
from typing import overload
from typing import Pattern
from typing import Sequence
from typing import Set
from typing import SupportsIndex
from typing import Tuple
from typing import Type
from typing import TYPE_CHECKING
from typing import TypeVar
from typing import Union
@@ -45,16 +42,22 @@ from _pytest._code.source import Source
from _pytest._io import TerminalWriter
from _pytest._io.saferepr import safeformat
from _pytest._io.saferepr import saferepr
from _pytest.compat import final
from _pytest.compat import get_real_func
from _pytest.deprecated import check_ispytest
from _pytest.pathlib import absolutepath
from _pytest.pathlib import bestrelpath
if TYPE_CHECKING:
from typing_extensions import Final
from typing_extensions import Literal
from typing_extensions import SupportsIndex
_TracebackStyle = Literal["long", "short", "line", "no", "native", "value", "auto"]
if sys.version_info[:2] < (3, 11):
from exceptiongroup import BaseExceptionGroup
_TracebackStyle = Literal["long", "short", "line", "no", "native", "value", "auto"]
class Code:
"""Wrapper around Python code objects."""
@@ -393,11 +396,11 @@ class Traceback(List[TracebackEntry]):
def filter(
self,
excinfo_or_fn: Union[
# TODO(py38): change to positional only.
_excinfo_or_fn: Union[
"ExceptionInfo[BaseException]",
Callable[[TracebackEntry], bool],
],
/,
) -> "Traceback":
"""Return a Traceback instance with certain items removed.
@@ -408,10 +411,10 @@ class Traceback(List[TracebackEntry]):
``TracebackEntry`` instance, and should return True when the item should
be added to the ``Traceback``, False when not.
"""
if isinstance(excinfo_or_fn, ExceptionInfo):
fn = lambda x: not x.ishidden(excinfo_or_fn) # noqa: E731
if isinstance(_excinfo_or_fn, ExceptionInfo):
fn = lambda x: not x.ishidden(_excinfo_or_fn) # noqa: E731
else:
fn = excinfo_or_fn
fn = _excinfo_or_fn
return Traceback(filter(fn, self))
def recursionindex(self) -> Optional[int]:
@@ -630,7 +633,7 @@ class ExceptionInfo(Generic[E]):
def getrepr(
self,
showlocals: bool = False,
style: _TracebackStyle = "long",
style: "_TracebackStyle" = "long",
abspath: bool = False,
tbfilter: Union[
bool, Callable[["ExceptionInfo[BaseException]"], Traceback]
@@ -697,14 +700,6 @@ class ExceptionInfo(Generic[E]):
)
return fmt.repr_excinfo(self)
def _stringify_exception(self, exc: BaseException) -> str:
return "\n".join(
[
str(exc),
*getattr(exc, "__notes__", []),
]
)
def match(self, regexp: Union[str, Pattern[str]]) -> "Literal[True]":
"""Check whether the regular expression `regexp` matches the string
representation of the exception using :func:`python:re.search`.
@@ -712,7 +707,7 @@ class ExceptionInfo(Generic[E]):
If it matches `True` is returned, otherwise an `AssertionError` is raised.
"""
__tracebackhide__ = True
value = self._stringify_exception(self.value)
value = str(self.value)
msg = f"Regex pattern did not match.\n Regex: {regexp!r}\n Input: {value!r}"
if regexp == value:
msg += "\n Did you mean to `re.escape()` the regex?"
@@ -720,69 +715,6 @@ class ExceptionInfo(Generic[E]):
# Return True to allow for "assert excinfo.match()".
return True
def _group_contains(
self,
exc_group: BaseExceptionGroup[BaseException],
expected_exception: Union[Type[BaseException], Tuple[Type[BaseException], ...]],
match: Union[str, Pattern[str], None],
target_depth: Optional[int] = None,
current_depth: int = 1,
) -> bool:
"""Return `True` if a `BaseExceptionGroup` contains a matching exception."""
if (target_depth is not None) and (current_depth > target_depth):
# already descended past the target depth
return False
for exc in exc_group.exceptions:
if isinstance(exc, BaseExceptionGroup):
if self._group_contains(
exc, expected_exception, match, target_depth, current_depth + 1
):
return True
if (target_depth is not None) and (current_depth != target_depth):
# not at the target depth, no match
continue
if not isinstance(exc, expected_exception):
continue
if match is not None:
value = self._stringify_exception(exc)
if not re.search(match, value):
continue
return True
return False
def group_contains(
self,
expected_exception: Union[Type[BaseException], Tuple[Type[BaseException], ...]],
*,
match: Union[str, Pattern[str], None] = None,
depth: Optional[int] = None,
) -> bool:
"""Check whether a captured exception group contains a matching exception.
:param Type[BaseException] | Tuple[Type[BaseException]] expected_exception:
The expected exception type, or a tuple if one of multiple possible
exception types are expected.
:param str | Pattern[str] | None match:
If specified, a string containing a regular expression,
or a regular expression object, that is tested against the string
representation of the exception and its `PEP-678 <https://peps.python.org/pep-0678/>` `__notes__`
using :func:`re.search`.
To match a literal string that may contain :ref:`special characters
<re-syntax>`, the pattern can first be escaped with :func:`re.escape`.
:param Optional[int] depth:
If `None`, will search for a matching exception at any nesting depth.
If >= 1, will only match an exception if it's at the specified depth (depth = 1 being
the exceptions contained within the topmost exception group).
"""
msg = "Captured exception is not an instance of `BaseExceptionGroup`"
assert isinstance(self.value, BaseExceptionGroup), msg
msg = "`depth` must be >= 1 if specified"
assert (depth is None) or (depth >= 1), msg
return self._group_contains(self.value, expected_exception, match, depth)
@dataclasses.dataclass
class FormattedExcinfo:
@@ -793,7 +725,7 @@ class FormattedExcinfo:
fail_marker: ClassVar = "E"
showlocals: bool = False
style: _TracebackStyle = "long"
style: "_TracebackStyle" = "long"
abspath: bool = True
tbfilter: Union[bool, Callable[[ExceptionInfo[BaseException]], Traceback]] = True
funcargs: bool = False
@@ -1158,7 +1090,7 @@ class ReprExceptionInfo(ExceptionRepr):
class ReprTraceback(TerminalRepr):
reprentries: Sequence[Union["ReprEntry", "ReprEntryNative"]]
extraline: Optional[str]
style: _TracebackStyle
style: "_TracebackStyle"
entrysep: ClassVar = "_ "
@@ -1192,7 +1124,7 @@ class ReprTracebackNative(ReprTraceback):
class ReprEntryNative(TerminalRepr):
lines: Sequence[str]
style: ClassVar[_TracebackStyle] = "native"
style: ClassVar["_TracebackStyle"] = "native"
def toterminal(self, tw: TerminalWriter) -> None:
tw.write("".join(self.lines))
@@ -1204,7 +1136,7 @@ class ReprEntry(TerminalRepr):
reprfuncargs: Optional["ReprFuncArgs"]
reprlocals: Optional["ReprLocals"]
reprfileloc: Optional["ReprFileLocation"]
style: _TracebackStyle
style: "_TracebackStyle"
def _write_entry_lines(self, tw: TerminalWriter) -> None:
"""Write the source code portions of a list of traceback entries with syntax highlighting.
+2 -1
View File
@@ -149,7 +149,8 @@ def get_statement_startend2(lineno: int, node: ast.AST) -> Tuple[int, Optional[i
values: List[int] = []
for x in ast.walk(node):
if isinstance(x, (ast.stmt, ast.ExceptHandler)):
# The lineno points to the class/def, so need to include the decorators.
# Before Python 3.8, the lineno of a decorated class or function pointed at the decorator.
# Since Python 3.8, the lineno points to the class/def, so need to include the decorators.
if isinstance(x, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)):
for d in x.decorator_list:
values.append(d.lineno - 1)
+5 -12
View File
@@ -2,13 +2,12 @@
import os
import shutil
import sys
from typing import final
from typing import Literal
from typing import Optional
from typing import Sequence
from typing import TextIO
from .wcwidth import wcswidth
from _pytest.compat import final
# This code was initially copied from py 1.8.1, file _io/terminalwriter.py.
@@ -194,21 +193,15 @@ class TerminalWriter:
for indent, new_line in zip(indents, new_lines):
self.line(indent + new_line)
def _highlight(
self, source: str, lexer: Literal["diff", "python"] = "python"
) -> str:
"""Highlight the given source if we have markup support."""
def _highlight(self, source: str) -> str:
"""Highlight the given source code if we have markup support."""
from _pytest.config.exceptions import UsageError
if not self.hasmarkup or not self.code_highlight:
return source
try:
from pygments.formatters.terminal import TerminalFormatter
if lexer == "python":
from pygments.lexers.python import PythonLexer as Lexer
elif lexer == "diff":
from pygments.lexers.diff import DiffLexer as Lexer
from pygments.lexers.python import PythonLexer
from pygments import highlight
import pygments.util
except ImportError:
@@ -217,7 +210,7 @@ class TerminalWriter:
try:
highlighted: str = highlight(
source,
Lexer(),
PythonLexer(),
TerminalFormatter(
bg=os.getenv("PYTEST_THEME_MODE", "dark"),
style=os.getenv("PYTEST_THEME"),
+6 -16
View File
@@ -25,12 +25,14 @@ from stat import S_ISREG
from typing import Any
from typing import Callable
from typing import cast
from typing import Literal
from typing import overload
from typing import TYPE_CHECKING
from . import error
if TYPE_CHECKING:
from typing import Literal
# Moved from local.py.
iswin32 = sys.platform == "win32" or (getattr(os, "_name", False) == "nt")
@@ -755,13 +757,7 @@ class LocalPath:
if ensure:
self.dirpath().ensure(dir=1)
if encoding:
# Using type ignore here because of this error:
# error: Argument 1 has incompatible type overloaded function;
# expected "Callable[[str, Any, Any], TextIOWrapper]" [arg-type]
# Which seems incorrect, given io.open supports the given argument types.
return error.checked_call(
io.open, self.strpath, mode, encoding=encoding # type:ignore[arg-type]
)
return error.checked_call(io.open, self.strpath, mode, encoding=encoding)
return error.checked_call(open, self.strpath, mode)
def _fastjoin(self, name):
@@ -1267,19 +1263,13 @@ class LocalPath:
@classmethod
def mkdtemp(cls, rootdir=None):
"""Return a Path object pointing to a fresh new temporary directory
(which we created ourselves).
(which we created ourself).
"""
import tempfile
if rootdir is None:
rootdir = cls.get_temproot()
# Using type ignore here because of this error:
# error: Argument 1 has incompatible type overloaded function; expected "Callable[[str], str]" [arg-type]
# Which seems incorrect, given tempfile.mkdtemp supports the given argument types.
path = error.checked_call(
tempfile.mkdtemp, dir=str(rootdir) # type:ignore[arg-type]
)
return cls(path)
return cls(error.checked_call(tempfile.mkdtemp, dir=str(rootdir)))
@classmethod
def make_numbered_dir(

Some files were not shown because too many files have changed in this diff Show More