Compare commits
57 Commits
Author | SHA1 | Date |
---|---|---|
|
029b2b6008 | |
|
e53f798932 | |
|
31afeeb0df | |
|
1b00a2f4fb | |
|
ff2f66d84a | |
|
8a8eed609c | |
|
74346f027c | |
|
b7657b4d6b | |
|
feb7c5e12e | |
|
090965574e | |
|
68524d4858 | |
|
d7d320a15a | |
|
93699166dc | |
|
a232abd56c | |
|
92203d2b78 | |
|
f1aa9226ac | |
|
d86d081563 | |
|
c554c3d200 | |
|
a6851e3459 | |
|
e6f6be3bc9 | |
|
23b91d12de | |
|
b188f4d10d | |
|
d60b6b0e28 | |
|
c11cdfabd1 | |
|
368cc6225e | |
|
06e592370e | |
|
a76aa6ff80 | |
|
73371a03da | |
|
eb698a64a0 | |
|
3e48ef64cd | |
|
8b70bb64d3 | |
|
169d775eec | |
|
42f709ed44 | |
|
24c681d4ee | |
|
478f8233bc | |
|
608590097a | |
|
3b41c65c81 | |
|
747072ad26 | |
|
011a475baf | |
|
97960bdd14 | |
|
6be0a3cbf7 | |
|
44ffe07165 | |
|
14ecb04973 | |
|
41c8dabee3 | |
|
6f4cbd7cd4 | |
|
b0c7f923aa | |
|
f15aff06dc | |
|
10c8898845 | |
|
5b7ddedbf9 | |
|
3ae38baca6 | |
|
6123b247d4 | |
|
bb6f5d1b10 | |
|
72eb1b7ad1 | |
|
620a454dba | |
|
838151638e | |
|
665e4e58d3 | |
|
e17d5ec871 |
|
@ -26,3 +26,6 @@ afc607cfd81458d4e4f3b1f3cf8cc931b933907e
|
||||||
|
|
||||||
# move argument parser to own file
|
# move argument parser to own file
|
||||||
c9df77cbd6a365dcb73c39618e4842711817e871
|
c9df77cbd6a365dcb73c39618e4842711817e871
|
||||||
|
|
||||||
|
# Replace reorder-python-imports by isort due to black incompatibility (#11896)
|
||||||
|
8b54596639f41dfac070030ef20394b9001fe63c
|
|
@ -26,7 +26,7 @@ jobs:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Build and Check Package
|
- name: Build and Check Package
|
||||||
uses: hynek/build-and-inspect-python-package@v1.5.4
|
uses: hynek/build-and-inspect-python-package@v2.0.0
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
if: github.repository == 'pytest-dev/pytest'
|
if: github.repository == 'pytest-dev/pytest'
|
||||||
|
@ -41,7 +41,7 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Download Package
|
- name: Download Package
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: Packages
|
name: Packages
|
||||||
path: dist
|
path: dist
|
||||||
|
@ -72,6 +72,12 @@ jobs:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Download Package
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: Packages
|
||||||
|
path: dist
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
|
@ -82,9 +88,14 @@ jobs:
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install --upgrade tox
|
pip install --upgrade tox
|
||||||
|
|
||||||
- name: Publish GitHub release notes
|
- name: Generate release notes
|
||||||
env:
|
|
||||||
GH_RELEASE_NOTES_TOKEN: ${{ github.token }}
|
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get install pandoc
|
sudo apt-get install pandoc
|
||||||
tox -e publish-gh-release-notes
|
tox -e generate-gh-release-notes -- ${{ github.event.inputs.version }} scripts/latest-release-notes.md
|
||||||
|
|
||||||
|
- name: Publish GitHub Release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
body_path: scripts/latest-release-notes.md
|
||||||
|
files: dist/*
|
||||||
|
tag_name: ${{ github.event.inputs.version }}
|
||||||
|
|
|
@ -35,7 +35,7 @@ jobs:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Build and Check Package
|
- name: Build and Check Package
|
||||||
uses: hynek/build-and-inspect-python-package@v1.5.4
|
uses: hynek/build-and-inspect-python-package@v2.0.0
|
||||||
|
|
||||||
build:
|
build:
|
||||||
needs: [package]
|
needs: [package]
|
||||||
|
@ -173,7 +173,7 @@ jobs:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Download Package
|
- name: Download Package
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: Packages
|
name: Packages
|
||||||
path: dist
|
path: dist
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/psf/black
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
rev: 23.12.1
|
rev: "v0.1.15"
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: ruff
|
||||||
args: [--safe, --quiet]
|
args: ["--fix"]
|
||||||
- repo: https://github.com/asottile/blacken-docs
|
- id: ruff-format
|
||||||
rev: 1.16.0
|
|
||||||
hooks:
|
|
||||||
- id: blacken-docs
|
|
||||||
additional_dependencies: [black==23.7.0]
|
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v4.5.0
|
rev: v4.5.0
|
||||||
hooks:
|
hooks:
|
||||||
|
@ -20,32 +16,11 @@ repos:
|
||||||
- id: debug-statements
|
- id: debug-statements
|
||||||
exclude: _pytest/(debugging|hookspec).py
|
exclude: _pytest/(debugging|hookspec).py
|
||||||
language_version: python3
|
language_version: python3
|
||||||
- repo: https://github.com/PyCQA/autoflake
|
- repo: https://github.com/adamchainz/blacken-docs
|
||||||
rev: v2.2.1
|
rev: 1.16.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: autoflake
|
- id: blacken-docs
|
||||||
name: autoflake
|
additional_dependencies: [black==24.1.1]
|
||||||
args: ["--in-place", "--remove-unused-variables", "--remove-all-unused-imports"]
|
|
||||||
language: python
|
|
||||||
files: \.py$
|
|
||||||
- repo: https://github.com/PyCQA/flake8
|
|
||||||
rev: 6.1.0
|
|
||||||
hooks:
|
|
||||||
- id: flake8
|
|
||||||
language_version: python3
|
|
||||||
additional_dependencies:
|
|
||||||
- flake8-typing-imports==1.12.0
|
|
||||||
- flake8-docstrings==1.5.0
|
|
||||||
- repo: https://github.com/asottile/reorder-python-imports
|
|
||||||
rev: v3.12.0
|
|
||||||
hooks:
|
|
||||||
- id: reorder-python-imports
|
|
||||||
args: ['--application-directories=.:src', --py38-plus]
|
|
||||||
- repo: https://github.com/asottile/pyupgrade
|
|
||||||
rev: v3.15.0
|
|
||||||
hooks:
|
|
||||||
- id: pyupgrade
|
|
||||||
args: [--py38-plus]
|
|
||||||
- repo: https://github.com/asottile/setup-cfg-fmt
|
- repo: https://github.com/asottile/setup-cfg-fmt
|
||||||
rev: v2.5.0
|
rev: v2.5.0
|
||||||
hooks:
|
hooks:
|
||||||
|
@ -59,14 +34,16 @@ repos:
|
||||||
rev: v1.8.0
|
rev: v1.8.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: mypy
|
- id: mypy
|
||||||
files: ^(src/|testing/)
|
files: ^(src/|testing/|scripts/)
|
||||||
args: []
|
args: []
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
- iniconfig>=1.1.0
|
- iniconfig>=1.1.0
|
||||||
- attrs>=19.2.0
|
- attrs>=19.2.0
|
||||||
|
- pluggy
|
||||||
- packaging
|
- packaging
|
||||||
- tomli
|
- tomli
|
||||||
- types-pkg_resources
|
- types-pkg_resources
|
||||||
|
- types-tabulate
|
||||||
# for mypy running on python>=3.11 since exceptiongroup is only a dependency
|
# for mypy running on python>=3.11 since exceptiongroup is only a dependency
|
||||||
# on <3.11
|
# on <3.11
|
||||||
- exceptiongroup>=1.0.0rc8
|
- exceptiongroup>=1.0.0rc8
|
||||||
|
|
5
AUTHORS
5
AUTHORS
|
@ -54,6 +54,7 @@ Aviral Verma
|
||||||
Aviv Palivoda
|
Aviv Palivoda
|
||||||
Babak Keyvani
|
Babak Keyvani
|
||||||
Barney Gale
|
Barney Gale
|
||||||
|
Ben Brown
|
||||||
Ben Gartner
|
Ben Gartner
|
||||||
Ben Webb
|
Ben Webb
|
||||||
Benjamin Peterson
|
Benjamin Peterson
|
||||||
|
@ -92,6 +93,7 @@ Christopher Dignam
|
||||||
Christopher Gilling
|
Christopher Gilling
|
||||||
Claire Cecil
|
Claire Cecil
|
||||||
Claudio Madotto
|
Claudio Madotto
|
||||||
|
Clément M.T. Robert
|
||||||
CrazyMerlyn
|
CrazyMerlyn
|
||||||
Cristian Vera
|
Cristian Vera
|
||||||
Cyrus Maden
|
Cyrus Maden
|
||||||
|
@ -137,6 +139,7 @@ Erik Hasse
|
||||||
Erik M. Bray
|
Erik M. Bray
|
||||||
Evan Kepner
|
Evan Kepner
|
||||||
Evgeny Seliverstov
|
Evgeny Seliverstov
|
||||||
|
Fabian Sturm
|
||||||
Fabien Zarifian
|
Fabien Zarifian
|
||||||
Fabio Zadrozny
|
Fabio Zadrozny
|
||||||
Felix Hofstätter
|
Felix Hofstätter
|
||||||
|
@ -181,6 +184,7 @@ Jake VanderPlas
|
||||||
Jakob van Santen
|
Jakob van Santen
|
||||||
Jakub Mitoraj
|
Jakub Mitoraj
|
||||||
James Bourbeau
|
James Bourbeau
|
||||||
|
James Frost
|
||||||
Jan Balster
|
Jan Balster
|
||||||
Janne Vanhala
|
Janne Vanhala
|
||||||
Jason R. Coombs
|
Jason R. Coombs
|
||||||
|
@ -336,6 +340,7 @@ Ronny Pfannschmidt
|
||||||
Ross Lawley
|
Ross Lawley
|
||||||
Ruaridh Williamson
|
Ruaridh Williamson
|
||||||
Russel Winder
|
Russel Winder
|
||||||
|
Russell Martin
|
||||||
Ryan Puddephatt
|
Ryan Puddephatt
|
||||||
Ryan Wooden
|
Ryan Wooden
|
||||||
Sadra Barikbin
|
Sadra Barikbin
|
||||||
|
|
|
@ -27,9 +27,6 @@
|
||||||
:target: https://results.pre-commit.ci/latest/github/pytest-dev/pytest/main
|
:target: https://results.pre-commit.ci/latest/github/pytest-dev/pytest/main
|
||||||
:alt: pre-commit.ci status
|
:alt: pre-commit.ci status
|
||||||
|
|
||||||
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
|
|
||||||
:target: https://github.com/psf/black
|
|
||||||
|
|
||||||
.. image:: https://www.codetriage.com/pytest-dev/pytest/badges/users.svg
|
.. image:: https://www.codetriage.com/pytest-dev/pytest/badges/users.svg
|
||||||
:target: https://www.codetriage.com/pytest-dev/pytest
|
:target: https://www.codetriage.com/pytest-dev/pytest
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import cProfile
|
import cProfile
|
||||||
import pytest # NOQA
|
|
||||||
import pstats
|
import pstats
|
||||||
|
|
||||||
|
import pytest # NOQA
|
||||||
|
|
||||||
script = sys.argv[1:] if len(sys.argv) > 1 else ["empty.py"]
|
script = sys.argv[1:] if len(sys.argv) > 1 else ["empty.py"]
|
||||||
cProfile.run("pytest.cmdline.main(%r)" % script, "prof")
|
cProfile.run("pytest.cmdline.main(%r)" % script, "prof")
|
||||||
p = pstats.Stats("prof")
|
p = pstats.Stats("prof")
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
# FastFilesCompleter 0.7383 1.0760
|
# FastFilesCompleter 0.7383 1.0760
|
||||||
import timeit
|
import timeit
|
||||||
|
|
||||||
|
|
||||||
imports = [
|
imports = [
|
||||||
"from argcomplete.completers import FilesCompleter as completer",
|
"from argcomplete.completers import FilesCompleter as completer",
|
||||||
"from _pytest._argcomplete import FastFilesCompleter as completer",
|
"from _pytest._argcomplete import FastFilesCompleter as completer",
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
SKIP = True
|
SKIP = True
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from unittest import TestCase # noqa: F401
|
from unittest import TestCase # noqa: F401
|
||||||
|
|
||||||
|
|
||||||
for i in range(15000):
|
for i in range(15000):
|
||||||
exec(
|
exec(
|
||||||
f"""
|
f"""
|
||||||
|
|
|
@ -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 +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.
|
|
|
@ -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 ``>``.
|
|
|
@ -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 +0,0 @@
|
||||||
Added a warning about modifying the root logger during tests when using ``caplog``.
|
|
|
@ -1,3 +0,0 @@
|
||||||
Use pytestconfig instead of request.config in cache example
|
|
||||||
|
|
||||||
to be consistent with the API documentation.
|
|
|
@ -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.
|
|
|
@ -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 +0,0 @@
|
||||||
Dropped support for Python 3.7, which `reached end-of-life on 2023-06-27 <https://devguide.python.org/versions/>`__.
|
|
|
@ -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 +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.
|
|
|
@ -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 +0,0 @@
|
||||||
Allow :func:`pytest.raises` ``match`` argument to match against `PEP-678 <https://peps.python.org/pep-0678/>` ``__notes__``.
|
|
|
@ -1 +0,0 @@
|
||||||
Fixed crash on `parametrize(..., scope="package")` without a package present.
|
|
|
@ -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.
|
|
|
@ -1,11 +0,0 @@
|
||||||
Sanitized the handling of the ``default`` parameter when defining configuration options.
|
|
||||||
|
|
||||||
Previously if ``default`` was not supplied for :meth:`parser.addini <pytest.Parser.addini>` and the configuration option value was not defined in a test session, then calls to :func:`config.getini <pytest.Config.getini>` returned an *empty list* or an *empty string* depending on whether ``type`` was supplied or not respectively, which is clearly incorrect. Also, ``None`` was not honored even if ``default=None`` was used explicitly while defining the option.
|
|
||||||
|
|
||||||
Now the behavior of :meth:`parser.addini <pytest.Parser.addini>` is as follows:
|
|
||||||
|
|
||||||
* If ``default`` is NOT passed but ``type`` is provided, then a type-specific default will be returned. For example ``type=bool`` will return ``False``, ``type=str`` will return ``""``, etc.
|
|
||||||
* If ``default=None`` is passed and the option is not defined in a test session, then ``None`` will be returned, regardless of the ``type``.
|
|
||||||
* If neither ``default`` nor ``type`` are provided, assume ``type=str`` and return ``""`` as default (this is as per previous behavior).
|
|
||||||
|
|
||||||
The team decided to not introduce a deprecation period for this change, as doing so would be complicated both in terms of communicating this to the community as well as implementing it, and also because the team believes this change should not break existing plugins except in rare cases.
|
|
|
@ -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.
|
|
|
@ -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.
|
|
|
@ -1,2 +0,0 @@
|
||||||
Corrected the spelling of ``Config.ArgsSource.INVOCATION_DIR``.
|
|
||||||
The previous spelling ``INCOVATION_DIR`` remains as an alias.
|
|
|
@ -1 +0,0 @@
|
||||||
pluggy>=1.3.0 is now required. This adds typing to :class:`~pytest.PytestPluginManager`.
|
|
|
@ -1,5 +0,0 @@
|
||||||
Added the new :confval:`verbosity_assertions` configuration option for fine-grained control of failed assertions verbosity.
|
|
||||||
|
|
||||||
See :ref:`Fine-grained verbosity <pytest.fine_grained_verbosity>` for more details.
|
|
||||||
|
|
||||||
For plugin authors, :attr:`config.get_verbosity <pytest.Config.get_verbosity>` can be used to retrieve the verbosity level for a specific verbosity type.
|
|
|
@ -1 +0,0 @@
|
||||||
:func:`pytest.deprecated_call` now also considers warnings of type :class:`FutureWarning`.
|
|
|
@ -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,5 +0,0 @@
|
||||||
Improved very verbose diff output to color it as a diff instead of only red.
|
|
||||||
|
|
||||||
Improved the error reporting to better separate each section.
|
|
||||||
|
|
||||||
Improved the error reporting to syntax-highlight Python code when Pygments is available.
|
|
|
@ -1 +0,0 @@
|
||||||
Fixed crash when using an empty string for the same parametrized value more than once.
|
|
|
@ -1 +0,0 @@
|
||||||
Improved the documentation and type signature for :func:`pytest.mark.xfail <pytest.mark.xfail>`'s ``condition`` param to use ``False`` as the default value.
|
|
|
@ -1,2 +0,0 @@
|
||||||
Added :func:`LogCaptureFixture.filtering() <pytest.LogCaptureFixture.filtering>` context manager that
|
|
||||||
adds a given :class:`logging.Filter` object to the caplog fixture.
|
|
|
@ -1 +0,0 @@
|
||||||
Fixed the selftests to pass correctly if ``FORCE_COLOR``, ``NO_COLOR`` or ``PY_COLORS`` is set in the calling environment.
|
|
|
@ -1,3 +0,0 @@
|
||||||
pytest's ``setup.py`` file is removed.
|
|
||||||
If you relied on this file, e.g. to install pytest using ``setup.py install``,
|
|
||||||
please see `Why you shouldn't invoke setup.py directly <https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html#summary>`_ for alternatives.
|
|
|
@ -1,3 +0,0 @@
|
||||||
The classes :class:`~_pytest.nodes.Node`, :class:`~pytest.Collector`, :class:`~pytest.Item`, :class:`~pytest.File`, :class:`~_pytest.nodes.FSCollector` are now marked abstract (see :mod:`abc`).
|
|
||||||
|
|
||||||
We do not expect this change to affect users and plugin authors, it will only cause errors when the code is already wrong or problematic.
|
|
|
@ -1 +0,0 @@
|
||||||
Fixed handling ``NO_COLOR`` and ``FORCE_COLOR`` to ignore an empty value.
|
|
|
@ -0,0 +1 @@
|
||||||
|
The documentation webpages now links to a canonical version to reduce outdated documentation in search engine results.
|
|
@ -1,4 +0,0 @@
|
||||||
Improved the very verbose diff for every standard library container types: the indentation is now consistent and the markers are on their own separate lines, which should reduce the diffs shown to users.
|
|
||||||
|
|
||||||
Previously, the default python pretty printer was used to generate the output, which puts opening and closing
|
|
||||||
markers on the same line as the first/last entry, in addition to not having consistent indentation.
|
|
|
@ -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.
|
|
|
@ -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 +0,0 @@
|
||||||
:class:`~pytest.FixtureDef` is now exported as ``pytest.FixtureDef`` for typing purposes.
|
|
|
@ -1,90 +0,0 @@
|
||||||
Added a new :class:`pytest.Directory` base collection node, which all collector nodes for filesystem directories are expected to subclass.
|
|
||||||
This is analogous to the existing :class:`pytest.File` for file nodes.
|
|
||||||
|
|
||||||
Changed :class:`pytest.Package` to be a subclass of :class:`pytest.Directory`.
|
|
||||||
A ``Package`` represents a filesystem directory which is a Python package,
|
|
||||||
i.e. contains an ``__init__.py`` file.
|
|
||||||
|
|
||||||
:class:`pytest.Package` now only collects files in its own directory; previously it collected recursively.
|
|
||||||
Sub-directories are collected as sub-collector nodes, thus creating a collection tree which mirrors the filesystem hierarchy.
|
|
||||||
|
|
||||||
Added a new :class:`pytest.Dir` concrete collection node, a subclass of :class:`pytest.Directory`.
|
|
||||||
This node represents a filesystem directory, which is not a :class:`pytest.Package`,
|
|
||||||
i.e. does not contain an ``__init__.py`` file.
|
|
||||||
Similarly to ``Package``, it only collects the files in its own directory,
|
|
||||||
while collecting sub-directories as sub-collector nodes.
|
|
||||||
|
|
||||||
Added a new hook :hook:`pytest_collect_directory`,
|
|
||||||
which is called by filesystem-traversing collector nodes,
|
|
||||||
such as :class:`pytest.Session`, :class:`pytest.Dir` and :class:`pytest.Package`,
|
|
||||||
to create a collector node for a sub-directory.
|
|
||||||
It is expected to return a subclass of :class:`pytest.Directory`.
|
|
||||||
This hook allows plugins to :ref:`customize the collection of directories <custom directory collectors>`.
|
|
||||||
|
|
||||||
:class:`pytest.Session` now only collects the initial arguments, without recursing into directories.
|
|
||||||
This work is now done by the :func:`recursive expansion process <pytest.Collector.collect>` of directory collector nodes.
|
|
||||||
|
|
||||||
:attr:`session.name <pytest.Session.name>` is now ``""``; previously it was the rootdir directory name.
|
|
||||||
This matches :attr:`session.nodeid <_pytest.nodes.Node.nodeid>` which has always been `""`.
|
|
||||||
|
|
||||||
Files and directories are now collected in alphabetical order jointly, unless changed by a plugin.
|
|
||||||
Previously, files were collected before directories.
|
|
||||||
|
|
||||||
The collection tree now contains directories/packages up to the :ref:`rootdir <rootdir>`,
|
|
||||||
for initial arguments that are found within the rootdir.
|
|
||||||
For files outside the rootdir, only the immediate directory/package is collected --
|
|
||||||
note however that collecting from outside the rootdir is discouraged.
|
|
||||||
|
|
||||||
As an example, given the following filesystem tree::
|
|
||||||
|
|
||||||
myroot/
|
|
||||||
pytest.ini
|
|
||||||
top/
|
|
||||||
├── aaa
|
|
||||||
│ └── test_aaa.py
|
|
||||||
├── test_a.py
|
|
||||||
├── test_b
|
|
||||||
│ ├── __init__.py
|
|
||||||
│ └── test_b.py
|
|
||||||
├── test_c.py
|
|
||||||
└── zzz
|
|
||||||
├── __init__.py
|
|
||||||
└── test_zzz.py
|
|
||||||
|
|
||||||
the collection tree, as shown by `pytest --collect-only top/` but with the otherwise-hidden :class:`~pytest.Session` node added for clarity,
|
|
||||||
is now the following::
|
|
||||||
|
|
||||||
<Session>
|
|
||||||
<Dir myroot>
|
|
||||||
<Dir top>
|
|
||||||
<Dir aaa>
|
|
||||||
<Module test_aaa.py>
|
|
||||||
<Function test_it>
|
|
||||||
<Module test_a.py>
|
|
||||||
<Function test_it>
|
|
||||||
<Package test_b>
|
|
||||||
<Module test_b.py>
|
|
||||||
<Function test_it>
|
|
||||||
<Module test_c.py>
|
|
||||||
<Function test_it>
|
|
||||||
<Package zzz>
|
|
||||||
<Module test_zzz.py>
|
|
||||||
<Function test_it>
|
|
||||||
|
|
||||||
Previously, it was::
|
|
||||||
|
|
||||||
<Session>
|
|
||||||
<Module top/test_a.py>
|
|
||||||
<Function test_it>
|
|
||||||
<Module top/test_c.py>
|
|
||||||
<Function test_it>
|
|
||||||
<Module top/aaa/test_aaa.py>
|
|
||||||
<Function test_it>
|
|
||||||
<Package test_b>
|
|
||||||
<Module test_b.py>
|
|
||||||
<Function test_it>
|
|
||||||
<Package zzz>
|
|
||||||
<Module test_zzz.py>
|
|
||||||
<Function test_it>
|
|
||||||
|
|
||||||
Code/plugins which rely on a specific shape of the collection tree might need to update.
|
|
|
@ -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 +0,0 @@
|
||||||
``pytest.warns`` and similar functions now capture warnings when an exception is raised inside a ``with`` block.
|
|
|
@ -1,7 +0,0 @@
|
||||||
:func:`~pytest.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.
|
|
|
@ -5,11 +5,10 @@
|
||||||
<div id="searchbox" style="display: none" role="search">
|
<div id="searchbox" style="display: none" role="search">
|
||||||
<div class="searchformwrapper">
|
<div class="searchformwrapper">
|
||||||
<form class="search" action="{{ pathto('search') }}" method="get">
|
<form class="search" action="{{ pathto('search') }}" method="get">
|
||||||
<input type="text" name="q" aria-labelledby="searchlabel"
|
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
|
||||||
placeholder="Search"/>
|
|
||||||
<input type="submit" value="{{ _('Go') }}" />
|
<input type="submit" value="{{ _('Go') }}" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
<script>document.getElementById('searchbox').style.display = "block"</script>
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
|
@ -6,6 +6,11 @@ Release announcements
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
|
|
||||||
|
release-8.0.2
|
||||||
|
release-8.0.1
|
||||||
|
release-8.0.0
|
||||||
|
release-8.0.0rc2
|
||||||
|
release-8.0.0rc1
|
||||||
release-7.4.4
|
release-7.4.4
|
||||||
release-7.4.3
|
release-7.4.3
|
||||||
release-7.4.2
|
release-7.4.2
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
pytest-8.0.0
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
The pytest team is proud to announce the 8.0.0 release!
|
||||||
|
|
||||||
|
This release contains new features, improvements, bug fixes, and breaking changes, so users
|
||||||
|
are encouraged to take a look at the CHANGELOG carefully:
|
||||||
|
|
||||||
|
https://docs.pytest.org/en/stable/changelog.html
|
||||||
|
|
||||||
|
For complete documentation, please visit:
|
||||||
|
|
||||||
|
https://docs.pytest.org/en/stable/
|
||||||
|
|
||||||
|
As usual, you can upgrade from PyPI via:
|
||||||
|
|
||||||
|
pip install -U pytest
|
||||||
|
|
||||||
|
Thanks to all of the contributors to this release:
|
||||||
|
|
||||||
|
* Bruno Oliveira
|
||||||
|
* Ran Benita
|
||||||
|
|
||||||
|
|
||||||
|
Happy testing,
|
||||||
|
The pytest Development Team
|
|
@ -0,0 +1,82 @@
|
||||||
|
pytest-8.0.0rc1
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
The pytest team is proud to announce the 8.0.0rc1 release!
|
||||||
|
|
||||||
|
This release contains new features, improvements, bug fixes, and breaking changes, so users
|
||||||
|
are encouraged to take a look at the CHANGELOG carefully:
|
||||||
|
|
||||||
|
https://docs.pytest.org/en/stable/changelog.html
|
||||||
|
|
||||||
|
For complete documentation, please visit:
|
||||||
|
|
||||||
|
https://docs.pytest.org/en/stable/
|
||||||
|
|
||||||
|
As usual, you can upgrade from PyPI via:
|
||||||
|
|
||||||
|
pip install -U pytest
|
||||||
|
|
||||||
|
Thanks to all of the contributors to this release:
|
||||||
|
|
||||||
|
* Akhilesh Ramakrishnan
|
||||||
|
* Aleksandr Brodin
|
||||||
|
* Anthony Sottile
|
||||||
|
* Arthur Richard
|
||||||
|
* Avasam
|
||||||
|
* Benjamin Schubert
|
||||||
|
* Bruno Oliveira
|
||||||
|
* Carsten Grohmann
|
||||||
|
* Cheukting
|
||||||
|
* Chris Mahoney
|
||||||
|
* Christoph Anton Mitterer
|
||||||
|
* DetachHead
|
||||||
|
* Erik Hasse
|
||||||
|
* Florian Bruhin
|
||||||
|
* Fraser Stark
|
||||||
|
* Ha Pam
|
||||||
|
* Hugo van Kemenade
|
||||||
|
* Isaac Virshup
|
||||||
|
* Israel Fruchter
|
||||||
|
* Jens Tröger
|
||||||
|
* Jon Parise
|
||||||
|
* Kenny Y
|
||||||
|
* Lesnek
|
||||||
|
* Marc Mueller
|
||||||
|
* Michał Górny
|
||||||
|
* Mihail Milushev
|
||||||
|
* Milan Lesnek
|
||||||
|
* Miro Hrončok
|
||||||
|
* Patrick Lannigan
|
||||||
|
* Ran Benita
|
||||||
|
* Reagan Lee
|
||||||
|
* Ronny Pfannschmidt
|
||||||
|
* Sadra Barikbin
|
||||||
|
* Sean Malloy
|
||||||
|
* Sean Patrick Malloy
|
||||||
|
* Sharad Nair
|
||||||
|
* Simon Blanchard
|
||||||
|
* Sourabh Beniwal
|
||||||
|
* Stefaan Lippens
|
||||||
|
* Tanya Agarwal
|
||||||
|
* Thomas Grainger
|
||||||
|
* Tom Mortimer-Jones
|
||||||
|
* Tushar Sadhwani
|
||||||
|
* Tyler Smart
|
||||||
|
* Uday Kumar
|
||||||
|
* Warren Markham
|
||||||
|
* WarrenTheRabbit
|
||||||
|
* Zac Hatfield-Dodds
|
||||||
|
* Ziad Kermadi
|
||||||
|
* akhilramkee
|
||||||
|
* antosikv
|
||||||
|
* bowugit
|
||||||
|
* mickeypash
|
||||||
|
* neilmartin2000
|
||||||
|
* pomponchik
|
||||||
|
* ryanpudd
|
||||||
|
* touilleWoman
|
||||||
|
* ubaumann
|
||||||
|
|
||||||
|
|
||||||
|
Happy testing,
|
||||||
|
The pytest Development Team
|
|
@ -0,0 +1,32 @@
|
||||||
|
pytest-8.0.0rc2
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
The pytest team is proud to announce the 8.0.0rc2 prerelease!
|
||||||
|
|
||||||
|
This is a prerelease, not intended for production use, but to test the upcoming features and improvements
|
||||||
|
in order to catch any major problems before the final version is released to the major public.
|
||||||
|
|
||||||
|
We appreciate your help testing this out before the final release, making sure to report any
|
||||||
|
regressions to our issue tracker:
|
||||||
|
|
||||||
|
https://github.com/pytest-dev/pytest/issues
|
||||||
|
|
||||||
|
When doing so, please include the string ``[prerelease]`` in the title.
|
||||||
|
|
||||||
|
You can upgrade from PyPI via:
|
||||||
|
|
||||||
|
pip install pytest==8.0.0rc2
|
||||||
|
|
||||||
|
Users are encouraged to take a look at the CHANGELOG carefully:
|
||||||
|
|
||||||
|
https://docs.pytest.org/en/release-8.0.0rc2/changelog.html
|
||||||
|
|
||||||
|
Thanks to all the contributors to this release:
|
||||||
|
|
||||||
|
* Ben Brown
|
||||||
|
* Bruno Oliveira
|
||||||
|
* Ran Benita
|
||||||
|
|
||||||
|
|
||||||
|
Happy testing,
|
||||||
|
The pytest Development Team
|
|
@ -0,0 +1,21 @@
|
||||||
|
pytest-8.0.1
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
pytest 8.0.1 has just been released to PyPI.
|
||||||
|
|
||||||
|
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||||
|
|
||||||
|
pip install --upgrade pytest
|
||||||
|
|
||||||
|
The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
|
||||||
|
|
||||||
|
Thanks to all of the contributors to this release:
|
||||||
|
|
||||||
|
* Bruno Oliveira
|
||||||
|
* Clément Robert
|
||||||
|
* Pierre Sassoulas
|
||||||
|
* Ran Benita
|
||||||
|
|
||||||
|
|
||||||
|
Happy testing,
|
||||||
|
The pytest Development Team
|
|
@ -0,0 +1,18 @@
|
||||||
|
pytest-8.0.2
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
pytest 8.0.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:
|
||||||
|
|
||||||
|
* Ran Benita
|
||||||
|
|
||||||
|
|
||||||
|
Happy testing,
|
||||||
|
The pytest Development Team
|
|
@ -18,11 +18,11 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||||
|
|
||||||
$ pytest --fixtures -v
|
$ pytest --fixtures -v
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
||||||
cachedir: .pytest_cache
|
cachedir: .pytest_cache
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 0 items
|
collected 0 items
|
||||||
cache -- .../_pytest/cacheprovider.py:532
|
cache -- .../_pytest/cacheprovider.py:527
|
||||||
Return a cache object that can persist state between testing sessions.
|
Return a cache object that can persist state between testing sessions.
|
||||||
|
|
||||||
cache.get(key, default)
|
cache.get(key, default)
|
||||||
|
@ -33,7 +33,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||||
|
|
||||||
Values can be any object handled by the json stdlib module.
|
Values can be any object handled by the json stdlib module.
|
||||||
|
|
||||||
capsysbinary -- .../_pytest/capture.py:1001
|
capsysbinary -- .../_pytest/capture.py:1007
|
||||||
Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``.
|
Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``.
|
||||||
|
|
||||||
The captured output is made available via ``capsysbinary.readouterr()``
|
The captured output is made available via ``capsysbinary.readouterr()``
|
||||||
|
@ -43,7 +43,6 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||||
Returns an instance of :class:`CaptureFixture[bytes] <pytest.CaptureFixture>`.
|
Returns an instance of :class:`CaptureFixture[bytes] <pytest.CaptureFixture>`.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
def test_output(capsysbinary):
|
def test_output(capsysbinary):
|
||||||
|
@ -51,7 +50,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||||
captured = capsysbinary.readouterr()
|
captured = capsysbinary.readouterr()
|
||||||
assert captured.out == b"hello\n"
|
assert captured.out == b"hello\n"
|
||||||
|
|
||||||
capfd -- .../_pytest/capture.py:1029
|
capfd -- .../_pytest/capture.py:1034
|
||||||
Enable text capturing of writes to file descriptors ``1`` and ``2``.
|
Enable text capturing of writes to file descriptors ``1`` and ``2``.
|
||||||
|
|
||||||
The captured output is made available via ``capfd.readouterr()`` method
|
The captured output is made available via ``capfd.readouterr()`` method
|
||||||
|
@ -61,7 +60,6 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||||
Returns an instance of :class:`CaptureFixture[str] <pytest.CaptureFixture>`.
|
Returns an instance of :class:`CaptureFixture[str] <pytest.CaptureFixture>`.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
def test_system_echo(capfd):
|
def test_system_echo(capfd):
|
||||||
|
@ -69,7 +67,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||||
captured = capfd.readouterr()
|
captured = capfd.readouterr()
|
||||||
assert captured.out == "hello\n"
|
assert captured.out == "hello\n"
|
||||||
|
|
||||||
capfdbinary -- .../_pytest/capture.py:1057
|
capfdbinary -- .../_pytest/capture.py:1061
|
||||||
Enable bytes capturing of writes to file descriptors ``1`` and ``2``.
|
Enable bytes capturing of writes to file descriptors ``1`` and ``2``.
|
||||||
|
|
||||||
The captured output is made available via ``capfd.readouterr()`` method
|
The captured output is made available via ``capfd.readouterr()`` method
|
||||||
|
@ -79,7 +77,6 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||||
Returns an instance of :class:`CaptureFixture[bytes] <pytest.CaptureFixture>`.
|
Returns an instance of :class:`CaptureFixture[bytes] <pytest.CaptureFixture>`.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
def test_system_echo(capfdbinary):
|
def test_system_echo(capfdbinary):
|
||||||
|
@ -87,7 +84,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||||
captured = capfdbinary.readouterr()
|
captured = capfdbinary.readouterr()
|
||||||
assert captured.out == b"hello\n"
|
assert captured.out == b"hello\n"
|
||||||
|
|
||||||
capsys -- .../_pytest/capture.py:973
|
capsys -- .../_pytest/capture.py:980
|
||||||
Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``.
|
Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``.
|
||||||
|
|
||||||
The captured output is made available via ``capsys.readouterr()`` method
|
The captured output is made available via ``capsys.readouterr()`` method
|
||||||
|
@ -97,7 +94,6 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||||
Returns an instance of :class:`CaptureFixture[str] <pytest.CaptureFixture>`.
|
Returns an instance of :class:`CaptureFixture[str] <pytest.CaptureFixture>`.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
def test_output(capsys):
|
def test_output(capsys):
|
||||||
|
@ -105,7 +101,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
assert captured.out == "hello\n"
|
assert captured.out == "hello\n"
|
||||||
|
|
||||||
doctest_namespace [session scope] -- .../_pytest/doctest.py:757
|
doctest_namespace [session scope] -- .../_pytest/doctest.py:745
|
||||||
Fixture that returns a :py:class:`dict` that will be injected into the
|
Fixture that returns a :py:class:`dict` that will be injected into the
|
||||||
namespace of doctests.
|
namespace of doctests.
|
||||||
|
|
||||||
|
@ -119,7 +115,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||||
|
|
||||||
For more details: :ref:`doctest_namespace`.
|
For more details: :ref:`doctest_namespace`.
|
||||||
|
|
||||||
pytestconfig [session scope] -- .../_pytest/fixtures.py:1353
|
pytestconfig [session scope] -- .../_pytest/fixtures.py:1354
|
||||||
Session-scoped fixture that returns the session's :class:`pytest.Config`
|
Session-scoped fixture that returns the session's :class:`pytest.Config`
|
||||||
object.
|
object.
|
||||||
|
|
||||||
|
@ -129,7 +125,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||||
if pytestconfig.getoption("verbose") > 0:
|
if pytestconfig.getoption("verbose") > 0:
|
||||||
...
|
...
|
||||||
|
|
||||||
record_property -- .../_pytest/junitxml.py:282
|
record_property -- .../_pytest/junitxml.py:283
|
||||||
Add extra properties to the calling test.
|
Add extra properties to the calling test.
|
||||||
|
|
||||||
User properties become part of the test report and are available to the
|
User properties become part of the test report and are available to the
|
||||||
|
@ -143,13 +139,13 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||||
def test_function(record_property):
|
def test_function(record_property):
|
||||||
record_property("example_key", 1)
|
record_property("example_key", 1)
|
||||||
|
|
||||||
record_xml_attribute -- .../_pytest/junitxml.py:305
|
record_xml_attribute -- .../_pytest/junitxml.py:306
|
||||||
Add extra xml attributes to the tag for the calling test.
|
Add extra xml attributes to the tag for the calling test.
|
||||||
|
|
||||||
The fixture is callable with ``name, value``. The value is
|
The fixture is callable with ``name, value``. The value is
|
||||||
automatically XML-encoded.
|
automatically XML-encoded.
|
||||||
|
|
||||||
record_testsuite_property [session scope] -- .../_pytest/junitxml.py:343
|
record_testsuite_property [session scope] -- .../_pytest/junitxml.py:344
|
||||||
Record a new ``<property>`` tag as child of the root ``<testsuite>``.
|
Record a new ``<property>`` tag as child of the root ``<testsuite>``.
|
||||||
|
|
||||||
This is suitable to writing global information regarding the entire test
|
This is suitable to writing global information regarding the entire test
|
||||||
|
@ -196,7 +192,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||||
|
|
||||||
.. _legacy_path: https://py.readthedocs.io/en/latest/path.html
|
.. _legacy_path: https://py.readthedocs.io/en/latest/path.html
|
||||||
|
|
||||||
caplog -- .../_pytest/logging.py:570
|
caplog -- .../_pytest/logging.py:594
|
||||||
Access and control log capturing.
|
Access and control log capturing.
|
||||||
|
|
||||||
Captured logs are available through the following properties/methods::
|
Captured logs are available through the following properties/methods::
|
||||||
|
@ -207,7 +203,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||||
* caplog.record_tuples -> list of (logger_name, level, message) tuples
|
* caplog.record_tuples -> list of (logger_name, level, message) tuples
|
||||||
* caplog.clear() -> clear captured records and formatted log output string
|
* caplog.clear() -> clear captured records and formatted log output string
|
||||||
|
|
||||||
monkeypatch -- .../_pytest/monkeypatch.py:30
|
monkeypatch -- .../_pytest/monkeypatch.py:32
|
||||||
A convenient fixture for monkey-patching.
|
A convenient fixture for monkey-patching.
|
||||||
|
|
||||||
The fixture provides these methods to modify objects, dictionaries, or
|
The fixture provides these methods to modify objects, dictionaries, or
|
||||||
|
@ -231,16 +227,16 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||||
To undo modifications done by the fixture in a contained scope,
|
To undo modifications done by the fixture in a contained scope,
|
||||||
use :meth:`context() <pytest.MonkeyPatch.context>`.
|
use :meth:`context() <pytest.MonkeyPatch.context>`.
|
||||||
|
|
||||||
recwarn -- .../_pytest/recwarn.py:30
|
recwarn -- .../_pytest/recwarn.py:32
|
||||||
Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions.
|
Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions.
|
||||||
|
|
||||||
See https://docs.pytest.org/en/latest/how-to/capture-warnings.html for information
|
See https://docs.pytest.org/en/latest/how-to/capture-warnings.html for information
|
||||||
on warning categories.
|
on warning categories.
|
||||||
|
|
||||||
tmp_path_factory [session scope] -- .../_pytest/tmpdir.py:245
|
tmp_path_factory [session scope] -- .../_pytest/tmpdir.py:241
|
||||||
Return a :class:`pytest.TempPathFactory` instance for the test session.
|
Return a :class:`pytest.TempPathFactory` instance for the test session.
|
||||||
|
|
||||||
tmp_path -- .../_pytest/tmpdir.py:260
|
tmp_path -- .../_pytest/tmpdir.py:256
|
||||||
Return a temporary directory path object which is unique to each test
|
Return a temporary directory path object which is unique to each test
|
||||||
function invocation, created as a sub directory of the base temporary
|
function invocation, created as a sub directory of the base temporary
|
||||||
directory.
|
directory.
|
||||||
|
|
|
@ -28,6 +28,465 @@ with advance notice in the **Deprecations** section of releases.
|
||||||
|
|
||||||
.. towncrier release notes start
|
.. towncrier release notes start
|
||||||
|
|
||||||
|
pytest 8.0.2 (2024-02-24)
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Bug Fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
- `#11895 <https://github.com/pytest-dev/pytest/issues/11895>`_: Fix collection on Windows where initial paths contain the short version of a path (for example ``c:\PROGRA~1\tests``).
|
||||||
|
|
||||||
|
|
||||||
|
- `#11953 <https://github.com/pytest-dev/pytest/issues/11953>`_: Fix an ``IndexError`` crash raising from ``getstatementrange_ast``.
|
||||||
|
|
||||||
|
|
||||||
|
- `#12021 <https://github.com/pytest-dev/pytest/issues/12021>`_: Reverted a fix to `--maxfail` handling in pytest 8.0.0 because it caused a regression in pytest-xdist whereby session fixture teardowns may get executed multiple times when the max-fails is reached.
|
||||||
|
|
||||||
|
|
||||||
|
pytest 8.0.1 (2024-02-16)
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Bug Fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
- `#11875 <https://github.com/pytest-dev/pytest/issues/11875>`_: Correctly handle errors from :func:`getpass.getuser` in Python 3.13.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11879 <https://github.com/pytest-dev/pytest/issues/11879>`_: Fix an edge case where ``ExceptionInfo._stringify_exception`` could crash :func:`pytest.raises`.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11906 <https://github.com/pytest-dev/pytest/issues/11906>`_: Fix regression with :func:`pytest.warns` using custom warning subclasses which have more than one parameter in their `__init__`.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11907 <https://github.com/pytest-dev/pytest/issues/11907>`_: Fix a regression in pytest 8.0.0 whereby calling :func:`pytest.skip` and similar control-flow exceptions within a :func:`pytest.warns()` block would get suppressed instead of propagating.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11929 <https://github.com/pytest-dev/pytest/issues/11929>`_: Fix a regression in pytest 8.0.0 whereby autouse fixtures defined in a module get ignored by the doctests in the module.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11937 <https://github.com/pytest-dev/pytest/issues/11937>`_: Fix a regression in pytest 8.0.0 whereby items would be collected in reverse order in some circumstances.
|
||||||
|
|
||||||
|
|
||||||
|
pytest 8.0.0 (2024-01-27)
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Bug Fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
- `#11842 <https://github.com/pytest-dev/pytest/issues/11842>`_: Properly escape the ``reason`` of a :ref:`skip <pytest.mark.skip ref>` mark when writing JUnit XML files.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11861 <https://github.com/pytest-dev/pytest/issues/11861>`_: Avoid microsecond exceeds ``1_000_000`` when using ``log-date-format`` with ``%f`` specifier, which might cause the test suite to crash.
|
||||||
|
|
||||||
|
|
||||||
|
pytest 8.0.0rc2 (2024-01-17)
|
||||||
|
============================
|
||||||
|
|
||||||
|
|
||||||
|
Improvements
|
||||||
|
------------
|
||||||
|
|
||||||
|
- `#11233 <https://github.com/pytest-dev/pytest/issues/11233>`_: Improvements to ``-r`` for xfailures and xpasses:
|
||||||
|
|
||||||
|
* Report tracebacks for xfailures when ``-rx`` is set.
|
||||||
|
* Report captured output for xpasses when ``-rX`` is set.
|
||||||
|
* For xpasses, add ``-`` in summary between test name and reason, to match how xfail is displayed.
|
||||||
|
|
||||||
|
- `#11825 <https://github.com/pytest-dev/pytest/issues/11825>`_: The :hook:`pytest_plugin_registered` hook has a new ``plugin_name`` parameter containing the name by which ``plugin`` is registered.
|
||||||
|
|
||||||
|
|
||||||
|
Bug Fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
- `#11706 <https://github.com/pytest-dev/pytest/issues/11706>`_: Fix reporting of teardown errors in higher-scoped fixtures when using `--maxfail` or `--stepwise`.
|
||||||
|
|
||||||
|
NOTE: This change was reverted in pytest 8.0.2 to fix a `regression <https://github.com/pytest-dev/pytest-xdist/issues/1024>`_ it caused in pytest-xdist.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11758 <https://github.com/pytest-dev/pytest/issues/11758>`_: Fixed ``IndexError: string index out of range`` crash in ``if highlighted[-1] == "\n" and source[-1] != "\n"``.
|
||||||
|
This bug was introduced in pytest 8.0.0rc1.
|
||||||
|
|
||||||
|
|
||||||
|
- `#9765 <https://github.com/pytest-dev/pytest/issues/9765>`_, `#11816 <https://github.com/pytest-dev/pytest/issues/11816>`_: Fixed a frustrating bug that afflicted some users with the only error being ``assert mod not in mods``. The issue was caused by the fact that ``str(Path(mod))`` and ``mod.__file__`` don't necessarily produce the same string, and was being erroneously used interchangably in some places in the code.
|
||||||
|
|
||||||
|
This fix also broke the internal API of ``PytestPluginManager.consider_conftest`` by introducing a new parameter -- we mention this in case it is being used by external code, even if marked as *private*.
|
||||||
|
|
||||||
|
|
||||||
|
pytest 8.0.0rc1 (2023-12-30)
|
||||||
|
============================
|
||||||
|
|
||||||
|
Breaking Changes
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Old Deprecations Are Now Errors
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
- `#7363 <https://github.com/pytest-dev/pytest/issues/7363>`_: **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`.
|
||||||
|
|
||||||
|
|
||||||
|
Version Compatibility
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
- `#11151 <https://github.com/pytest-dev/pytest/issues/11151>`_: Dropped support for Python 3.7, which `reached end-of-life on 2023-06-27 <https://devguide.python.org/versions/>`__.
|
||||||
|
|
||||||
|
|
||||||
|
- ``pluggy>=1.3.0`` is now required.
|
||||||
|
|
||||||
|
|
||||||
|
Collection Changes
|
||||||
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
In this version we've made several breaking changes to pytest's collection phase,
|
||||||
|
particularly around how filesystem directories and Python packages are collected,
|
||||||
|
fixing deficiencies and allowing for cleanups and improvements to pytest's internals.
|
||||||
|
A deprecation period for these changes was not possible.
|
||||||
|
|
||||||
|
|
||||||
|
- `#7777 <https://github.com/pytest-dev/pytest/issues/7777>`_: Files and directories are now collected in alphabetical order jointly, unless changed by a plugin.
|
||||||
|
Previously, files were collected before directories.
|
||||||
|
See below for an example.
|
||||||
|
|
||||||
|
|
||||||
|
- `#8976 <https://github.com/pytest-dev/pytest/issues/8976>`_: 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`.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11137 <https://github.com/pytest-dev/pytest/issues/11137>`_: :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).
|
||||||
|
|
||||||
|
|
||||||
|
- `#7777 <https://github.com/pytest-dev/pytest/issues/7777>`_: Added a new :class:`pytest.Directory` base collection node, which all collector nodes for filesystem directories are expected to subclass.
|
||||||
|
This is analogous to the existing :class:`pytest.File` for file nodes.
|
||||||
|
|
||||||
|
Changed :class:`pytest.Package` to be a subclass of :class:`pytest.Directory`.
|
||||||
|
A ``Package`` represents a filesystem directory which is a Python package,
|
||||||
|
i.e. contains an ``__init__.py`` file.
|
||||||
|
|
||||||
|
:class:`pytest.Package` now only collects files in its own directory; previously it collected recursively.
|
||||||
|
Sub-directories are collected as their own collector nodes, which then collect themselves, thus creating a collection tree which mirrors the filesystem hierarchy.
|
||||||
|
|
||||||
|
Added a new :class:`pytest.Dir` concrete collection node, a subclass of :class:`pytest.Directory`.
|
||||||
|
This node represents a filesystem directory, which is not a :class:`pytest.Package`,
|
||||||
|
that is, does not contain an ``__init__.py`` file.
|
||||||
|
Similarly to ``Package``, it only collects the files in its own directory.
|
||||||
|
|
||||||
|
:class:`pytest.Session` now only collects the initial arguments, without recursing into directories.
|
||||||
|
This work is now done by the :func:`recursive expansion process <pytest.Collector.collect>` of directory collector nodes.
|
||||||
|
|
||||||
|
:attr:`session.name <pytest.Session.name>` is now ``""``; previously it was the rootdir directory name.
|
||||||
|
This matches :attr:`session.nodeid <_pytest.nodes.Node.nodeid>` which has always been `""`.
|
||||||
|
|
||||||
|
The collection tree now contains directories/packages up to the :ref:`rootdir <rootdir>`,
|
||||||
|
for initial arguments that are found within the rootdir.
|
||||||
|
For files outside the rootdir, only the immediate directory/package is collected --
|
||||||
|
note however that collecting from outside the rootdir is discouraged.
|
||||||
|
|
||||||
|
As an example, given the following filesystem tree::
|
||||||
|
|
||||||
|
myroot/
|
||||||
|
pytest.ini
|
||||||
|
top/
|
||||||
|
├── aaa
|
||||||
|
│ └── test_aaa.py
|
||||||
|
├── test_a.py
|
||||||
|
├── test_b
|
||||||
|
│ ├── __init__.py
|
||||||
|
│ └── test_b.py
|
||||||
|
├── test_c.py
|
||||||
|
└── zzz
|
||||||
|
├── __init__.py
|
||||||
|
└── test_zzz.py
|
||||||
|
|
||||||
|
the collection tree, as shown by `pytest --collect-only top/` but with the otherwise-hidden :class:`~pytest.Session` node added for clarity,
|
||||||
|
is now the following::
|
||||||
|
|
||||||
|
<Session>
|
||||||
|
<Dir myroot>
|
||||||
|
<Dir top>
|
||||||
|
<Dir aaa>
|
||||||
|
<Module test_aaa.py>
|
||||||
|
<Function test_it>
|
||||||
|
<Module test_a.py>
|
||||||
|
<Function test_it>
|
||||||
|
<Package test_b>
|
||||||
|
<Module test_b.py>
|
||||||
|
<Function test_it>
|
||||||
|
<Module test_c.py>
|
||||||
|
<Function test_it>
|
||||||
|
<Package zzz>
|
||||||
|
<Module test_zzz.py>
|
||||||
|
<Function test_it>
|
||||||
|
|
||||||
|
Previously, it was::
|
||||||
|
|
||||||
|
<Session>
|
||||||
|
<Module top/test_a.py>
|
||||||
|
<Function test_it>
|
||||||
|
<Module top/test_c.py>
|
||||||
|
<Function test_it>
|
||||||
|
<Module top/aaa/test_aaa.py>
|
||||||
|
<Function test_it>
|
||||||
|
<Package test_b>
|
||||||
|
<Module test_b.py>
|
||||||
|
<Function test_it>
|
||||||
|
<Package zzz>
|
||||||
|
<Module test_zzz.py>
|
||||||
|
<Function test_it>
|
||||||
|
|
||||||
|
Code/plugins which rely on a specific shape of the collection tree might need to update.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11676 <https://github.com/pytest-dev/pytest/issues/11676>`_: The classes :class:`~_pytest.nodes.Node`, :class:`~pytest.Collector`, :class:`~pytest.Item`, :class:`~pytest.File`, :class:`~_pytest.nodes.FSCollector` are now marked abstract (see :mod:`abc`).
|
||||||
|
|
||||||
|
We do not expect this change to affect users and plugin authors, it will only cause errors when the code is already wrong or problematic.
|
||||||
|
|
||||||
|
|
||||||
|
Other breaking changes
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
These are breaking changes where deprecation was not possible.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11282 <https://github.com/pytest-dev/pytest/issues/11282>`_: Sanitized the handling of the ``default`` parameter when defining configuration options.
|
||||||
|
|
||||||
|
Previously if ``default`` was not supplied for :meth:`parser.addini <pytest.Parser.addini>` and the configuration option value was not defined in a test session, then calls to :func:`config.getini <pytest.Config.getini>` returned an *empty list* or an *empty string* depending on whether ``type`` was supplied or not respectively, which is clearly incorrect. Also, ``None`` was not honored even if ``default=None`` was used explicitly while defining the option.
|
||||||
|
|
||||||
|
Now the behavior of :meth:`parser.addini <pytest.Parser.addini>` is as follows:
|
||||||
|
|
||||||
|
* If ``default`` is NOT passed but ``type`` is provided, then a type-specific default will be returned. For example ``type=bool`` will return ``False``, ``type=str`` will return ``""``, etc.
|
||||||
|
* If ``default=None`` is passed and the option is not defined in a test session, then ``None`` will be returned, regardless of the ``type``.
|
||||||
|
* If neither ``default`` nor ``type`` are provided, assume ``type=str`` and return ``""`` as default (this is as per previous behavior).
|
||||||
|
|
||||||
|
The team decided to not introduce a deprecation period for this change, as doing so would be complicated both in terms of communicating this to the community as well as implementing it, and also because the team believes this change should not break existing plugins except in rare cases.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11667 <https://github.com/pytest-dev/pytest/issues/11667>`_: pytest's ``setup.py`` file is removed.
|
||||||
|
If you relied on this file, e.g. to install pytest using ``setup.py install``,
|
||||||
|
please see `Why you shouldn't invoke setup.py directly <https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html#summary>`_ for alternatives.
|
||||||
|
|
||||||
|
|
||||||
|
- `#9288 <https://github.com/pytest-dev/pytest/issues/9288>`_: :func:`~pytest.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 announce it 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.
|
||||||
|
|
||||||
|
|
||||||
|
- The internal ``FixtureManager.getfixtureclosure`` method has changed. Plugins which use this method or
|
||||||
|
which subclass ``FixtureManager`` and overwrite that method will need to adapt to the change.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Deprecations
|
||||||
|
------------
|
||||||
|
|
||||||
|
- `#10465 <https://github.com/pytest-dev/pytest/issues/10465>`_: 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.
|
||||||
|
|
||||||
|
|
||||||
|
- `#3664 <https://github.com/pytest-dev/pytest/issues/3664>`_: 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 pytest 9.0.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Features and Improvements
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Improved Diffs
|
||||||
|
^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
These changes improve the diffs that pytest prints when an assertion fails.
|
||||||
|
Note that syntax highlighting requires the ``pygments`` package.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11520 <https://github.com/pytest-dev/pytest/issues/11520>`_: The very verbose (``-vv``) diff output is now colored as a diff instead of a big chunk of red.
|
||||||
|
|
||||||
|
Python code in error reports is now syntax-highlighted as Python.
|
||||||
|
|
||||||
|
The sections in the error reports are now better separated.
|
||||||
|
|
||||||
|
|
||||||
|
- `#1531 <https://github.com/pytest-dev/pytest/issues/1531>`_: The very verbose diff (``-vv``) for every standard library container type is improved. The indentation is now consistent and the markers are on their own separate lines, which should reduce the diffs shown to users.
|
||||||
|
|
||||||
|
Previously, the standard Python pretty printer was used to generate the output, which puts opening and closing
|
||||||
|
markers on the same line as the first/last entry, in addition to not having consistent indentation.
|
||||||
|
|
||||||
|
|
||||||
|
- `#10617 <https://github.com/pytest-dev/pytest/issues/10617>`_: Added more comprehensive set assertion rewrites for comparisons other than equality ``==``, with
|
||||||
|
the following operations now providing better failure messages: ``!=``, ``<=``, ``>=``, ``<``, and ``>``.
|
||||||
|
|
||||||
|
|
||||||
|
Separate Control For Assertion Verbosity
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
- `#11387 <https://github.com/pytest-dev/pytest/issues/11387>`_: Added the new :confval:`verbosity_assertions` configuration option for fine-grained control of failed assertions verbosity.
|
||||||
|
|
||||||
|
If you've ever wished that pytest always show you full diffs, but without making everything else verbose, this is for you.
|
||||||
|
|
||||||
|
See :ref:`Fine-grained verbosity <pytest.fine_grained_verbosity>` for more details.
|
||||||
|
|
||||||
|
For plugin authors, :attr:`config.get_verbosity <pytest.Config.get_verbosity>` can be used to retrieve the verbosity level for a specific verbosity type.
|
||||||
|
|
||||||
|
|
||||||
|
Additional Support For Exception Groups and ``__notes__``
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
These changes improve pytest's support for exception groups.
|
||||||
|
|
||||||
|
|
||||||
|
- `#10441 <https://github.com/pytest-dev/pytest/issues/10441>`_: Added :func:`ExceptionInfo.group_contains() <pytest.ExceptionInfo.group_contains>`, an assertion helper that tests if an :class:`ExceptionGroup` contains a matching exception.
|
||||||
|
|
||||||
|
See :ref:`assert-matching-exception-groups` for an example.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11227 <https://github.com/pytest-dev/pytest/issues/11227>`_: Allow :func:`pytest.raises` ``match`` argument to match against `PEP-678 <https://peps.python.org/pep-0678/>` ``__notes__``.
|
||||||
|
|
||||||
|
|
||||||
|
Custom Directory collectors
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
- `#7777 <https://github.com/pytest-dev/pytest/issues/7777>`_: Added a new hook :hook:`pytest_collect_directory`,
|
||||||
|
which is called by filesystem-traversing collector nodes,
|
||||||
|
such as :class:`pytest.Session`, :class:`pytest.Dir` and :class:`pytest.Package`,
|
||||||
|
to create a collector node for a sub-directory.
|
||||||
|
It is expected to return a subclass of :class:`pytest.Directory`.
|
||||||
|
This hook allows plugins to :ref:`customize the collection of directories <custom directory collectors>`.
|
||||||
|
|
||||||
|
|
||||||
|
"New-style" Hook Wrappers
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
- `#11122 <https://github.com/pytest-dev/pytest/issues/11122>`_: 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 ``pytest>=8``.
|
||||||
|
|
||||||
|
|
||||||
|
Other Improvements
|
||||||
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
- `#11216 <https://github.com/pytest-dev/pytest/issues/11216>`_: 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.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11314 <https://github.com/pytest-dev/pytest/issues/11314>`_: 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.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11610 <https://github.com/pytest-dev/pytest/issues/11610>`_: Added the :func:`LogCaptureFixture.filtering() <pytest.LogCaptureFixture.filtering>` context manager which
|
||||||
|
adds a given :class:`logging.Filter` object to the :fixture:`caplog` fixture.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11447 <https://github.com/pytest-dev/pytest/issues/11447>`_: :func:`pytest.deprecated_call` now also considers warnings of type :class:`FutureWarning`.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11600 <https://github.com/pytest-dev/pytest/issues/11600>`_: Improved the documentation and type signature for :func:`pytest.mark.xfail <pytest.mark.xfail>`'s ``condition`` param to use ``False`` as the default value.
|
||||||
|
|
||||||
|
|
||||||
|
- `#7469 <https://github.com/pytest-dev/pytest/issues/7469>`_: :class:`~pytest.FixtureDef` is now exported as ``pytest.FixtureDef`` for typing purposes.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11353 <https://github.com/pytest-dev/pytest/issues/11353>`_: Added typing to :class:`~pytest.PytestPluginManager`.
|
||||||
|
|
||||||
|
|
||||||
|
Bug Fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
- `#10701 <https://github.com/pytest-dev/pytest/issues/10701>`_: :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.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11255 <https://github.com/pytest-dev/pytest/issues/11255>`_: Fixed crash on `parametrize(..., scope="package")` without a package present.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11277 <https://github.com/pytest-dev/pytest/issues/11277>`_: 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.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11456 <https://github.com/pytest-dev/pytest/issues/11456>`_: 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.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11563 <https://github.com/pytest-dev/pytest/issues/11563>`_: Fixed a crash when using an empty string for the same parametrized value more than once.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11712 <https://github.com/pytest-dev/pytest/issues/11712>`_: Fixed handling ``NO_COLOR`` and ``FORCE_COLOR`` to ignore an empty value.
|
||||||
|
|
||||||
|
|
||||||
|
- `#9036 <https://github.com/pytest-dev/pytest/issues/9036>`_: ``pytest.warns`` and similar functions now capture warnings when an exception is raised inside a ``with`` block.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Improved Documentation
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
- `#11011 <https://github.com/pytest-dev/pytest/issues/11011>`_: Added a warning about modifying the root logger during tests when using ``caplog``.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11065 <https://github.com/pytest-dev/pytest/issues/11065>`_: Use ``pytestconfig`` instead of ``request.config`` in cache example to be consistent with the API documentation.
|
||||||
|
|
||||||
|
|
||||||
|
Trivial/Internal Changes
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
- `#11208 <https://github.com/pytest-dev/pytest/issues/11208>`_: 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.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11218 <https://github.com/pytest-dev/pytest/issues/11218>`_: (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.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11315 <https://github.com/pytest-dev/pytest/issues/11315>`_: 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.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11333 <https://github.com/pytest-dev/pytest/issues/11333>`_: Corrected the spelling of ``Config.ArgsSource.INVOCATION_DIR``.
|
||||||
|
The previous spelling ``INCOVATION_DIR`` remains as an alias.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11638 <https://github.com/pytest-dev/pytest/issues/11638>`_: Fixed the selftests to pass correctly if ``FORCE_COLOR``, ``NO_COLOR`` or ``PY_COLORS`` is set in the calling environment.
|
||||||
|
|
||||||
pytest 7.4.4 (2023-12-31)
|
pytest 7.4.4 (2023-12-31)
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from _pytest import __version__ as version
|
from _pytest import __version__ as version
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
import sphinx.application
|
import sphinx.application
|
||||||
|
|
||||||
|
@ -315,6 +316,9 @@ html_show_sourcelink = False
|
||||||
# Output file base name for HTML help builder.
|
# Output file base name for HTML help builder.
|
||||||
htmlhelp_basename = "pytestdoc"
|
htmlhelp_basename = "pytestdoc"
|
||||||
|
|
||||||
|
# The base URL which points to the root of the HTML documentation. It is used
|
||||||
|
# to indicate the location of document using the canonical link relation (#12363).
|
||||||
|
html_baseurl = "https://docs.pytest.org/en/stable/"
|
||||||
|
|
||||||
# -- Options for LaTeX output --------------------------------------------------
|
# -- Options for LaTeX output --------------------------------------------------
|
||||||
|
|
||||||
|
@ -441,9 +445,10 @@ intersphinx_mapping = {
|
||||||
|
|
||||||
def configure_logging(app: "sphinx.application.Sphinx") -> None:
|
def configure_logging(app: "sphinx.application.Sphinx") -> None:
|
||||||
"""Configure Sphinx's WarningHandler to handle (expected) missing include."""
|
"""Configure Sphinx's WarningHandler to handle (expected) missing include."""
|
||||||
import sphinx.util.logging
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
import sphinx.util.logging
|
||||||
|
|
||||||
class WarnLogFilter(logging.Filter):
|
class WarnLogFilter(logging.Filter):
|
||||||
def filter(self, record: logging.LogRecord) -> bool:
|
def filter(self, record: logging.LogRecord) -> bool:
|
||||||
"""Ignore warnings about missing include with "only" directive.
|
"""Ignore warnings about missing include with "only" directive.
|
||||||
|
|
|
@ -47,11 +47,9 @@ they are in fact part of the ``nose`` support.
|
||||||
def teardown(self):
|
def teardown(self):
|
||||||
self.resource.close()
|
self.resource.close()
|
||||||
|
|
||||||
def test_foo(self):
|
def test_foo(self): ...
|
||||||
...
|
|
||||||
|
|
||||||
def test_bar(self):
|
def test_bar(self): ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -66,11 +64,9 @@ Native pytest support uses ``setup_method`` and ``teardown_method`` (see :ref:`x
|
||||||
def teardown_method(self):
|
def teardown_method(self):
|
||||||
self.resource.close()
|
self.resource.close()
|
||||||
|
|
||||||
def test_foo(self):
|
def test_foo(self): ...
|
||||||
...
|
|
||||||
|
|
||||||
def test_bar(self):
|
def test_bar(self): ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
This is easy to do in an entire code base by doing a simple find/replace.
|
This is easy to do in an entire code base by doing a simple find/replace.
|
||||||
|
@ -85,17 +81,14 @@ Code using `@with_setup <with-setup-nose>`_ such as this:
|
||||||
from nose.tools import with_setup
|
from nose.tools import with_setup
|
||||||
|
|
||||||
|
|
||||||
def setup_some_resource():
|
def setup_some_resource(): ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
def teardown_some_resource():
|
def teardown_some_resource(): ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
@with_setup(setup_some_resource, teardown_some_resource)
|
@with_setup(setup_some_resource, teardown_some_resource)
|
||||||
def test_foo():
|
def test_foo(): ...
|
||||||
...
|
|
||||||
|
|
||||||
Will also need to be ported to a supported pytest style. One way to do it is using a fixture:
|
Will also need to be ported to a supported pytest style. One way to do it is using a fixture:
|
||||||
|
|
||||||
|
@ -104,12 +97,10 @@ Will also need to be ported to a supported pytest style. One way to do it is usi
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
def setup_some_resource():
|
def setup_some_resource(): ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
def teardown_some_resource():
|
def teardown_some_resource(): ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -119,8 +110,7 @@ Will also need to be ported to a supported pytest style. One way to do it is usi
|
||||||
teardown_some_resource()
|
teardown_some_resource()
|
||||||
|
|
||||||
|
|
||||||
def test_foo(some_resource):
|
def test_foo(some_resource): ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
.. _`with-setup-nose`: https://nose.readthedocs.io/en/latest/testing_tools.html?highlight=with_setup#nose.tools.with_setup
|
.. _`with-setup-nose`: https://nose.readthedocs.io/en/latest/testing_tools.html?highlight=with_setup#nose.tools.with_setup
|
||||||
|
@ -197,13 +187,11 @@ have been available since years and should be used instead.
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@pytest.mark.tryfirst
|
@pytest.mark.tryfirst
|
||||||
def pytest_runtest_call():
|
def pytest_runtest_call(): ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
# or
|
# or
|
||||||
def pytest_runtest_call():
|
def pytest_runtest_call(): ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
pytest_runtest_call.tryfirst = True
|
pytest_runtest_call.tryfirst = True
|
||||||
|
@ -213,8 +201,7 @@ should be changed to:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@pytest.hookimpl(tryfirst=True)
|
@pytest.hookimpl(tryfirst=True)
|
||||||
def pytest_runtest_call():
|
def pytest_runtest_call(): ...
|
||||||
...
|
|
||||||
|
|
||||||
Changed ``hookimpl`` attributes:
|
Changed ``hookimpl`` attributes:
|
||||||
|
|
||||||
|
@ -317,8 +304,7 @@ Implement the :hook:`pytest_load_initial_conftests` hook instead.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
def pytest_cmdline_preparse(config: Config, args: List[str]) -> None:
|
def pytest_cmdline_preparse(config: Config, args: List[str]) -> None: ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
# becomes:
|
# becomes:
|
||||||
|
@ -326,8 +312,7 @@ Implement the :hook:`pytest_load_initial_conftests` hook instead.
|
||||||
|
|
||||||
def pytest_load_initial_conftests(
|
def pytest_load_initial_conftests(
|
||||||
early_config: Config, parser: Parser, args: List[str]
|
early_config: Config, parser: Parser, args: List[str]
|
||||||
) -> None:
|
) -> None: ...
|
||||||
...
|
|
||||||
|
|
||||||
.. _diamond-inheritance-deprecated:
|
.. _diamond-inheritance-deprecated:
|
||||||
|
|
||||||
|
@ -391,8 +376,7 @@ Applying a mark to a fixture function never had any effect, but it is a common u
|
||||||
|
|
||||||
@pytest.mark.usefixtures("clean_database")
|
@pytest.mark.usefixtures("clean_database")
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def user() -> User:
|
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.
|
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.
|
||||||
|
|
||||||
|
@ -907,8 +891,7 @@ Applying marks to values of a ``pytest.mark.parametrize`` call is now deprecated
|
||||||
(50, 500),
|
(50, 500),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_foo(a, b):
|
def test_foo(a, b): ...
|
||||||
...
|
|
||||||
|
|
||||||
This code applies the ``pytest.mark.xfail(reason="flaky")`` mark to the ``(6, 36)`` value of the above parametrization
|
This code applies the ``pytest.mark.xfail(reason="flaky")`` mark to the ``(6, 36)`` value of the above parametrization
|
||||||
call.
|
call.
|
||||||
|
@ -931,8 +914,7 @@ To update the code, use ``pytest.param``:
|
||||||
(50, 500),
|
(50, 500),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_foo(a, b):
|
def test_foo(a, b): ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
.. _pytest_funcarg__ prefix deprecated:
|
.. _pytest_funcarg__ prefix deprecated:
|
||||||
|
@ -1083,15 +1065,13 @@ This is just a matter of renaming the fixture as the API is the same:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
def test_foo(record_xml_property):
|
def test_foo(record_xml_property): ...
|
||||||
...
|
|
||||||
|
|
||||||
Change to:
|
Change to:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
def test_foo(record_property):
|
def test_foo(record_property): ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
.. _passing command-line string to pytest.main deprecated:
|
.. _passing command-line string to pytest.main deprecated:
|
||||||
|
@ -1253,8 +1233,7 @@ Example of usage:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
class MySymbol:
|
class MySymbol: ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
def pytest_namespace():
|
def pytest_namespace():
|
||||||
|
|
|
@ -2,6 +2,7 @@ import os.path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
mydir = os.path.dirname(__file__)
|
mydir = os.path.dirname(__file__)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import os.path
|
import os.path
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
|
|
||||||
failure_demo = os.path.join(os.path.dirname(__file__), "failure_demo.py")
|
failure_demo = os.path.join(os.path.dirname(__file__), "failure_demo.py")
|
||||||
pytest_plugins = ("pytester",)
|
pytest_plugins = ("pytester",)
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ You can then restrict a test run to only run tests marked with ``webtest``:
|
||||||
|
|
||||||
$ pytest -v -m webtest
|
$ pytest -v -m webtest
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
||||||
cachedir: .pytest_cache
|
cachedir: .pytest_cache
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collecting ... collected 4 items / 3 deselected / 1 selected
|
collecting ... collected 4 items / 3 deselected / 1 selected
|
||||||
|
@ -60,7 +60,7 @@ Or the inverse, running all tests except the webtest ones:
|
||||||
|
|
||||||
$ pytest -v -m "not webtest"
|
$ pytest -v -m "not webtest"
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
||||||
cachedir: .pytest_cache
|
cachedir: .pytest_cache
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collecting ... collected 4 items / 1 deselected / 3 selected
|
collecting ... collected 4 items / 1 deselected / 3 selected
|
||||||
|
@ -82,7 +82,7 @@ tests based on their module, class, method, or function name:
|
||||||
|
|
||||||
$ pytest -v test_server.py::TestClass::test_method
|
$ pytest -v test_server.py::TestClass::test_method
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
||||||
cachedir: .pytest_cache
|
cachedir: .pytest_cache
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collecting ... collected 1 item
|
collecting ... collected 1 item
|
||||||
|
@ -97,7 +97,7 @@ You can also select on the class:
|
||||||
|
|
||||||
$ pytest -v test_server.py::TestClass
|
$ pytest -v test_server.py::TestClass
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
||||||
cachedir: .pytest_cache
|
cachedir: .pytest_cache
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collecting ... collected 1 item
|
collecting ... collected 1 item
|
||||||
|
@ -112,7 +112,7 @@ Or select multiple nodes:
|
||||||
|
|
||||||
$ pytest -v test_server.py::TestClass test_server.py::test_send_http
|
$ pytest -v test_server.py::TestClass test_server.py::test_send_http
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
||||||
cachedir: .pytest_cache
|
cachedir: .pytest_cache
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collecting ... collected 2 items
|
collecting ... collected 2 items
|
||||||
|
@ -156,7 +156,7 @@ The expression matching is now case-insensitive.
|
||||||
|
|
||||||
$ pytest -v -k http # running with the above defined example module
|
$ pytest -v -k http # running with the above defined example module
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
||||||
cachedir: .pytest_cache
|
cachedir: .pytest_cache
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collecting ... collected 4 items / 3 deselected / 1 selected
|
collecting ... collected 4 items / 3 deselected / 1 selected
|
||||||
|
@ -171,7 +171,7 @@ And you can also run all tests except the ones that match the keyword:
|
||||||
|
|
||||||
$ pytest -k "not send_http" -v
|
$ pytest -k "not send_http" -v
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
||||||
cachedir: .pytest_cache
|
cachedir: .pytest_cache
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collecting ... collected 4 items / 1 deselected / 3 selected
|
collecting ... collected 4 items / 1 deselected / 3 selected
|
||||||
|
@ -188,7 +188,7 @@ Or to select "http" and "quick" tests:
|
||||||
|
|
||||||
$ pytest -k "http or quick" -v
|
$ pytest -k "http or quick" -v
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
||||||
cachedir: .pytest_cache
|
cachedir: .pytest_cache
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collecting ... collected 4 items / 2 deselected / 2 selected
|
collecting ... collected 4 items / 2 deselected / 2 selected
|
||||||
|
@ -397,7 +397,7 @@ the test needs:
|
||||||
|
|
||||||
$ pytest -E stage2
|
$ pytest -E stage2
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 1 item
|
collected 1 item
|
||||||
|
|
||||||
|
@ -411,7 +411,7 @@ and here is one that specifies exactly the environment needed:
|
||||||
|
|
||||||
$ pytest -E stage1
|
$ pytest -E stage1
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 1 item
|
collected 1 item
|
||||||
|
|
||||||
|
@ -604,7 +604,7 @@ then you will see two tests skipped and two executed tests as expected:
|
||||||
|
|
||||||
$ pytest -rs # this option reports skip reasons
|
$ pytest -rs # this option reports skip reasons
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 4 items
|
collected 4 items
|
||||||
|
|
||||||
|
@ -620,7 +620,7 @@ Note that if you specify a platform via the marker-command line option like this
|
||||||
|
|
||||||
$ pytest -m linux
|
$ pytest -m linux
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 4 items / 3 deselected / 1 selected
|
collected 4 items / 3 deselected / 1 selected
|
||||||
|
|
||||||
|
@ -683,7 +683,7 @@ We can now use the ``-m option`` to select one set:
|
||||||
|
|
||||||
$ pytest -m interface --tb=short
|
$ pytest -m interface --tb=short
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 4 items / 2 deselected / 2 selected
|
collected 4 items / 2 deselected / 2 selected
|
||||||
|
|
||||||
|
@ -709,7 +709,7 @@ or to select both "event" and "interface" tests:
|
||||||
|
|
||||||
$ pytest -m "interface or event" --tb=short
|
$ pytest -m "interface or event" --tb=short
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 4 items / 1 deselected / 3 selected
|
collected 4 items / 1 deselected / 3 selected
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
"""Module containing a parametrized tests testing cross-python serialization
|
"""Module containing a parametrized tests testing cross-python serialization
|
||||||
via the pickle module."""
|
via the pickle module."""
|
||||||
|
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import textwrap
|
import textwrap
|
||||||
|
@ -32,14 +33,12 @@ class Python:
|
||||||
dumpfile = self.picklefile.with_name("dump.py")
|
dumpfile = self.picklefile.with_name("dump.py")
|
||||||
dumpfile.write_text(
|
dumpfile.write_text(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
r"""
|
rf"""
|
||||||
import pickle
|
import pickle
|
||||||
f = open({!r}, 'wb')
|
f = open({str(self.picklefile)!r}, 'wb')
|
||||||
s = pickle.dump({!r}, f, protocol=2)
|
s = pickle.dump({obj!r}, f, protocol=2)
|
||||||
f.close()
|
f.close()
|
||||||
""".format(
|
"""
|
||||||
str(self.picklefile), obj
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
subprocess.run((self.pythonpath, str(dumpfile)), check=True)
|
subprocess.run((self.pythonpath, str(dumpfile)), check=True)
|
||||||
|
@ -48,17 +47,15 @@ class Python:
|
||||||
loadfile = self.picklefile.with_name("load.py")
|
loadfile = self.picklefile.with_name("load.py")
|
||||||
loadfile.write_text(
|
loadfile.write_text(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
r"""
|
rf"""
|
||||||
import pickle
|
import pickle
|
||||||
f = open({!r}, 'rb')
|
f = open({str(self.picklefile)!r}, 'rb')
|
||||||
obj = pickle.load(f)
|
obj = pickle.load(f)
|
||||||
f.close()
|
f.close()
|
||||||
res = eval({!r})
|
res = eval({expression!r})
|
||||||
if not res:
|
if not res:
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
""".format(
|
"""
|
||||||
str(self.picklefile), expression
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
print(loadfile)
|
print(loadfile)
|
||||||
|
|
|
@ -28,7 +28,7 @@ now execute the test specification:
|
||||||
|
|
||||||
nonpython $ pytest test_simple.yaml
|
nonpython $ pytest test_simple.yaml
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project/nonpython
|
rootdir: /home/sweet/project/nonpython
|
||||||
collected 2 items
|
collected 2 items
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ consulted when reporting in ``verbose`` mode:
|
||||||
|
|
||||||
nonpython $ pytest -v
|
nonpython $ pytest -v
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
||||||
cachedir: .pytest_cache
|
cachedir: .pytest_cache
|
||||||
rootdir: /home/sweet/project/nonpython
|
rootdir: /home/sweet/project/nonpython
|
||||||
collecting ... collected 2 items
|
collecting ... collected 2 items
|
||||||
|
@ -90,7 +90,7 @@ interesting to just look at the collection tree:
|
||||||
|
|
||||||
nonpython $ pytest --collect-only
|
nonpython $ pytest --collect-only
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project/nonpython
|
rootdir: /home/sweet/project/nonpython
|
||||||
collected 2 items
|
collected 2 items
|
||||||
|
|
||||||
|
|
|
@ -158,19 +158,20 @@ objects, they are still using the default pytest representation:
|
||||||
|
|
||||||
$ pytest test_time.py --collect-only
|
$ pytest test_time.py --collect-only
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 8 items
|
collected 8 items
|
||||||
|
|
||||||
<Module test_time.py>
|
<Dir parametrize.rst-194>
|
||||||
<Function test_timedistance_v0[a0-b0-expected0]>
|
<Module test_time.py>
|
||||||
<Function test_timedistance_v0[a1-b1-expected1]>
|
<Function test_timedistance_v0[a0-b0-expected0]>
|
||||||
<Function test_timedistance_v1[forward]>
|
<Function test_timedistance_v0[a1-b1-expected1]>
|
||||||
<Function test_timedistance_v1[backward]>
|
<Function test_timedistance_v1[forward]>
|
||||||
<Function test_timedistance_v2[20011212-20011211-expected0]>
|
<Function test_timedistance_v1[backward]>
|
||||||
<Function test_timedistance_v2[20011211-20011212-expected1]>
|
<Function test_timedistance_v2[20011212-20011211-expected0]>
|
||||||
<Function test_timedistance_v3[forward]>
|
<Function test_timedistance_v2[20011211-20011212-expected1]>
|
||||||
<Function test_timedistance_v3[backward]>
|
<Function test_timedistance_v3[forward]>
|
||||||
|
<Function test_timedistance_v3[backward]>
|
||||||
|
|
||||||
======================== 8 tests collected in 0.12s ========================
|
======================== 8 tests collected in 0.12s ========================
|
||||||
|
|
||||||
|
@ -220,7 +221,7 @@ this is a fully self-contained example which you can run with:
|
||||||
|
|
||||||
$ pytest test_scenarios.py
|
$ pytest test_scenarios.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 4 items
|
collected 4 items
|
||||||
|
|
||||||
|
@ -234,16 +235,17 @@ If you just collect tests you'll also nicely see 'advanced' and 'basic' as varia
|
||||||
|
|
||||||
$ pytest --collect-only test_scenarios.py
|
$ pytest --collect-only test_scenarios.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 4 items
|
collected 4 items
|
||||||
|
|
||||||
<Module test_scenarios.py>
|
<Dir parametrize.rst-194>
|
||||||
<Class TestSampleWithScenarios>
|
<Module test_scenarios.py>
|
||||||
<Function test_demo1[basic]>
|
<Class TestSampleWithScenarios>
|
||||||
<Function test_demo2[basic]>
|
<Function test_demo1[basic]>
|
||||||
<Function test_demo1[advanced]>
|
<Function test_demo2[basic]>
|
||||||
<Function test_demo2[advanced]>
|
<Function test_demo1[advanced]>
|
||||||
|
<Function test_demo2[advanced]>
|
||||||
|
|
||||||
======================== 4 tests collected in 0.12s ========================
|
======================== 4 tests collected in 0.12s ========================
|
||||||
|
|
||||||
|
@ -312,13 +314,14 @@ Let's first see how it looks like at collection time:
|
||||||
|
|
||||||
$ pytest test_backends.py --collect-only
|
$ pytest test_backends.py --collect-only
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 2 items
|
collected 2 items
|
||||||
|
|
||||||
<Module test_backends.py>
|
<Dir parametrize.rst-194>
|
||||||
<Function test_db_initialized[d1]>
|
<Module test_backends.py>
|
||||||
<Function test_db_initialized[d2]>
|
<Function test_db_initialized[d1]>
|
||||||
|
<Function test_db_initialized[d2]>
|
||||||
|
|
||||||
======================== 2 tests collected in 0.12s ========================
|
======================== 2 tests collected in 0.12s ========================
|
||||||
|
|
||||||
|
@ -410,7 +413,7 @@ The result of this test will be successful:
|
||||||
|
|
||||||
$ pytest -v test_indirect_list.py
|
$ pytest -v test_indirect_list.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
||||||
cachedir: .pytest_cache
|
cachedir: .pytest_cache
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collecting ... collected 1 item
|
collecting ... collected 1 item
|
||||||
|
@ -500,12 +503,11 @@ Running it results in some skips if we don't have all the python interpreters in
|
||||||
.. code-block:: pytest
|
.. code-block:: pytest
|
||||||
|
|
||||||
. $ pytest -rs -q multipython.py
|
. $ pytest -rs -q multipython.py
|
||||||
sssssssssssssssssssssssssss [100%]
|
ssssssssssss...ssssssssssss [100%]
|
||||||
========================= short test summary info ==========================
|
========================= short test summary info ==========================
|
||||||
SKIPPED [9] multipython.py:69: 'python3.5' not found
|
SKIPPED [12] multipython.py:65: 'python3.9' not found
|
||||||
SKIPPED [9] multipython.py:69: 'python3.6' not found
|
SKIPPED [12] multipython.py:65: 'python3.11' not found
|
||||||
SKIPPED [9] multipython.py:69: 'python3.7' not found
|
3 passed, 24 skipped in 0.12s
|
||||||
27 skipped in 0.12s
|
|
||||||
|
|
||||||
Parametrization of optional implementations/imports
|
Parametrization of optional implementations/imports
|
||||||
---------------------------------------------------
|
---------------------------------------------------
|
||||||
|
@ -565,7 +567,7 @@ If you run this with reporting for skips enabled:
|
||||||
|
|
||||||
$ pytest -rs test_module.py
|
$ pytest -rs test_module.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 2 items
|
collected 2 items
|
||||||
|
|
||||||
|
@ -626,7 +628,7 @@ Then run ``pytest`` with verbose mode and with only the ``basic`` marker:
|
||||||
|
|
||||||
$ pytest -v -m basic
|
$ pytest -v -m basic
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
||||||
cachedir: .pytest_cache
|
cachedir: .pytest_cache
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collecting ... collected 24 items / 21 deselected / 3 selected
|
collecting ... collected 24 items / 21 deselected / 3 selected
|
||||||
|
|
|
@ -147,15 +147,16 @@ The test collection would look like this:
|
||||||
|
|
||||||
$ pytest --collect-only
|
$ pytest --collect-only
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
configfile: pytest.ini
|
configfile: pytest.ini
|
||||||
collected 2 items
|
collected 2 items
|
||||||
|
|
||||||
<Module check_myapp.py>
|
<Dir pythoncollection.rst-195>
|
||||||
<Class CheckMyApp>
|
<Module check_myapp.py>
|
||||||
<Function simple_check>
|
<Class CheckMyApp>
|
||||||
<Function complex_check>
|
<Function simple_check>
|
||||||
|
<Function complex_check>
|
||||||
|
|
||||||
======================== 2 tests collected in 0.12s ========================
|
======================== 2 tests collected in 0.12s ========================
|
||||||
|
|
||||||
|
@ -209,16 +210,18 @@ You can always peek at the collection tree without running tests like this:
|
||||||
|
|
||||||
. $ pytest --collect-only pythoncollection.py
|
. $ pytest --collect-only pythoncollection.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
configfile: pytest.ini
|
configfile: pytest.ini
|
||||||
collected 3 items
|
collected 3 items
|
||||||
|
|
||||||
<Module CWD/pythoncollection.py>
|
<Dir pythoncollection.rst-195>
|
||||||
<Function test_function>
|
<Dir CWD>
|
||||||
<Class TestClass>
|
<Module pythoncollection.py>
|
||||||
<Function test_method>
|
<Function test_function>
|
||||||
<Function test_anothermethod>
|
<Class TestClass>
|
||||||
|
<Function test_method>
|
||||||
|
<Function test_anothermethod>
|
||||||
|
|
||||||
======================== 3 tests collected in 0.12s ========================
|
======================== 3 tests collected in 0.12s ========================
|
||||||
|
|
||||||
|
@ -291,7 +294,7 @@ file will be left out:
|
||||||
|
|
||||||
$ pytest --collect-only
|
$ pytest --collect-only
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
configfile: pytest.ini
|
configfile: pytest.ini
|
||||||
collected 0 items
|
collected 0 items
|
||||||
|
|
|
@ -9,7 +9,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
|
||||||
|
|
||||||
assertion $ pytest failure_demo.py
|
assertion $ pytest failure_demo.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project/assertion
|
rootdir: /home/sweet/project/assertion
|
||||||
collected 44 items
|
collected 44 items
|
||||||
|
|
||||||
|
@ -80,6 +80,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
|
||||||
def test_eq_text(self):
|
def test_eq_text(self):
|
||||||
> assert "spam" == "eggs"
|
> assert "spam" == "eggs"
|
||||||
E AssertionError: assert 'spam' == 'eggs'
|
E AssertionError: assert 'spam' == 'eggs'
|
||||||
|
E
|
||||||
E - eggs
|
E - eggs
|
||||||
E + spam
|
E + spam
|
||||||
|
|
||||||
|
@ -91,6 +92,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
|
||||||
def test_eq_similar_text(self):
|
def test_eq_similar_text(self):
|
||||||
> assert "foo 1 bar" == "foo 2 bar"
|
> assert "foo 1 bar" == "foo 2 bar"
|
||||||
E AssertionError: assert 'foo 1 bar' == 'foo 2 bar'
|
E AssertionError: assert 'foo 1 bar' == 'foo 2 bar'
|
||||||
|
E
|
||||||
E - foo 2 bar
|
E - foo 2 bar
|
||||||
E ? ^
|
E ? ^
|
||||||
E + foo 1 bar
|
E + foo 1 bar
|
||||||
|
@ -104,6 +106,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
|
||||||
def test_eq_multiline_text(self):
|
def test_eq_multiline_text(self):
|
||||||
> assert "foo\nspam\nbar" == "foo\neggs\nbar"
|
> assert "foo\nspam\nbar" == "foo\neggs\nbar"
|
||||||
E AssertionError: assert 'foo\nspam\nbar' == 'foo\neggs\nbar'
|
E AssertionError: assert 'foo\nspam\nbar' == 'foo\neggs\nbar'
|
||||||
|
E
|
||||||
E foo
|
E foo
|
||||||
E - eggs
|
E - eggs
|
||||||
E + spam
|
E + spam
|
||||||
|
@ -119,6 +122,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
|
||||||
b = "1" * 100 + "b" + "2" * 100
|
b = "1" * 100 + "b" + "2" * 100
|
||||||
> assert a == b
|
> assert a == b
|
||||||
E AssertionError: assert '111111111111...2222222222222' == '111111111111...2222222222222'
|
E AssertionError: assert '111111111111...2222222222222' == '111111111111...2222222222222'
|
||||||
|
E
|
||||||
E Skipping 90 identical leading characters in diff, use -v to show
|
E Skipping 90 identical leading characters in diff, use -v to show
|
||||||
E Skipping 91 identical trailing characters in diff, use -v to show
|
E Skipping 91 identical trailing characters in diff, use -v to show
|
||||||
E - 1111111111b222222222
|
E - 1111111111b222222222
|
||||||
|
@ -136,15 +140,15 @@ Here is a nice run of several failures and how ``pytest`` presents things:
|
||||||
b = "1\n" * 100 + "b" + "2\n" * 100
|
b = "1\n" * 100 + "b" + "2\n" * 100
|
||||||
> assert a == b
|
> assert a == b
|
||||||
E AssertionError: assert '1\n1\n1\n1\n...n2\n2\n2\n2\n' == '1\n1\n1\n1\n...n2\n2\n2\n2\n'
|
E AssertionError: assert '1\n1\n1\n1\n...n2\n2\n2\n2\n' == '1\n1\n1\n1\n...n2\n2\n2\n2\n'
|
||||||
|
E
|
||||||
E Skipping 190 identical leading characters in diff, use -v to show
|
E Skipping 190 identical leading characters in diff, use -v to show
|
||||||
E Skipping 191 identical trailing characters in diff, use -v to show
|
E Skipping 191 identical trailing characters in diff, use -v to show
|
||||||
E 1
|
E 1
|
||||||
E 1
|
E 1
|
||||||
E 1
|
E 1
|
||||||
E 1
|
|
||||||
E 1...
|
E 1...
|
||||||
E
|
E
|
||||||
E ...Full output truncated (6 lines hidden), use '-vv' to show
|
E ...Full output truncated (7 lines hidden), use '-vv' to show
|
||||||
|
|
||||||
failure_demo.py:60: AssertionError
|
failure_demo.py:60: AssertionError
|
||||||
_________________ TestSpecialisedExplanations.test_eq_list _________________
|
_________________ TestSpecialisedExplanations.test_eq_list _________________
|
||||||
|
@ -154,6 +158,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
|
||||||
def test_eq_list(self):
|
def test_eq_list(self):
|
||||||
> assert [0, 1, 2] == [0, 1, 3]
|
> assert [0, 1, 2] == [0, 1, 3]
|
||||||
E assert [0, 1, 2] == [0, 1, 3]
|
E assert [0, 1, 2] == [0, 1, 3]
|
||||||
|
E
|
||||||
E At index 2 diff: 2 != 3
|
E At index 2 diff: 2 != 3
|
||||||
E Use -v to get more diff
|
E Use -v to get more diff
|
||||||
|
|
||||||
|
@ -167,6 +172,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
|
||||||
b = [0] * 100 + [2] + [3] * 100
|
b = [0] * 100 + [2] + [3] * 100
|
||||||
> assert a == b
|
> assert a == b
|
||||||
E assert [0, 0, 0, 0, 0, 0, ...] == [0, 0, 0, 0, 0, 0, ...]
|
E assert [0, 0, 0, 0, 0, 0, ...] == [0, 0, 0, 0, 0, 0, ...]
|
||||||
|
E
|
||||||
E At index 100 diff: 1 != 2
|
E At index 100 diff: 1 != 2
|
||||||
E Use -v to get more diff
|
E Use -v to get more diff
|
||||||
|
|
||||||
|
@ -178,6 +184,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
|
||||||
def test_eq_dict(self):
|
def test_eq_dict(self):
|
||||||
> assert {"a": 0, "b": 1, "c": 0} == {"a": 0, "b": 2, "d": 0}
|
> assert {"a": 0, "b": 1, "c": 0} == {"a": 0, "b": 2, "d": 0}
|
||||||
E AssertionError: assert {'a': 0, 'b': 1, 'c': 0} == {'a': 0, 'b': 2, 'd': 0}
|
E AssertionError: assert {'a': 0, 'b': 1, 'c': 0} == {'a': 0, 'b': 2, 'd': 0}
|
||||||
|
E
|
||||||
E Omitting 1 identical items, use -vv to show
|
E Omitting 1 identical items, use -vv to show
|
||||||
E Differing items:
|
E Differing items:
|
||||||
E {'b': 1} != {'b': 2}
|
E {'b': 1} != {'b': 2}
|
||||||
|
@ -195,6 +202,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
|
||||||
def test_eq_set(self):
|
def test_eq_set(self):
|
||||||
> assert {0, 10, 11, 12} == {0, 20, 21}
|
> assert {0, 10, 11, 12} == {0, 20, 21}
|
||||||
E assert {0, 10, 11, 12} == {0, 20, 21}
|
E assert {0, 10, 11, 12} == {0, 20, 21}
|
||||||
|
E
|
||||||
E Extra items in the left set:
|
E Extra items in the left set:
|
||||||
E 10
|
E 10
|
||||||
E 11
|
E 11
|
||||||
|
@ -212,6 +220,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
|
||||||
def test_eq_longer_list(self):
|
def test_eq_longer_list(self):
|
||||||
> assert [1, 2] == [1, 2, 3]
|
> assert [1, 2] == [1, 2, 3]
|
||||||
E assert [1, 2] == [1, 2, 3]
|
E assert [1, 2] == [1, 2, 3]
|
||||||
|
E
|
||||||
E Right contains one more item: 3
|
E Right contains one more item: 3
|
||||||
E Use -v to get more diff
|
E Use -v to get more diff
|
||||||
|
|
||||||
|
@ -233,6 +242,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
|
||||||
text = "some multiline\ntext\nwhich\nincludes foo\nand a\ntail"
|
text = "some multiline\ntext\nwhich\nincludes foo\nand a\ntail"
|
||||||
> assert "foo" not in text
|
> assert "foo" not in text
|
||||||
E AssertionError: assert 'foo' not in 'some multil...nand a\ntail'
|
E AssertionError: assert 'foo' not in 'some multil...nand a\ntail'
|
||||||
|
E
|
||||||
E 'foo' is contained here:
|
E 'foo' is contained here:
|
||||||
E some multiline
|
E some multiline
|
||||||
E text
|
E text
|
||||||
|
@ -251,6 +261,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
|
||||||
text = "single foo line"
|
text = "single foo line"
|
||||||
> assert "foo" not in text
|
> assert "foo" not in text
|
||||||
E AssertionError: assert 'foo' not in 'single foo line'
|
E AssertionError: assert 'foo' not in 'single foo line'
|
||||||
|
E
|
||||||
E 'foo' is contained here:
|
E 'foo' is contained here:
|
||||||
E single foo line
|
E single foo line
|
||||||
E ? +++
|
E ? +++
|
||||||
|
@ -264,6 +275,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
|
||||||
text = "head " * 50 + "foo " + "tail " * 20
|
text = "head " * 50 + "foo " + "tail " * 20
|
||||||
> assert "foo" not in text
|
> assert "foo" not in text
|
||||||
E AssertionError: assert 'foo' not in 'head head h...l tail tail '
|
E AssertionError: assert 'foo' not in 'head head h...l tail tail '
|
||||||
|
E
|
||||||
E 'foo' is contained here:
|
E 'foo' is contained here:
|
||||||
E head head foo tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail
|
E head head foo tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail
|
||||||
E ? +++
|
E ? +++
|
||||||
|
@ -277,6 +289,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
|
||||||
text = "head " * 50 + "f" * 70 + "tail " * 20
|
text = "head " * 50 + "f" * 70 + "tail " * 20
|
||||||
> assert "f" * 70 not in text
|
> assert "f" * 70 not in text
|
||||||
E AssertionError: assert 'fffffffffff...ffffffffffff' not in 'head head h...l tail tail '
|
E AssertionError: assert 'fffffffffff...ffffffffffff' not in 'head head h...l tail tail '
|
||||||
|
E
|
||||||
E 'ffffffffffffffffff...fffffffffffffffffff' is contained here:
|
E 'ffffffffffffffffff...fffffffffffffffffff' is contained here:
|
||||||
E head head fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffftail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail
|
E head head fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffftail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail
|
||||||
E ? ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
E ? ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
|
@ -232,7 +232,7 @@ directory with the above conftest.py:
|
||||||
|
|
||||||
$ pytest
|
$ pytest
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 0 items
|
collected 0 items
|
||||||
|
|
||||||
|
@ -296,7 +296,7 @@ and when running it will see a skipped "slow" test:
|
||||||
|
|
||||||
$ pytest -rs # "-rs" means report details on the little 's'
|
$ pytest -rs # "-rs" means report details on the little 's'
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 2 items
|
collected 2 items
|
||||||
|
|
||||||
|
@ -312,7 +312,7 @@ Or run it including the ``slow`` marked test:
|
||||||
|
|
||||||
$ pytest --runslow
|
$ pytest --runslow
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 2 items
|
collected 2 items
|
||||||
|
|
||||||
|
@ -456,7 +456,7 @@ which will add the string to the test header accordingly:
|
||||||
|
|
||||||
$ pytest
|
$ pytest
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
project deps: mylib-1.1
|
project deps: mylib-1.1
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 0 items
|
collected 0 items
|
||||||
|
@ -484,7 +484,7 @@ which will add info only when run with "--v":
|
||||||
|
|
||||||
$ pytest -v
|
$ pytest -v
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
||||||
cachedir: .pytest_cache
|
cachedir: .pytest_cache
|
||||||
info1: did you know that ...
|
info1: did you know that ...
|
||||||
did you?
|
did you?
|
||||||
|
@ -499,7 +499,7 @@ and nothing when run plainly:
|
||||||
|
|
||||||
$ pytest
|
$ pytest
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 0 items
|
collected 0 items
|
||||||
|
|
||||||
|
@ -538,7 +538,7 @@ Now we can profile which test functions execute the slowest:
|
||||||
|
|
||||||
$ pytest --durations=3
|
$ pytest --durations=3
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 3 items
|
collected 3 items
|
||||||
|
|
||||||
|
@ -644,7 +644,7 @@ If we run this:
|
||||||
|
|
||||||
$ pytest -rx
|
$ pytest -rx
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 4 items
|
collected 4 items
|
||||||
|
|
||||||
|
@ -660,6 +660,31 @@ If we run this:
|
||||||
E assert 0
|
E assert 0
|
||||||
|
|
||||||
test_step.py:11: AssertionError
|
test_step.py:11: AssertionError
|
||||||
|
================================ XFAILURES =================================
|
||||||
|
______________________ TestUserHandling.test_deletion ______________________
|
||||||
|
|
||||||
|
item = <Function test_deletion>
|
||||||
|
|
||||||
|
def pytest_runtest_setup(item):
|
||||||
|
if "incremental" in item.keywords:
|
||||||
|
# retrieve the class name of the test
|
||||||
|
cls_name = str(item.cls)
|
||||||
|
# check if a previous test has failed for this class
|
||||||
|
if cls_name in _test_failed_incremental:
|
||||||
|
# retrieve the index of the test (if parametrize is used in combination with incremental)
|
||||||
|
parametrize_index = (
|
||||||
|
tuple(item.callspec.indices.values())
|
||||||
|
if hasattr(item, "callspec")
|
||||||
|
else ()
|
||||||
|
)
|
||||||
|
# retrieve the name of the first test function to fail for this class name and index
|
||||||
|
test_name = _test_failed_incremental[cls_name].get(parametrize_index, None)
|
||||||
|
# if name found, test has failed for the combination of class name & test name
|
||||||
|
if test_name is not None:
|
||||||
|
> pytest.xfail(f"previous test failed ({test_name})")
|
||||||
|
E _pytest.outcomes.XFailed: previous test failed (test_modification)
|
||||||
|
|
||||||
|
conftest.py:47: XFailed
|
||||||
========================= short test summary info ==========================
|
========================= short test summary info ==========================
|
||||||
XFAIL test_step.py::TestUserHandling::test_deletion - reason: previous test failed (test_modification)
|
XFAIL test_step.py::TestUserHandling::test_deletion - reason: previous test failed (test_modification)
|
||||||
================== 1 failed, 2 passed, 1 xfailed in 0.12s ==================
|
================== 1 failed, 2 passed, 1 xfailed in 0.12s ==================
|
||||||
|
@ -726,14 +751,14 @@ We can run this:
|
||||||
|
|
||||||
$ pytest
|
$ pytest
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 7 items
|
collected 7 items
|
||||||
|
|
||||||
test_step.py .Fx. [ 57%]
|
a/test_db.py F [ 14%]
|
||||||
a/test_db.py F [ 71%]
|
a/test_db2.py F [ 28%]
|
||||||
a/test_db2.py F [ 85%]
|
b/test_error.py E [ 42%]
|
||||||
b/test_error.py E [100%]
|
test_step.py .Fx. [100%]
|
||||||
|
|
||||||
================================== ERRORS ==================================
|
================================== ERRORS ==================================
|
||||||
_______________________ ERROR at setup of test_root ________________________
|
_______________________ ERROR at setup of test_root ________________________
|
||||||
|
@ -745,39 +770,39 @@ We can run this:
|
||||||
|
|
||||||
/home/sweet/project/b/test_error.py:1
|
/home/sweet/project/b/test_error.py:1
|
||||||
================================= FAILURES =================================
|
================================= FAILURES =================================
|
||||||
|
_________________________________ test_a1 __________________________________
|
||||||
|
|
||||||
|
db = <conftest.DB object at 0xdeadbeef0002>
|
||||||
|
|
||||||
|
def test_a1(db):
|
||||||
|
> assert 0, db # to show value
|
||||||
|
E AssertionError: <conftest.DB object at 0xdeadbeef0002>
|
||||||
|
E assert 0
|
||||||
|
|
||||||
|
a/test_db.py:2: AssertionError
|
||||||
|
_________________________________ test_a2 __________________________________
|
||||||
|
|
||||||
|
db = <conftest.DB object at 0xdeadbeef0002>
|
||||||
|
|
||||||
|
def test_a2(db):
|
||||||
|
> assert 0, db # to show value
|
||||||
|
E AssertionError: <conftest.DB object at 0xdeadbeef0002>
|
||||||
|
E assert 0
|
||||||
|
|
||||||
|
a/test_db2.py:2: AssertionError
|
||||||
____________________ TestUserHandling.test_modification ____________________
|
____________________ TestUserHandling.test_modification ____________________
|
||||||
|
|
||||||
self = <test_step.TestUserHandling object at 0xdeadbeef0002>
|
self = <test_step.TestUserHandling object at 0xdeadbeef0003>
|
||||||
|
|
||||||
def test_modification(self):
|
def test_modification(self):
|
||||||
> assert 0
|
> assert 0
|
||||||
E assert 0
|
E assert 0
|
||||||
|
|
||||||
test_step.py:11: AssertionError
|
test_step.py:11: AssertionError
|
||||||
_________________________________ test_a1 __________________________________
|
|
||||||
|
|
||||||
db = <conftest.DB object at 0xdeadbeef0003>
|
|
||||||
|
|
||||||
def test_a1(db):
|
|
||||||
> assert 0, db # to show value
|
|
||||||
E AssertionError: <conftest.DB object at 0xdeadbeef0003>
|
|
||||||
E assert 0
|
|
||||||
|
|
||||||
a/test_db.py:2: AssertionError
|
|
||||||
_________________________________ test_a2 __________________________________
|
|
||||||
|
|
||||||
db = <conftest.DB object at 0xdeadbeef0003>
|
|
||||||
|
|
||||||
def test_a2(db):
|
|
||||||
> assert 0, db # to show value
|
|
||||||
E AssertionError: <conftest.DB object at 0xdeadbeef0003>
|
|
||||||
E assert 0
|
|
||||||
|
|
||||||
a/test_db2.py:2: AssertionError
|
|
||||||
========================= short test summary info ==========================
|
========================= short test summary info ==========================
|
||||||
FAILED test_step.py::TestUserHandling::test_modification - assert 0
|
|
||||||
FAILED a/test_db.py::test_a1 - AssertionError: <conftest.DB object at 0x7...
|
FAILED a/test_db.py::test_a1 - AssertionError: <conftest.DB object at 0x7...
|
||||||
FAILED a/test_db2.py::test_a2 - AssertionError: <conftest.DB object at 0x...
|
FAILED a/test_db2.py::test_a2 - AssertionError: <conftest.DB object at 0x...
|
||||||
|
FAILED test_step.py::TestUserHandling::test_modification - assert 0
|
||||||
ERROR b/test_error.py::test_root
|
ERROR b/test_error.py::test_root
|
||||||
============= 3 failed, 2 passed, 1 xfailed, 1 error in 0.12s ==============
|
============= 3 failed, 2 passed, 1 xfailed, 1 error in 0.12s ==============
|
||||||
|
|
||||||
|
@ -846,7 +871,7 @@ and run them:
|
||||||
|
|
||||||
$ pytest test_module.py
|
$ pytest test_module.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 2 items
|
collected 2 items
|
||||||
|
|
||||||
|
@ -955,7 +980,7 @@ and run it:
|
||||||
|
|
||||||
$ pytest -s test_module.py
|
$ pytest -s test_module.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 3 items
|
collected 3 items
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
xfail = pytest.mark.xfail
|
xfail = pytest.mark.xfail
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -99,8 +99,7 @@ sets. pytest-2.3 introduces a decorator for use on the factory itself:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@pytest.fixture(params=["mysql", "pg"])
|
@pytest.fixture(params=["mysql", "pg"])
|
||||||
def db(request):
|
def db(request): ... # use request.param
|
||||||
... # use request.param
|
|
||||||
|
|
||||||
Here the factory will be invoked twice (with the respective "mysql"
|
Here the factory will be invoked twice (with the respective "mysql"
|
||||||
and "pg" values set as ``request.param`` attributes) and all of
|
and "pg" values set as ``request.param`` attributes) and all of
|
||||||
|
@ -141,8 +140,7 @@ argument:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def db(request):
|
def db(request): ...
|
||||||
...
|
|
||||||
|
|
||||||
The name under which the funcarg resource can be requested is ``db``.
|
The name under which the funcarg resource can be requested is ``db``.
|
||||||
|
|
||||||
|
@ -151,8 +149,7 @@ aka:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
def pytest_funcarg__db(request):
|
def pytest_funcarg__db(request): ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
But it is then not possible to define scoping and parametrization.
|
But it is then not possible to define scoping and parametrization.
|
||||||
|
|
|
@ -22,7 +22,7 @@ Install ``pytest``
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ pytest --version
|
$ pytest --version
|
||||||
pytest 7.4.4
|
pytest 8.0.2
|
||||||
|
|
||||||
.. _`simpletest`:
|
.. _`simpletest`:
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ The test
|
||||||
|
|
||||||
$ pytest
|
$ pytest
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 1 item
|
collected 1 item
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ Use the :ref:`raises <assertraises>` helper to assert that some code raises an e
|
||||||
f()
|
f()
|
||||||
|
|
||||||
You can also use the context provided by :ref:`raises <assertraises>` to
|
You can also use the context provided by :ref:`raises <assertraises>` to
|
||||||
assert that an expected exception is part of a raised ``ExceptionGroup``:
|
assert that an expected exception is part of a raised :class:`ExceptionGroup`:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
|
|
@ -227,8 +227,7 @@ to use strings:
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif("sys.version_info >= (3,3)")
|
@pytest.mark.skipif("sys.version_info >= (3,3)")
|
||||||
def test_function():
|
def test_function(): ...
|
||||||
...
|
|
||||||
|
|
||||||
During test function setup the skipif condition is evaluated by calling
|
During test function setup the skipif condition is evaluated by calling
|
||||||
``eval('sys.version_info >= (3,0)', namespace)``. The namespace contains
|
``eval('sys.version_info >= (3,0)', namespace)``. The namespace contains
|
||||||
|
@ -262,8 +261,7 @@ configuration value which you might have added:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@pytest.mark.skipif("not config.getvalue('db')")
|
@pytest.mark.skipif("not config.getvalue('db')")
|
||||||
def test_function():
|
def test_function(): ...
|
||||||
...
|
|
||||||
|
|
||||||
The equivalent with "boolean conditions" is:
|
The equivalent with "boolean conditions" is:
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ you will see the return value of the function call:
|
||||||
|
|
||||||
$ pytest test_assert1.py
|
$ pytest test_assert1.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 1 item
|
collected 1 item
|
||||||
|
|
||||||
|
@ -143,16 +143,18 @@ Notes:
|
||||||
* The ``match`` parameter also matches against `PEP-678 <https://peps.python.org/pep-0678/>`__ ``__notes__``.
|
* The ``match`` parameter also matches against `PEP-678 <https://peps.python.org/pep-0678/>`__ ``__notes__``.
|
||||||
|
|
||||||
|
|
||||||
|
.. _`assert-matching-exception-groups`:
|
||||||
|
|
||||||
Matching exception groups
|
Matching exception groups
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
You can also use the :func:`excinfo.group_contains() <pytest.ExceptionInfo.group_contains>`
|
You can also use the :func:`excinfo.group_contains() <pytest.ExceptionInfo.group_contains>`
|
||||||
method to test for exceptions returned as part of an ``ExceptionGroup``:
|
method to test for exceptions returned as part of an :class:`ExceptionGroup`:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
def test_exception_in_group():
|
def test_exception_in_group():
|
||||||
with pytest.raises(RuntimeError) as excinfo:
|
with pytest.raises(ExceptionGroup) as excinfo:
|
||||||
raise ExceptionGroup(
|
raise ExceptionGroup(
|
||||||
"Group message",
|
"Group message",
|
||||||
[
|
[
|
||||||
|
@ -174,7 +176,7 @@ exception at a specific level; exceptions contained directly in the top
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
def test_exception_in_group_at_given_depth():
|
def test_exception_in_group_at_given_depth():
|
||||||
with pytest.raises(RuntimeError) as excinfo:
|
with pytest.raises(ExceptionGroup) as excinfo:
|
||||||
raise ExceptionGroup(
|
raise ExceptionGroup(
|
||||||
"Group message",
|
"Group message",
|
||||||
[
|
[
|
||||||
|
@ -278,7 +280,7 @@ if you run this module:
|
||||||
|
|
||||||
$ pytest test_assert2.py
|
$ pytest test_assert2.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 1 item
|
collected 1 item
|
||||||
|
|
||||||
|
@ -292,6 +294,7 @@ if you run this module:
|
||||||
set2 = set("8035")
|
set2 = set("8035")
|
||||||
> assert set1 == set2
|
> assert set1 == set2
|
||||||
E AssertionError: assert {'0', '1', '3', '8'} == {'0', '3', '5', '8'}
|
E AssertionError: assert {'0', '1', '3', '8'} == {'0', '3', '5', '8'}
|
||||||
|
E
|
||||||
E Extra items in the left set:
|
E Extra items in the left set:
|
||||||
E '1'
|
E '1'
|
||||||
E Extra items in the right set:
|
E Extra items in the right set:
|
||||||
|
|
|
@ -86,7 +86,7 @@ If you then run it with ``--lf``:
|
||||||
|
|
||||||
$ pytest --lf
|
$ pytest --lf
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 2 items
|
collected 2 items
|
||||||
run-last-failure: rerun previous 2 failures
|
run-last-failure: rerun previous 2 failures
|
||||||
|
@ -132,7 +132,7 @@ of ``FF`` and dots):
|
||||||
|
|
||||||
$ pytest --ff
|
$ pytest --ff
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 50 items
|
collected 50 items
|
||||||
run-last-failure: rerun previous 2 failures first
|
run-last-failure: rerun previous 2 failures first
|
||||||
|
@ -281,7 +281,7 @@ You can always peek at the content of the cache using the
|
||||||
|
|
||||||
$ pytest --cache-show
|
$ pytest --cache-show
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
cachedir: /home/sweet/project/.pytest_cache
|
cachedir: /home/sweet/project/.pytest_cache
|
||||||
--------------------------- cache values for '*' ---------------------------
|
--------------------------- cache values for '*' ---------------------------
|
||||||
|
@ -303,7 +303,7 @@ filtering:
|
||||||
|
|
||||||
$ pytest --cache-show example/*
|
$ pytest --cache-show example/*
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
cachedir: /home/sweet/project/.pytest_cache
|
cachedir: /home/sweet/project/.pytest_cache
|
||||||
----------------------- cache values for 'example/*' -----------------------
|
----------------------- cache values for 'example/*' -----------------------
|
||||||
|
|
|
@ -83,7 +83,7 @@ of the failing function and hide the other one:
|
||||||
|
|
||||||
$ pytest
|
$ pytest
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 2 items
|
collected 2 items
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ Running pytest now produces this output:
|
||||||
|
|
||||||
$ pytest test_show_warnings.py
|
$ pytest test_show_warnings.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 1 item
|
collected 1 item
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ then you can just invoke ``pytest`` directly:
|
||||||
|
|
||||||
$ pytest
|
$ pytest
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 1 item
|
collected 1 item
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ and functions, including from test modules:
|
||||||
|
|
||||||
$ pytest --doctest-modules
|
$ pytest --doctest-modules
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 2 items
|
collected 2 items
|
||||||
|
|
||||||
|
|
|
@ -433,7 +433,7 @@ marked ``smtp_connection`` fixture function. Running the test looks like this:
|
||||||
|
|
||||||
$ pytest test_module.py
|
$ pytest test_module.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 2 items
|
collected 2 items
|
||||||
|
|
||||||
|
@ -771,7 +771,7 @@ For yield fixtures, the first teardown code to run is from the right-most fixtur
|
||||||
|
|
||||||
$ pytest -s test_finalizers.py
|
$ pytest -s test_finalizers.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 1 item
|
collected 1 item
|
||||||
|
|
||||||
|
@ -805,7 +805,7 @@ For finalizers, the first fixture to run is last call to `request.addfinalizer`.
|
||||||
|
|
||||||
$ pytest -s test_finalizers.py
|
$ pytest -s test_finalizers.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 1 item
|
collected 1 item
|
||||||
|
|
||||||
|
@ -1414,27 +1414,28 @@ Running the above tests results in the following test IDs being used:
|
||||||
|
|
||||||
$ pytest --collect-only
|
$ pytest --collect-only
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 12 items
|
collected 12 items
|
||||||
|
|
||||||
<Module test_anothersmtp.py>
|
<Dir fixtures.rst-213>
|
||||||
<Function test_showhelo[smtp.gmail.com]>
|
<Module test_anothersmtp.py>
|
||||||
<Function test_showhelo[mail.python.org]>
|
<Function test_showhelo[smtp.gmail.com]>
|
||||||
<Module test_emaillib.py>
|
<Function test_showhelo[mail.python.org]>
|
||||||
<Function test_email_received>
|
<Module test_emaillib.py>
|
||||||
<Module test_finalizers.py>
|
<Function test_email_received>
|
||||||
<Function test_bar>
|
<Module test_finalizers.py>
|
||||||
<Module test_ids.py>
|
<Function test_bar>
|
||||||
<Function test_a[spam]>
|
<Module test_ids.py>
|
||||||
<Function test_a[ham]>
|
<Function test_a[spam]>
|
||||||
<Function test_b[eggs]>
|
<Function test_a[ham]>
|
||||||
<Function test_b[1]>
|
<Function test_b[eggs]>
|
||||||
<Module test_module.py>
|
<Function test_b[1]>
|
||||||
<Function test_ehlo[smtp.gmail.com]>
|
<Module test_module.py>
|
||||||
<Function test_noop[smtp.gmail.com]>
|
<Function test_ehlo[smtp.gmail.com]>
|
||||||
<Function test_ehlo[mail.python.org]>
|
<Function test_noop[smtp.gmail.com]>
|
||||||
<Function test_noop[mail.python.org]>
|
<Function test_ehlo[mail.python.org]>
|
||||||
|
<Function test_noop[mail.python.org]>
|
||||||
|
|
||||||
======================= 12 tests collected in 0.12s ========================
|
======================= 12 tests collected in 0.12s ========================
|
||||||
|
|
||||||
|
@ -1468,7 +1469,7 @@ Running this test will *skip* the invocation of ``data_set`` with value ``2``:
|
||||||
|
|
||||||
$ pytest test_fixture_marks.py -v
|
$ pytest test_fixture_marks.py -v
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
||||||
cachedir: .pytest_cache
|
cachedir: .pytest_cache
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collecting ... collected 3 items
|
collecting ... collected 3 items
|
||||||
|
@ -1518,7 +1519,7 @@ Here we declare an ``app`` fixture which receives the previously defined
|
||||||
|
|
||||||
$ pytest -v test_appsetup.py
|
$ pytest -v test_appsetup.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
||||||
cachedir: .pytest_cache
|
cachedir: .pytest_cache
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collecting ... collected 2 items
|
collecting ... collected 2 items
|
||||||
|
@ -1598,7 +1599,7 @@ Let's run the tests in verbose mode and with looking at the print-output:
|
||||||
|
|
||||||
$ pytest -v -s test_module.py
|
$ pytest -v -s test_module.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
|
||||||
cachedir: .pytest_cache
|
cachedir: .pytest_cache
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collecting ... collected 8 items
|
collecting ... collected 8 items
|
||||||
|
@ -1720,8 +1721,7 @@ You can specify multiple fixtures like this:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@pytest.mark.usefixtures("cleandir", "anotherfixture")
|
@pytest.mark.usefixtures("cleandir", "anotherfixture")
|
||||||
def test():
|
def test(): ...
|
||||||
...
|
|
||||||
|
|
||||||
and you may specify fixture usage at the test module level using :globalvar:`pytestmark`:
|
and you may specify fixture usage at the test module level using :globalvar:`pytestmark`:
|
||||||
|
|
||||||
|
@ -1749,8 +1749,7 @@ into an ini-file:
|
||||||
|
|
||||||
@pytest.mark.usefixtures("my_other_fixture")
|
@pytest.mark.usefixtures("my_other_fixture")
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def my_fixture_that_sadly_wont_use_my_other_fixture():
|
def my_fixture_that_sadly_wont_use_my_other_fixture(): ...
|
||||||
...
|
|
||||||
|
|
||||||
This generates a deprecation warning, and will become an error in Pytest 8.
|
This generates a deprecation warning, and will become an error in Pytest 8.
|
||||||
|
|
||||||
|
|
|
@ -100,6 +100,7 @@ Executing pytest normally gives us this output (we are skipping the header to fo
|
||||||
fruits2 = ["banana", "apple", "orange", "melon", "kiwi"]
|
fruits2 = ["banana", "apple", "orange", "melon", "kiwi"]
|
||||||
> assert fruits1 == fruits2
|
> assert fruits1 == fruits2
|
||||||
E AssertionError: assert ['banana', 'a...elon', 'kiwi'] == ['banana', 'a...elon', 'kiwi']
|
E AssertionError: assert ['banana', 'a...elon', 'kiwi'] == ['banana', 'a...elon', 'kiwi']
|
||||||
|
E
|
||||||
E At index 2 diff: 'grapes' != 'orange'
|
E At index 2 diff: 'grapes' != 'orange'
|
||||||
E Use -v to get more diff
|
E Use -v to get more diff
|
||||||
|
|
||||||
|
@ -111,6 +112,7 @@ Executing pytest normally gives us this output (we are skipping the header to fo
|
||||||
number_to_text2 = {str(x * 10): x * 10 for x in range(5)}
|
number_to_text2 = {str(x * 10): x * 10 for x in range(5)}
|
||||||
> assert number_to_text1 == number_to_text2
|
> assert number_to_text1 == number_to_text2
|
||||||
E AssertionError: assert {'0': 0, '1':..., '3': 3, ...} == {'0': 0, '10'...'30': 30, ...}
|
E AssertionError: assert {'0': 0, '1':..., '3': 3, ...} == {'0': 0, '10'...'30': 30, ...}
|
||||||
|
E
|
||||||
E Omitting 1 identical items, use -vv to show
|
E Omitting 1 identical items, use -vv to show
|
||||||
E Left contains 4 more items:
|
E Left contains 4 more items:
|
||||||
E {'1': 1, '2': 2, '3': 3, '4': 4}
|
E {'1': 1, '2': 2, '3': 3, '4': 4}
|
||||||
|
@ -162,12 +164,15 @@ Now we can increase pytest's verbosity:
|
||||||
fruits2 = ["banana", "apple", "orange", "melon", "kiwi"]
|
fruits2 = ["banana", "apple", "orange", "melon", "kiwi"]
|
||||||
> assert fruits1 == fruits2
|
> assert fruits1 == fruits2
|
||||||
E AssertionError: assert ['banana', 'a...elon', 'kiwi'] == ['banana', 'a...elon', 'kiwi']
|
E AssertionError: assert ['banana', 'a...elon', 'kiwi'] == ['banana', 'a...elon', 'kiwi']
|
||||||
|
E
|
||||||
E At index 2 diff: 'grapes' != 'orange'
|
E At index 2 diff: 'grapes' != 'orange'
|
||||||
|
E
|
||||||
E Full diff:
|
E Full diff:
|
||||||
E - ['banana', 'apple', 'orange', 'melon', 'kiwi']
|
E [
|
||||||
E ? ^ ^^
|
E 'banana',
|
||||||
E + ['banana', 'apple', 'grapes', 'melon', 'kiwi']
|
E 'apple',...
|
||||||
E ? ^ ^ +
|
E
|
||||||
|
E ...Full output truncated (7 lines hidden), use '-vv' to show
|
||||||
|
|
||||||
test_verbosity_example.py:8: AssertionError
|
test_verbosity_example.py:8: AssertionError
|
||||||
____________________________ test_numbers_fail _____________________________
|
____________________________ test_numbers_fail _____________________________
|
||||||
|
@ -177,15 +182,15 @@ Now we can increase pytest's verbosity:
|
||||||
number_to_text2 = {str(x * 10): x * 10 for x in range(5)}
|
number_to_text2 = {str(x * 10): x * 10 for x in range(5)}
|
||||||
> assert number_to_text1 == number_to_text2
|
> assert number_to_text1 == number_to_text2
|
||||||
E AssertionError: assert {'0': 0, '1':..., '3': 3, ...} == {'0': 0, '10'...'30': 30, ...}
|
E AssertionError: assert {'0': 0, '1':..., '3': 3, ...} == {'0': 0, '10'...'30': 30, ...}
|
||||||
|
E
|
||||||
E Omitting 1 identical items, use -vv to show
|
E Omitting 1 identical items, use -vv to show
|
||||||
E Left contains 4 more items:
|
E Left contains 4 more items:
|
||||||
E {'1': 1, '2': 2, '3': 3, '4': 4}
|
E {'1': 1, '2': 2, '3': 3, '4': 4}
|
||||||
E Right contains 4 more items:
|
E Right contains 4 more items:
|
||||||
E {'10': 10, '20': 20, '30': 30, '40': 40}
|
E {'10': 10, '20': 20, '30': 30, '40': 40}
|
||||||
E Full diff:
|
E ...
|
||||||
E - {'0': 0, '10': 10, '20': 20, '30': 30, '40': 40}
|
E
|
||||||
E ? - - - - - - - -
|
E ...Full output truncated (16 lines hidden), use '-vv' to show
|
||||||
E + {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4}
|
|
||||||
|
|
||||||
test_verbosity_example.py:14: AssertionError
|
test_verbosity_example.py:14: AssertionError
|
||||||
___________________________ test_long_text_fail ____________________________
|
___________________________ test_long_text_fail ____________________________
|
||||||
|
@ -231,12 +236,20 @@ Now if we increase verbosity even more:
|
||||||
fruits2 = ["banana", "apple", "orange", "melon", "kiwi"]
|
fruits2 = ["banana", "apple", "orange", "melon", "kiwi"]
|
||||||
> assert fruits1 == fruits2
|
> assert fruits1 == fruits2
|
||||||
E AssertionError: assert ['banana', 'apple', 'grapes', 'melon', 'kiwi'] == ['banana', 'apple', 'orange', 'melon', 'kiwi']
|
E AssertionError: assert ['banana', 'apple', 'grapes', 'melon', 'kiwi'] == ['banana', 'apple', 'orange', 'melon', 'kiwi']
|
||||||
|
E
|
||||||
E At index 2 diff: 'grapes' != 'orange'
|
E At index 2 diff: 'grapes' != 'orange'
|
||||||
|
E
|
||||||
E Full diff:
|
E Full diff:
|
||||||
E - ['banana', 'apple', 'orange', 'melon', 'kiwi']
|
E [
|
||||||
E ? ^ ^^
|
E 'banana',
|
||||||
E + ['banana', 'apple', 'grapes', 'melon', 'kiwi']
|
E 'apple',
|
||||||
E ? ^ ^ +
|
E - 'orange',
|
||||||
|
E ? ^ ^^
|
||||||
|
E + 'grapes',
|
||||||
|
E ? ^ ^ +
|
||||||
|
E 'melon',
|
||||||
|
E 'kiwi',
|
||||||
|
E ]
|
||||||
|
|
||||||
test_verbosity_example.py:8: AssertionError
|
test_verbosity_example.py:8: AssertionError
|
||||||
____________________________ test_numbers_fail _____________________________
|
____________________________ test_numbers_fail _____________________________
|
||||||
|
@ -246,16 +259,30 @@ Now if we increase verbosity even more:
|
||||||
number_to_text2 = {str(x * 10): x * 10 for x in range(5)}
|
number_to_text2 = {str(x * 10): x * 10 for x in range(5)}
|
||||||
> assert number_to_text1 == number_to_text2
|
> assert number_to_text1 == number_to_text2
|
||||||
E AssertionError: assert {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4} == {'0': 0, '10': 10, '20': 20, '30': 30, '40': 40}
|
E AssertionError: assert {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4} == {'0': 0, '10': 10, '20': 20, '30': 30, '40': 40}
|
||||||
|
E
|
||||||
E Common items:
|
E Common items:
|
||||||
E {'0': 0}
|
E {'0': 0}
|
||||||
E Left contains 4 more items:
|
E Left contains 4 more items:
|
||||||
E {'1': 1, '2': 2, '3': 3, '4': 4}
|
E {'1': 1, '2': 2, '3': 3, '4': 4}
|
||||||
E Right contains 4 more items:
|
E Right contains 4 more items:
|
||||||
E {'10': 10, '20': 20, '30': 30, '40': 40}
|
E {'10': 10, '20': 20, '30': 30, '40': 40}
|
||||||
|
E
|
||||||
E Full diff:
|
E Full diff:
|
||||||
E - {'0': 0, '10': 10, '20': 20, '30': 30, '40': 40}
|
E {
|
||||||
E ? - - - - - - - -
|
E '0': 0,
|
||||||
E + {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4}
|
E - '10': 10,
|
||||||
|
E ? - -
|
||||||
|
E + '1': 1,
|
||||||
|
E - '20': 20,
|
||||||
|
E ? - -
|
||||||
|
E + '2': 2,
|
||||||
|
E - '30': 30,
|
||||||
|
E ? - -
|
||||||
|
E + '3': 3,
|
||||||
|
E - '40': 40,
|
||||||
|
E ? - -
|
||||||
|
E + '4': 4,
|
||||||
|
E }
|
||||||
|
|
||||||
test_verbosity_example.py:14: AssertionError
|
test_verbosity_example.py:14: AssertionError
|
||||||
___________________________ test_long_text_fail ____________________________
|
___________________________ test_long_text_fail ____________________________
|
||||||
|
@ -354,7 +381,7 @@ Example:
|
||||||
|
|
||||||
$ pytest -ra
|
$ pytest -ra
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 6 items
|
collected 6 items
|
||||||
|
|
||||||
|
@ -377,10 +404,19 @@ Example:
|
||||||
E assert 0
|
E assert 0
|
||||||
|
|
||||||
test_example.py:14: AssertionError
|
test_example.py:14: AssertionError
|
||||||
|
================================ XFAILURES =================================
|
||||||
|
________________________________ test_xfail ________________________________
|
||||||
|
|
||||||
|
def test_xfail():
|
||||||
|
> pytest.xfail("xfailing this test")
|
||||||
|
E _pytest.outcomes.XFailed: xfailing this test
|
||||||
|
|
||||||
|
test_example.py:26: XFailed
|
||||||
|
================================= XPASSES ==================================
|
||||||
========================= short test summary info ==========================
|
========================= short test summary info ==========================
|
||||||
SKIPPED [1] test_example.py:22: skipping this test
|
SKIPPED [1] test_example.py:22: skipping this test
|
||||||
XFAIL test_example.py::test_xfail - reason: xfailing this test
|
XFAIL test_example.py::test_xfail - reason: xfailing this test
|
||||||
XPASS test_example.py::test_xpass always xfail
|
XPASS test_example.py::test_xpass - always xfail
|
||||||
ERROR test_example.py::test_error - assert 0
|
ERROR test_example.py::test_error - assert 0
|
||||||
FAILED test_example.py::test_fail - assert 0
|
FAILED test_example.py::test_fail - assert 0
|
||||||
== 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12s ===
|
== 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12s ===
|
||||||
|
@ -410,7 +446,7 @@ More than one character can be used, so for example to only see failed and skipp
|
||||||
|
|
||||||
$ pytest -rfs
|
$ pytest -rfs
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 6 items
|
collected 6 items
|
||||||
|
|
||||||
|
@ -445,7 +481,7 @@ captured output:
|
||||||
|
|
||||||
$ pytest -rpP
|
$ pytest -rpP
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 6 items
|
collected 6 items
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ them in turn:
|
||||||
|
|
||||||
$ pytest
|
$ pytest
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 3 items
|
collected 3 items
|
||||||
|
|
||||||
|
@ -167,7 +167,7 @@ Let's run this:
|
||||||
|
|
||||||
$ pytest
|
$ pytest
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 3 items
|
collected 3 items
|
||||||
|
|
||||||
|
|
|
@ -47,8 +47,7 @@ which may be passed an optional ``reason``:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@pytest.mark.skip(reason="no way of currently testing this")
|
@pytest.mark.skip(reason="no way of currently testing this")
|
||||||
def test_the_unknown():
|
def test_the_unknown(): ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
Alternatively, it is also possible to skip imperatively during test execution or setup
|
Alternatively, it is also possible to skip imperatively during test execution or setup
|
||||||
|
@ -93,8 +92,7 @@ when run on an interpreter earlier than Python3.10:
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(sys.version_info < (3, 10), reason="requires python3.10 or higher")
|
@pytest.mark.skipif(sys.version_info < (3, 10), reason="requires python3.10 or higher")
|
||||||
def test_function():
|
def test_function(): ...
|
||||||
...
|
|
||||||
|
|
||||||
If the condition evaluates to ``True`` during collection, the test function will be skipped,
|
If the condition evaluates to ``True`` during collection, the test function will be skipped,
|
||||||
with the specified reason appearing in the summary when using ``-rs``.
|
with the specified reason appearing in the summary when using ``-rs``.
|
||||||
|
@ -112,8 +110,7 @@ You can share ``skipif`` markers between modules. Consider this test module:
|
||||||
|
|
||||||
|
|
||||||
@minversion
|
@minversion
|
||||||
def test_function():
|
def test_function(): ...
|
||||||
...
|
|
||||||
|
|
||||||
You can import the marker and reuse it in another test module:
|
You can import the marker and reuse it in another test module:
|
||||||
|
|
||||||
|
@ -124,8 +121,7 @@ You can import the marker and reuse it in another test module:
|
||||||
|
|
||||||
|
|
||||||
@minversion
|
@minversion
|
||||||
def test_anotherfunction():
|
def test_anotherfunction(): ...
|
||||||
...
|
|
||||||
|
|
||||||
For larger test suites it's usually a good idea to have one file
|
For larger test suites it's usually a good idea to have one file
|
||||||
where you define the markers which you then consistently apply
|
where you define the markers which you then consistently apply
|
||||||
|
@ -232,8 +228,7 @@ expect a test to fail:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@pytest.mark.xfail
|
@pytest.mark.xfail
|
||||||
def test_function():
|
def test_function(): ...
|
||||||
...
|
|
||||||
|
|
||||||
This test will run but no traceback will be reported when it fails. Instead, terminal
|
This test will run but no traceback will be reported when it fails. Instead, terminal
|
||||||
reporting will list it in the "expected to fail" (``XFAIL``) or "unexpectedly
|
reporting will list it in the "expected to fail" (``XFAIL``) or "unexpectedly
|
||||||
|
@ -275,8 +270,7 @@ that condition as the first parameter:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@pytest.mark.xfail(sys.platform == "win32", reason="bug in a 3rd party library")
|
@pytest.mark.xfail(sys.platform == "win32", reason="bug in a 3rd party library")
|
||||||
def test_function():
|
def test_function(): ...
|
||||||
...
|
|
||||||
|
|
||||||
Note that you have to pass a reason as well (see the parameter description at
|
Note that you have to pass a reason as well (see the parameter description at
|
||||||
:ref:`pytest.mark.xfail ref`).
|
:ref:`pytest.mark.xfail ref`).
|
||||||
|
@ -289,8 +283,7 @@ You can specify the motive of an expected failure with the ``reason`` parameter:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@pytest.mark.xfail(reason="known parser issue")
|
@pytest.mark.xfail(reason="known parser issue")
|
||||||
def test_function():
|
def test_function(): ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
``raises`` parameter
|
``raises`` parameter
|
||||||
|
@ -302,8 +295,7 @@ a single exception, or a tuple of exceptions, in the ``raises`` argument.
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@pytest.mark.xfail(raises=RuntimeError)
|
@pytest.mark.xfail(raises=RuntimeError)
|
||||||
def test_function():
|
def test_function(): ...
|
||||||
...
|
|
||||||
|
|
||||||
Then the test will be reported as a regular failure if it fails with an
|
Then the test will be reported as a regular failure if it fails with an
|
||||||
exception not mentioned in ``raises``.
|
exception not mentioned in ``raises``.
|
||||||
|
@ -317,8 +309,7 @@ even executed, use the ``run`` parameter as ``False``:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@pytest.mark.xfail(run=False)
|
@pytest.mark.xfail(run=False)
|
||||||
def test_function():
|
def test_function(): ...
|
||||||
...
|
|
||||||
|
|
||||||
This is specially useful for xfailing tests that are crashing the interpreter and should be
|
This is specially useful for xfailing tests that are crashing the interpreter and should be
|
||||||
investigated later.
|
investigated later.
|
||||||
|
@ -334,8 +325,7 @@ You can change this by setting the ``strict`` keyword-only parameter to ``True``
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@pytest.mark.xfail(strict=True)
|
@pytest.mark.xfail(strict=True)
|
||||||
def test_function():
|
def test_function(): ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
This will make ``XPASS`` ("unexpectedly passing") results from this test to fail the test suite.
|
This will make ``XPASS`` ("unexpectedly passing") results from this test to fail the test suite.
|
||||||
|
|
|
@ -36,7 +36,7 @@ Running this would result in a passed test except for the last
|
||||||
|
|
||||||
$ pytest test_tmp_path.py
|
$ pytest test_tmp_path.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 1 item
|
collected 1 item
|
||||||
|
|
||||||
|
|
|
@ -140,7 +140,7 @@ the ``self.db`` values in the traceback:
|
||||||
|
|
||||||
$ pytest test_unittest_db.py
|
$ pytest test_unittest_db.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 2 items
|
collected 2 items
|
||||||
|
|
||||||
|
|
|
@ -448,7 +448,7 @@ in our ``pytest.ini`` to tell pytest where to look for example files.
|
||||||
|
|
||||||
$ pytest
|
$ pytest
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
configfile: pytest.ini
|
configfile: pytest.ini
|
||||||
collected 2 items
|
collected 2 items
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
:orphan:
|
:orphan:
|
||||||
|
|
||||||
.. sidebar:: Next Open Trainings
|
.. sidebar:: Next Open Trainings and Events
|
||||||
|
|
||||||
- `Professional Testing with Python <https://python-academy.com/courses/python_course_testing.html>`_, via `Python Academy <https://www.python-academy.com/>`_, **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/>`_ (3 day in-depth training):
|
||||||
|
* **June 11th to 13th 2024**, Remote
|
||||||
|
* **March 4th to 6th 2025**, Leipzig, Germany / Remote
|
||||||
|
- `pytest development sprint <https://github.com/pytest-dev/pytest/discussions/11655>`_, June 2024 (`date poll <https://nuudel.digitalcourage.de/2tEsEpRcwMNcAXVO>`_)
|
||||||
|
|
||||||
Also see :doc:`previous talks and blogposts <talks>`.
|
Also see :doc:`previous talks and blogposts <talks>`.
|
||||||
|
|
||||||
|
@ -42,7 +45,7 @@ To execute it:
|
||||||
|
|
||||||
$ pytest
|
$ pytest
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
|
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 1 item
|
collected 1 item
|
||||||
|
|
||||||
|
|
|
@ -164,8 +164,7 @@ Add warning filters to marked test items.
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@pytest.mark.filterwarnings("ignore:.*usage will be deprecated.*:DeprecationWarning")
|
@pytest.mark.filterwarnings("ignore:.*usage will be deprecated.*:DeprecationWarning")
|
||||||
def test_foo():
|
def test_foo(): ...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
.. _`pytest.mark.parametrize ref`:
|
.. _`pytest.mark.parametrize ref`:
|
||||||
|
@ -276,8 +275,7 @@ For example:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@pytest.mark.timeout(10, "slow", method="thread")
|
@pytest.mark.timeout(10, "slow", method="thread")
|
||||||
def test_function():
|
def test_function(): ...
|
||||||
...
|
|
||||||
|
|
||||||
Will create and attach a :class:`Mark <pytest.Mark>` object to the collected
|
Will create and attach a :class:`Mark <pytest.Mark>` object to the collected
|
||||||
:class:`Item <pytest.Item>`, which can then be accessed by fixtures or hooks with
|
:class:`Item <pytest.Item>`, which can then be accessed by fixtures or hooks with
|
||||||
|
@ -294,8 +292,7 @@ Example for using multiple custom markers:
|
||||||
|
|
||||||
@pytest.mark.timeout(10, "slow", method="thread")
|
@pytest.mark.timeout(10, "slow", method="thread")
|
||||||
@pytest.mark.slow
|
@pytest.mark.slow
|
||||||
def test_function():
|
def test_function(): ...
|
||||||
...
|
|
||||||
|
|
||||||
When :meth:`Node.iter_markers <_pytest.nodes.Node.iter_markers>` or :meth:`Node.iter_markers_with_node <_pytest.nodes.Node.iter_markers_with_node>` is used with multiple markers, the marker closest to the function will be iterated over first. The above example will result in ``@pytest.mark.slow`` followed by ``@pytest.mark.timeout(...)``.
|
When :meth:`Node.iter_markers <_pytest.nodes.Node.iter_markers>` or :meth:`Node.iter_markers_with_node <_pytest.nodes.Node.iter_markers_with_node>` is used with multiple markers, the marker closest to the function will be iterated over first. The above example will result in ``@pytest.mark.slow`` followed by ``@pytest.mark.timeout(...)``.
|
||||||
|
|
||||||
|
@ -2036,7 +2033,7 @@ All the command-line flags can be obtained by running ``pytest --help``::
|
||||||
failure
|
failure
|
||||||
--doctest-glob=pat Doctests file matching pattern, default: test*.txt
|
--doctest-glob=pat Doctests file matching pattern, default: test*.txt
|
||||||
--doctest-ignore-import-errors
|
--doctest-ignore-import-errors
|
||||||
Ignore doctest ImportErrors
|
Ignore doctest collection errors
|
||||||
--doctest-continue-on-failure
|
--doctest-continue-on-failure
|
||||||
For a given doctest, continue to run after the first
|
For a given doctest, continue to run after the first
|
||||||
failure
|
failure
|
||||||
|
@ -2141,6 +2138,10 @@ All the command-line flags can be obtained by running ``pytest --help``::
|
||||||
enable_assertion_pass_hook (bool):
|
enable_assertion_pass_hook (bool):
|
||||||
Enables the pytest_assertion_pass hook. Make sure to
|
Enables the pytest_assertion_pass hook. Make sure to
|
||||||
delete any previously generated pyc cache files.
|
delete any previously generated pyc cache files.
|
||||||
|
verbosity_assertions (string):
|
||||||
|
Specify a verbosity level for assertions, overriding
|
||||||
|
the main level. Higher levels will provide more
|
||||||
|
detailed explanation when an assertion fails.
|
||||||
junit_suite_name (string):
|
junit_suite_name (string):
|
||||||
Test suite name for JUnit report
|
Test suite name for JUnit report
|
||||||
junit_logging (string):
|
junit_logging (string):
|
||||||
|
|
|
@ -2,7 +2,7 @@ pallets-sphinx-themes
|
||||||
pluggy>=1.2.0
|
pluggy>=1.2.0
|
||||||
pygments-pytest>=2.3.0
|
pygments-pytest>=2.3.0
|
||||||
sphinx-removed-in>=0.2.0
|
sphinx-removed-in>=0.2.0
|
||||||
sphinx>=5,<8
|
sphinx>=7
|
||||||
sphinxcontrib-trio
|
sphinxcontrib-trio
|
||||||
sphinxcontrib-svg2pdfconverter
|
sphinxcontrib-svg2pdfconverter
|
||||||
# Pin packaging because it no longer handles 'latest' version, which
|
# Pin packaging because it no longer handles 'latest' version, which
|
||||||
|
|
|
@ -3,6 +3,7 @@ from pathlib import Path
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
|
||||||
issues_url = "https://api.github.com/repos/pytest-dev/pytest/issues"
|
issues_url = "https://api.github.com/repos/pytest-dev/pytest/issues"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -123,3 +123,58 @@ target-version = ['py38']
|
||||||
[tool.check-wheel-contents]
|
[tool.check-wheel-contents]
|
||||||
# W009: Wheel contains multiple toplevel library entries
|
# W009: Wheel contains multiple toplevel library entries
|
||||||
ignore = "W009"
|
ignore = "W009"
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
src = ["src"]
|
||||||
|
line-length = 88
|
||||||
|
select = [
|
||||||
|
"D", # pydocstyle
|
||||||
|
"E", # pycodestyle
|
||||||
|
"F", # pyflakes
|
||||||
|
"I", # isort
|
||||||
|
"UP", # pyupgrade
|
||||||
|
"W", # pycodestyle
|
||||||
|
]
|
||||||
|
ignore = [
|
||||||
|
# pycodestyle ignore
|
||||||
|
# pytest can do weird low-level things, and we usually know
|
||||||
|
# what we're doing when we use type(..) is ...
|
||||||
|
"E721", # Do not compare types, use `isinstance()`
|
||||||
|
# pydocstyle ignore
|
||||||
|
"D100", # Missing docstring in public module
|
||||||
|
"D101", # Missing docstring in public class
|
||||||
|
"D102", # Missing docstring in public method
|
||||||
|
"D103", # Missing docstring in public function
|
||||||
|
"D104", # Missing docstring in public package
|
||||||
|
"D105", # Missing docstring in magic method
|
||||||
|
"D106", # Missing docstring in public nested class
|
||||||
|
"D107", # Missing docstring in `__init__`
|
||||||
|
"D209", # [*] Multi-line docstring closing quotes should be on a separate line
|
||||||
|
"D205", # 1 blank line required between summary line and description
|
||||||
|
"D400", # First line should end with a period
|
||||||
|
"D401", # First line of docstring should be in imperative mood
|
||||||
|
"D402", # First line should not be the function's signature
|
||||||
|
"D404", # First word of the docstring should not be "This"
|
||||||
|
"D415", # First line should end with a period, question mark, or exclamation point
|
||||||
|
# Temp for backport 8.0.x
|
||||||
|
"E501",
|
||||||
|
"UP031",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.ruff.format]
|
||||||
|
docstring-code-format = true
|
||||||
|
|
||||||
|
[tool.ruff.lint.pycodestyle]
|
||||||
|
# In order to be able to format for 88 char in ruff format
|
||||||
|
max-line-length = 120
|
||||||
|
|
||||||
|
[tool.ruff.lint.pydocstyle]
|
||||||
|
convention = "pep257"
|
||||||
|
|
||||||
|
[tool.ruff.lint.isort]
|
||||||
|
force-single-line = true
|
||||||
|
combine-as-imports = true
|
||||||
|
force-sort-within-sections = true
|
||||||
|
order-by-type = false
|
||||||
|
known-local-folder = ["pytest", "_pytest"]
|
||||||
|
lines-after-imports = 2
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
latest-release-notes.md
|
|
@ -0,0 +1,66 @@
|
||||||
|
# mypy: disallow-untyped-defs
|
||||||
|
"""
|
||||||
|
Script used to generate a Markdown file containing only the changelog entries of a specific pytest release, which
|
||||||
|
is then published as a GitHub Release during deploy (see workflows/deploy.yml).
|
||||||
|
|
||||||
|
The script requires ``pandoc`` to be previously installed in the system -- we need to convert from RST (the format of
|
||||||
|
our CHANGELOG) into Markdown (which is required by GitHub Releases).
|
||||||
|
|
||||||
|
Requires Python3.6+.
|
||||||
|
"""
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
|
import pypandoc
|
||||||
|
|
||||||
|
|
||||||
|
def extract_changelog_entries_for(version: str) -> str:
|
||||||
|
p = Path(__file__).parent.parent / "doc/en/changelog.rst"
|
||||||
|
changelog_lines = p.read_text(encoding="UTF-8").splitlines()
|
||||||
|
|
||||||
|
title_regex = re.compile(r"pytest (\d\.\d+\.\d+\w*) \(\d{4}-\d{2}-\d{2}\)")
|
||||||
|
consuming_version = False
|
||||||
|
version_lines = []
|
||||||
|
for line in changelog_lines:
|
||||||
|
m = title_regex.match(line)
|
||||||
|
if m:
|
||||||
|
# Found the version we want: start to consume lines until we find the next version title.
|
||||||
|
if m.group(1) == version:
|
||||||
|
consuming_version = True
|
||||||
|
# Found a new version title while parsing the version we want: break out.
|
||||||
|
elif consuming_version:
|
||||||
|
break
|
||||||
|
if consuming_version:
|
||||||
|
version_lines.append(line)
|
||||||
|
|
||||||
|
return "\n".join(version_lines)
|
||||||
|
|
||||||
|
|
||||||
|
def convert_rst_to_md(text: str) -> str:
|
||||||
|
result = pypandoc.convert_text(
|
||||||
|
text, "md", format="rst", extra_args=["--wrap=preserve"]
|
||||||
|
)
|
||||||
|
assert isinstance(result, str), repr(result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv: Sequence[str]) -> int:
|
||||||
|
if len(argv) != 3:
|
||||||
|
print("Usage: generate-gh-release-notes VERSION FILE")
|
||||||
|
return 2
|
||||||
|
|
||||||
|
version, filename = argv[1:3]
|
||||||
|
print(f"Generating GitHub release notes for version {version}")
|
||||||
|
rst_body = extract_changelog_entries_for(version)
|
||||||
|
md_body = convert_rst_to_md(rst_body)
|
||||||
|
Path(filename).write_text(md_body, encoding="UTF-8")
|
||||||
|
print()
|
||||||
|
print(f"Done: {filename}")
|
||||||
|
print()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main(sys.argv))
|
|
@ -1,3 +1,4 @@
|
||||||
|
# mypy: disallow-untyped-defs
|
||||||
"""
|
"""
|
||||||
This script is part of the pytest release process which is triggered manually in the Actions
|
This script is part of the pytest release process which is triggered manually in the Actions
|
||||||
tab of the repository.
|
tab of the repository.
|
||||||
|
@ -13,8 +14,8 @@ After that, it will create a release using the `release` tox environment, and pu
|
||||||
`pytest bot <pytestbot@gmail.com>` commit author.
|
`pytest bot <pytestbot@gmail.com>` commit author.
|
||||||
"""
|
"""
|
||||||
import argparse
|
import argparse
|
||||||
import re
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import re
|
||||||
from subprocess import check_call
|
from subprocess import check_call
|
||||||
from subprocess import check_output
|
from subprocess import check_output
|
||||||
from subprocess import run
|
from subprocess import run
|
||||||
|
|
|
@ -1,102 +0,0 @@
|
||||||
"""
|
|
||||||
Script used to publish GitHub release notes extracted from CHANGELOG.rst.
|
|
||||||
|
|
||||||
This script is meant to be executed after a successful deployment in GitHub actions.
|
|
||||||
|
|
||||||
Uses the following environment variables:
|
|
||||||
|
|
||||||
* GIT_TAG: the name of the tag of the current commit.
|
|
||||||
* GH_RELEASE_NOTES_TOKEN: a personal access token with 'repo' permissions.
|
|
||||||
|
|
||||||
Create one at:
|
|
||||||
|
|
||||||
https://github.com/settings/tokens
|
|
||||||
|
|
||||||
This token should be set in a secret in the repository, which is exposed as an
|
|
||||||
environment variable in the main.yml workflow file.
|
|
||||||
|
|
||||||
The script also requires ``pandoc`` to be previously installed in the system.
|
|
||||||
|
|
||||||
Requires Python3.6+.
|
|
||||||
"""
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import github3
|
|
||||||
import pypandoc
|
|
||||||
|
|
||||||
|
|
||||||
def publish_github_release(slug, token, tag_name, body):
|
|
||||||
github = github3.login(token=token)
|
|
||||||
owner, repo = slug.split("/")
|
|
||||||
repo = github.repository(owner, repo)
|
|
||||||
return repo.create_release(tag_name=tag_name, body=body)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_changelog(tag_name):
|
|
||||||
p = Path(__file__).parent.parent / "doc/en/changelog.rst"
|
|
||||||
changelog_lines = p.read_text(encoding="UTF-8").splitlines()
|
|
||||||
|
|
||||||
title_regex = re.compile(r"pytest (\d\.\d+\.\d+) \(\d{4}-\d{2}-\d{2}\)")
|
|
||||||
consuming_version = False
|
|
||||||
version_lines = []
|
|
||||||
for line in changelog_lines:
|
|
||||||
m = title_regex.match(line)
|
|
||||||
if m:
|
|
||||||
# found the version we want: start to consume lines until we find the next version title
|
|
||||||
if m.group(1) == tag_name:
|
|
||||||
consuming_version = True
|
|
||||||
# found a new version title while parsing the version we want: break out
|
|
||||||
elif consuming_version:
|
|
||||||
break
|
|
||||||
if consuming_version:
|
|
||||||
version_lines.append(line)
|
|
||||||
|
|
||||||
return "\n".join(version_lines)
|
|
||||||
|
|
||||||
|
|
||||||
def convert_rst_to_md(text):
|
|
||||||
return pypandoc.convert_text(
|
|
||||||
text, "md", format="rst", extra_args=["--wrap=preserve"]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def main(argv):
|
|
||||||
if len(argv) > 1:
|
|
||||||
tag_name = argv[1]
|
|
||||||
else:
|
|
||||||
tag_name = os.environ.get("GITHUB_REF")
|
|
||||||
if not tag_name:
|
|
||||||
print("tag_name not given and $GITHUB_REF not set", file=sys.stderr)
|
|
||||||
return 1
|
|
||||||
if tag_name.startswith("refs/tags/"):
|
|
||||||
tag_name = tag_name[len("refs/tags/") :]
|
|
||||||
|
|
||||||
token = os.environ.get("GH_RELEASE_NOTES_TOKEN")
|
|
||||||
if not token:
|
|
||||||
print("GH_RELEASE_NOTES_TOKEN not set", file=sys.stderr)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
slug = os.environ.get("GITHUB_REPOSITORY")
|
|
||||||
if not slug:
|
|
||||||
print("GITHUB_REPOSITORY not set", file=sys.stderr)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
rst_body = parse_changelog(tag_name)
|
|
||||||
md_body = convert_rst_to_md(rst_body)
|
|
||||||
if not publish_github_release(slug, token, tag_name, md_body):
|
|
||||||
print("Could not publish release notes:", file=sys.stderr)
|
|
||||||
print(md_body, file=sys.stderr)
|
|
||||||
return 5
|
|
||||||
|
|
||||||
print()
|
|
||||||
print(f"Release notes for {tag_name} published successfully:")
|
|
||||||
print(f"https://github.com/{slug}/releases/tag/{tag_name}")
|
|
||||||
print()
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main(sys.argv))
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
# mypy: disallow-untyped-defs
|
||||||
"""Invoke development tasks."""
|
"""Invoke development tasks."""
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
|
@ -10,15 +11,15 @@ from colorama import Fore
|
||||||
from colorama import init
|
from colorama import init
|
||||||
|
|
||||||
|
|
||||||
def announce(version, template_name, doc_version):
|
def announce(version: str, template_name: str, doc_version: str) -> None:
|
||||||
"""Generates a new release announcement entry in the docs."""
|
"""Generates a new release announcement entry in the docs."""
|
||||||
# Get our list of authors
|
# Get our list of authors
|
||||||
stdout = check_output(["git", "describe", "--abbrev=0", "--tags"])
|
stdout = check_output(["git", "describe", "--abbrev=0", "--tags"], encoding="UTF-8")
|
||||||
stdout = stdout.decode("utf-8")
|
|
||||||
last_version = stdout.strip()
|
last_version = stdout.strip()
|
||||||
|
|
||||||
stdout = check_output(["git", "log", f"{last_version}..HEAD", "--format=%aN"])
|
stdout = check_output(
|
||||||
stdout = stdout.decode("utf-8")
|
["git", "log", f"{last_version}..HEAD", "--format=%aN"], encoding="UTF-8"
|
||||||
|
)
|
||||||
|
|
||||||
contributors = {
|
contributors = {
|
||||||
name
|
name
|
||||||
|
@ -61,7 +62,7 @@ def announce(version, template_name, doc_version):
|
||||||
check_call(["git", "add", str(target)])
|
check_call(["git", "add", str(target)])
|
||||||
|
|
||||||
|
|
||||||
def regen(version):
|
def regen(version: str) -> None:
|
||||||
"""Call regendoc tool to update examples and pytest output in the docs."""
|
"""Call regendoc tool to update examples and pytest output in the docs."""
|
||||||
print(f"{Fore.CYAN}[generate.regen] {Fore.RESET}Updating docs")
|
print(f"{Fore.CYAN}[generate.regen] {Fore.RESET}Updating docs")
|
||||||
check_call(
|
check_call(
|
||||||
|
@ -70,7 +71,7 @@ def regen(version):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def fix_formatting():
|
def fix_formatting() -> None:
|
||||||
"""Runs pre-commit in all files to ensure they are formatted correctly"""
|
"""Runs pre-commit in all files to ensure they are formatted correctly"""
|
||||||
print(
|
print(
|
||||||
f"{Fore.CYAN}[generate.fix linting] {Fore.RESET}Fixing formatting using pre-commit"
|
f"{Fore.CYAN}[generate.fix linting] {Fore.RESET}Fixing formatting using pre-commit"
|
||||||
|
@ -78,13 +79,15 @@ def fix_formatting():
|
||||||
call(["pre-commit", "run", "--all-files"])
|
call(["pre-commit", "run", "--all-files"])
|
||||||
|
|
||||||
|
|
||||||
def check_links():
|
def check_links() -> None:
|
||||||
"""Runs sphinx-build to check links"""
|
"""Runs sphinx-build to check links"""
|
||||||
print(f"{Fore.CYAN}[generate.check_links] {Fore.RESET}Checking links")
|
print(f"{Fore.CYAN}[generate.check_links] {Fore.RESET}Checking links")
|
||||||
check_call(["tox", "-e", "docs-checklinks"])
|
check_call(["tox", "-e", "docs-checklinks"])
|
||||||
|
|
||||||
|
|
||||||
def pre_release(version, template_name, doc_version, *, skip_check_links):
|
def pre_release(
|
||||||
|
version: str, template_name: str, doc_version: str, *, skip_check_links: bool
|
||||||
|
) -> None:
|
||||||
"""Generates new docs, release announcements and creates a local tag."""
|
"""Generates new docs, release announcements and creates a local tag."""
|
||||||
announce(version, template_name, doc_version)
|
announce(version, template_name, doc_version)
|
||||||
regen(version)
|
regen(version)
|
||||||
|
@ -102,12 +105,12 @@ def pre_release(version, template_name, doc_version, *, skip_check_links):
|
||||||
print("Please push your branch and open a PR.")
|
print("Please push your branch and open a PR.")
|
||||||
|
|
||||||
|
|
||||||
def changelog(version, write_out=False):
|
def changelog(version: str, write_out: bool = False) -> None:
|
||||||
addopts = [] if write_out else ["--draft"]
|
addopts = [] if write_out else ["--draft"]
|
||||||
check_call(["towncrier", "--yes", "--version", version] + addopts)
|
check_call(["towncrier", "--yes", "--version", version] + addopts)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main() -> None:
|
||||||
init(autoreset=True)
|
init(autoreset=True)
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("version", help="Release version")
|
parser.add_argument("version", help="Release version")
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import sys
|
# mypy: disallow-untyped-defs
|
||||||
from subprocess import call
|
from subprocess import call
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main() -> int:
|
||||||
"""
|
"""
|
||||||
Platform agnostic wrapper script for towncrier.
|
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.
|
Fixes the issue (#7251) where Windows users are unable to natively run tox -e docs to build pytest docs.
|
||||||
"""
|
"""
|
||||||
with open(
|
with open(
|
||||||
"doc/en/_changelog_towncrier_draft.rst", "w", encoding="utf-8"
|
"doc/en/_changelog_towncrier_draft.rst", "w", encoding="utf-8"
|
||||||
|
|
|
@ -1,18 +1,23 @@
|
||||||
|
# mypy: disallow-untyped-defs
|
||||||
import datetime
|
import datetime
|
||||||
import pathlib
|
import pathlib
|
||||||
import re
|
import re
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
from textwrap import indent
|
from textwrap import indent
|
||||||
|
from typing import Any
|
||||||
|
from typing import Iterable
|
||||||
|
from typing import Iterator
|
||||||
|
from typing import TypedDict
|
||||||
|
|
||||||
import packaging.version
|
import packaging.version
|
||||||
import platformdirs
|
import platformdirs
|
||||||
import tabulate
|
|
||||||
import wcwidth
|
|
||||||
from requests_cache import CachedResponse
|
from requests_cache import CachedResponse
|
||||||
from requests_cache import CachedSession
|
from requests_cache import CachedSession
|
||||||
from requests_cache import OriginalResponse
|
from requests_cache import OriginalResponse
|
||||||
from requests_cache import SQLiteCache
|
from requests_cache import SQLiteCache
|
||||||
|
import tabulate
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
import wcwidth
|
||||||
|
|
||||||
|
|
||||||
FILE_HEAD = r"""
|
FILE_HEAD = r"""
|
||||||
|
@ -56,6 +61,7 @@ DEVELOPMENT_STATUS_CLASSIFIERS = (
|
||||||
)
|
)
|
||||||
ADDITIONAL_PROJECTS = { # set of additional projects to consider as plugins
|
ADDITIONAL_PROJECTS = { # set of additional projects to consider as plugins
|
||||||
"logassert",
|
"logassert",
|
||||||
|
"logot",
|
||||||
"nuts",
|
"nuts",
|
||||||
"flask_fixture",
|
"flask_fixture",
|
||||||
}
|
}
|
||||||
|
@ -81,7 +87,6 @@ def project_response_with_refresh(
|
||||||
|
|
||||||
force refresh in case of last serial mismatch
|
force refresh in case of last serial mismatch
|
||||||
"""
|
"""
|
||||||
|
|
||||||
response = session.get(f"https://pypi.org/pypi/{name}/json")
|
response = session.get(f"https://pypi.org/pypi/{name}/json")
|
||||||
if int(response.headers.get("X-PyPI-Last-Serial", -1)) != last_serial:
|
if int(response.headers.get("X-PyPI-Last-Serial", -1)) != last_serial:
|
||||||
response = session.get(f"https://pypi.org/pypi/{name}/json", refresh=True)
|
response = session.get(f"https://pypi.org/pypi/{name}/json", refresh=True)
|
||||||
|
@ -109,7 +114,17 @@ def pytest_plugin_projects_from_pypi(session: CachedSession) -> dict[str, int]:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def iter_plugins():
|
class PluginInfo(TypedDict):
|
||||||
|
"""Relevant information about a plugin to generate the summary."""
|
||||||
|
|
||||||
|
name: str
|
||||||
|
summary: str
|
||||||
|
last_release: str
|
||||||
|
status: str
|
||||||
|
requires: str
|
||||||
|
|
||||||
|
|
||||||
|
def iter_plugins() -> Iterator[PluginInfo]:
|
||||||
session = get_session()
|
session = get_session()
|
||||||
name_2_serial = pytest_plugin_projects_from_pypi(session)
|
name_2_serial = pytest_plugin_projects_from_pypi(session)
|
||||||
|
|
||||||
|
@ -136,7 +151,7 @@ def iter_plugins():
|
||||||
requires = requirement
|
requires = requirement
|
||||||
break
|
break
|
||||||
|
|
||||||
def version_sort_key(version_string):
|
def version_sort_key(version_string: str) -> Any:
|
||||||
"""
|
"""
|
||||||
Return the sort key for the given version string
|
Return the sort key for the given version string
|
||||||
returned by the API.
|
returned by the API.
|
||||||
|
@ -162,20 +177,19 @@ def iter_plugins():
|
||||||
yield {
|
yield {
|
||||||
"name": name,
|
"name": name,
|
||||||
"summary": summary.strip(),
|
"summary": summary.strip(),
|
||||||
"last release": last_release,
|
"last_release": last_release,
|
||||||
"status": status,
|
"status": status,
|
||||||
"requires": requires,
|
"requires": requires,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def plugin_definitions(plugins):
|
def plugin_definitions(plugins: Iterable[PluginInfo]) -> Iterator[str]:
|
||||||
"""Return RST for the plugin list that fits better on a vertical page."""
|
"""Return RST for the plugin list that fits better on a vertical page."""
|
||||||
|
|
||||||
for plugin in plugins:
|
for plugin in plugins:
|
||||||
yield dedent(
|
yield dedent(
|
||||||
f"""
|
f"""
|
||||||
{plugin['name']}
|
{plugin['name']}
|
||||||
*last release*: {plugin["last release"]},
|
*last release*: {plugin["last_release"]},
|
||||||
*status*: {plugin["status"]},
|
*status*: {plugin["status"]},
|
||||||
*requires*: {plugin["requires"]}
|
*requires*: {plugin["requires"]}
|
||||||
|
|
||||||
|
@ -184,7 +198,7 @@ def plugin_definitions(plugins):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main() -> None:
|
||||||
plugins = [*iter_plugins()]
|
plugins = [*iter_plugins()]
|
||||||
|
|
||||||
reference_dir = pathlib.Path("doc", "en", "reference")
|
reference_dir = pathlib.Path("doc", "en", "reference")
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
__all__ = ["__version__", "version_tuple"]
|
__all__ = ["__version__", "version_tuple"]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from ._version import version as __version__, version_tuple
|
from ._version import version as __version__
|
||||||
|
from ._version import version_tuple
|
||||||
except ImportError: # pragma: no cover
|
except ImportError: # pragma: no cover
|
||||||
# broken installation, we don't even try
|
# broken installation, we don't even try
|
||||||
# unknown only works because we do poor mans version compare
|
# unknown only works because we do poor mans version compare
|
||||||
|
|
|
@ -61,10 +61,11 @@ If things do not work right away:
|
||||||
which should throw a KeyError: 'COMPLINE' (which is properly set by the
|
which should throw a KeyError: 'COMPLINE' (which is properly set by the
|
||||||
global argcomplete script).
|
global argcomplete script).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
from glob import glob
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from glob import glob
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import List
|
from typing import List
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue