Compare commits

..

1 Commits

Author SHA1 Message Date
pytest bot bf3bcb2bf1 [automated] Update plugin list 2023-06-11 00:27:03 +00:00
99 changed files with 706 additions and 1747 deletions
+16 -46
View File
@@ -1,23 +1,26 @@
name: deploy
on:
workflow_dispatch:
inputs:
version:
description: 'Release version'
required: true
default: '1.2.3'
push:
tags:
# These tags are protected, see:
# https://github.com/pytest-dev/pytest/settings/tag_protection
- "[0-9]+.[0-9]+.[0-9]+"
- "[0-9]+.[0-9]+.[0-9]+rc[0-9]+"
# Set permissions at the job level.
permissions: {}
jobs:
package:
deploy:
if: github.repository == 'pytest-dev/pytest'
runs-on: ubuntu-latest
env:
SETUPTOOLS_SCM_PRETEND_VERSION: ${{ github.event.inputs.version }}
timeout-minutes: 10
timeout-minutes: 30
permissions:
contents: write
steps:
- uses: actions/checkout@v3
@@ -28,18 +31,6 @@ jobs:
- name: Build and Check Package
uses: hynek/build-and-inspect-python-package@v1.5
deploy:
if: github.repository == 'pytest-dev/pytest'
needs: [package]
runs-on: ubuntu-latest
environment: deploy
timeout-minutes: 30
permissions:
id-token: write
contents: write
steps:
- uses: actions/checkout@v3
- name: Download Package
uses: actions/download-artifact@v3
with:
@@ -47,35 +38,14 @@ jobs:
path: dist
- name: Publish package to PyPI
uses: pypa/gh-action-pypi-publish@v1.8.5
- name: Push tag
run: |
git config user.name "pytest bot"
git config user.email "pytestbot@gmail.com"
git tag --annotate --message=v${{ github.event.inputs.version }} v${{ github.event.inputs.version }} ${{ github.sha }}
git push origin v${{ github.event.inputs.version }}
release-notes:
# todo: generate the content in the build job
# the goal being of using a github action script to push the release data
# after success instead of creating a complete python/tox env
needs: [deploy]
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: write
steps:
- uses: actions/checkout@v3
uses: pypa/gh-action-pypi-publish@release/v1
with:
fetch-depth: 0
persist-credentials: false
password: ${{ secrets.pypi_token }}
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.10"
python-version: "3.7"
- name: Install tox
run: |
+24 -32
View File
@@ -27,19 +27,7 @@ concurrency:
permissions: {}
jobs:
package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
persist-credentials: false
- name: Build and Check Package
uses: hynek/build-and-inspect-python-package@v1.5
build:
needs: [package]
runs-on: ${{ matrix.os }}
timeout-minutes: 45
permissions:
@@ -50,17 +38,17 @@ jobs:
matrix:
name: [
"windows-py37",
"windows-py37-pluggy",
"windows-py38",
"windows-py38-pluggy",
"windows-py39",
"windows-py310",
"windows-py311",
"windows-py312",
"ubuntu-py37",
"ubuntu-py37-pluggy",
"ubuntu-py37-freeze",
"ubuntu-py38",
"ubuntu-py38-pluggy",
"ubuntu-py39",
"ubuntu-py310",
"ubuntu-py311",
@@ -72,6 +60,7 @@ jobs:
"macos-py310",
"macos-py312",
"docs",
"doctesting",
"plugins",
]
@@ -81,15 +70,15 @@ jobs:
python: "3.7"
os: windows-latest
tox_env: "py37-numpy"
- name: "windows-py37-pluggy"
python: "3.7"
os: windows-latest
tox_env: "py37-pluggymain-pylib-xdist"
- name: "windows-py38"
python: "3.8"
os: windows-latest
tox_env: "py38-unittestextras"
use_coverage: true
- name: "windows-py38-pluggy"
python: "3.8"
os: windows-latest
tox_env: "py38-pluggymain-pylib-xdist"
- name: "windows-py39"
python: "3.9"
os: windows-latest
@@ -112,6 +101,10 @@ jobs:
os: ubuntu-latest
tox_env: "py37-lsof-numpy-pexpect"
use_coverage: true
- name: "ubuntu-py37-pluggy"
python: "3.7"
os: ubuntu-latest
tox_env: "py37-pluggymain-pylib-xdist"
- name: "ubuntu-py37-freeze"
python: "3.7"
os: ubuntu-latest
@@ -120,10 +113,6 @@ jobs:
python: "3.8"
os: ubuntu-latest
tox_env: "py38-xdist"
- name: "ubuntu-py38-pluggy"
python: "3.8"
os: ubuntu-latest
tox_env: "py38-pluggymain-pylib-xdist"
- name: "ubuntu-py39"
python: "3.9"
os: ubuntu-latest
@@ -170,6 +159,10 @@ jobs:
os: ubuntu-latest
tox_env: "plugins"
- name: "docs"
python: "3.7"
os: ubuntu-latest
tox_env: "docs"
- name: "doctesting"
python: "3.7"
os: ubuntu-latest
@@ -182,12 +175,6 @@ jobs:
fetch-depth: 0
persist-credentials: false
- name: Download Package
uses: actions/download-artifact@v3
with:
name: Packages
path: dist
- name: Set up Python ${{ matrix.python }}
uses: actions/setup-python@v4
with:
@@ -201,13 +188,11 @@ jobs:
- name: Test without coverage
if: "! matrix.use_coverage"
shell: bash
run: tox run -e ${{ matrix.tox_env }} --installpkg `find dist/*.tar.gz`
run: "tox -e ${{ matrix.tox_env }}"
- name: Test with coverage
if: "matrix.use_coverage"
shell: bash
run: tox run -e ${{ matrix.tox_env }}-coverage --installpkg `find dist/*.tar.gz`
run: "tox -e ${{ matrix.tox_env }}-coverage"
- name: Generate coverage report
if: "matrix.use_coverage"
@@ -221,3 +206,10 @@ jobs:
fail_ci_if_error: true
files: ./coverage.xml
verbose: true
check-package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build and Check Package
uses: hynek/build-and-inspect-python-package@v1.5
+1 -1
View File
@@ -38,7 +38,7 @@ jobs:
run: python scripts/update-plugin-list.py
- name: Create Pull Request
uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38
uses: peter-evans/create-pull-request@5b4a9f6a9e2af26e5f02351490b90d01eb8ec1e5
with:
commit-message: '[automated] Update plugin list'
author: 'pytest bot <pytestbot@users.noreply.github.com>'
+3 -3
View File
@@ -5,7 +5,7 @@ repos:
- id: black
args: [--safe, --quiet]
- repo: https://github.com/asottile/blacken-docs
rev: 1.14.0
rev: 1.13.0
hooks:
- id: blacken-docs
additional_dependencies: [black==23.1.0]
@@ -37,12 +37,12 @@ repos:
- flake8-typing-imports==1.12.0
- flake8-docstrings==1.5.0
- repo: https://github.com/asottile/reorder-python-imports
rev: v3.10.0
rev: v3.9.0
hooks:
- id: reorder-python-imports
args: ['--application-directories=.:src', --py37-plus]
- repo: https://github.com/asottile/pyupgrade
rev: v3.7.0
rev: v3.4.0
hooks:
- id: pyupgrade
args: [--py37-plus]
-4
View File
@@ -9,10 +9,6 @@ python:
path: .
- requirements: doc/en/requirements.txt
sphinx:
configuration: doc/en/conf.py
fail_on_warning: true
build:
os: ubuntu-20.04
tools:
-7
View File
@@ -166,8 +166,6 @@ Ian Bicking
Ian Lesperance
Ilya Konstantinov
Ionuț Turturică
Isaac Virshup
Israel Fruchter
Itxaso Aizpurua
Iwan Briquemont
Jaap Broekhuizen
@@ -231,7 +229,6 @@ Maho
Maik Figura
Mandeep Bhutani
Manuel Krebber
Marc Mueller
Marc Schlaich
Marcelo Duarte Trevisani
Marcin Bachry
@@ -337,13 +334,11 @@ Serhii Mozghovyi
Seth Junot
Shantanu Jain
Shubham Adep
Simon Blanchard
Simon Gomizelj
Simon Holesch
Simon Kerr
Skylar Downes
Srinivas Reddy Thatiparthy
Stefaan Lippens
Stefan Farmbauer
Stefan Scherfke
Stefan Zimmermann
@@ -376,14 +371,12 @@ Tony Narlock
Tor Colvin
Trevor Bekolay
Tyler Goodlet
Tyler Smart
Tzu-ping Chung
Vasily Kuznetsov
Victor Maryama
Victor Rodriguez
Victor Uriarte
Vidar T. Fauske
Vijay Arora
Virgil Dupras
Vitaly Lashmanov
Vivaan Verma
+1 -1
View File
@@ -50,7 +50,7 @@ Fix bugs
--------
Look through the `GitHub issues for bugs <https://github.com/pytest-dev/pytest/labels/type:%20bug>`_.
See also the `"good first issue" issues <https://github.com/pytest-dev/pytest/labels/good%20first%20issue>`_
See also the `"status: easy" issues <https://github.com/pytest-dev/pytest/labels/status%3A%20easy>`_
that are friendly to new contributors.
:ref:`Talk <contact>` to developers to find out how you can fix specific bugs. To indicate that you are going
+7 -5
View File
@@ -133,12 +133,14 @@ Releasing
Both automatic and manual processes described above follow the same steps from this point onward.
#. After all tests pass and the PR has been approved, trigger the ``deploy`` job
in https://github.com/pytest-dev/pytest/actions/workflows/deploy.yml, using the ``release-MAJOR.MINOR.PATCH`` branch
as source.
#. After all tests pass and the PR has been approved, tag the release commit
in the ``release-MAJOR.MINOR.PATCH`` branch and push it. This will publish to PyPI::
This job will require approval from ``pytest-dev/core``, after which it will publish to PyPI
and tag the repository.
git fetch upstream
git tag MAJOR.MINOR.PATCH upstream/release-MAJOR.MINOR.PATCH
git push upstream MAJOR.MINOR.PATCH
Wait for the deploy to complete, then make sure it is `available on PyPI <https://pypi.org/project/pytest>`_.
#. Merge the PR. **Make sure it's not squash-merged**, so that the tagged commit ends up in the main branch.
+1
View File
@@ -0,0 +1 @@
Terminal Reporting: Fixed bug when running in ``--tb=line`` mode where ``pytest.fail(pytrace=False)`` tests report ``None``.
+1
View File
@@ -0,0 +1 @@
Update test log report annotation to named tuple and fixed inconsistency in docs for :hook:`pytest_report_teststatus` hook.
+2
View File
@@ -0,0 +1,2 @@
Added :func:`ExceptionInfo.from_exception() <pytest.ExceptionInfo.from_exception>`, a simpler way to create an :class:`~pytest.ExceptionInfo` from an exception.
This can replace :func:`ExceptionInfo.from_exc_info() <pytest.ExceptionInfo.from_exc_info()>` for most uses.
+5
View File
@@ -0,0 +1,5 @@
When an exception traceback to be displayed is completely filtered out (by mechanisms such as ``__tracebackhide__``, internal frames, and similar), now only the exception string and the following message are shown:
"All traceback entries are hidden. Pass `--full-trace` to see hidden and internal frames.".
Previously, the last frame of the traceback was shown, even though it was hidden.
+3
View File
@@ -0,0 +1,3 @@
Improved verbose output (``-vv``) of ``skip`` and ``xfail`` reasons by performing text wrapping while leaving a clear margin for progress output.
Added :func:`TerminalReporter.wrap_write() <pytest.TerminalReporter.wrap_write>` as a helper for that.
+1
View File
@@ -0,0 +1 @@
Added handling of ``%f`` directive to print microseconds in log format options, such as ``log-date-format``.
+1
View File
@@ -0,0 +1 @@
Added underlying exception to cache provider path creation and write warning messages.
+1
View File
@@ -0,0 +1 @@
Added warning when :confval:`testpaths` is set, but paths are not found by glob. In this case, pytest will fall back to searching from the current directory.
+1
View File
@@ -0,0 +1 @@
Enhanced the CLI flag for ``-c`` to now include ``--config-file`` to make it clear that this flag applies to the usage of a custom config file.
+3
View File
@@ -0,0 +1,3 @@
When `--confcutdir` is not specified, and there is no config file present, the conftest cutoff directory (`--confcutdir`) is now set to the :ref:`rootdir`.
Previously in such cases, `conftest.py` files would be probed all the way to the root directory of the filesystem.
If you are badly affected by this change, consider adding an empty config file to your desired cutoff directory, or explicitly set `--confcutdir`.
+1
View File
@@ -0,0 +1 @@
Fixed the ``--last-failed`` whole-file skipping functionality ("skipped N files") for :ref:`non-python test files <non-python tests>`.
+7
View File
@@ -0,0 +1,7 @@
The :confval:`norecursedir` check is now performed in a :hook:`pytest_ignore_collect` implementation, so plugins can affect it.
If after updating to this version you see that your `norecursedir` setting is not being respected,
it means that a conftest or a plugin you use has a bad `pytest_ignore_collect` implementation.
Most likely, your hook returns `False` for paths it does not want to ignore,
which ends the processing and doesn't allow other plugins, including pytest itself, to ignore the path.
The fix is to return `None` instead of `False` for paths your hook doesn't want to ignore.
+1
View File
@@ -0,0 +1 @@
Fixed traceback entries hidden with ``__tracebackhide__ = True`` still being shown for chained exceptions (parts after "... the above exception ..." message).
+3
View File
@@ -0,0 +1,3 @@
:func:`_pytest.logging.LogCaptureFixture.set_level` and :func:`_pytest.logging.LogCaptureFixture.at_level`
will temporarily enable the requested ``level`` if ``level`` was disabled globally via
``logging.disable(LEVEL)``.
-4
View File
@@ -6,10 +6,6 @@ Release announcements
:maxdepth: 2
release-7.4.3
release-7.4.2
release-7.4.1
release-7.4.0
release-7.3.2
release-7.3.1
release-7.3.0
-49
View File
@@ -1,49 +0,0 @@
pytest-7.4.0
=======================================
The pytest team is proud to announce the 7.4.0 release!
This release contains new features, improvements, and bug fixes,
the full list of changes is available in the changelog:
https://docs.pytest.org/en/stable/changelog.html
For complete documentation, please visit:
https://docs.pytest.org/en/stable/
As usual, you can upgrade from PyPI via:
pip install -U pytest
Thanks to all of the contributors to this release:
* Adam J. Stewart
* Alessio Izzo
* Alex
* Alex Lambson
* Brian Larsen
* Bruno Oliveira
* Bryan Ricker
* Chris Mahoney
* Facundo Batista
* Florian Bruhin
* Jarrett Keifer
* Kenny Y
* Miro Hrončok
* Ran Benita
* Roberto Aldera
* Ronny Pfannschmidt
* Sergey Kim
* Stefanie Molin
* Vijay Arora
* Ville Skyttä
* Zac Hatfield-Dodds
* bzoracler
* leeyueh
* nondescryptid
* theirix
Happy testing,
The pytest Development Team
-20
View File
@@ -1,20 +0,0 @@
pytest-7.4.1
=======================================
pytest 7.4.1 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
Thanks to all of the contributors to this release:
* Bruno Oliveira
* Florian Bruhin
* Ran Benita
Happy testing,
The pytest Development Team
-18
View File
@@ -1,18 +0,0 @@
pytest-7.4.2
=======================================
pytest 7.4.2 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
Thanks to all of the contributors to this release:
* Bruno Oliveira
Happy testing,
The pytest Development Team
-19
View File
@@ -1,19 +0,0 @@
pytest-7.4.3
=======================================
pytest 7.4.3 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
Thanks to all of the contributors to this release:
* Bruno Oliveira
* Marc Mueller
Happy testing,
The pytest Development Team
+4 -4
View File
@@ -22,7 +22,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
cachedir: .pytest_cache
rootdir: /home/sweet/project
collected 0 items
cache -- .../_pytest/cacheprovider.py:532
cache -- .../_pytest/cacheprovider.py:510
Return a cache object that can persist state between testing sessions.
cache.get(key, default)
@@ -105,7 +105,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
captured = capsys.readouterr()
assert captured.out == "hello\n"
doctest_namespace [session scope] -- .../_pytest/doctest.py:757
doctest_namespace [session scope] -- .../_pytest/doctest.py:737
Fixture that returns a :py:class:`dict` that will be injected into the
namespace of doctests.
@@ -119,7 +119,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
For more details: :ref:`doctest_namespace`.
pytestconfig [session scope] -- .../_pytest/fixtures.py:1353
pytestconfig [session scope] -- .../_pytest/fixtures.py:1360
Session-scoped fixture that returns the session's :class:`pytest.Config`
object.
@@ -196,7 +196,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
.. _legacy_path: https://py.readthedocs.io/en/latest/path.html
caplog -- .../_pytest/logging.py:570
caplog -- .../_pytest/logging.py:498
Access and control log capturing.
Captured logs are available through the following properties/methods::
-149
View File
@@ -28,155 +28,6 @@ with advance notice in the **Deprecations** section of releases.
.. towncrier release notes start
pytest 7.4.3 (2023-10-24)
=========================
Bug Fixes
---------
- `#10447 <https://github.com/pytest-dev/pytest/issues/10447>`_: Markers are now considered in the reverse mro order to ensure base class markers are considered first -- this resolves a regression.
- `#11239 <https://github.com/pytest-dev/pytest/issues/11239>`_: Fixed ``:=`` in asserts impacting unrelated test cases.
- `#11439 <https://github.com/pytest-dev/pytest/issues/11439>`_: Handled an edge case where :data:`sys.stderr` might already be closed when :ref:`faulthandler` is tearing down.
pytest 7.4.2 (2023-09-07)
=========================
Bug Fixes
---------
- `#11237 <https://github.com/pytest-dev/pytest/issues/11237>`_: Fix doctest collection of `functools.cached_property` objects.
- `#11306 <https://github.com/pytest-dev/pytest/issues/11306>`_: Fixed bug using ``--importmode=importlib`` which would cause package ``__init__.py`` files to be imported more than once in some cases.
- `#11367 <https://github.com/pytest-dev/pytest/issues/11367>`_: Fixed bug where `user_properties` where not being saved in the JUnit XML file if a fixture failed during teardown.
- `#11394 <https://github.com/pytest-dev/pytest/issues/11394>`_: Fixed crash when parsing long command line arguments that might be interpreted as files.
Improved Documentation
----------------------
- `#11391 <https://github.com/pytest-dev/pytest/issues/11391>`_: Improved disclaimer on pytest plugin reference page to better indicate this is an automated, non-curated listing.
pytest 7.4.1 (2023-09-02)
=========================
Bug Fixes
---------
- `#10337 <https://github.com/pytest-dev/pytest/issues/10337>`_: Fixed bug where fake intermediate modules generated by ``--import-mode=importlib`` would not include the
child modules as attributes of the parent modules.
- `#10702 <https://github.com/pytest-dev/pytest/issues/10702>`_: Fixed error assertion handling in :func:`pytest.approx` when ``None`` is an expected or received value when comparing dictionaries.
- `#10811 <https://github.com/pytest-dev/pytest/issues/10811>`_: Fixed issue when using ``--import-mode=importlib`` together with ``--doctest-modules`` that caused modules
to be imported more than once, causing problems with modules that have import side effects.
pytest 7.4.0 (2023-06-23)
=========================
Features
--------
- `#10901 <https://github.com/pytest-dev/pytest/issues/10901>`_: Added :func:`ExceptionInfo.from_exception() <pytest.ExceptionInfo.from_exception>`, a simpler way to create an :class:`~pytest.ExceptionInfo` from an exception.
This can replace :func:`ExceptionInfo.from_exc_info() <pytest.ExceptionInfo.from_exc_info()>` for most uses.
Improvements
------------
- `#10872 <https://github.com/pytest-dev/pytest/issues/10872>`_: Update test log report annotation to named tuple and fixed inconsistency in docs for :hook:`pytest_report_teststatus` hook.
- `#10907 <https://github.com/pytest-dev/pytest/issues/10907>`_: When an exception traceback to be displayed is completely filtered out (by mechanisms such as ``__tracebackhide__``, internal frames, and similar), now only the exception string and the following message are shown:
"All traceback entries are hidden. Pass `--full-trace` to see hidden and internal frames.".
Previously, the last frame of the traceback was shown, even though it was hidden.
- `#10940 <https://github.com/pytest-dev/pytest/issues/10940>`_: Improved verbose output (``-vv``) of ``skip`` and ``xfail`` reasons by performing text wrapping while leaving a clear margin for progress output.
Added ``TerminalReporter.wrap_write()`` as a helper for that.
- `#10991 <https://github.com/pytest-dev/pytest/issues/10991>`_: Added handling of ``%f`` directive to print microseconds in log format options, such as ``log-date-format``.
- `#11005 <https://github.com/pytest-dev/pytest/issues/11005>`_: Added the underlying exception to the cache provider's path creation and write warning messages.
- `#11013 <https://github.com/pytest-dev/pytest/issues/11013>`_: Added warning when :confval:`testpaths` is set, but paths are not found by glob. In this case, pytest will fall back to searching from the current directory.
- `#11043 <https://github.com/pytest-dev/pytest/issues/11043>`_: When `--confcutdir` is not specified, and there is no config file present, the conftest cutoff directory (`--confcutdir`) is now set to the :ref:`rootdir <rootdir>`.
Previously in such cases, `conftest.py` files would be probed all the way to the root directory of the filesystem.
If you are badly affected by this change, consider adding an empty config file to your desired cutoff directory, or explicitly set `--confcutdir`.
- `#11081 <https://github.com/pytest-dev/pytest/issues/11081>`_: The :confval:`norecursedirs` check is now performed in a :hook:`pytest_ignore_collect` implementation, so plugins can affect it.
If after updating to this version you see that your `norecursedirs` setting is not being respected,
it means that a conftest or a plugin you use has a bad `pytest_ignore_collect` implementation.
Most likely, your hook returns `False` for paths it does not want to ignore,
which ends the processing and doesn't allow other plugins, including pytest itself, to ignore the path.
The fix is to return `None` instead of `False` for paths your hook doesn't want to ignore.
- `#8711 <https://github.com/pytest-dev/pytest/issues/8711>`_: :func:`caplog.set_level() <pytest.LogCaptureFixture.set_level>` and :func:`caplog.at_level() <pytest.LogCaptureFixture.at_level>`
will temporarily enable the requested ``level`` if ``level`` was disabled globally via
``logging.disable(LEVEL)``.
Bug Fixes
---------
- `#10831 <https://github.com/pytest-dev/pytest/issues/10831>`_: Terminal Reporting: Fixed bug when running in ``--tb=line`` mode where ``pytest.fail(pytrace=False)`` tests report ``None``.
- `#11068 <https://github.com/pytest-dev/pytest/issues/11068>`_: Fixed the ``--last-failed`` whole-file skipping functionality ("skipped N files") for :ref:`non-python test files <non-python tests>`.
- `#11104 <https://github.com/pytest-dev/pytest/issues/11104>`_: Fixed a regression in pytest 7.3.2 which caused to :confval:`testpaths` to be considered for loading initial conftests,
even when it was not utilized (e.g. when explicit paths were given on the command line).
Now the ``testpaths`` are only considered when they are in use.
- `#1904 <https://github.com/pytest-dev/pytest/issues/1904>`_: Fixed traceback entries hidden with ``__tracebackhide__ = True`` still being shown for chained exceptions (parts after "... the above exception ..." message).
- `#7781 <https://github.com/pytest-dev/pytest/issues/7781>`_: Fix writing non-encodable text to log file when using ``--debug``.
Improved Documentation
----------------------
- `#9146 <https://github.com/pytest-dev/pytest/issues/9146>`_: Improved documentation for :func:`caplog.set_level() <pytest.LogCaptureFixture.set_level>`.
Trivial/Internal Changes
------------------------
- `#11031 <https://github.com/pytest-dev/pytest/issues/11031>`_: Enhanced the CLI flag for ``-c`` to now include ``--config-file`` to make it clear that this flag applies to the usage of a custom config file.
pytest 7.3.2 (2023-06-10)
=========================
+1 -1
View File
@@ -12,7 +12,7 @@ class YamlFile(pytest.File):
# We need a yaml parser, e.g. PyYAML.
import yaml
raw = yaml.safe_load(self.path.open(encoding="utf-8"))
raw = yaml.safe_load(self.path.open())
for name, spec in sorted(raw.items()):
yield YamlItem.from_parent(self, name=name, spec=spec)
+1 -1
View File
@@ -817,7 +817,7 @@ case we just write some information out to a ``failures`` file:
# we only look at actual failing test calls, not setup/teardown
if rep.when == "call" and rep.failed:
mode = "a" if os.path.exists("failures") else "w"
with open("failures", mode, encoding="utf-8") as f:
with open("failures", mode) as f:
# let's also access a fixture for the fun of it
if "tmp_path" in item.fixturenames:
extra = " ({})".format(item.funcargs["tmp_path"])
+1 -1
View File
@@ -22,7 +22,7 @@ Install ``pytest``
.. code-block:: bash
$ pytest --version
pytest 7.4.3
pytest 7.3.2
.. _`simpletest`:
+3 -2
View File
@@ -54,13 +54,14 @@ operators. (See :ref:`tbreportdemo`). This allows you to use the
idiomatic python constructs without boilerplate code while not losing
introspection information.
If a message is specified with the assertion like this:
However, if you specify a message with the assertion like this:
.. code-block:: python
assert a % 2 == 0, "value was odd, should be even"
it is printed alongside the assertion introspection in the traceback.
then no assertion introspection takes places at all and the message
will be simply shown in the traceback.
See :ref:`assert-details` for more information on assertion introspection.
+5 -12
View File
@@ -176,21 +176,14 @@ with more recent files coming first.
Behavior when no tests failed in the last run
---------------------------------------------
The ``--lfnf/--last-failed-no-failures`` option governs the behavior of ``--last-failed``.
Determines whether to execute tests when there are no previously (known)
failures or when no cached ``lastfailed`` data was found.
There are two options:
* ``all``: when there are no known test failures, runs all tests (the full test suite). This is the default.
* ``none``: when there are no known test failures, just emits a message stating this and exit successfully.
Example:
When no tests failed in the last run, or when no cached ``lastfailed`` data was
found, ``pytest`` can be configured either to run all of the tests or no tests,
using the ``--last-failed-no-failures`` option, which takes one of the following values:
.. code-block:: bash
pytest --last-failed --last-failed-no-failures all # runs the full test suite (default behavior)
pytest --last-failed --last-failed-no-failures none # runs no tests and exits successfully
pytest --last-failed --last-failed-no-failures all # run all tests (default behavior)
pytest --last-failed --last-failed-no-failures none # run no tests and exit
The new config.cache object
--------------------------------
+1 -1
View File
@@ -1698,7 +1698,7 @@ and declare its use in a test module via a ``usefixtures`` marker:
class TestDirectoryInit:
def test_cwd_starts_empty(self):
assert os.listdir(os.getcwd()) == []
with open("myfile", "w", encoding="utf-8") as f:
with open("myfile", "w") as f:
f.write("hello")
def test_cwd_again_starts_empty(self):
+4 -4
View File
@@ -24,8 +24,8 @@ created in the `base temporary directory`_.
d = tmp_path / "sub"
d.mkdir()
p = d / "hello.txt"
p.write_text(CONTENT, encoding="utf-8")
assert p.read_text(encoding="utf-8") == CONTENT
p.write_text(CONTENT)
assert p.read_text() == CONTENT
assert len(list(tmp_path.iterdir())) == 1
assert 0
@@ -51,8 +51,8 @@ Running this would result in a passed test except for the last
d = tmp_path / "sub"
d.mkdir()
p = d / "hello.txt"
p.write_text(CONTENT, encoding="utf-8")
assert p.read_text(encoding="utf-8") == CONTENT
p.write_text(CONTENT)
assert p.read_text() == CONTENT
assert len(list(tmp_path.iterdir())) == 1
> assert 0
E assert 0
+2 -2
View File
@@ -207,10 +207,10 @@ creation of a per-test temporary directory:
@pytest.fixture(autouse=True)
def initdir(self, tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path) # change to pytest-provided temporary directory
tmp_path.joinpath("samplefile.ini").write_text("# testdata", encoding="utf-8")
tmp_path.joinpath("samplefile.ini").write_text("# testdata")
def test_method(self):
with open("samplefile.ini", encoding="utf-8") as f:
with open("samplefile.ini") as f:
s = f.read()
assert "testdata" in s
+1 -2
View File
@@ -173,8 +173,7 @@ You can invoke ``pytest`` from Python code directly:
this acts as if you would call "pytest" from the command line.
It will not raise :class:`SystemExit` but return the :ref:`exit code <exit-codes>` instead.
If you don't pass it any arguments, ``main`` reads the arguments from the command line arguments of the process (:data:`sys.argv`), which may be undesirable.
You can pass in options and arguments explicitly:
You can pass in options and arguments:
.. code-block:: python
+1 -1
View File
@@ -2,7 +2,7 @@
.. sidebar:: Next Open Trainings
- `Professional Testing with Python <https://python-academy.com/courses/python_course_testing.html>`_, via `Python Academy <https://www.python-academy.com/>`_, **March 5th to 7th 2024** (3 day in-depth training), **Leipzig, Germany / Remote**
- `Professional Testing with Python <https://python-academy.com/courses/python_course_testing.html>`_, via `Python Academy <https://www.python-academy.com/>`_, March 5th to 7th 2024 (3 day in-depth training), Leipzig/Remote
Also see :doc:`previous talks and blogposts <talks>`.
+1 -1
View File
@@ -90,7 +90,7 @@ and can also be used to hold pytest configuration if they have a ``[pytest]`` se
setup.cfg
~~~~~~~~~
``setup.cfg`` files are general purpose configuration files, used originally by ``distutils`` (now deprecated) and `setuptools <https://setuptools.pypa.io/en/latest/userguide/declarative_config.html>`__, and can also be used to hold pytest configuration
``setup.cfg`` files are general purpose configuration files, used originally by :doc:`distutils <python:distutils/configfile>`, and can also be used to hold pytest configuration
if they have a ``[tool:pytest]`` section.
.. code-block:: ini
+88 -132
View File
@@ -1,31 +1,19 @@
.. _plugin-list:
Pytest Plugin List
==================
Plugin List
===========
Below is an automated compilation of ``pytest``` plugins available on `PyPI <https://pypi.org>`_.
It includes PyPI projects whose names begin with "pytest-" and a handful of manually selected projects.
PyPI projects that match "pytest-\*" are considered plugins and are listed
automatically together with a manually-maintained list in `the source
code <https://github.com/pytest-dev/pytest/blob/main/scripts/update-plugin-list.py>`_.
Packages classified as inactive are excluded.
For detailed insights into how this list is generated,
please refer to `the update script <https://github.com/pytest-dev/pytest/blob/main/scripts/update-plugin-list.py>`_.
.. warning::
Please be aware that this list is not a curated collection of projects
and does not undergo a systematic review process.
It serves purely as an informational resource to aid in the discovery of ``pytest`` plugins.
Do not presume any endorsement from the ``pytest`` project or its developers,
and always conduct your own quality assessment before incorporating any of these plugins into your own projects.
.. The following conditional uses a different format for this list when
creating a PDF, because otherwise the table gets far too wide for the
page.
This list contains 1267 plugins.
This list contains 1263 plugins.
.. only:: not latex
@@ -244,7 +232,7 @@ This list contains 1267 plugins.
:pypi:`pytest-coveragemarkers` Using pytest markers to track functional coverage and filtering of tests Nov 29, 2022 N/A pytest (>=7.1.2,<8.0.0)
:pypi:`pytest-cov-exclude` Pytest plugin for excluding tests based on coverage data Apr 29, 2016 4 - Beta pytest (>=2.8.0,<2.9.0); extra == 'dev'
:pypi:`pytest-cpp` Use pytest's runner to discover and execute C++ tests Jan 30, 2023 5 - Production/Stable pytest (>=7.0)
:pypi:`pytest-cppython` A pytest plugin that imports CPPython testing types Jun 14, 2023 N/A N/A
:pypi:`pytest-cppython` A pytest plugin that imports CPPython testing types Jun 02, 2023 N/A N/A
:pypi:`pytest-cqase` Custom qase pytest plugin Aug 22, 2022 N/A pytest (>=7.1.2,<8.0.0)
:pypi:`pytest-cram` Run cram tests with pytest. Aug 08, 2020 N/A N/A
:pypi:`pytest-crate` Manages CrateDB instances during your integration tests May 28, 2019 3 - Alpha pytest (>=4.0)
@@ -368,7 +356,7 @@ This list contains 1267 plugins.
:pypi:`pytest-duration-insights` Jun 25, 2021 N/A N/A
:pypi:`pytest-durations` Pytest plugin reporting fixtures and test functions execution time. Apr 22, 2022 5 - Production/Stable pytest (>=4.6)
:pypi:`pytest-dynamicrerun` A pytest plugin to rerun tests dynamically based off of test outcome and output. Aug 15, 2020 4 - Beta N/A
:pypi:`pytest-dynamodb` DynamoDB fixtures for pytest Jun 12, 2023 5 - Production/Stable pytest
:pypi:`pytest-dynamodb` DynamoDB fixtures for pytest Mar 27, 2023 5 - Production/Stable pytest
:pypi:`pytest-easy-addoption` pytest-easy-addoption: Easy way to work with pytest addoption Jan 22, 2020 N/A N/A
:pypi:`pytest-easy-api` Simple API testing with pytest Mar 26, 2018 N/A N/A
:pypi:`pytest-easyMPI` Package that supports mpi tests in pytest Oct 21, 2020 N/A N/A
@@ -383,13 +371,13 @@ This list contains 1267 plugins.
:pypi:`pytest-eliot` An eliot plugin for pytest. Aug 31, 2022 1 - Planning pytest (>=5.4.0)
:pypi:`pytest-elk-reporter` A simple plugin to use with pytest Jan 24, 2021 4 - Beta pytest (>=3.5.0)
:pypi:`pytest-email` Send execution result email Jul 08, 2020 N/A pytest
:pypi:`pytest-embedded` A pytest plugin that designed for embedded testing. Jun 14, 2023 5 - Production/Stable pytest>=7.0
:pypi:`pytest-embedded-arduino` Make pytest-embedded plugin work with Arduino. Jun 14, 2023 5 - Production/Stable N/A
:pypi:`pytest-embedded-idf` Make pytest-embedded plugin work with ESP-IDF. Jun 14, 2023 5 - Production/Stable N/A
:pypi:`pytest-embedded-jtag` Make pytest-embedded plugin work with JTAG. Jun 14, 2023 5 - Production/Stable N/A
:pypi:`pytest-embedded-qemu` Make pytest-embedded plugin work with QEMU. Jun 14, 2023 5 - Production/Stable N/A
:pypi:`pytest-embedded-serial` Make pytest-embedded plugin work with Serial. Jun 14, 2023 5 - Production/Stable N/A
:pypi:`pytest-embedded-serial-esp` Make pytest-embedded plugin work with Espressif target boards. Jun 14, 2023 5 - Production/Stable N/A
:pypi:`pytest-embedded` pytest embedded plugin Jun 06, 2023 N/A pytest (>=7.0)
:pypi:`pytest-embedded-arduino` pytest embedded plugin for Arduino projects Jun 06, 2023 5 - Production/Stable N/A
:pypi:`pytest-embedded-idf` pytest embedded plugin for esp-idf project Jun 06, 2023 5 - Production/Stable N/A
:pypi:`pytest-embedded-jtag` pytest embedded plugin for testing with jtag Jun 06, 2023 N/A N/A
:pypi:`pytest-embedded-qemu` pytest embedded plugin for qemu, not target chip Jun 06, 2023 5 - Production/Stable N/A
:pypi:`pytest-embedded-serial` pytest embedded plugin for testing serial ports Jun 06, 2023 N/A N/A
:pypi:`pytest-embedded-serial-esp` pytest embedded plugin for testing espressif boards via serial ports Jun 06, 2023 N/A N/A
:pypi:`pytest-embrace` 💝 Dataclasses-as-tests. Describe the runtime once and multiply coverage with no boilerplate. Mar 25, 2023 N/A pytest (>=7.0,<8.0)
:pypi:`pytest-emoji` A pytest plugin that adds emojis to your test result report Feb 19, 2019 4 - Beta pytest (>=4.2.1)
:pypi:`pytest-emoji-output` Pytest plugin to represent test output with emoji support Apr 09, 2023 4 - Beta pytest (==7.0.1)
@@ -398,7 +386,7 @@ This list contains 1267 plugins.
:pypi:`pytest-encode-kane` set your encoding and logger Nov 16, 2021 N/A pytest
:pypi:`pytest-enhanced-reports` Enhanced test reports for pytest Dec 15, 2022 N/A N/A
:pypi:`pytest-enhancements` Improvements for pytest (rejected upstream) Oct 30, 2019 4 - Beta N/A
:pypi:`pytest-env` py.test plugin that allows you to add environment variables. Jun 15, 2023 5 - Production/Stable pytest>=7.3.1
:pypi:`pytest-env` py.test plugin that allows you to add environment variables. Oct 23, 2022 5 - Production/Stable pytest>=7.1.3
:pypi:`pytest-envfiles` A py.test plugin that parses environment files before running tests Oct 08, 2015 3 - Alpha N/A
:pypi:`pytest-env-info` Push information about the running pytest into envvars Nov 25, 2017 4 - Beta pytest (>=3.1.1)
:pypi:`pytest-envraw` py.test plugin that allows you to add environment variables. Aug 27, 2020 4 - Beta pytest (>=2.6.0)
@@ -444,7 +432,7 @@ This list contains 1267 plugins.
:pypi:`pytest-fantasy` Pytest plugin for Flask Fantasy Framework Mar 14, 2019 N/A N/A
:pypi:`pytest-fastapi` Dec 27, 2020 N/A N/A
:pypi:`pytest-fastapi-deps` A fixture which allows easy replacement of fastapi dependencies for testing Jul 20, 2022 5 - Production/Stable pytest
:pypi:`pytest-fastest` Use SCM and coverage to run only needed tests Jun 15, 2023 4 - Beta pytest (>=4.4)
:pypi:`pytest-fastest` Use SCM and coverage to run only needed tests Mar 05, 2020 N/A N/A
:pypi:`pytest-fast-first` Pytest plugin that runs fast tests first Jan 19, 2023 3 - Alpha pytest
:pypi:`pytest-faulthandler` py.test plugin that activates the fault handler module for tests (dummy package) Jul 04, 2019 6 - Mature pytest (>=5.0)
:pypi:`pytest-fauxfactory` Integration of fauxfactory into pytest. Dec 06, 2017 5 - Production/Stable pytest (>=3.2)
@@ -469,7 +457,7 @@ This list contains 1267 plugins.
:pypi:`pytest-fixture-tools` Plugin for pytest which provides tools for fixtures Aug 18, 2020 6 - Mature pytest
:pypi:`pytest-fixture-typecheck` A pytest plugin to assert type annotations at runtime. Aug 24, 2021 N/A pytest
:pypi:`pytest-flake8` pytest plugin to check FLAKE8 requirements Mar 18, 2022 4 - Beta pytest (>=7.0)
:pypi:`pytest-flake8-path` A pytest fixture for testing flake8 plugins. Jun 16, 2023 5 - Production/Stable pytest
:pypi:`pytest-flake8-path` A pytest fixture for testing flake8 plugins. May 11, 2022 5 - Production/Stable pytest
:pypi:`pytest-flake8-v2` pytest plugin to check FLAKE8 requirements Mar 01, 2022 5 - Production/Stable pytest (>=7.0)
:pypi:`pytest-flakefinder` Runs tests multiple times to expose flakiness. Oct 26, 2022 4 - Beta pytest (>=2.7.1)
:pypi:`pytest-flakes` pytest plugin to check source code with pyflakes Dec 02, 2021 5 - Production/Stable pytest (>=5)
@@ -488,7 +476,7 @@ This list contains 1267 plugins.
:pypi:`pytest-forward-compatibility` A pytest plugin to shim pytest commandline options for fowards compatibility Sep 29, 2020 N/A N/A
:pypi:`pytest-frappe` Pytest Frappe Plugin - A set of pytest fixtures to test Frappe applications May 03, 2023 4 - Beta pytest>=7.0.0
:pypi:`pytest-freezegun` Wrap tests with fixtures in freeze_time Jul 19, 2020 4 - Beta pytest (>=3.0.0)
:pypi:`pytest-freezer` Pytest plugin providing a fixture interface for spulec/freezegun Jun 17, 2023 N/A pytest>=3.6
:pypi:`pytest-freezer` Pytest plugin providing a fixture interface for spulec/freezegun Oct 20, 2022 N/A pytest>=3.6
:pypi:`pytest-freeze-reqs` Check if requirement files are frozen Apr 29, 2021 N/A N/A
:pypi:`pytest-frozen-uuids` Deterministically frozen UUID's for your tests Apr 17, 2022 N/A pytest (>=3.0)
:pypi:`pytest-func-cov` Pytest plugin for measuring function coverage Apr 15, 2021 3 - Alpha pytest (>=5)
@@ -503,7 +491,7 @@ This list contains 1267 plugins.
:pypi:`pytest-gherkin` A flexible framework for executing BDD gherkin tests Jul 27, 2019 3 - Alpha pytest (>=5.0.0)
:pypi:`pytest-gh-log-group` pytest plugin for gh actions Jan 11, 2022 3 - Alpha pytest
:pypi:`pytest-ghostinspector` For finding/executing Ghost Inspector tests May 17, 2016 3 - Alpha N/A
:pypi:`pytest-girder` A set of pytest fixtures for testing Girder applications. Jun 14, 2023 N/A N/A
:pypi:`pytest-girder` A set of pytest fixtures for testing Girder applications. Jun 08, 2023 N/A N/A
:pypi:`pytest-git` Git repository fixture for py.test May 28, 2019 5 - Production/Stable pytest
:pypi:`pytest-gitcov` Pytest plugin for reporting on coverage of the last git commit. Jan 11, 2020 2 - Pre-Alpha N/A
:pypi:`pytest-git-fixtures` Pytest fixtures for testing with git. Mar 11, 2021 4 - Beta pytest
@@ -535,10 +523,10 @@ This list contains 1267 plugins.
:pypi:`pytest-historic` Custom report to display pytest historical execution records Apr 08, 2020 N/A pytest
:pypi:`pytest-historic-hook` Custom listener to store execution results into MYSQL DB, which is used for pytest-historic report Apr 08, 2020 N/A pytest
:pypi:`pytest-homeassistant` A pytest plugin for use with homeassistant custom components. Aug 12, 2020 4 - Beta N/A
:pypi:`pytest-homeassistant-custom-component` Experimental package to automatically extract test plugins for Home Assistant custom components Jun 16, 2023 3 - Alpha pytest (==7.3.1)
:pypi:`pytest-homeassistant-custom-component` Experimental package to automatically extract test plugins for Home Assistant custom components Jun 09, 2023 3 - Alpha pytest (==7.3.1)
:pypi:`pytest-honey` A simple plugin to use with pytest Jan 07, 2022 4 - Beta pytest (>=3.5.0)
:pypi:`pytest-honors` Report on tests that honor constraints, and guard against regressions Mar 06, 2020 4 - Beta N/A
:pypi:`pytest-hot-reloading` Jun 16, 2023 N/A N/A
:pypi:`pytest-hot-reloading` May 18, 2023 N/A N/A
:pypi:`pytest-hot-test` A plugin that tracks test changes Dec 10, 2022 4 - Beta pytest (>=3.5.0)
:pypi:`pytest-hoverfly` Simplify working with Hoverfly from pytest Jan 30, 2023 N/A pytest (>=5.0)
:pypi:`pytest-hoverfly-wrapper` Integrates the Hoverfly HTTP proxy into Pytest Feb 27, 2023 5 - Production/Stable pytest (>=3.7.0)
@@ -596,7 +584,7 @@ This list contains 1267 plugins.
:pypi:`pytest-ipynb` THIS PROJECT IS ABANDONED Jan 29, 2019 3 - Alpha N/A
:pypi:`pytest-isolate` Feb 20, 2023 4 - Beta pytest
:pypi:`pytest-isort` py.test plugin to check import ordering using isort Oct 31, 2022 5 - Production/Stable pytest (>=5.0)
:pypi:`pytest-is-running` pytest plugin providing a function to check if pytest is running. Jun 16, 2023 5 - Production/Stable N/A
:pypi:`pytest-is-running` pytest plugin providing a function to check if pytest is running. Aug 19, 2022 5 - Production/Stable N/A
:pypi:`pytest-it` Pytest plugin to display test reports as a plaintext spec, inspired by Rspec: https://github.com/mattduck/pytest-it. Jan 22, 2020 4 - Beta N/A
:pypi:`pytest-iterassert` Nicer list and iterable assertion messages for pytest May 11, 2020 3 - Alpha N/A
:pypi:`pytest-iters` A contextmanager pytest fixture for handling multiple mock iters May 24, 2022 N/A N/A
@@ -604,8 +592,8 @@ This list contains 1267 plugins.
:pypi:`pytest-jelastic` Pytest plugin defining the necessary command-line options to pass to pytests testing a Jelastic environment. Nov 16, 2022 N/A pytest (>=7.2.0,<8.0.0)
:pypi:`pytest-jest` A custom jest-pytest oriented Pytest reporter May 22, 2018 4 - Beta pytest (>=3.3.2)
:pypi:`pytest-jinja` A plugin to generate customizable jinja-based HTML reports in pytest Oct 04, 2022 3 - Alpha pytest (>=6.2.5,<7.0.0)
:pypi:`pytest-jira` py.test JIRA integration plugin, using markers Jun 12, 2023 3 - Alpha N/A
:pypi:`pytest-jira-xfail` Plugin skips (xfail) tests if unresolved Jira issue(s) linked Jun 14, 2023 N/A pytest (>=7.2.0)
:pypi:`pytest-jira` py.test JIRA integration plugin, using markers Apr 07, 2022 3 - Alpha N/A
:pypi:`pytest-jira-xfail` Plugin skips (xfail) tests if unresolved Jira issue(s) linked Dec 01, 2022 N/A pytest (~=7.2.0)
:pypi:`pytest-jira-xray` pytest plugin to integrate tests with JIRA XRAY Jun 06, 2023 4 - Beta pytest
:pypi:`pytest-job-selection` A pytest plugin for load balancing test suites Jan 30, 2023 4 - Beta pytest (>=3.5.0)
:pypi:`pytest-jobserver` Limit parallel tests with posix jobserver. May 15, 2019 5 - Production/Stable pytest
@@ -617,7 +605,7 @@ This list contains 1267 plugins.
:pypi:`pytest-jtr` pytest plugin supporting json test report output Nov 29, 2022 N/A pytest (>=7.1.2,<8.0.0)
:pypi:`pytest-jupyter` A pytest plugin for testing Jupyter libraries and extensions. Mar 30, 2023 4 - Beta pytest
:pypi:`pytest-jupyterhub` A reusable JupyterHub pytest plugin Apr 25, 2023 5 - Production/Stable pytest
:pypi:`pytest-kafka` Zookeeper, Kafka server, and Kafka consumer fixtures for Pytest Jun 14, 2023 N/A pytest
:pypi:`pytest-kafka` Zookeeper, Kafka server, and Kafka consumer fixtures for Pytest Oct 01, 2022 N/A pytest
:pypi:`pytest-kafkavents` A plugin to send pytest events to Kafka Sep 08, 2021 4 - Beta pytest
:pypi:`pytest-kasima` Display horizontal lines above and below the captured standard output for easy viewing. Jan 26, 2023 5 - Production/Stable pytest (>=7.2.1,<8.0.0)
:pypi:`pytest-keep-together` Pytest plugin to customize test ordering by running all 'related' tests together Dec 07, 2022 5 - Production/Stable pytest
@@ -706,7 +694,7 @@ This list contains 1267 plugins.
:pypi:`pytest-missing-fixtures` Pytest plugin that creates missing fixtures Oct 14, 2020 4 - Beta pytest (>=3.5.0)
:pypi:`pytest-ml` Test your machine learning! May 04, 2019 4 - Beta N/A
:pypi:`pytest-mocha` pytest plugin to display test execution output like a mochajs Apr 02, 2020 4 - Beta pytest (>=5.4.0)
:pypi:`pytest-mock` Thin-wrapper around the mock package for easier use with pytest Jun 15, 2023 5 - Production/Stable pytest (>=5.0)
:pypi:`pytest-mock` Thin-wrapper around the mock package for easier use with pytest Oct 05, 2022 5 - Production/Stable pytest (>=5.0)
:pypi:`pytest-mock-api` A mock API server with configurable routes and responses available as a fixture. Feb 13, 2019 1 - Planning pytest (>=4.0.0)
:pypi:`pytest-mock-generator` A pytest fixture wrapper for https://pypi.org/project/mock-generator May 16, 2022 5 - Production/Stable N/A
:pypi:`pytest-mock-helper` Help you mock HTTP call and generate mock code Jan 24, 2018 N/A pytest
@@ -816,7 +804,7 @@ This list contains 1267 plugins.
:pypi:`pytest-percent` Change the exit code of pytest test sessions when a required percent of tests pass. May 21, 2020 N/A pytest (>=5.2.0)
:pypi:`pytest-perf` Run performance tests against the mainline code. Jun 02, 2023 5 - Production/Stable pytest (>=6) ; extra == 'testing'
:pypi:`pytest-performance` A simple plugin to ensure the execution of critical sections of code has not been impacted Sep 11, 2020 5 - Production/Stable pytest (>=3.7.0)
:pypi:`pytest-persistence` Pytest tool for persistent objects Jun 14, 2023 N/A N/A
:pypi:`pytest-persistence` Pytest tool for persistent objects May 16, 2023 N/A N/A
:pypi:`pytest-pg` A tiny plugin for pytest which runs PostgreSQL in Docker May 04, 2023 5 - Production/Stable pytest (>=6.0.0)
:pypi:`pytest-pgsql` Pytest plugins and helpers for tests using a Postgres database. May 13, 2020 5 - Production/Stable pytest (>=3.0.0)
:pypi:`pytest-phmdoctest` pytest plugin to test Python examples in Markdown using phmdoctest. Apr 15, 2022 4 - Beta pytest (>=5.4.3)
@@ -859,7 +847,7 @@ This list contains 1267 plugins.
:pypi:`pytest-pretty` pytest plugin for printing summary data as I want it Apr 05, 2023 5 - Production/Stable pytest>=7
:pypi:`pytest-pretty-terminal` pytest plugin for generating prettier terminal output Jan 31, 2022 N/A pytest (>=3.4.1)
:pypi:`pytest-pride` Minitest-style test colors Apr 02, 2016 3 - Alpha N/A
:pypi:`pytest-print` pytest-print adds the printer fixture you can use to print messages to the user (directly to the pytest runner, not stdout) Jun 16, 2023 5 - Production/Stable pytest>=7.3.2
:pypi:`pytest-print` pytest-print adds the printer fixture you can use to print messages to the user (directly to the pytest runner, not stdout) Dec 28, 2021 5 - Production/Stable pytest (>=6)
:pypi:`pytest-profiles` pytest plugin for configuration profiles Dec 09, 2021 4 - Beta pytest (>=3.7.0)
:pypi:`pytest-profiling` Profiling plugin for py.test May 28, 2019 5 - Production/Stable pytest
:pypi:`pytest-progress` pytest plugin for instant test progress status Jan 31, 2022 5 - Production/Stable N/A
@@ -880,7 +868,7 @@ This list contains 1267 plugins.
:pypi:`pytest-pydocstyle` pytest plugin to run pydocstyle Jan 05, 2023 3 - Alpha N/A
:pypi:`pytest-pylint` pytest plugin to check source code with pylint Sep 10, 2022 5 - Production/Stable pytest (>=5.4)
:pypi:`pytest-pymysql-autorecord` Record PyMySQL queries and mock with the stored data. Sep 02, 2022 N/A N/A
:pypi:`pytest-pyodide` "Pytest plugin for testing applications that use Pyodide" Jun 11, 2023 N/A pytest
:pypi:`pytest-pyodide` "Pytest plugin for testing applications that use Pyodide" Jun 02, 2023 N/A pytest
:pypi:`pytest-pypi` Easily test your HTTP library against a local copy of pypi Mar 04, 2018 3 - Alpha N/A
:pypi:`pytest-pypom-navigation` Core engine for cookiecutter-qa and pytest-play packages Feb 18, 2019 4 - Beta pytest (>=3.0.7)
:pypi:`pytest-pyppeteer` A plugin to run pyppeteer in pytest Apr 28, 2022 N/A pytest (>=6.2.5,<7.0.0)
@@ -905,7 +893,7 @@ This list contains 1267 plugins.
:pypi:`pytest-qt-app` QT app fixture for py.test Dec 23, 2015 5 - Production/Stable N/A
:pypi:`pytest-quarantine` A plugin for pytest to manage expected test failures Nov 24, 2019 5 - Production/Stable pytest (>=4.6)
:pypi:`pytest-quickcheck` pytest plugin to generate random data inspired by QuickCheck Nov 05, 2022 4 - Beta pytest (>=4.0)
:pypi:`pytest-rabbitmq` RabbitMQ process and client fixtures for pytest Jun 16, 2023 5 - Production/Stable pytest (>=6.2)
:pypi:`pytest-rabbitmq` RabbitMQ process and client fixtures for pytest Feb 11, 2022 5 - Production/Stable pytest (>=3.0.0)
:pypi:`pytest-race` Race conditions tester for pytest Jun 07, 2022 4 - Beta N/A
:pypi:`pytest-rage` pytest plugin to implement PEP712 Oct 21, 2011 3 - Alpha N/A
:pypi:`pytest-rail` pytest plugin for creating TestRail runs and adding results May 02, 2022 N/A pytest (>=3.6)
@@ -970,7 +958,7 @@ This list contains 1267 plugins.
:pypi:`pytest-responsemock` Simplified requests calls mocking for pytest Mar 10, 2022 5 - Production/Stable N/A
:pypi:`pytest-responses` py.test integration for responses Oct 11, 2022 N/A pytest (>=2.5)
:pypi:`pytest-rest-api` Aug 08, 2022 N/A pytest (>=7.1.2,<8.0.0)
:pypi:`pytest-restrict` Pytest plugin to restrict the test types allowed Jun 16, 2023 5 - Production/Stable pytest
:pypi:`pytest-restrict` Pytest plugin to restrict the test types allowed May 11, 2022 5 - Production/Stable pytest
:pypi:`pytest-result-log` Write the execution result of the case to the log Apr 17, 2023 N/A pytest>=7.2.0
:pypi:`pytest-result-sender` Apr 20, 2023 N/A pytest>=7.3.1
:pypi:`pytest-resume` A Pytest plugin to resuming from the last run test Apr 22, 2023 4 - Beta pytest (>=7.0)
@@ -978,7 +966,7 @@ This list contains 1267 plugins.
:pypi:`pytest-retry` Adds the ability to retry flaky tests in CI environments Aug 16, 2022 N/A pytest (>=7.0.0)
:pypi:`pytest-retry-class` A pytest plugin to rerun entire class on failure Mar 25, 2023 N/A pytest (>=5.3)
:pypi:`pytest-reusable-testcases` Apr 28, 2023 N/A N/A
:pypi:`pytest-reverse` Pytest plugin to reverse test order. Jun 16, 2023 5 - Production/Stable pytest
:pypi:`pytest-reverse` Pytest plugin to reverse test order. May 11, 2022 5 - Production/Stable pytest
:pypi:`pytest-rich` Leverage rich for richer test session output Mar 03, 2022 4 - Beta pytest (>=7.0)
:pypi:`pytest-rich-reporter` A pytest plugin using Rich for beautiful test result formatting. Feb 17, 2022 1 - Planning pytest (>=5.0.0)
:pypi:`pytest-richtrace` Nov 05, 2022 N/A pytest (>=7.2.0,<8.0.0)
@@ -999,7 +987,6 @@ This list contains 1267 plugins.
:pypi:`pytest-run-subprocess` Pytest Plugin for running and testing subprocesses. Nov 12, 2022 5 - Production/Stable pytest
:pypi:`pytest-runtime-types` Checks type annotations on runtime while running tests. Feb 09, 2023 N/A pytest
:pypi:`pytest-runtime-xfail` Call runtime_xfail() to mark running test as xfail. Aug 26, 2021 N/A pytest>=5.0.0
:pypi:`pytest-runtime-yoyo` run case mark timeout Jun 12, 2023 N/A pytest (>=7.2.0)
:pypi:`pytest-ry-demo1` 测试 Mar 26, 2023 N/A N/A
:pypi:`pytest-saccharin` pytest-saccharin is a updated fork of pytest-sugar, a plugin for pytest that changes the default look and feel of pytest (e.g. progressbar, show tests that fail instantly). Oct 31, 2022 3 - Alpha N/A
:pypi:`pytest-salt` Pytest Salt Plugin Jan 27, 2020 4 - Beta N/A
@@ -1010,14 +997,14 @@ This list contains 1267 plugins.
:pypi:`pytest-sanic` a pytest plugin for Sanic Oct 25, 2021 N/A pytest (>=5.2)
:pypi:`pytest-sanity` Dec 07, 2020 N/A N/A
:pypi:`pytest-sa-pg` May 14, 2019 N/A N/A
:pypi:`pytest-sbase` A complete web automation framework for end-to-end testing. Jun 13, 2023 5 - Production/Stable N/A
:pypi:`pytest-sbase` A complete web automation framework for end-to-end testing. Jun 09, 2023 5 - Production/Stable N/A
:pypi:`pytest-scenario` pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A
:pypi:`pytest-schedule` The job of test scheduling for humans. Jan 07, 2023 5 - Production/Stable N/A
:pypi:`pytest-schema` 👍 Validate return values against a schema-like object in testing Mar 14, 2022 5 - Production/Stable pytest (>=3.5.0)
:pypi:`pytest-securestore` An encrypted password store for use within pytest cases Nov 08, 2021 4 - Beta N/A
:pypi:`pytest-select` A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0)
:pypi:`pytest-selenium` pytest plugin for Selenium May 28, 2023 5 - Production/Stable pytest>=6.0.0
:pypi:`pytest-seleniumbase` A complete web automation framework for end-to-end testing. Jun 13, 2023 5 - Production/Stable N/A
:pypi:`pytest-seleniumbase` A complete web automation framework for end-to-end testing. Jun 09, 2023 5 - Production/Stable N/A
:pypi:`pytest-selenium-enhancer` pytest plugin for Selenium Apr 29, 2022 5 - Production/Stable N/A
:pypi:`pytest-selenium-pdiff` A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A
:pypi:`pytest-send-email` Send pytest execution result email Dec 04, 2019 N/A N/A
@@ -1145,7 +1132,7 @@ This list contains 1267 plugins.
:pypi:`pytest-test-groups` A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Oct 25, 2016 5 - Production/Stable N/A
:pypi:`pytest-testinfra` Test infrastructures May 21, 2023 5 - Production/Stable pytest (!=3.0.2)
:pypi:`pytest-testlink-adaptor` pytest reporting plugin for testlink Dec 20, 2018 4 - Beta pytest (>=2.6)
:pypi:`pytest-testmon` selects tests affected by changed files and methods Jun 16, 2023 4 - Beta N/A
:pypi:`pytest-testmon` selects tests affected by changed files and methods Jun 09, 2023 4 - Beta N/A
:pypi:`pytest-testmon-dev` selects tests affected by changed files and methods Mar 30, 2023 4 - Beta pytest (<8,>=5)
:pypi:`pytest-testmon-oc` nOly selects tests affected by changed files and methods Jun 01, 2022 4 - Beta pytest (<8,>=5)
:pypi:`pytest-testmon-skip-libraries` selects tests affected by changed files and methods Mar 03, 2023 4 - Beta pytest (<8,>=5)
@@ -1172,7 +1159,6 @@ This list contains 1267 plugins.
:pypi:`pytest-thawgun` Pytest plugin for time travel May 26, 2020 3 - Alpha N/A
:pypi:`pytest-threadleak` Detects thread leaks Jul 03, 2022 4 - Beta pytest (>=3.1.1)
:pypi:`pytest-tick` Ticking on tests Aug 31, 2021 5 - Production/Stable pytest (>=6.2.5,<7.0.0)
:pypi:`pytest-time` Jun 16, 2023 3 - Alpha pytest
:pypi:`pytest-timeit` A pytest plugin to time test function runs Oct 13, 2016 4 - Beta N/A
:pypi:`pytest-timeout` pytest plugin to abort hanging tests Jan 18, 2022 5 - Production/Stable pytest (>=5.0.0)
:pypi:`pytest-timeouts` Linux-only Pytest plugin to control durations of various test case execution phases Sep 21, 2019 5 - Production/Stable N/A
@@ -1209,7 +1195,7 @@ This list contains 1267 plugins.
:pypi:`pytest-tspwplib` A simple plugin to use with tspwplib Jan 08, 2021 4 - Beta pytest (>=3.5.0)
:pypi:`pytest-tst` Customize pytest options, output and exit code to make it compatible with tst Apr 27, 2022 N/A pytest (>=5.0.0)
:pypi:`pytest-tstcls` Test Class Base Mar 23, 2020 5 - Production/Stable N/A
:pypi:`pytest-tui` Text User Interface (TUI) and HTML report for Pytest test runs Jun 12, 2023 4 - Beta N/A
:pypi:`pytest-tui` Text User Interface (TUI) and HTML report for Pytest test runs Apr 06, 2023 4 - Beta N/A
:pypi:`pytest-tutorials` Mar 11, 2023 N/A N/A
:pypi:`pytest-twilio-conversations-client-mock` Aug 02, 2022 N/A N/A
:pypi:`pytest-twisted` A twisted plugin for pytest. Oct 16, 2022 5 - Production/Stable pytest (>=2.3)
@@ -1227,7 +1213,6 @@ This list contains 1267 plugins.
:pypi:`pytest-unmarked` Run only unmarked tests Aug 27, 2019 5 - Production/Stable N/A
:pypi:`pytest-unordered` Test equality of unordered collections in pytest Nov 28, 2022 4 - Beta pytest (>=6.0.0)
:pypi:`pytest-unstable` Set a test as unstable to return 0 even if it failed Sep 27, 2022 4 - Beta N/A
:pypi:`pytest-unused-fixtures` A pytest plugin to list unused fixtures after a test run. Jun 15, 2023 4 - Beta pytest (>=7.3.2,<8.0.0)
:pypi:`pytest-upload-report` pytest-upload-report is a plugin for pytest that upload your test report for test results. Jun 18, 2021 5 - Production/Stable N/A
:pypi:`pytest-utils` Some helpers for pytest. Feb 02, 2023 4 - Beta pytest (>=7.0.0,<8.0.0)
:pypi:`pytest-vagrant` A py.test plugin providing access to vagrant. Sep 07, 2021 5 - Production/Stable pytest
@@ -1254,7 +1239,7 @@ This list contains 1267 plugins.
:pypi:`pytest-wa-e2e-plugin` Pytest plugin for testing whatsapp bots with end to end tests Feb 18, 2020 4 - Beta pytest (>=3.5.0)
:pypi:`pytest-wake` May 11, 2023 N/A pytest
:pypi:`pytest-watch` Local continuous test runner with pytest and watchdog. May 20, 2018 N/A N/A
:pypi:`pytest-watcher` Automatically rerun your tests on file modifications Jun 11, 2023 4 - Beta N/A
:pypi:`pytest-watcher` Automatically rerun your tests on file modifications Jun 07, 2023 4 - Beta N/A
:pypi:`pytest-wdl` Pytest plugin for testing WDL workflows. Nov 17, 2020 5 - Production/Stable N/A
:pypi:`pytest-web3-data` Sep 15, 2022 4 - Beta pytest
:pypi:`pytest-webdriver` Selenium webdriver fixture for py.test May 28, 2019 5 - Production/Stable pytest
@@ -1271,7 +1256,6 @@ This list contains 1267 plugins.
:pypi:`pytest-xdist-debug-for-graingert` pytest xdist plugin for distributed testing and loop-on-failing modes Jul 24, 2019 5 - Production/Stable pytest (>=4.4.0)
:pypi:`pytest-xdist-forked` forked from pytest-xdist Feb 10, 2020 5 - Production/Stable pytest (>=4.4.0)
:pypi:`pytest-xdist-tracker` pytest plugin helps to reproduce failures for particular xdist node Nov 18, 2021 3 - Alpha pytest (>=3.5.1)
:pypi:`pytest-xdist-worker-stats` A pytest plugin to list worker statistics after a xdist run. Jun 15, 2023 4 - Beta pytest (>=7.3.2,<8.0.0)
:pypi:`pytest-xfaillist` Maintain a xfaillist in an additional file to avoid merge-conflicts. Sep 17, 2021 N/A pytest (>=6.2.2,<7.0.0)
:pypi:`pytest-xfiles` Pytest fixtures providing data read from function, module or package related (x)files. Feb 27, 2018 N/A N/A
:pypi:`pytest-xlog` Extended logging for test and decorators May 31, 2020 4 - Beta N/A
@@ -2789,7 +2773,7 @@ This list contains 1267 plugins.
Use pytest's runner to discover and execute C++ tests
:pypi:`pytest-cppython`
*last release*: Jun 14, 2023,
*last release*: Jun 02, 2023,
*status*: N/A,
*requires*: N/A
@@ -3657,7 +3641,7 @@ This list contains 1267 plugins.
A pytest plugin to rerun tests dynamically based off of test outcome and output.
:pypi:`pytest-dynamodb`
*last release*: Jun 12, 2023,
*last release*: Mar 27, 2023,
*status*: 5 - Production/Stable,
*requires*: pytest
@@ -3762,53 +3746,53 @@ This list contains 1267 plugins.
Send execution result email
:pypi:`pytest-embedded`
*last release*: Jun 14, 2023,
*status*: 5 - Production/Stable,
*requires*: pytest>=7.0
*last release*: Jun 06, 2023,
*status*: N/A,
*requires*: pytest (>=7.0)
A pytest plugin that designed for embedded testing.
pytest embedded plugin
:pypi:`pytest-embedded-arduino`
*last release*: Jun 14, 2023,
*last release*: Jun 06, 2023,
*status*: 5 - Production/Stable,
*requires*: N/A
Make pytest-embedded plugin work with Arduino.
pytest embedded plugin for Arduino projects
:pypi:`pytest-embedded-idf`
*last release*: Jun 14, 2023,
*last release*: Jun 06, 2023,
*status*: 5 - Production/Stable,
*requires*: N/A
Make pytest-embedded plugin work with ESP-IDF.
pytest embedded plugin for esp-idf project
:pypi:`pytest-embedded-jtag`
*last release*: Jun 14, 2023,
*status*: 5 - Production/Stable,
*last release*: Jun 06, 2023,
*status*: N/A,
*requires*: N/A
Make pytest-embedded plugin work with JTAG.
pytest embedded plugin for testing with jtag
:pypi:`pytest-embedded-qemu`
*last release*: Jun 14, 2023,
*last release*: Jun 06, 2023,
*status*: 5 - Production/Stable,
*requires*: N/A
Make pytest-embedded plugin work with QEMU.
pytest embedded plugin for qemu, not target chip
:pypi:`pytest-embedded-serial`
*last release*: Jun 14, 2023,
*status*: 5 - Production/Stable,
*last release*: Jun 06, 2023,
*status*: N/A,
*requires*: N/A
Make pytest-embedded plugin work with Serial.
pytest embedded plugin for testing serial ports
:pypi:`pytest-embedded-serial-esp`
*last release*: Jun 14, 2023,
*status*: 5 - Production/Stable,
*last release*: Jun 06, 2023,
*status*: N/A,
*requires*: N/A
Make pytest-embedded plugin work with Espressif target boards.
pytest embedded plugin for testing espressif boards via serial ports
:pypi:`pytest-embrace`
*last release*: Mar 25, 2023,
@@ -3867,9 +3851,9 @@ This list contains 1267 plugins.
Improvements for pytest (rejected upstream)
:pypi:`pytest-env`
*last release*: Jun 15, 2023,
*last release*: Oct 23, 2022,
*status*: 5 - Production/Stable,
*requires*: pytest>=7.3.1
*requires*: pytest>=7.1.3
py.test plugin that allows you to add environment variables.
@@ -4189,9 +4173,9 @@ This list contains 1267 plugins.
A fixture which allows easy replacement of fastapi dependencies for testing
:pypi:`pytest-fastest`
*last release*: Jun 15, 2023,
*status*: 4 - Beta,
*requires*: pytest (>=4.4)
*last release*: Mar 05, 2020,
*status*: N/A,
*requires*: N/A
Use SCM and coverage to run only needed tests
@@ -4364,7 +4348,7 @@ This list contains 1267 plugins.
pytest plugin to check FLAKE8 requirements
:pypi:`pytest-flake8-path`
*last release*: Jun 16, 2023,
*last release*: May 11, 2022,
*status*: 5 - Production/Stable,
*requires*: pytest
@@ -4497,7 +4481,7 @@ This list contains 1267 plugins.
Wrap tests with fixtures in freeze_time
:pypi:`pytest-freezer`
*last release*: Jun 17, 2023,
*last release*: Oct 20, 2022,
*status*: N/A,
*requires*: pytest>=3.6
@@ -4602,7 +4586,7 @@ This list contains 1267 plugins.
For finding/executing Ghost Inspector tests
:pypi:`pytest-girder`
*last release*: Jun 14, 2023,
*last release*: Jun 08, 2023,
*status*: N/A,
*requires*: N/A
@@ -4826,7 +4810,7 @@ This list contains 1267 plugins.
A pytest plugin for use with homeassistant custom components.
:pypi:`pytest-homeassistant-custom-component`
*last release*: Jun 16, 2023,
*last release*: Jun 09, 2023,
*status*: 3 - Alpha,
*requires*: pytest (==7.3.1)
@@ -4847,7 +4831,7 @@ This list contains 1267 plugins.
Report on tests that honor constraints, and guard against regressions
:pypi:`pytest-hot-reloading`
*last release*: Jun 16, 2023,
*last release*: May 18, 2023,
*status*: N/A,
*requires*: N/A
@@ -5253,7 +5237,7 @@ This list contains 1267 plugins.
py.test plugin to check import ordering using isort
:pypi:`pytest-is-running`
*last release*: Jun 16, 2023,
*last release*: Aug 19, 2022,
*status*: 5 - Production/Stable,
*requires*: N/A
@@ -5309,16 +5293,16 @@ This list contains 1267 plugins.
A plugin to generate customizable jinja-based HTML reports in pytest
:pypi:`pytest-jira`
*last release*: Jun 12, 2023,
*last release*: Apr 07, 2022,
*status*: 3 - Alpha,
*requires*: N/A
py.test JIRA integration plugin, using markers
:pypi:`pytest-jira-xfail`
*last release*: Jun 14, 2023,
*last release*: Dec 01, 2022,
*status*: N/A,
*requires*: pytest (>=7.2.0)
*requires*: pytest (~=7.2.0)
Plugin skips (xfail) tests if unresolved Jira issue(s) linked
@@ -5400,7 +5384,7 @@ This list contains 1267 plugins.
A reusable JupyterHub pytest plugin
:pypi:`pytest-kafka`
*last release*: Jun 14, 2023,
*last release*: Oct 01, 2022,
*status*: N/A,
*requires*: pytest
@@ -6023,7 +6007,7 @@ This list contains 1267 plugins.
pytest plugin to display test execution output like a mochajs
:pypi:`pytest-mock`
*last release*: Jun 15, 2023,
*last release*: Oct 05, 2022,
*status*: 5 - Production/Stable,
*requires*: pytest (>=5.0)
@@ -6793,7 +6777,7 @@ This list contains 1267 plugins.
A simple plugin to ensure the execution of critical sections of code has not been impacted
:pypi:`pytest-persistence`
*last release*: Jun 14, 2023,
*last release*: May 16, 2023,
*status*: N/A,
*requires*: N/A
@@ -7094,9 +7078,9 @@ This list contains 1267 plugins.
Minitest-style test colors
:pypi:`pytest-print`
*last release*: Jun 16, 2023,
*last release*: Dec 28, 2021,
*status*: 5 - Production/Stable,
*requires*: pytest>=7.3.2
*requires*: pytest (>=6)
pytest-print adds the printer fixture you can use to print messages to the user (directly to the pytest runner, not stdout)
@@ -7241,7 +7225,7 @@ This list contains 1267 plugins.
Record PyMySQL queries and mock with the stored data.
:pypi:`pytest-pyodide`
*last release*: Jun 11, 2023,
*last release*: Jun 02, 2023,
*status*: N/A,
*requires*: pytest
@@ -7416,9 +7400,9 @@ This list contains 1267 plugins.
pytest plugin to generate random data inspired by QuickCheck
:pypi:`pytest-rabbitmq`
*last release*: Jun 16, 2023,
*last release*: Feb 11, 2022,
*status*: 5 - Production/Stable,
*requires*: pytest (>=6.2)
*requires*: pytest (>=3.0.0)
RabbitMQ process and client fixtures for pytest
@@ -7871,7 +7855,7 @@ This list contains 1267 plugins.
:pypi:`pytest-restrict`
*last release*: Jun 16, 2023,
*last release*: May 11, 2022,
*status*: 5 - Production/Stable,
*requires*: pytest
@@ -7927,7 +7911,7 @@ This list contains 1267 plugins.
:pypi:`pytest-reverse`
*last release*: Jun 16, 2023,
*last release*: May 11, 2022,
*status*: 5 - Production/Stable,
*requires*: pytest
@@ -8073,13 +8057,6 @@ This list contains 1267 plugins.
Call runtime_xfail() to mark running test as xfail.
:pypi:`pytest-runtime-yoyo`
*last release*: Jun 12, 2023,
*status*: N/A,
*requires*: pytest (>=7.2.0)
run case mark timeout
:pypi:`pytest-ry-demo1`
*last release*: Mar 26, 2023,
*status*: N/A,
@@ -8151,7 +8128,7 @@ This list contains 1267 plugins.
:pypi:`pytest-sbase`
*last release*: Jun 13, 2023,
*last release*: Jun 09, 2023,
*status*: 5 - Production/Stable,
*requires*: N/A
@@ -8200,7 +8177,7 @@ This list contains 1267 plugins.
pytest plugin for Selenium
:pypi:`pytest-seleniumbase`
*last release*: Jun 13, 2023,
*last release*: Jun 09, 2023,
*status*: 5 - Production/Stable,
*requires*: N/A
@@ -9096,7 +9073,7 @@ This list contains 1267 plugins.
pytest reporting plugin for testlink
:pypi:`pytest-testmon`
*last release*: Jun 16, 2023,
*last release*: Jun 09, 2023,
*status*: 4 - Beta,
*requires*: N/A
@@ -9284,13 +9261,6 @@ This list contains 1267 plugins.
Ticking on tests
:pypi:`pytest-time`
*last release*: Jun 16, 2023,
*status*: 3 - Alpha,
*requires*: pytest
:pypi:`pytest-timeit`
*last release*: Oct 13, 2016,
*status*: 4 - Beta,
@@ -9544,7 +9514,7 @@ This list contains 1267 plugins.
Test Class Base
:pypi:`pytest-tui`
*last release*: Jun 12, 2023,
*last release*: Apr 06, 2023,
*status*: 4 - Beta,
*requires*: N/A
@@ -9669,13 +9639,6 @@ This list contains 1267 plugins.
Set a test as unstable to return 0 even if it failed
:pypi:`pytest-unused-fixtures`
*last release*: Jun 15, 2023,
*status*: 4 - Beta,
*requires*: pytest (>=7.3.2,<8.0.0)
A pytest plugin to list unused fixtures after a test run.
:pypi:`pytest-upload-report`
*last release*: Jun 18, 2021,
*status*: 5 - Production/Stable,
@@ -9859,7 +9822,7 @@ This list contains 1267 plugins.
Local continuous test runner with pytest and watchdog.
:pypi:`pytest-watcher`
*last release*: Jun 11, 2023,
*last release*: Jun 07, 2023,
*status*: 4 - Beta,
*requires*: N/A
@@ -9977,13 +9940,6 @@ This list contains 1267 plugins.
pytest plugin helps to reproduce failures for particular xdist node
:pypi:`pytest-xdist-worker-stats`
*last release*: Jun 15, 2023,
*status*: 4 - Beta,
*requires*: pytest (>=7.3.2,<8.0.0)
A pytest plugin to list worker statistics after a xdist run.
:pypi:`pytest-xfaillist`
*last release*: Sep 17, 2021,
*status*: N/A,
+71 -96
View File
@@ -82,8 +82,6 @@ pytest.exit
pytest.main
~~~~~~~~~~~
**Tutorial**: :ref:`pytest.main-usage`
.. autofunction:: pytest.main
pytest.param
@@ -785,66 +783,18 @@ reporting or interaction with exceptions:
.. autofunction:: pytest_leave_pdb
Collection tree objects
-----------------------
Objects
-------
These are the collector and item classes (collectively called "nodes") which
make up the collection tree.
Full reference to objects accessible from :ref:`fixtures <fixture>` or :ref:`hooks <hook-reference>`.
Node
~~~~
.. autoclass:: _pytest.nodes.Node()
CallInfo
~~~~~~~~
.. autoclass:: pytest.CallInfo()
:members:
Collector
~~~~~~~~~
.. autoclass:: pytest.Collector()
:members:
:show-inheritance:
Item
~~~~
.. autoclass:: pytest.Item()
:members:
:show-inheritance:
File
~~~~
.. autoclass:: pytest.File()
:members:
:show-inheritance:
FSCollector
~~~~~~~~~~~
.. autoclass:: _pytest.nodes.FSCollector()
:members:
:show-inheritance:
Session
~~~~~~~
.. autoclass:: pytest.Session()
:members:
:show-inheritance:
Package
~~~~~~~
.. autoclass:: pytest.Package()
:members:
:show-inheritance:
Module
~~~~~~
.. autoclass:: pytest.Module()
:members:
:show-inheritance:
Class
~~~~~
@@ -853,34 +803,13 @@ Class
:members:
:show-inheritance:
Function
~~~~~~~~
Collector
~~~~~~~~~
.. autoclass:: pytest.Function()
.. autoclass:: pytest.Collector()
:members:
:show-inheritance:
FunctionDefinition
~~~~~~~~~~~~~~~~~~
.. autoclass:: _pytest.python.FunctionDefinition()
:members:
:show-inheritance:
Objects
-------
Objects accessible from :ref:`fixtures <fixture>` or :ref:`hooks <hook-reference>`
or importable from ``pytest``.
CallInfo
~~~~~~~~
.. autoclass:: pytest.CallInfo()
:members:
CollectReport
~~~~~~~~~~~~~
@@ -908,6 +837,13 @@ ExitCode
.. autoclass:: pytest.ExitCode
:members:
File
~~~~
.. autoclass:: pytest.File()
:members:
:show-inheritance:
FixtureDef
~~~~~~~~~~
@@ -916,6 +852,34 @@ FixtureDef
:members:
:show-inheritance:
FSCollector
~~~~~~~~~~~
.. autoclass:: _pytest.nodes.FSCollector()
:members:
:show-inheritance:
Function
~~~~~~~~
.. autoclass:: pytest.Function()
:members:
:show-inheritance:
FunctionDefinition
~~~~~~~~~~~~~~~~~~
.. autoclass:: _pytest.python.FunctionDefinition()
:members:
:show-inheritance:
Item
~~~~
.. autoclass:: pytest.Item()
:members:
:show-inheritance:
MarkDecorator
~~~~~~~~~~~~~
@@ -943,6 +907,19 @@ Metafunc
.. autoclass:: pytest.Metafunc()
:members:
Module
~~~~~~
.. autoclass:: pytest.Module()
:members:
:show-inheritance:
Node
~~~~
.. autoclass:: _pytest.nodes.Node()
:members:
Parser
~~~~~~
@@ -964,6 +941,13 @@ PytestPluginManager
:inherited-members:
:show-inheritance:
Session
~~~~~~~
.. autoclass:: pytest.Session()
:members:
:show-inheritance:
TestReport
~~~~~~~~~~
@@ -978,10 +962,10 @@ TestShortLogReport
.. autoclass:: pytest.TestShortLogReport()
:members:
Result
_Result
~~~~~~~
Result object used within :ref:`hook wrappers <hookwrapper>`, see :py:class:`Result in the pluggy documentation <pluggy.Result>` for more information.
Result object used within :ref:`hook wrappers <hookwrapper>`, see :py:class:`_Result in the pluggy documentation <pluggy._callers._Result>` for more information.
Stash
~~~~~
@@ -1719,11 +1703,6 @@ passed multiple times. The expected format is ``name=value``. For example::
[pytest]
pythonpath = src1 src2
.. note::
``pythonpath`` does not affect some imports that happen very early,
most notably plugins loaded using the ``-p`` command line option.
.. confval:: required_plugins
@@ -1887,12 +1866,8 @@ All the command-line flags can be obtained by running ``pytest --help``::
tests. Optional argument: glob (default: '*').
--cache-clear Remove all cache contents at start of test run
--lfnf={all,none}, --last-failed-no-failures={all,none}
With ``--lf``, determines whether to execute tests
when there are no previously (known) failures or
when no cached ``lastfailed`` data was found.
``all`` (the default) runs the full test suite
again. ``none`` just emits a message about no known
failures and exits successfully.
Which tests to run with no previously (known)
failures
--sw, --stepwise Exit on test failure and continue from last failing
test next time
--sw-skip, --stepwise-skip
@@ -1943,9 +1918,9 @@ All the command-line flags can be obtained by running ``pytest --help``::
--strict-markers Markers not registered in the `markers` section of
the configuration file raise errors
--strict (Deprecated) alias to --strict-markers
-c FILE, --config-file=FILE
-c, --config-file FILE
Load configuration from `FILE` instead of trying to
locate one of the implicit configuration files.
locate one of the implicit configuration files
--continue-on-collection-errors
Force test execution even if collection errors occur
--rootdir=ROOTDIR Define root directory for tests. Can be relative
+3 -9
View File
@@ -31,16 +31,10 @@ class InvalidFeatureRelease(Exception):
SLUG = "pytest-dev/pytest"
PR_BODY = """\
Created by the [prepare release pr](https://github.com/pytest-dev/pytest/actions/workflows/prepare-release-pr.yml)
workflow.
Created automatically from manual trigger.
Once all builds pass and it has been **approved** by one or more maintainers,
start the [deploy](https://github.com/pytest-dev/pytest/actions/workflows/deploy.yml) workflow, using these parameters:
* `Use workflow from`: `release-{version}`.
* `Release version`: `{version}`.
After the `deploy` workflow has been approved by a core maintainer, the package will be uploaded to PyPI automatically.
Once all builds pass and it has been **approved** by one or more maintainers, the build
can be released by pushing a tag `{version}` to this repository.
"""
+1 -3
View File
@@ -7,9 +7,7 @@ def main():
Platform agnostic wrapper script for towncrier.
Fixes the issue (#7251) where windows users are unable to natively run tox -e docs to build pytest docs.
"""
with open(
"doc/en/_changelog_towncrier_draft.rst", "w", encoding="utf-8"
) as draft_file:
with open("doc/en/_changelog_towncrier_draft.rst", "w") as draft_file:
return call(("towncrier", "--draft"), stdout=draft_file)
+5 -17
View File
@@ -13,26 +13,14 @@ from tqdm import tqdm
FILE_HEAD = r"""
.. _plugin-list:
Pytest Plugin List
==================
Plugin List
===========
Below is an automated compilation of ``pytest``` plugins available on `PyPI <https://pypi.org>`_.
It includes PyPI projects whose names begin with "pytest-" and a handful of manually selected projects.
PyPI projects that match "pytest-\*" are considered plugins and are listed
automatically together with a manually-maintained list in `the source
code <https://github.com/pytest-dev/pytest/blob/main/scripts/update-plugin-list.py>`_.
Packages classified as inactive are excluded.
For detailed insights into how this list is generated,
please refer to `the update script <https://github.com/pytest-dev/pytest/blob/main/scripts/update-plugin-list.py>`_.
.. warning::
Please be aware that this list is not a curated collection of projects
and does not undergo a systematic review process.
It serves purely as an informational resource to aid in the discovery of ``pytest`` plugins.
Do not presume any endorsement from the ``pytest`` project or its developers,
and always conduct your own quality assessment before incorporating any of these plugins into your own projects.
.. The following conditional uses a different format for this list when
creating a PDF, because otherwise the table gets far too wide for the
page.
+14 -40
View File
@@ -13,7 +13,6 @@ import struct
import sys
import tokenize
import types
from collections import defaultdict
from pathlib import Path
from pathlib import PurePath
from typing import Callable
@@ -57,10 +56,6 @@ else:
astNum = ast.Num
class Sentinel:
pass
assertstate_key = StashKey["AssertionState"]()
# pytest caches rewritten pycs in pycache dirs
@@ -68,9 +63,6 @@ PYTEST_TAG = f"{sys.implementation.cache_tag}-pytest-{version}"
PYC_EXT = ".py" + (__debug__ and "c" or "o")
PYC_TAIL = "." + PYTEST_TAG + PYC_EXT
# Special marker that denotes we have just left a scope definition
_SCOPE_END_MARKER = Sentinel()
class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader):
"""PEP302/PEP451 import hook which rewrites asserts."""
@@ -653,8 +645,6 @@ class AssertionRewriter(ast.NodeVisitor):
.push_format_context() and .pop_format_context() which allows
to build another %-formatted string while already building one.
:scope: A tuple containing the current scope used for variables_overwrite.
:variables_overwrite: A dict filled with references to variables
that change value within an assert. This happens when a variable is
reassigned with the walrus operator
@@ -676,10 +666,7 @@ class AssertionRewriter(ast.NodeVisitor):
else:
self.enable_assertion_pass_hook = False
self.source = source
self.scope: tuple[ast.AST, ...] = ()
self.variables_overwrite: defaultdict[
tuple[ast.AST, ...], Dict[str, str]
] = defaultdict(dict)
self.variables_overwrite: Dict[str, str] = {}
def run(self, mod: ast.Module) -> None:
"""Find all assert statements in *mod* and rewrite them."""
@@ -745,17 +732,9 @@ class AssertionRewriter(ast.NodeVisitor):
mod.body[pos:pos] = imports
# Collect asserts.
self.scope = (mod,)
nodes: List[Union[ast.AST, Sentinel]] = [mod]
nodes: List[ast.AST] = [mod]
while nodes:
node = nodes.pop()
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
self.scope = tuple((*self.scope, node))
nodes.append(_SCOPE_END_MARKER)
if node == _SCOPE_END_MARKER:
self.scope = self.scope[:-1]
continue
assert isinstance(node, ast.AST)
for name, field in ast.iter_fields(node):
if isinstance(field, list):
new: List[ast.AST] = []
@@ -1026,7 +1005,7 @@ class AssertionRewriter(ast.NodeVisitor):
]
):
pytest_temp = self.variable()
self.variables_overwrite[self.scope][
self.variables_overwrite[
v.left.target.id
] = v.left # type:ignore[assignment]
v.left.target.id = pytest_temp
@@ -1069,20 +1048,17 @@ class AssertionRewriter(ast.NodeVisitor):
new_args = []
new_kwargs = []
for arg in call.args:
if isinstance(arg, ast.Name) and arg.id in self.variables_overwrite.get(
self.scope, {}
):
arg = self.variables_overwrite[self.scope][
arg.id
] # type:ignore[assignment]
if isinstance(arg, ast.Name) and arg.id in self.variables_overwrite:
arg = self.variables_overwrite[arg.id] # type:ignore[assignment]
res, expl = self.visit(arg)
arg_expls.append(expl)
new_args.append(res)
for keyword in call.keywords:
if isinstance(
keyword.value, ast.Name
) and keyword.value.id in self.variables_overwrite.get(self.scope, {}):
keyword.value = self.variables_overwrite[self.scope][
if (
isinstance(keyword.value, ast.Name)
and keyword.value.id in self.variables_overwrite
):
keyword.value = self.variables_overwrite[
keyword.value.id
] # type:ignore[assignment]
res, expl = self.visit(keyword.value)
@@ -1118,14 +1094,12 @@ class AssertionRewriter(ast.NodeVisitor):
def visit_Compare(self, comp: ast.Compare) -> Tuple[ast.expr, str]:
self.push_format_context()
# We first check if we have overwritten a variable in the previous assert
if isinstance(
comp.left, ast.Name
) and comp.left.id in self.variables_overwrite.get(self.scope, {}):
comp.left = self.variables_overwrite[self.scope][
if isinstance(comp.left, ast.Name) and comp.left.id in self.variables_overwrite:
comp.left = self.variables_overwrite[
comp.left.id
] # type:ignore[assignment]
if isinstance(comp.left, namedExpr):
self.variables_overwrite[self.scope][
self.variables_overwrite[
comp.left.target.id
] = comp.left # type:ignore[assignment]
left_res, left_expl = self.visit(comp.left)
@@ -1145,7 +1119,7 @@ class AssertionRewriter(ast.NodeVisitor):
and next_operand.target.id == left_res.id
):
next_operand.target.id = self.variable()
self.variables_overwrite[self.scope][
self.variables_overwrite[
left_res.id
] = next_operand # type:ignore[assignment]
next_res, next_expl = self.visit(next_operand)
+1 -5
View File
@@ -505,11 +505,7 @@ def pytest_addoption(parser: Parser) -> None:
dest="last_failed_no_failures",
choices=("all", "none"),
default="all",
help="With ``--lf``, determines whether to execute tests when there "
"are no previously (known) failures or when no "
"cached ``lastfailed`` data was found. "
"``all`` (the default) runs the full test suite again. "
"``none`` just emits a message about no known failures and exits successfully.",
help="Which tests to run with no previously (known) failures",
)
+8 -17
View File
@@ -380,24 +380,15 @@ else:
def get_user_id() -> int | None:
"""Return the current process's real user id or None if it could not be
determined.
:return: The user id or None if it could not be determined.
"""
# mypy follows the version and platform checking expectation of PEP 484:
# https://mypy.readthedocs.io/en/stable/common_issues.html?highlight=platform#python-version-and-system-platform-checks
# Containment checks are too complex for mypy v1.5.0 and cause failure.
if sys.platform == "win32" or sys.platform == "emscripten":
# win32 does not have a getuid() function.
# Emscripten has a return 0 stub.
"""Return the current user id, or None if we cannot get it reliably on the current platform."""
# win32 does not have a getuid() function.
# On Emscripten, getuid() is a stub that always returns 0.
if sys.platform in ("win32", "emscripten"):
return None
else:
# On other platforms, a return value of -1 is assumed to indicate that
# the current process's real user id could not be determined.
ERROR = -1
uid = os.getuid()
return uid if uid != ERROR else None
# getuid shouldn't fail, but cpython defines such a case.
# Let's hope for the best.
uid = os.getuid()
return uid if uid != -1 else None
# Perform exhaustiveness checking.
+54 -93
View File
@@ -57,7 +57,6 @@ from _pytest.pathlib import bestrelpath
from _pytest.pathlib import import_path
from _pytest.pathlib import ImportMode
from _pytest.pathlib import resolve_package_path
from _pytest.pathlib import safe_exists
from _pytest.stash import Stash
from _pytest.warning_types import PytestConfigWarning
from _pytest.warning_types import warn_explicit_for
@@ -138,9 +137,7 @@ def main(
) -> Union[int, ExitCode]:
"""Perform an in-process test run.
:param args:
List of command line arguments. If `None` or not given, defaults to reading
arguments directly from the process command line (:data:`sys.argv`).
:param args: List of command line arguments.
:param plugins: List of plugin objects to be auto-registered during initialization.
:returns: An exit code.
@@ -445,10 +442,10 @@ class PytestPluginManager(PluginManager):
# so we avoid accessing possibly non-readable attributes
# (see issue #1073).
if not name.startswith("pytest_"):
return None
return
# Ignore names which can not be hooks.
if name == "pytest_plugins":
return None
return
opts = super().parse_hookimpl_opts(plugin, name)
if opts is not None:
@@ -457,9 +454,9 @@ class PytestPluginManager(PluginManager):
method = getattr(plugin, name)
# Consider only actual functions for hooks (#3775).
if not inspect.isroutine(method):
return None
return
# Collect unmarked hooks as long as they have the `pytest_' prefix.
return _get_legacy_hook_marks( # type: ignore[return-value]
return _get_legacy_hook_marks(
method, "impl", ("tryfirst", "trylast", "optionalhook", "hookwrapper")
)
@@ -468,7 +465,7 @@ class PytestPluginManager(PluginManager):
if opts is None:
method = getattr(module_or_class, name)
if name.startswith("pytest_"):
opts = _get_legacy_hook_marks( # type: ignore[assignment]
opts = _get_legacy_hook_marks(
method,
"spec",
("firstresult", "historic"),
@@ -530,12 +527,9 @@ class PytestPluginManager(PluginManager):
#
def _set_initial_conftests(
self,
args: Sequence[Union[str, Path]],
pyargs: bool,
noconftest: bool,
namespace: argparse.Namespace,
rootpath: Path,
confcutdir: Optional[Path],
importmode: Union[ImportMode, str],
testpaths_ini: Sequence[str],
) -> None:
"""Load initial conftest files given a preparsed "namespace".
@@ -545,12 +539,17 @@ class PytestPluginManager(PluginManager):
common options will not confuse our logic here.
"""
current = Path.cwd()
self._confcutdir = absolutepath(current / confcutdir) if confcutdir else None
self._noconftest = noconftest
self._using_pyargs = pyargs
self._confcutdir = (
absolutepath(current / namespace.confcutdir)
if namespace.confcutdir
else None
)
self._noconftest = namespace.noconftest
self._using_pyargs = namespace.pyargs
testpaths = namespace.file_or_dir + testpaths_ini
foundanchor = False
for intitial_path in args:
path = str(intitial_path)
for testpath in testpaths:
path = str(testpath)
# remove node-id syntax
i = path.find("::")
if i != -1:
@@ -558,12 +557,16 @@ class PytestPluginManager(PluginManager):
anchor = absolutepath(current / path)
# Ensure we do not break if what appears to be an anchor
# is in fact a very long option (#10169, #11394).
if safe_exists(anchor):
self._try_load_conftest(anchor, importmode, rootpath)
# is in fact a very long option (#10169).
try:
anchor_exists = anchor.exists()
except OSError: # pragma: no cover
anchor_exists = False
if anchor_exists:
self._try_load_conftest(anchor, namespace.importmode, rootpath)
foundanchor = True
if not foundanchor:
self._try_load_conftest(current, importmode, rootpath)
self._try_load_conftest(current, namespace.importmode, rootpath)
def _is_in_confcutdir(self, path: Path) -> bool:
"""Whether a path is within the confcutdir.
@@ -1062,10 +1065,9 @@ class Config:
fin()
def get_terminal_writer(self) -> TerminalWriter:
terminalreporter: Optional[TerminalReporter] = self.pluginmanager.get_plugin(
terminalreporter: TerminalReporter = self.pluginmanager.get_plugin(
"terminalreporter"
)
assert terminalreporter is not None
return terminalreporter._tw
def pytest_cmdline_parse(
@@ -1138,25 +1140,10 @@ class Config:
@hookimpl(trylast=True)
def pytest_load_initial_conftests(self, early_config: "Config") -> None:
# We haven't fully parsed the command line arguments yet, so
# early_config.args it not set yet. But we need it for
# discovering the initial conftests. So "pre-run" the logic here.
# It will be done for real in `parse()`.
args, args_source = early_config._decide_args(
args=early_config.known_args_namespace.file_or_dir,
pyargs=early_config.known_args_namespace.pyargs,
testpaths=early_config.getini("testpaths"),
invocation_dir=early_config.invocation_params.dir,
rootpath=early_config.rootpath,
warn=False,
)
self.pluginmanager._set_initial_conftests(
args=args,
pyargs=early_config.known_args_namespace.pyargs,
noconftest=early_config.known_args_namespace.noconftest,
early_config.known_args_namespace,
rootpath=early_config.rootpath,
confcutdir=early_config.known_args_namespace.confcutdir,
importmode=early_config.known_args_namespace.importmode,
testpaths_ini=self.getini("testpaths"),
)
def _initini(self, args: Sequence[str]) -> None:
@@ -1236,49 +1223,6 @@ class Config:
return args
def _decide_args(
self,
*,
args: List[str],
pyargs: List[str],
testpaths: List[str],
invocation_dir: Path,
rootpath: Path,
warn: bool,
) -> Tuple[List[str], ArgsSource]:
"""Decide the args (initial paths/nodeids) to use given the relevant inputs.
:param warn: Whether can issue warnings.
"""
if args:
source = Config.ArgsSource.ARGS
result = args
else:
if invocation_dir == rootpath:
source = Config.ArgsSource.TESTPATHS
if pyargs:
result = testpaths
else:
result = []
for path in testpaths:
result.extend(sorted(glob.iglob(path, recursive=True)))
if testpaths and not result:
if warn:
warning_text = (
"No files were found in testpaths; "
"consider removing or adjusting your testpaths configuration. "
"Searching recursively from the current directory instead."
)
self.issue_config_time_warning(
PytestConfigWarning(warning_text), stacklevel=3
)
else:
result = []
if not result:
source = Config.ArgsSource.INCOVATION_DIR
result = [str(invocation_dir)]
return result, source
def _preparse(self, args: List[str], addopts: bool = True) -> None:
if addopts:
env_addopts = os.environ.get("PYTEST_ADDOPTS", "")
@@ -1427,17 +1371,34 @@ class Config:
self.hook.pytest_cmdline_preparse(config=self, args=args)
self._parser.after_preparse = True # type: ignore
try:
source = Config.ArgsSource.ARGS
args = self._parser.parse_setoption(
args, self.option, namespace=self.option
)
self.args, self.args_source = self._decide_args(
args=args,
pyargs=self.known_args_namespace.pyargs,
testpaths=self.getini("testpaths"),
invocation_dir=self.invocation_params.dir,
rootpath=self.rootpath,
warn=True,
)
if not args:
if self.invocation_params.dir == self.rootpath:
source = Config.ArgsSource.TESTPATHS
testpaths: List[str] = self.getini("testpaths")
if self.known_args_namespace.pyargs:
args = testpaths
else:
args = []
for path in testpaths:
args.extend(sorted(glob.iglob(path, recursive=True)))
if testpaths and not args:
warning_text = (
"No files were found in testpaths; "
"consider removing or adjusting your testpaths configuration. "
"Searching recursively from the current directory instead."
)
self.issue_config_time_warning(
PytestConfigWarning(warning_text), stacklevel=3
)
if not args:
source = Config.ArgsSource.INCOVATION_DIR
args = [str(self.invocation_params.dir)]
self.args = args
self.args_source = source
except PrintHelp:
pass
+8 -1
View File
@@ -16,7 +16,6 @@ from .exceptions import UsageError
from _pytest.outcomes import fail
from _pytest.pathlib import absolutepath
from _pytest.pathlib import commonpath
from _pytest.pathlib import safe_exists
if TYPE_CHECKING:
from . import Config
@@ -152,6 +151,14 @@ def get_dirs_from_args(args: Iterable[str]) -> List[Path]:
return path
return path.parent
def safe_exists(path: Path) -> bool:
# This can throw on paths that contain characters unrepresentable at the OS level,
# or with invalid syntax on Windows (https://bugs.python.org/issue35306)
try:
return path.exists()
except OSError:
return False
# These look like paths but may not exist
possible_paths = (
absolutepath(get_file_part_from_node_id(arg))
-20
View File
@@ -1,6 +1,5 @@
"""Discover and run doctests in modules and test files."""
import bdb
import functools
import inspect
import os
import platform
@@ -537,25 +536,6 @@ class DoctestModule(Module):
tests, obj, name, module, source_lines, globs, seen
)
if sys.version_info < (3, 13):
def _from_module(self, module, object):
"""`cached_property` objects are never considered a part
of the 'current module'. As such they are skipped by doctest.
Here we override `_from_module` to check the underlying
function instead. https://github.com/python/cpython/issues/107995
"""
if hasattr(functools, "cached_property") and isinstance(
object, functools.cached_property
):
object = object.func
# Type ignored because this is a private function.
return super()._from_module(module, object) # type: ignore[misc]
else: # pragma: no cover
pass
if self.path.name == "conftest.py":
module = self.config.pluginmanager._importconftest(
self.path,
+2 -1
View File
@@ -1,3 +1,4 @@
import io
import os
import sys
from typing import Generator
@@ -50,7 +51,7 @@ def get_stderr_fileno() -> int:
if fileno == -1:
raise AttributeError()
return fileno
except (AttributeError, ValueError):
except (AttributeError, io.UnsupportedOperation):
# pytest-xdist monkeypatches sys.stderr with an object that is not an actual file.
# https://docs.python.org/3/library/faulthandler.html#issue-with-file-descriptors
# This is potentially dangerous, but the best we can do.
+2 -6
View File
@@ -11,7 +11,6 @@ from _pytest.config import Config
from _pytest.config import ExitCode
from _pytest.config import PrintHelp
from _pytest.config.argparsing import Parser
from _pytest.terminal import TerminalReporter
class HelpAction(Action):
@@ -106,7 +105,7 @@ def pytest_cmdline_parse():
if config.option.debug:
# --debug | --debug <file.log> was provided.
path = config.option.debug
debugfile = open(path, "w", encoding="utf-8")
debugfile = open(path, "w")
debugfile.write(
"versions pytest-%s, "
"python-%s\ncwd=%s\nargs=%s\n\n"
@@ -160,10 +159,7 @@ def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]:
def showhelp(config: Config) -> None:
import textwrap
reporter: Optional[TerminalReporter] = config.pluginmanager.get_plugin(
"terminalreporter"
)
assert reporter is not None
reporter = config.pluginmanager.get_plugin("terminalreporter")
tw = reporter._tw
tw.write(config._parser.optparser.format_help())
tw.line()
+3 -4
View File
@@ -502,10 +502,6 @@ class LogXML:
# Local hack to handle xdist report order.
workernode = getattr(report, "node", None)
reporter = self.node_reporters.pop((nodeid, workernode))
for propname, propvalue in report.user_properties:
reporter.add_property(propname, str(propvalue))
if reporter is not None:
reporter.finalize()
@@ -603,6 +599,9 @@ class LogXML:
reporter = self._opentestcase(report)
reporter.write_captured_output(report)
for propname, propvalue in report.user_properties:
reporter.add_property(propname, str(propvalue))
self.finalize(report)
report_wid = getattr(report, "worker_id", None)
report_ii = getattr(report, "item_index", None)
+1 -5
View File
@@ -515,9 +515,7 @@ class LogCaptureFixture:
return original_disable_level
def set_level(self, level: Union[int, str], logger: Optional[str] = None) -> None:
"""Set the threshold level of a logger for the duration of a test.
Logging messages which are less severe than this level will not be captured.
"""Set the level of a logger for the duration of a test.
.. versionchanged:: 3.4
The levels of the loggers changed by this function will be
@@ -660,8 +658,6 @@ class LoggingPlugin:
)
if self._log_cli_enabled():
terminal_reporter = config.pluginmanager.get_plugin("terminalreporter")
# Guaranteed by `_log_cli_enabled()`.
assert terminal_reporter is not None
capture_manager = config.pluginmanager.get_plugin("capturemanager")
# if capturemanager plugin is disabled, live logging still works.
self.log_cli_handler: Union[
+1 -7
View File
@@ -36,7 +36,6 @@ from _pytest.outcomes import exit
from _pytest.pathlib import absolutepath
from _pytest.pathlib import bestrelpath
from _pytest.pathlib import fnmatch_ex
from _pytest.pathlib import safe_exists
from _pytest.pathlib import visit
from _pytest.reports import CollectReport
from _pytest.reports import TestReport
@@ -463,11 +462,6 @@ class _bestrelpath_cache(Dict[Path, str]):
@final
class Session(nodes.FSCollector):
"""The root of the collection tree.
``Session`` collects the initial paths given as arguments to pytest.
"""
Interrupted = Interrupted
Failed = Failed
# Set on the session by runner.pytest_sessionstart.
@@ -896,7 +890,7 @@ def resolve_collection_argument(
strpath = search_pypath(strpath)
fspath = invocation_path / strpath
fspath = absolutepath(fspath)
if not safe_exists(fspath):
if not fspath.exists():
msg = (
"module or package not found: {arg} (missing __init__.py?)"
if as_pypath
+1 -3
View File
@@ -373,9 +373,7 @@ def get_unpacked_marks(
if not consider_mro:
mark_lists = [obj.__dict__.get("pytestmark", [])]
else:
mark_lists = [
x.__dict__.get("pytestmark", []) for x in reversed(obj.__mro__)
]
mark_lists = [x.__dict__.get("pytestmark", []) for x in obj.__mro__]
mark_list = []
for item in mark_lists:
if isinstance(item, list):
+8 -13
View File
@@ -157,11 +157,10 @@ class NodeMeta(type):
class Node(metaclass=NodeMeta):
r"""Base class of :class:`Collector` and :class:`Item`, the components of
the test collection tree.
"""Base class for Collector and Item, the components of the test
collection tree.
``Collector``\'s are the internal nodes of the tree, and ``Item``\'s are the
leaf nodes.
Collector subclasses have children; Items are leaf nodes.
"""
# Implemented in the legacypath plugin.
@@ -526,17 +525,15 @@ def get_fslocation_from_item(node: "Node") -> Tuple[Union[str, Path], Optional[i
class Collector(Node):
"""Base class of all collectors.
Collector create children through `collect()` and thus iteratively build
the collection tree.
"""
"""Collector instances create children through collect() and thus
iteratively build a tree."""
class CollectError(Exception):
"""An error during collection, contains a custom message."""
def collect(self) -> Iterable[Union["Item", "Collector"]]:
"""Collect children (items and collectors) for this collector."""
"""Return a list of children (items and collectors) for this
collection node."""
raise NotImplementedError("abstract")
# TODO: This omits the style= parameter which breaks Liskov Substitution.
@@ -580,8 +577,6 @@ def _check_initialpaths_for_relpath(session: "Session", path: Path) -> Optional[
class FSCollector(Collector):
"""Base class for filesystem collectors."""
def __init__(
self,
fspath: Optional[LEGACY_PATH] = None,
@@ -665,7 +660,7 @@ class File(FSCollector):
class Item(Node):
"""Base class of all test invocation items.
"""A basic test invocation item.
Note that for a single function there might be multiple test invocation items.
"""
+1 -30
View File
@@ -523,8 +523,6 @@ def import_path(
if mode is ImportMode.importlib:
module_name = module_name_from_path(path, root)
with contextlib.suppress(KeyError):
return sys.modules[module_name]
for meta_importer in sys.meta_path:
spec = meta_importer.find_spec(module_name, [str(path.parent)])
@@ -623,11 +621,6 @@ def module_name_from_path(path: Path, root: Path) -> str:
# Use the parts for the relative path to the root path.
path_parts = relative_path.parts
# Module name for packages do not contain the __init__ file, unless
# the `__init__.py` file is at the root.
if len(path_parts) >= 2 and path_parts[-1] == "__init__":
path_parts = path_parts[:-1]
return ".".join(path_parts)
@@ -640,9 +633,6 @@ def insert_missing_modules(modules: Dict[str, ModuleType], module_name: str) ->
otherwise "src.tests.test_foo" is not importable by ``__import__``.
"""
module_parts = module_name.split(".")
child_module: Union[ModuleType, None] = None
module: Union[ModuleType, None] = None
child_name: str = ""
while module_name:
if module_name not in modules:
try:
@@ -652,22 +642,13 @@ def insert_missing_modules(modules: Dict[str, ModuleType], module_name: str) ->
# ourselves to fall back to creating a dummy module.
if not sys.meta_path:
raise ModuleNotFoundError
module = importlib.import_module(module_name)
importlib.import_module(module_name)
except ModuleNotFoundError:
module = ModuleType(
module_name,
doc="Empty module created by pytest's importmode=importlib.",
)
else:
module = modules[module_name]
if child_module:
# Add child attribute to the parent that can reference the child
# modules.
if not hasattr(module, child_name):
setattr(module, child_name, child_module)
modules[module_name] = module
# Keep track of the child module while moving up the tree.
child_module, child_name = module, module_name.rpartition(".")[-1]
module_parts.pop(-1)
module_name = ".".join(module_parts)
@@ -792,13 +773,3 @@ def copytree(source: Path, target: Path) -> None:
shutil.copyfile(x, newx)
elif x.is_dir():
newx.mkdir(exist_ok=True)
def safe_exists(p: Path) -> bool:
"""Like Path.exists(), but account for input arguments that might be too long (#11394)."""
try:
return p.exists()
except (ValueError, OSError):
# ValueError: stat: path too long for Windows
# OSError: [WinError 123] The filename, directory name, or volume label syntax is incorrect
return False
+1 -3
View File
@@ -6,7 +6,6 @@ import collections.abc
import contextlib
import gc
import importlib
import locale
import os
import platform
import re
@@ -130,7 +129,6 @@ class LsofFdLeakChecker:
stderr=subprocess.DEVNULL,
check=True,
text=True,
encoding=locale.getpreferredencoding(False),
).stdout
def isopen(line: str) -> bool:
@@ -752,7 +750,7 @@ class Pytester:
def make_hook_recorder(self, pluginmanager: PytestPluginManager) -> HookRecorder:
"""Create a new :class:`HookRecorder` for a :class:`PytestPluginManager`."""
pluginmanager.reprec = reprec = HookRecorder(pluginmanager, _ispytest=True) # type: ignore[attr-defined]
pluginmanager.reprec = reprec = HookRecorder(pluginmanager, _ispytest=True)
self._request.addfinalizer(reprec.finish_recording)
return reprec
+10 -12
View File
@@ -522,7 +522,7 @@ class PyCollector(PyobjMixin, nodes.Collector):
class Module(nodes.File, PyCollector):
"""Collector for test classes and functions in a Python module."""
"""Collector for test classes and functions."""
def _getobj(self):
return self._importtestmodule()
@@ -659,9 +659,6 @@ class Module(nodes.File, PyCollector):
class Package(Module):
"""Collector for files and directories in a Python packages -- directories
with an `__init__.py` file."""
def __init__(
self,
fspath: Optional[LEGACY_PATH],
@@ -791,7 +788,7 @@ def _get_first_non_fixture_func(obj: object, names: Iterable[str]) -> Optional[o
class Class(PyCollector):
"""Collector for test methods (and nested classes) in a Python class."""
"""Collector for test methods."""
@classmethod
def from_parent(cls, parent, *, name, obj=None, **kw):
@@ -1152,7 +1149,7 @@ class CallSpec2:
arg2scope = self._arg2scope.copy()
for arg, val in zip(argnames, valset):
if arg in params or arg in funcargs:
raise ValueError(f"duplicate parametrization of {arg!r}")
raise ValueError(f"duplicate {arg!r}")
valtype_for_arg = valtypes[arg]
if valtype_for_arg == "params":
params[arg] = val
@@ -1243,9 +1240,8 @@ class Metafunc:
during the collection phase. If you need to setup expensive resources
see about setting indirect to do it rather than at test setup time.
Can be called multiple times per test function (but only on different
argument names), in which case each call parametrizes all previous
parametrizations, e.g.
Can be called multiple times, in which case each call parametrizes all
previous parametrizations, e.g.
::
@@ -1677,7 +1673,7 @@ def write_docstring(tw: TerminalWriter, doc: str, indent: str = " ") -> None:
class Function(PyobjMixin, nodes.Item):
"""Item responsible for setting up and executing a Python test function.
"""An Item responsible for setting up and executing a Python test function.
:param name:
The full function name, including any decorations like those
@@ -1834,8 +1830,10 @@ class Function(PyobjMixin, nodes.Item):
class FunctionDefinition(Function):
"""This class is a stop gap solution until we evolve to have actual function
definition nodes and manage to get rid of ``metafunc``."""
"""
This class is a step gap solution until we evolve to have actual function definition nodes
and manage to get rid of ``metafunc``.
"""
def runtest(self) -> None:
raise RuntimeError("function definitions are not supposed to be run as tests")
+12 -13
View File
@@ -266,20 +266,19 @@ class ApproxMapping(ApproxBase):
approx_side_as_map.items(), other_side.values()
):
if approx_value != other_value:
if approx_value.expected is not None and other_value is not None:
max_abs_diff = max(
max_abs_diff, abs(approx_value.expected - other_value)
max_abs_diff = max(
max_abs_diff, abs(approx_value.expected - other_value)
)
if approx_value.expected == 0.0:
max_rel_diff = math.inf
else:
max_rel_diff = max(
max_rel_diff,
abs(
(approx_value.expected - other_value)
/ approx_value.expected
),
)
if approx_value.expected == 0.0:
max_rel_diff = math.inf
else:
max_rel_diff = max(
max_rel_diff,
abs(
(approx_value.expected - other_value)
/ approx_value.expected
),
)
different_ids.append(approx_key)
message_data = [
+39 -59
View File
@@ -1,9 +1,7 @@
import contextlib
import multiprocessing
import os
import sys
import time
import warnings
from unittest import mock
import pytest
@@ -11,14 +9,6 @@ from py import error
from py.path import local
@contextlib.contextmanager
def ignore_encoding_warning():
with warnings.catch_warnings():
with contextlib.suppress(NameError): # new in 3.10
warnings.simplefilter("ignore", EncodingWarning)
yield
class CommonFSTests:
def test_constructor_equality(self, path1):
p = path1.__class__(path1)
@@ -233,8 +223,7 @@ class CommonFSTests:
assert not (path1 < path1)
def test_simple_read(self, path1):
with ignore_encoding_warning():
x = path1.join("samplefile").read("r")
x = path1.join("samplefile").read("r")
assert x == "samplefile\n"
def test_join_div_operator(self, path1):
@@ -276,14 +265,12 @@ class CommonFSTests:
def test_readlines(self, path1):
fn = path1.join("samplefile")
with ignore_encoding_warning():
contents = fn.readlines()
contents = fn.readlines()
assert contents == ["samplefile\n"]
def test_readlines_nocr(self, path1):
fn = path1.join("samplefile")
with ignore_encoding_warning():
contents = fn.readlines(cr=0)
contents = fn.readlines(cr=0)
assert contents == ["samplefile", ""]
def test_file(self, path1):
@@ -375,8 +362,8 @@ class CommonFSTests:
initpy.copy(copied)
try:
assert copied.check()
s1 = initpy.read_text(encoding="utf-8")
s2 = copied.read_text(encoding="utf-8")
s1 = initpy.read()
s2 = copied.read()
assert s1 == s2
finally:
if copied.check():
@@ -389,8 +376,8 @@ class CommonFSTests:
otherdir.copy(copied)
assert copied.check(dir=1)
assert copied.join("__init__.py").check(file=1)
s1 = otherdir.join("__init__.py").read_text(encoding="utf-8")
s2 = copied.join("__init__.py").read_text(encoding="utf-8")
s1 = otherdir.join("__init__.py").read()
s2 = copied.join("__init__.py").read()
assert s1 == s2
finally:
if copied.check(dir=1):
@@ -476,13 +463,13 @@ def setuptestfs(path):
return
# print "setting up test fs for", repr(path)
samplefile = path.ensure("samplefile")
samplefile.write_text("samplefile\n", encoding="utf-8")
samplefile.write("samplefile\n")
execfile = path.ensure("execfile")
execfile.write_text("x=42", encoding="utf-8")
execfile.write("x=42")
execfilepy = path.ensure("execfile.py")
execfilepy.write_text("x=42", encoding="utf-8")
execfilepy.write("x=42")
d = {1: 2, "hello": "world", "answer": 42}
path.ensure("samplepickle").dump(d)
@@ -494,24 +481,22 @@ def setuptestfs(path):
otherdir.ensure("__init__.py")
module_a = otherdir.ensure("a.py")
module_a.write_text("from .b import stuff as result\n", encoding="utf-8")
module_a.write("from .b import stuff as result\n")
module_b = otherdir.ensure("b.py")
module_b.write_text('stuff="got it"\n', encoding="utf-8")
module_b.write('stuff="got it"\n')
module_c = otherdir.ensure("c.py")
module_c.write_text(
module_c.write(
"""import py;
import otherdir.a
value = otherdir.a.result
""",
encoding="utf-8",
"""
)
module_d = otherdir.ensure("d.py")
module_d.write_text(
module_d.write(
"""import py;
from otherdir import a
value2 = a.result
""",
encoding="utf-8",
"""
)
@@ -549,11 +534,9 @@ def batch_make_numbered_dirs(rootdir, repeats):
for i in range(repeats):
dir_ = local.make_numbered_dir(prefix="repro-", rootdir=rootdir)
file_ = dir_.join("foo")
file_.write_text("%s" % i, encoding="utf-8")
actual = int(file_.read_text(encoding="utf-8"))
assert (
actual == i
), f"int(file_.read_text(encoding='utf-8')) is {actual} instead of {i}"
file_.write("%s" % i)
actual = int(file_.read())
assert actual == i, f"int(file_.read()) is {actual} instead of {i}"
dir_.join(".lock").remove(ignore_errors=True)
return True
@@ -709,14 +692,14 @@ class TestLocalPath(CommonFSTests):
def test_open_and_ensure(self, path1):
p = path1.join("sub1", "sub2", "file")
with p.open("w", ensure=1, encoding="utf-8") as f:
with p.open("w", ensure=1) as f:
f.write("hello")
assert p.read_text(encoding="utf-8") == "hello"
assert p.read() == "hello"
def test_write_and_ensure(self, path1):
p = path1.join("sub1", "sub2", "file")
p.write_text("hello", ensure=1, encoding="utf-8")
assert p.read_text(encoding="utf-8") == "hello"
p.write("hello", ensure=1)
assert p.read() == "hello"
@pytest.mark.parametrize("bin", (False, True))
def test_dump(self, tmpdir, bin):
@@ -787,9 +770,9 @@ class TestLocalPath(CommonFSTests):
newfile = tmpdir.join("test1", "test")
newfile.ensure()
assert newfile.check(file=1)
newfile.write_text("42", encoding="utf-8")
newfile.write("42")
newfile.ensure()
s = newfile.read_text(encoding="utf-8")
s = newfile.read()
assert s == "42"
def test_ensure_filepath_withoutdir(self, tmpdir):
@@ -823,9 +806,9 @@ class TestLocalPath(CommonFSTests):
newfilename = "/test" * 60 # type:ignore[unreachable]
l1 = tmpdir.join(newfilename)
l1.ensure(file=True)
l1.write_text("foo", encoding="utf-8")
l1.write("foo")
l2 = tmpdir.join(newfilename)
assert l2.read_text(encoding="utf-8") == "foo"
assert l2.read() == "foo"
def test_visit_depth_first(self, tmpdir):
tmpdir.ensure("a", "1")
@@ -1295,14 +1278,14 @@ class TestPOSIXLocalPath:
def test_hardlink(self, tmpdir):
linkpath = tmpdir.join("test")
filepath = tmpdir.join("file")
filepath.write_text("Hello", encoding="utf-8")
filepath.write("Hello")
nlink = filepath.stat().nlink
linkpath.mklinkto(filepath)
assert filepath.stat().nlink == nlink + 1
def test_symlink_are_identical(self, tmpdir):
filepath = tmpdir.join("file")
filepath.write_text("Hello", encoding="utf-8")
filepath.write("Hello")
linkpath = tmpdir.join("test")
linkpath.mksymlinkto(filepath)
assert linkpath.readlink() == str(filepath)
@@ -1310,7 +1293,7 @@ class TestPOSIXLocalPath:
def test_symlink_isfile(self, tmpdir):
linkpath = tmpdir.join("test")
filepath = tmpdir.join("file")
filepath.write_text("", encoding="utf-8")
filepath.write("")
linkpath.mksymlinkto(filepath)
assert linkpath.check(file=1)
assert not linkpath.check(link=0, file=1)
@@ -1319,12 +1302,10 @@ class TestPOSIXLocalPath:
def test_symlink_relative(self, tmpdir):
linkpath = tmpdir.join("test")
filepath = tmpdir.join("file")
filepath.write_text("Hello", encoding="utf-8")
filepath.write("Hello")
linkpath.mksymlinkto(filepath, absolute=False)
assert linkpath.readlink() == "file"
assert filepath.read_text(encoding="utf-8") == linkpath.read_text(
encoding="utf-8"
)
assert filepath.read() == linkpath.read()
def test_symlink_not_existing(self, tmpdir):
linkpath = tmpdir.join("testnotexisting")
@@ -1357,7 +1338,7 @@ class TestPOSIXLocalPath:
def test_realpath_file(self, tmpdir):
linkpath = tmpdir.join("test")
filepath = tmpdir.join("file")
filepath.write_text("", encoding="utf-8")
filepath.write("")
linkpath.mksymlinkto(filepath)
realpath = linkpath.realpath()
assert realpath.basename == "file"
@@ -1402,7 +1383,7 @@ class TestPOSIXLocalPath:
atime1 = path.atime()
# we could wait here but timer resolution is very
# system dependent
path.read_binary()
path.read()
time.sleep(ATIME_RESOLUTION)
atime2 = path.atime()
time.sleep(ATIME_RESOLUTION)
@@ -1486,7 +1467,7 @@ class TestPOSIXLocalPath:
test_files = ["a", "b", "c"]
src = tmpdir.join("src")
for f in test_files:
src.join(f).write_text(f, ensure=True, encoding="utf-8")
src.join(f).write(f, ensure=True)
dst = tmpdir.join("dst")
# a small delay before the copy
time.sleep(ATIME_RESOLUTION)
@@ -1540,11 +1521,10 @@ class TestUnicodePy2Py3:
def test_read_write(self, tmpdir):
x = tmpdir.join("hello")
part = "hällo"
with ignore_encoding_warning():
x.write(part)
assert x.read() == part
x.write(part.encode(sys.getdefaultencoding()))
assert x.read() == part.encode(sys.getdefaultencoding())
x.write(part)
assert x.read() == part
x.write(part.encode(sys.getdefaultencoding()))
assert x.read() == part.encode(sys.getdefaultencoding())
class TestBinaryAndTextMethods:
+14 -54
View File
@@ -267,7 +267,7 @@ class TestGeneralUsage:
def test_issue109_sibling_conftests_not_loaded(self, pytester: Pytester) -> None:
sub1 = pytester.mkdir("sub1")
sub2 = pytester.mkdir("sub2")
sub1.joinpath("conftest.py").write_text("assert 0", encoding="utf-8")
sub1.joinpath("conftest.py").write_text("assert 0")
result = pytester.runpytest(sub2)
assert result.ret == ExitCode.NO_TESTS_COLLECTED
sub2.joinpath("__init__.py").touch()
@@ -467,7 +467,7 @@ class TestGeneralUsage:
assert "invalid" in str(excinfo.value)
p = pytester.path.joinpath("test_test_plugins_given_as_strings.py")
p.write_text("def test_foo(): pass", encoding="utf-8")
p.write_text("def test_foo(): pass")
mod = types.ModuleType("myplugin")
monkeypatch.setitem(sys.modules, "myplugin", mod)
assert pytest.main(args=[str(pytester.path)], plugins=["myplugin"]) == 0
@@ -587,7 +587,7 @@ class TestInvocationVariants:
def test_pyargs_importerror(self, pytester: Pytester, monkeypatch) -> None:
monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", False)
path = pytester.mkpydir("tpkg")
path.joinpath("test_hello.py").write_text("raise ImportError", encoding="utf-8")
path.joinpath("test_hello.py").write_text("raise ImportError")
result = pytester.runpytest("--pyargs", "tpkg.test_hello", syspathinsert=True)
assert result.ret != 0
@@ -597,10 +597,10 @@ class TestInvocationVariants:
def test_pyargs_only_imported_once(self, pytester: Pytester) -> None:
pkg = pytester.mkpydir("foo")
pkg.joinpath("test_foo.py").write_text(
"print('hello from test_foo')\ndef test(): pass", encoding="utf-8"
"print('hello from test_foo')\ndef test(): pass"
)
pkg.joinpath("conftest.py").write_text(
"def pytest_configure(config): print('configuring')", encoding="utf-8"
"def pytest_configure(config): print('configuring')"
)
result = pytester.runpytest(
@@ -613,7 +613,7 @@ class TestInvocationVariants:
def test_pyargs_filename_looks_like_module(self, pytester: Pytester) -> None:
pytester.path.joinpath("conftest.py").touch()
pytester.path.joinpath("t.py").write_text("def test(): pass", encoding="utf-8")
pytester.path.joinpath("t.py").write_text("def test(): pass")
result = pytester.runpytest("--pyargs", "t.py")
assert result.ret == ExitCode.OK
@@ -622,12 +622,8 @@ class TestInvocationVariants:
monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", False)
path = pytester.mkpydir("tpkg")
path.joinpath("test_hello.py").write_text(
"def test_hello(): pass", encoding="utf-8"
)
path.joinpath("test_world.py").write_text(
"def test_world(): pass", encoding="utf-8"
)
path.joinpath("test_hello.py").write_text("def test_hello(): pass")
path.joinpath("test_world.py").write_text("def test_world(): pass")
result = pytester.runpytest("--pyargs", "tpkg")
assert result.ret == 0
result.stdout.fnmatch_lines(["*2 passed*"])
@@ -666,15 +662,13 @@ class TestInvocationVariants:
ns = d.joinpath("ns_pkg")
ns.mkdir()
ns.joinpath("__init__.py").write_text(
"__import__('pkg_resources').declare_namespace(__name__)",
encoding="utf-8",
"__import__('pkg_resources').declare_namespace(__name__)"
)
lib = ns.joinpath(dirname)
lib.mkdir()
lib.joinpath("__init__.py").touch()
lib.joinpath(f"test_{dirname}.py").write_text(
f"def test_{dirname}(): pass\ndef test_other():pass",
encoding="utf-8",
f"def test_{dirname}(): pass\ndef test_other():pass"
)
# The structure of the test directory is now:
@@ -760,10 +754,10 @@ class TestInvocationVariants:
lib.mkdir()
lib.joinpath("__init__.py").touch()
lib.joinpath("test_bar.py").write_text(
"def test_bar(): pass\ndef test_other(a_fixture):pass", encoding="utf-8"
"def test_bar(): pass\ndef test_other(a_fixture):pass"
)
lib.joinpath("conftest.py").write_text(
"import pytest\n@pytest.fixture\ndef a_fixture():pass", encoding="utf-8"
"import pytest\n@pytest.fixture\ndef a_fixture():pass"
)
d_local = pytester.mkdir("symlink_root")
@@ -1282,7 +1276,8 @@ def test_tee_stdio_captures_and_live_prints(pytester: Pytester) -> None:
result.stderr.fnmatch_lines(["*@this is stderr@*"])
# now ensure the output is in the junitxml
fullXml = pytester.path.joinpath("output.xml").read_text(encoding="utf-8")
with open(pytester.path.joinpath("output.xml")) as f:
fullXml = f.read()
assert "@this is stdout@\n" in fullXml
assert "@this is stderr@\n" in fullXml
@@ -1317,38 +1312,3 @@ def test_function_return_non_none_warning(pytester: Pytester) -> None:
)
res = pytester.runpytest()
res.stdout.fnmatch_lines(["*Did you mean to use `assert` instead of `return`?*"])
def test_doctest_and_normal_imports_with_importlib(pytester: Pytester) -> None:
"""
Regression test for #10811: previously import_path with ImportMode.importlib would
not return a module if already in sys.modules, resulting in modules being imported
multiple times, which causes problems with modules that have import side effects.
"""
# Uses the exact reproducer form #10811, given it is very minimal
# and illustrates the problem well.
pytester.makepyfile(
**{
"pmxbot/commands.py": "from . import logging",
"pmxbot/logging.py": "",
"tests/__init__.py": "",
"tests/test_commands.py": """
import importlib
from pmxbot import logging
class TestCommands:
def test_boo(self):
assert importlib.import_module('pmxbot.logging') is logging
""",
}
)
pytester.makeini(
"""
[pytest]
addopts=
--doctest-modules
--import-mode importlib
"""
)
result = pytester.runpytest_subprocess()
result.stdout.fnmatch_lines("*1 passed*")
+3 -3
View File
@@ -374,7 +374,7 @@ def test_excinfo_no_sourcecode():
def test_excinfo_no_python_sourcecode(tmp_path: Path) -> None:
# XXX: simplified locally testable version
tmp_path.joinpath("test.txt").write_text("{{ h()}}:", encoding="utf-8")
tmp_path.joinpath("test.txt").write_text("{{ h()}}:")
jinja2 = pytest.importorskip("jinja2")
loader = jinja2.FileSystemLoader(str(tmp_path))
@@ -451,7 +451,7 @@ class TestFormattedExcinfo:
source = textwrap.dedent(source)
modpath = tmp_path.joinpath("mod.py")
tmp_path.joinpath("__init__.py").touch()
modpath.write_text(source, encoding="utf-8")
modpath.write_text(source)
importlib.invalidate_caches()
return import_path(modpath, root=tmp_path)
@@ -1023,7 +1023,7 @@ raise ValueError()
"""
)
excinfo = pytest.raises(ValueError, mod.f)
tmp_path.joinpath("mod.py").write_text("asdf", encoding="utf-8")
tmp_path.joinpath("mod.py").write_text("asdf")
excinfo.traceback = excinfo.traceback.filter(excinfo)
repr = excinfo.getrepr()
repr.toterminal(tw_mock)
+1 -1
View File
@@ -294,7 +294,7 @@ def test_source_of_class_at_eof_without_newline(_sys_snapshot, tmp_path: Path) -
"""
)
path = tmp_path.joinpath("a.py")
path.write_text(str(source), encoding="utf-8")
path.write_text(str(source))
mod: Any = import_path(path, root=tmp_path)
s2 = Source(mod.A)
assert str(source).strip() == str(s2).strip()
-9
View File
@@ -21,15 +21,6 @@ if sys.gettrace():
sys.settrace(orig_trace)
@pytest.fixture(autouse=True)
def set_column_width(monkeypatch: pytest.MonkeyPatch) -> None:
"""
Force terminal width to 80: some tests check the formatting of --help, which is sensible
to terminal width.
"""
monkeypatch.setenv("COLUMNS", "80")
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_collection_modifyitems(items):
"""Prefer faster tests.
+23 -33
View File
@@ -1,7 +1,5 @@
# mypy: disable-error-code="attr-defined"
# mypy: disallow-untyped-defs
import logging
from typing import Iterator
import pytest
from _pytest.logging import caplog_records_key
@@ -11,8 +9,8 @@ logger = logging.getLogger(__name__)
sublogger = logging.getLogger(__name__ + ".baz")
@pytest.fixture(autouse=True)
def cleanup_disabled_logging() -> Iterator[None]:
@pytest.fixture
def cleanup_disabled_logging():
"""Simple fixture that ensures that a test doesn't disable logging.
This is necessary because ``logging.disable()`` is global, so a test disabling logging
@@ -29,7 +27,7 @@ def test_fixture_help(pytester: Pytester) -> None:
result.stdout.fnmatch_lines(["*caplog*"])
def test_change_level(caplog: pytest.LogCaptureFixture) -> None:
def test_change_level(caplog):
caplog.set_level(logging.INFO)
logger.debug("handler DEBUG level")
logger.info("handler INFO level")
@@ -44,7 +42,7 @@ def test_change_level(caplog: pytest.LogCaptureFixture) -> None:
assert "CRITICAL" in caplog.text
def test_change_level_logging_disabled(caplog: pytest.LogCaptureFixture) -> None:
def test_change_level_logging_disabled(caplog, cleanup_disabled_logging):
logging.disable(logging.CRITICAL)
assert logging.root.manager.disable == logging.CRITICAL
caplog.set_level(logging.WARNING)
@@ -87,7 +85,9 @@ def test_change_level_undo(pytester: Pytester) -> None:
result.stdout.no_fnmatch_line("*log from test2*")
def test_change_disabled_level_undo(pytester: Pytester) -> None:
def test_change_disabled_level_undo(
pytester: Pytester, cleanup_disabled_logging
) -> None:
"""Ensure that '_force_enable_logging' in 'set_level' is undone after the end of the test.
Tests the logging output themselves (affected by disabled logging level).
@@ -144,7 +144,7 @@ def test_change_level_undos_handler_level(pytester: Pytester) -> None:
result.assert_outcomes(passed=3)
def test_with_statement(caplog: pytest.LogCaptureFixture) -> None:
def test_with_statement(caplog):
with caplog.at_level(logging.INFO):
logger.debug("handler DEBUG level")
logger.info("handler INFO level")
@@ -159,7 +159,7 @@ def test_with_statement(caplog: pytest.LogCaptureFixture) -> None:
assert "CRITICAL" in caplog.text
def test_with_statement_logging_disabled(caplog: pytest.LogCaptureFixture) -> None:
def test_with_statement_logging_disabled(caplog, cleanup_disabled_logging):
logging.disable(logging.CRITICAL)
assert logging.root.manager.disable == logging.CRITICAL
with caplog.at_level(logging.WARNING):
@@ -198,8 +198,8 @@ def test_with_statement_logging_disabled(caplog: pytest.LogCaptureFixture) -> No
],
)
def test_force_enable_logging_level_string(
caplog: pytest.LogCaptureFixture, level_str: str, expected_disable_level: int
) -> None:
caplog, cleanup_disabled_logging, level_str, expected_disable_level
):
"""Test _force_enable_logging using a level string.
``expected_disable_level`` is one level below ``level_str`` because the disabled log level
@@ -218,7 +218,7 @@ def test_force_enable_logging_level_string(
assert test_logger.manager.disable == expected_disable_level
def test_log_access(caplog: pytest.LogCaptureFixture) -> None:
def test_log_access(caplog):
caplog.set_level(logging.INFO)
logger.info("boo %s", "arg")
assert caplog.records[0].levelname == "INFO"
@@ -226,7 +226,7 @@ def test_log_access(caplog: pytest.LogCaptureFixture) -> None:
assert "boo arg" in caplog.text
def test_messages(caplog: pytest.LogCaptureFixture) -> None:
def test_messages(caplog):
caplog.set_level(logging.INFO)
logger.info("boo %s", "arg")
logger.info("bar %s\nbaz %s", "arg1", "arg2")
@@ -247,14 +247,14 @@ def test_messages(caplog: pytest.LogCaptureFixture) -> None:
assert "Exception" not in caplog.messages[-1]
def test_record_tuples(caplog: pytest.LogCaptureFixture) -> None:
def test_record_tuples(caplog):
caplog.set_level(logging.INFO)
logger.info("boo %s", "arg")
assert caplog.record_tuples == [(__name__, logging.INFO, "boo arg")]
def test_unicode(caplog: pytest.LogCaptureFixture) -> None:
def test_unicode(caplog):
caplog.set_level(logging.INFO)
logger.info("")
assert caplog.records[0].levelname == "INFO"
@@ -262,7 +262,7 @@ def test_unicode(caplog: pytest.LogCaptureFixture) -> None:
assert "" in caplog.text
def test_clear(caplog: pytest.LogCaptureFixture) -> None:
def test_clear(caplog):
caplog.set_level(logging.INFO)
logger.info("")
assert len(caplog.records)
@@ -273,9 +273,7 @@ def test_clear(caplog: pytest.LogCaptureFixture) -> None:
@pytest.fixture
def logging_during_setup_and_teardown(
caplog: pytest.LogCaptureFixture,
) -> Iterator[None]:
def logging_during_setup_and_teardown(caplog):
caplog.set_level("INFO")
logger.info("a_setup_log")
yield
@@ -283,9 +281,7 @@ def logging_during_setup_and_teardown(
assert [x.message for x in caplog.get_records("teardown")] == ["a_teardown_log"]
def test_caplog_captures_for_all_stages(
caplog: pytest.LogCaptureFixture, logging_during_setup_and_teardown: None
) -> None:
def test_caplog_captures_for_all_stages(caplog, logging_during_setup_and_teardown):
assert not caplog.records
assert not caplog.get_records("call")
logger.info("a_call_log")
@@ -294,31 +290,25 @@ def test_caplog_captures_for_all_stages(
assert [x.message for x in caplog.get_records("setup")] == ["a_setup_log"]
# This reaches into private API, don't use this type of thing in real tests!
caplog_records = caplog._item.stash[caplog_records_key]
assert set(caplog_records) == {"setup", "call"}
assert set(caplog._item.stash[caplog_records_key]) == {"setup", "call"}
def test_clear_for_call_stage(
caplog: pytest.LogCaptureFixture, logging_during_setup_and_teardown: None
) -> None:
def test_clear_for_call_stage(caplog, logging_during_setup_and_teardown):
logger.info("a_call_log")
assert [x.message for x in caplog.get_records("call")] == ["a_call_log"]
assert [x.message for x in caplog.get_records("setup")] == ["a_setup_log"]
caplog_records = caplog._item.stash[caplog_records_key]
assert set(caplog_records) == {"setup", "call"}
assert set(caplog._item.stash[caplog_records_key]) == {"setup", "call"}
caplog.clear()
assert caplog.get_records("call") == []
assert [x.message for x in caplog.get_records("setup")] == ["a_setup_log"]
caplog_records = caplog._item.stash[caplog_records_key]
assert set(caplog_records) == {"setup", "call"}
assert set(caplog._item.stash[caplog_records_key]) == {"setup", "call"}
logging.info("a_call_log_after_clear")
assert [x.message for x in caplog.get_records("call")] == ["a_call_log_after_clear"]
assert [x.message for x in caplog.get_records("setup")] == ["a_setup_log"]
caplog_records = caplog._item.stash[caplog_records_key]
assert set(caplog_records) == {"setup", "call"}
assert set(caplog._item.stash[caplog_records_key]) == {"setup", "call"}
def test_ini_controls_global_log_level(pytester: Pytester) -> None:
+9 -9
View File
@@ -81,7 +81,7 @@ def test_root_logger_affected(pytester: Pytester) -> None:
# not the info one, because the default level of the root logger is
# WARNING.
assert os.path.isfile(log_file)
with open(log_file, encoding="utf-8") as rfh:
with open(log_file) as rfh:
contents = rfh.read()
assert "info text going to logger" not in contents
assert "warning text going to logger" in contents
@@ -656,7 +656,7 @@ def test_log_file_cli(pytester: Pytester) -> None:
# make sure that we get a '0' exit code for the testsuite
assert result.ret == 0
assert os.path.isfile(log_file)
with open(log_file, encoding="utf-8") as rfh:
with open(log_file) as rfh:
contents = rfh.read()
assert "This log message will be shown" in contents
assert "This log message won't be shown" not in contents
@@ -687,7 +687,7 @@ def test_log_file_cli_level(pytester: Pytester) -> None:
# make sure that we get a '0' exit code for the testsuite
assert result.ret == 0
assert os.path.isfile(log_file)
with open(log_file, encoding="utf-8") as rfh:
with open(log_file) as rfh:
contents = rfh.read()
assert "This log message will be shown" in contents
assert "This log message won't be shown" not in contents
@@ -738,7 +738,7 @@ def test_log_file_ini(pytester: Pytester) -> None:
# make sure that we get a '0' exit code for the testsuite
assert result.ret == 0
assert os.path.isfile(log_file)
with open(log_file, encoding="utf-8") as rfh:
with open(log_file) as rfh:
contents = rfh.read()
assert "This log message will be shown" in contents
assert "This log message won't be shown" not in contents
@@ -777,7 +777,7 @@ def test_log_file_ini_level(pytester: Pytester) -> None:
# make sure that we get a '0' exit code for the testsuite
assert result.ret == 0
assert os.path.isfile(log_file)
with open(log_file, encoding="utf-8") as rfh:
with open(log_file) as rfh:
contents = rfh.read()
assert "This log message will be shown" in contents
assert "This log message won't be shown" not in contents
@@ -985,7 +985,7 @@ def test_log_in_hooks(pytester: Pytester) -> None:
)
result = pytester.runpytest()
result.stdout.fnmatch_lines(["*sessionstart*", "*runtestloop*", "*sessionfinish*"])
with open(log_file, encoding="utf-8") as rfh:
with open(log_file) as rfh:
contents = rfh.read()
assert "sessionstart" in contents
assert "runtestloop" in contents
@@ -1021,7 +1021,7 @@ def test_log_in_runtest_logreport(pytester: Pytester) -> None:
"""
)
pytester.runpytest()
with open(log_file, encoding="utf-8") as rfh:
with open(log_file) as rfh:
contents = rfh.read()
assert contents.count("logreport") == 3
@@ -1065,11 +1065,11 @@ def test_log_set_path(pytester: Pytester) -> None:
"""
)
pytester.runpytest()
with open(os.path.join(report_dir_base, "test_first"), encoding="utf-8") as rfh:
with open(os.path.join(report_dir_base, "test_first")) as rfh:
content = rfh.read()
assert "message from test 1" in content
with open(os.path.join(report_dir_base, "test_second"), encoding="utf-8") as rfh:
with open(os.path.join(report_dir_base, "test_second")) as rfh:
content = rfh.read()
assert "message from test 2" in content
+2 -2
View File
@@ -1,12 +1,12 @@
anyio[curio,trio]==3.7.0
django==4.2.2
django==4.2.1
pytest-asyncio==0.21.0
pytest-bdd==6.1.1
pytest-cov==4.1.0
pytest-django==4.5.2
pytest-flakes==4.0.5
pytest-html==3.2.0
pytest-mock==3.11.1
pytest-mock==3.10.0
pytest-rerunfailures==11.1.2
pytest-sugar==0.9.7
pytest-trio==0.7.0
-17
View File
@@ -122,23 +122,6 @@ class TestApprox:
],
)
assert_approx_raises_regex(
{"a": 1.0, "b": None, "c": None},
{
"a": None,
"b": 1000.0,
"c": None,
},
[
r" comparison failed. Mismatched elements: 2 / 3:",
r" Max absolute difference: -inf",
r" Max relative difference: -inf",
r" Index \| Obtained\s+\| Expected\s+",
rf" a \| {SOME_FLOAT} \| None",
rf" b \| None\s+\| {SOME_FLOAT} ± {SOME_FLOAT}",
],
)
assert_approx_raises_regex(
[1.0, 2.0, 3.0, 4.0],
[1.0, 3.0, 3.0, 5.0],
+16 -31
View File
@@ -60,8 +60,7 @@ class TestModule:
""".format(
str(root2)
)
),
encoding="utf-8",
)
)
with monkeypatch.context() as mp:
mp.chdir(root2)
@@ -833,8 +832,7 @@ class TestConftestCustomization:
mod = outcome.get_result()
mod.obj.hello = "world"
"""
),
encoding="utf-8",
)
)
b.joinpath("test_module.py").write_text(
textwrap.dedent(
@@ -842,8 +840,7 @@ class TestConftestCustomization:
def test_hello():
assert hello == "world"
"""
),
encoding="utf-8",
)
)
reprec = pytester.inline_run()
reprec.assertoutcome(passed=1)
@@ -864,8 +861,7 @@ class TestConftestCustomization:
for func in result:
func._some123 = "world"
"""
),
encoding="utf-8",
)
)
b.joinpath("test_module.py").write_text(
textwrap.dedent(
@@ -878,8 +874,7 @@ class TestConftestCustomization:
def test_hello(obj):
assert obj == "world"
"""
),
encoding="utf-8",
)
)
reprec = pytester.inline_run()
reprec.assertoutcome(passed=1)
@@ -979,8 +974,7 @@ def test_setup_only_available_in_subdir(pytester: Pytester) -> None:
def pytest_runtest_teardown(item):
assert item.path.stem == "test_in_sub1"
"""
),
encoding="utf-8",
)
)
sub2.joinpath("conftest.py").write_text(
textwrap.dedent(
@@ -993,11 +987,10 @@ def test_setup_only_available_in_subdir(pytester: Pytester) -> None:
def pytest_runtest_teardown(item):
assert item.path.stem == "test_in_sub2"
"""
),
encoding="utf-8",
)
)
sub1.joinpath("test_in_sub1.py").write_text("def test_1(): pass", encoding="utf-8")
sub2.joinpath("test_in_sub2.py").write_text("def test_2(): pass", encoding="utf-8")
sub1.joinpath("test_in_sub1.py").write_text("def test_1(): pass")
sub2.joinpath("test_in_sub2.py").write_text("def test_2(): pass")
result = pytester.runpytest("-v", "-s")
result.assert_outcomes(passed=2)
@@ -1385,8 +1378,7 @@ def test_skip_duplicates_by_default(pytester: Pytester) -> None:
def test_real():
pass
"""
),
encoding="utf-8",
)
)
result = pytester.runpytest(str(a), str(a))
result.stdout.fnmatch_lines(["*collected 1 item*"])
@@ -1406,8 +1398,7 @@ def test_keep_duplicates(pytester: Pytester) -> None:
def test_real():
pass
"""
),
encoding="utf-8",
)
)
result = pytester.runpytest("--keep-duplicates", str(a), str(a))
result.stdout.fnmatch_lines(["*collected 2 item*"])
@@ -1452,12 +1443,8 @@ def test_package_with_modules(pytester: Pytester) -> None:
sub2_test = sub2.joinpath("test")
sub2_test.mkdir(parents=True)
sub1_test.joinpath("test_in_sub1.py").write_text(
"def test_1(): pass", encoding="utf-8"
)
sub2_test.joinpath("test_in_sub2.py").write_text(
"def test_2(): pass", encoding="utf-8"
)
sub1_test.joinpath("test_in_sub1.py").write_text("def test_1(): pass")
sub2_test.joinpath("test_in_sub2.py").write_text("def test_2(): pass")
# Execute from .
result = pytester.runpytest("-v", "-s")
@@ -1501,11 +1488,9 @@ def test_package_ordering(pytester: Pytester) -> None:
sub2_test = sub2.joinpath("test")
sub2_test.mkdir(parents=True)
root.joinpath("Test_root.py").write_text("def test_1(): pass", encoding="utf-8")
sub1.joinpath("Test_sub1.py").write_text("def test_2(): pass", encoding="utf-8")
sub2_test.joinpath("test_sub2.py").write_text(
"def test_3(): pass", encoding="utf-8"
)
root.joinpath("Test_root.py").write_text("def test_1(): pass")
sub1.joinpath("Test_sub1.py").write_text("def test_2(): pass")
sub2_test.joinpath("test_sub2.py").write_text("def test_3(): pass")
# Execute from .
result = pytester.runpytest("-v", "-s")
+32 -62
View File
@@ -287,8 +287,7 @@ class TestFillFixtures:
def spam():
return 'spam'
"""
),
encoding="utf-8",
)
)
testfile = subdir.joinpath("test_spam.py")
testfile.write_text(
@@ -297,8 +296,7 @@ class TestFillFixtures:
def test_spam(spam):
assert spam == "spam"
"""
),
encoding="utf-8",
)
)
result = pytester.runpytest()
result.stdout.fnmatch_lines(["*1 passed*"])
@@ -361,8 +359,7 @@ class TestFillFixtures:
def spam(request):
return request.param
"""
),
encoding="utf-8",
)
)
testfile = subdir.joinpath("test_spam.py")
testfile.write_text(
@@ -374,8 +371,7 @@ class TestFillFixtures:
assert spam == params['spam']
params['spam'] += 1
"""
),
encoding="utf-8",
)
)
result = pytester.runpytest()
result.stdout.fnmatch_lines(["*3 passed*"])
@@ -407,8 +403,7 @@ class TestFillFixtures:
def spam(request):
return request.param
"""
),
encoding="utf-8",
)
)
testfile = subdir.joinpath("test_spam.py")
testfile.write_text(
@@ -420,8 +415,7 @@ class TestFillFixtures:
assert spam == params['spam']
params['spam'] += 1
"""
),
encoding="utf-8",
)
)
result = pytester.runpytest()
result.stdout.fnmatch_lines(["*3 passed*"])
@@ -1043,11 +1037,10 @@ class TestRequestBasic:
def arg1():
pass
"""
),
encoding="utf-8",
)
)
p = b.joinpath("test_module.py")
p.write_text("def test_func(arg1): pass", encoding="utf-8")
p.write_text("def test_func(arg1): pass")
result = pytester.runpytest(p, "--fixtures")
assert result.ret == 0
result.stdout.fnmatch_lines(
@@ -1624,8 +1617,7 @@ class TestFixtureManagerParseFactories:
def one():
return 1
"""
),
encoding="utf-8",
)
)
package.joinpath("test_x.py").write_text(
textwrap.dedent(
@@ -1633,8 +1625,7 @@ class TestFixtureManagerParseFactories:
def test_x(one):
assert one == 1
"""
),
encoding="utf-8",
)
)
sub = package.joinpath("sub")
sub.mkdir()
@@ -1647,8 +1638,7 @@ class TestFixtureManagerParseFactories:
def one():
return 2
"""
),
encoding="utf-8",
)
)
sub.joinpath("test_y.py").write_text(
textwrap.dedent(
@@ -1656,8 +1646,7 @@ class TestFixtureManagerParseFactories:
def test_x(one):
assert one == 2
"""
),
encoding="utf-8",
)
)
reprec = pytester.inline_run()
reprec.assertoutcome(passed=2)
@@ -1682,8 +1671,7 @@ class TestFixtureManagerParseFactories:
def teardown_module():
values[:] = []
"""
),
encoding="utf-8",
)
)
package.joinpath("test_x.py").write_text(
textwrap.dedent(
@@ -1692,8 +1680,7 @@ class TestFixtureManagerParseFactories:
def test_x():
assert values == ["package"]
"""
),
encoding="utf-8",
)
)
package = pytester.mkdir("package2")
package.joinpath("__init__.py").write_text(
@@ -1705,8 +1692,7 @@ class TestFixtureManagerParseFactories:
def teardown_module():
values[:] = []
"""
),
encoding="utf-8",
)
)
package.joinpath("test_x.py").write_text(
textwrap.dedent(
@@ -1715,8 +1701,7 @@ class TestFixtureManagerParseFactories:
def test_x():
assert values == ["package2"]
"""
),
encoding="utf-8",
)
)
reprec = pytester.inline_run()
reprec.assertoutcome(passed=2)
@@ -1729,7 +1714,7 @@ class TestFixtureManagerParseFactories:
)
pytester.syspathinsert(pytester.path.name)
package = pytester.mkdir("package")
package.joinpath("__init__.py").write_text("", encoding="utf-8")
package.joinpath("__init__.py").write_text("")
package.joinpath("conftest.py").write_text(
textwrap.dedent(
"""\
@@ -1746,8 +1731,7 @@ class TestFixtureManagerParseFactories:
yield values
values.pop()
"""
),
encoding="utf-8",
)
)
package.joinpath("test_x.py").write_text(
textwrap.dedent(
@@ -1758,8 +1742,7 @@ class TestFixtureManagerParseFactories:
def test_package(one):
assert values == ["package-auto", "package"]
"""
),
encoding="utf-8",
)
)
reprec = pytester.inline_run()
reprec.assertoutcome(passed=2)
@@ -1909,12 +1892,8 @@ class TestAutouseDiscovery:
"""
)
conftest.rename(a.joinpath(conftest.name))
a.joinpath("test_something.py").write_text(
"def test_func(): pass", encoding="utf-8"
)
b.joinpath("test_otherthing.py").write_text(
"def test_func(): pass", encoding="utf-8"
)
a.joinpath("test_something.py").write_text("def test_func(): pass")
b.joinpath("test_otherthing.py").write_text("def test_func(): pass")
result = pytester.runpytest()
result.stdout.fnmatch_lines(
"""
@@ -1960,8 +1939,7 @@ class TestAutouseManagement:
import sys
sys._myapp = "hello"
"""
),
encoding="utf-8",
)
)
sub = pkgdir.joinpath("tests")
sub.mkdir()
@@ -1974,8 +1952,7 @@ class TestAutouseManagement:
def test_app():
assert sys._myapp == "hello"
"""
),
encoding="utf-8",
)
)
reprec = pytester.inline_run("-s")
reprec.assertoutcome(passed=1)
@@ -2905,7 +2882,7 @@ class TestFixtureMarker:
def browser(request):
def finalize():
sys.stdout.write_text('Finalized', encoding='utf-8')
sys.stdout.write_text('Finalized')
request.addfinalizer(finalize)
return {}
"""
@@ -2923,8 +2900,7 @@ class TestFixtureMarker:
def test_browser(browser):
assert browser['visited'] is True
"""
),
encoding="utf-8",
)
)
reprec = pytester.runpytest("-s")
for test in ["test_browser"]:
@@ -3879,8 +3855,7 @@ class TestParameterizedSubRequest:
def fix_with_param(request):
return request.param
"""
),
encoding="utf-8",
)
)
testfile = tests_dir.joinpath("test_foos.py")
@@ -3892,8 +3867,7 @@ class TestParameterizedSubRequest:
def test_foo(request):
request.getfixturevalue('fix_with_param')
"""
),
encoding="utf-8",
)
)
os.chdir(tests_dir)
@@ -4222,7 +4196,7 @@ class TestScopeOrdering:
test_2.py
"""
root = pytester.mkdir("root")
root.joinpath("__init__.py").write_text("values = []", encoding="utf-8")
root.joinpath("__init__.py").write_text("values = []")
sub1 = root.joinpath("sub1")
sub1.mkdir()
sub1.joinpath("__init__.py").touch()
@@ -4237,8 +4211,7 @@ class TestScopeOrdering:
yield values
assert values.pop() == "pre-sub1"
"""
),
encoding="utf-8",
)
)
sub1.joinpath("test_1.py").write_text(
textwrap.dedent(
@@ -4247,8 +4220,7 @@ class TestScopeOrdering:
def test_1(fix):
assert values == ["pre-sub1"]
"""
),
encoding="utf-8",
)
)
sub2 = root.joinpath("sub2")
sub2.mkdir()
@@ -4264,8 +4236,7 @@ class TestScopeOrdering:
yield values
assert values.pop() == "pre-sub2"
"""
),
encoding="utf-8",
)
)
sub2.joinpath("test_2.py").write_text(
textwrap.dedent(
@@ -4274,8 +4245,7 @@ class TestScopeOrdering:
def test_2(fix):
assert values == ["pre-sub2"]
"""
),
encoding="utf-8",
)
)
reprec = pytester.inline_run()
reprec.assertoutcome(passed=2)
+4 -10
View File
@@ -1443,8 +1443,7 @@ class TestMetafuncFunctional:
def pytest_generate_tests(metafunc):
assert metafunc.function.__name__ == "test_1"
"""
),
encoding="utf-8",
)
)
sub2.joinpath("conftest.py").write_text(
textwrap.dedent(
@@ -1452,15 +1451,10 @@ class TestMetafuncFunctional:
def pytest_generate_tests(metafunc):
assert metafunc.function.__name__ == "test_2"
"""
),
encoding="utf-8",
)
sub1.joinpath("test_in_sub1.py").write_text(
"def test_1(): pass", encoding="utf-8"
)
sub2.joinpath("test_in_sub2.py").write_text(
"def test_2(): pass", encoding="utf-8"
)
)
sub1.joinpath("test_in_sub1.py").write_text("def test_1(): pass")
sub2.joinpath("test_in_sub2.py").write_text("def test_2(): pass")
result = pytester.runpytest("--keep-duplicates", "-v", "-s", sub1, sub2, sub1)
result.assert_outcomes(passed=3)
+4 -4
View File
@@ -1392,14 +1392,14 @@ def test_sequence_comparison_uses_repr(pytester: Pytester) -> None:
def test_assertrepr_loaded_per_dir(pytester: Pytester) -> None:
pytester.makepyfile(test_base=["def test_base(): assert 1 == 2"])
a = pytester.mkdir("a")
a.joinpath("test_a.py").write_text("def test_a(): assert 1 == 2", encoding="utf-8")
a.joinpath("test_a.py").write_text("def test_a(): assert 1 == 2")
a.joinpath("conftest.py").write_text(
'def pytest_assertrepr_compare(): return ["summary a"]', encoding="utf-8"
'def pytest_assertrepr_compare(): return ["summary a"]'
)
b = pytester.mkdir("b")
b.joinpath("test_b.py").write_text("def test_b(): assert 1 == 2", encoding="utf-8")
b.joinpath("test_b.py").write_text("def test_b(): assert 1 == 2")
b.joinpath("conftest.py").write_text(
'def pytest_assertrepr_compare(): return ["summary b"]', encoding="utf-8"
'def pytest_assertrepr_compare(): return ["summary b"]'
)
result = pytester.runpytest()
+9 -36
View File
@@ -160,8 +160,7 @@ class TestAssertionRewrite:
"def special_asserter():\n"
" def special_assert(x, y):\n"
" assert x == y\n"
" return special_assert\n",
encoding="utf-8",
" return special_assert\n"
)
pytester.makeconftest('pytest_plugins = ["plugin"]')
pytester.makepyfile("def test(special_asserter): special_asserter(1, 2)\n")
@@ -174,9 +173,7 @@ class TestAssertionRewrite:
pytester.makepyfile(test_y="x = 1")
xdir = pytester.mkdir("x")
pytester.mkpydir(str(xdir.joinpath("test_Y")))
xdir.joinpath("test_Y").joinpath("__init__.py").write_text(
"x = 2", encoding="utf-8"
)
xdir.joinpath("test_Y").joinpath("__init__.py").write_text("x = 2")
pytester.makepyfile(
"import test_y\n"
"import test_Y\n"
@@ -729,7 +726,7 @@ class TestAssertionRewrite:
class TestRewriteOnImport:
def test_pycache_is_a_file(self, pytester: Pytester) -> None:
pytester.path.joinpath("__pycache__").write_text("Hello", encoding="utf-8")
pytester.path.joinpath("__pycache__").write_text("Hello")
pytester.makepyfile(
"""
def test_rewritten():
@@ -906,8 +903,7 @@ def test_rewritten():
pkg.joinpath("test_blah.py").write_text(
"""
def test_rewritten():
assert "@py_builtins" in globals()""",
encoding="utf-8",
assert "@py_builtins" in globals()"""
)
assert pytester.runpytest().ret == 0
@@ -1070,7 +1066,7 @@ class TestAssertionRewriteHookDetails:
source = tmp_path / "source.py"
pyc = Path(str(source) + "c")
source.write_text("def test(): pass", encoding="utf-8")
source.write_text("def test(): pass")
py_compile.compile(str(source), str(pyc))
contents = pyc.read_bytes()
@@ -1096,7 +1092,7 @@ class TestAssertionRewriteHookDetails:
fn = tmp_path / "source.py"
pyc = Path(str(fn) + "c")
fn.write_text("def test(): assert True", encoding="utf-8")
fn.write_text("def test(): assert True")
source_stat, co = _rewrite_test(fn, config)
_write_pyc(state, co, source_stat, pyc)
@@ -1161,7 +1157,7 @@ class TestAssertionRewriteHookDetails:
return False
def rewrite_self():
with open(__file__, 'w', encoding='utf-8') as self:
with open(__file__, 'w') as self:
self.write('def reloaded(): return True')
""",
test_fun="""
@@ -1191,10 +1187,9 @@ class TestAssertionRewriteHookDetails:
data = pkgutil.get_data('foo.test_foo', 'data.txt')
assert data == b'Hey'
"""
),
encoding="utf-8",
)
)
path.joinpath("data.txt").write_text("Hey", encoding="utf-8")
path.joinpath("data.txt").write_text("Hey")
result = pytester.runpytest()
result.stdout.fnmatch_lines(["*1 passed*"])
@@ -1531,28 +1526,6 @@ class TestIssue11028:
result.stdout.fnmatch_lines(["*assert 4 > 5", "*where 5 = add_one(4)"])
class TestIssue11239:
@pytest.mark.skipif(sys.version_info[:2] <= (3, 7), reason="Only Python 3.8+")
def test_assertion_walrus_different_test_cases(self, pytester: Pytester) -> None:
"""Regression for (#11239)
Walrus operator rewriting would leak to separate test cases if they used the same variables.
"""
pytester.makepyfile(
"""
def test_1():
state = {"x": 2}.get("x")
assert state is not None
def test_2():
db = {"x": 2}
assert (state := db.get("x")) is not None
"""
)
result = pytester.runpytest()
assert result.ret == 0
@pytest.mark.skipif(
sys.maxsize <= (2**31 - 1), reason="Causes OverflowError on 32bit systems"
)
+4 -9
View File
@@ -38,9 +38,7 @@ class TestNewAPI:
@pytest.mark.filterwarnings("ignore:could not create cache path")
def test_cache_writefail_cachfile_silent(self, pytester: Pytester) -> None:
pytester.makeini("[pytest]")
pytester.path.joinpath(".pytest_cache").write_text(
"gone wrong", encoding="utf-8"
)
pytester.path.joinpath(".pytest_cache").write_text("gone wrong")
config = pytester.parseconfigure()
cache = config.cache
assert cache is not None
@@ -1136,9 +1134,7 @@ class TestNewFirst:
["*test_2/test_2.py::test_1 PASSED*", "*test_1/test_1.py::test_1 PASSED*"]
)
p1.write_text(
"def test_1(): assert 1\n" "def test_2(): assert 1\n", encoding="utf-8"
)
p1.write_text("def test_1(): assert 1\n" "def test_2(): assert 1\n")
os.utime(p1, ns=(p1.stat().st_atime_ns, int(1e9)))
result = pytester.runpytest("--nf", "--collect-only", "-q")
@@ -1211,8 +1207,7 @@ class TestNewFirst:
p1.write_text(
"import pytest\n"
"@pytest.mark.parametrize('num', [1, 2, 3])\n"
"def test_1(num): assert num\n",
encoding="utf-8",
"def test_1(num): assert num\n"
)
os.utime(p1, ns=(p1.stat().st_atime_ns, int(1e9)))
@@ -1264,7 +1259,7 @@ def test_gitignore(pytester: Pytester) -> None:
assert gitignore_path.read_text(encoding="UTF-8") == msg
# Does not overwrite existing/custom one.
gitignore_path.write_text("custom", encoding="utf-8")
gitignore_path.write_text("custom")
cache.set("something", "else")
assert gitignore_path.read_text(encoding="UTF-8") == "custom"
+6 -7
View File
@@ -750,10 +750,9 @@ def test_setup_failure_does_not_kill_capturing(pytester: Pytester) -> None:
def pytest_runtest_setup(item):
raise ValueError(42)
"""
),
encoding="utf-8",
)
)
sub1.joinpath("test_mod.py").write_text("def test_func1(): pass", encoding="utf-8")
sub1.joinpath("test_mod.py").write_text("def test_func1(): pass")
result = pytester.runpytest(pytester.path, "--traceconfig")
result.stdout.fnmatch_lines(["*ValueError(42)*", "*1 error*"])
@@ -1524,9 +1523,9 @@ def test_global_capture_with_live_logging(pytester: Pytester) -> None:
def pytest_runtest_logreport(report):
if "test_global" in report.nodeid:
if report.when == "teardown":
with open("caplog", "w", encoding="utf-8") as f:
with open("caplog", "w") as f:
f.write(report.caplog)
with open("capstdout", "w", encoding="utf-8") as f:
with open("capstdout", "w") as f:
f.write(report.capstdout)
"""
)
@@ -1556,14 +1555,14 @@ def test_global_capture_with_live_logging(pytester: Pytester) -> None:
result = pytester.runpytest_subprocess("--log-cli-level=INFO")
assert result.ret == 0
with open("caplog", encoding="utf-8") as f:
with open("caplog") as f:
caplog = f.read()
assert "fix setup" in caplog
assert "something in test" in caplog
assert "fix teardown" in caplog
with open("capstdout", encoding="utf-8") as f:
with open("capstdout") as f:
capstdout = f.read()
assert "fix setup" in capstdout
+26 -56
View File
@@ -140,7 +140,7 @@ class TestCollectFS:
ensure_file(tmp_path / ".bzr" / "test_notfound.py")
ensure_file(tmp_path / "normal" / "test_found.py")
for x in tmp_path.rglob("test_*.py"):
x.write_text("def test_hello(): pass", encoding="utf-8")
x.write_text("def test_hello(): pass", "utf-8")
result = pytester.runpytest("--collect-only")
s = result.stdout.str()
@@ -162,7 +162,7 @@ class TestCollectFS:
bindir = "Scripts" if sys.platform.startswith("win") else "bin"
ensure_file(pytester.path / "virtual" / bindir / fname)
testfile = ensure_file(pytester.path / "virtual" / "test_invenv.py")
testfile.write_text("def test_hello(): pass", encoding="utf-8")
testfile.write_text("def test_hello(): pass")
# by default, ignore tests inside a virtualenv
result = pytester.runpytest()
@@ -192,7 +192,7 @@ class TestCollectFS:
# norecursedirs takes priority
ensure_file(pytester.path / ".virtual" / bindir / fname)
testfile = ensure_file(pytester.path / ".virtual" / "test_invenv.py")
testfile.write_text("def test_hello(): pass", encoding="utf-8")
testfile.write_text("def test_hello(): pass")
result = pytester.runpytest("--collect-in-virtualenv")
result.stdout.no_fnmatch_line("*test_invenv*")
# ...unless the virtualenv is explicitly given on the CLI
@@ -231,14 +231,10 @@ class TestCollectFS:
)
tmp_path = pytester.path
ensure_file(tmp_path / "mydir" / "test_hello.py").write_text(
"def test_1(): pass", encoding="utf-8"
)
ensure_file(tmp_path / "xyz123" / "test_2.py").write_text(
"def test_2(): 0/0", encoding="utf-8"
)
ensure_file(tmp_path / "xy" / "test_ok.py").write_text(
"def test_3(): pass", encoding="utf-8"
"def test_1(): pass"
)
ensure_file(tmp_path / "xyz123" / "test_2.py").write_text("def test_2(): 0/0")
ensure_file(tmp_path / "xy" / "test_ok.py").write_text("def test_3(): pass")
rec = pytester.inline_run()
rec.assertoutcome(passed=1)
rec = pytester.inline_run("xyz123/test_2.py")
@@ -252,14 +248,12 @@ class TestCollectFS:
"""
)
tmp_path = pytester.path
ensure_file(tmp_path / "a" / "test_1.py").write_text(
"def test_a(): pass", encoding="utf-8"
)
ensure_file(tmp_path / "a" / "test_1.py").write_text("def test_a(): pass")
ensure_file(tmp_path / "b" / "tests" / "test_2.py").write_text(
"def test_b(): pass", encoding="utf-8"
"def test_b(): pass"
)
ensure_file(tmp_path / "c" / "tests" / "test_3.py").write_text(
"def test_c(): pass", encoding="utf-8"
"def test_c(): pass"
)
# executing from rootdir only tests from `testpaths` directories
@@ -355,8 +349,8 @@ class TestCustomConftests:
"""
)
sub = pytester.mkdir("xy123")
ensure_file(sub / "test_hello.py").write_text("syntax error", encoding="utf-8")
sub.joinpath("conftest.py").write_text("syntax error", encoding="utf-8")
ensure_file(sub / "test_hello.py").write_text("syntax error")
sub.joinpath("conftest.py").write_text("syntax error")
pytester.makepyfile("def test_hello(): pass")
pytester.makepyfile(test_one="syntax error")
result = pytester.runpytest("--fulltrace")
@@ -1066,18 +1060,13 @@ def test_fixture_scope_sibling_conftests(pytester: Pytester) -> None:
def fix():
return 1
"""
),
encoding="utf-8",
)
foo_path.joinpath("test_foo.py").write_text(
"def test_foo(fix): assert fix == 1", encoding="utf-8"
)
)
foo_path.joinpath("test_foo.py").write_text("def test_foo(fix): assert fix == 1")
# Tests in `food/` should not see the conftest fixture from `foo/`
food_path = pytester.mkpydir("food")
food_path.joinpath("test_food.py").write_text(
"def test_food(fix): assert fix == 1", encoding="utf-8"
)
food_path.joinpath("test_food.py").write_text("def test_food(fix): assert fix == 1")
res = pytester.runpytest()
assert res.ret == 1
@@ -1208,8 +1197,7 @@ def test_collect_with_chdir_during_import(pytester: Pytester) -> None:
os.chdir(%r)
"""
% (str(subdir),)
),
encoding="utf-8",
)
)
pytester.makepyfile(
"""
@@ -1239,12 +1227,8 @@ def test_collect_pyargs_with_testpaths(
) -> None:
testmod = pytester.mkdir("testmod")
# NOTE: __init__.py is not collected since it does not match python_files.
testmod.joinpath("__init__.py").write_text(
"def test_func(): pass", encoding="utf-8"
)
testmod.joinpath("test_file.py").write_text(
"def test_func(): pass", encoding="utf-8"
)
testmod.joinpath("__init__.py").write_text("def test_func(): pass")
testmod.joinpath("test_file.py").write_text("def test_func(): pass")
root = pytester.mkdir("root")
root.joinpath("pytest.ini").write_text(
@@ -1254,8 +1238,7 @@ def test_collect_pyargs_with_testpaths(
addopts = --pyargs
testpaths = testmod
"""
),
encoding="utf-8",
)
)
monkeypatch.setenv("PYTHONPATH", str(pytester.path), prepend=os.pathsep)
with monkeypatch.context() as mp:
@@ -1273,8 +1256,7 @@ def test_initial_conftests_with_testpaths(pytester: Pytester) -> None:
def pytest_sessionstart(session):
raise Exception("pytest_sessionstart hook successfully run")
"""
),
encoding="utf-8",
)
)
pytester.makeini(
"""
@@ -1282,18 +1264,11 @@ def test_initial_conftests_with_testpaths(pytester: Pytester) -> None:
testpaths = some_path
"""
)
# No command line args - falls back to testpaths.
result = pytester.runpytest()
assert result.ret == ExitCode.INTERNAL_ERROR
result.stdout.fnmatch_lines(
"INTERNALERROR* Exception: pytest_sessionstart hook successfully run"
)
# No fallback.
result = pytester.runpytest(".")
assert result.ret == ExitCode.NO_TESTS_COLLECTED
def test_large_option_breaks_initial_conftests(pytester: Pytester) -> None:
"""Long option values do not break initial conftests handling (#10169)."""
@@ -1341,7 +1316,6 @@ def test_collect_symlink_out_of_tree(pytester: Pytester) -> None:
assert request.node.nodeid == "test_real.py::test_nodeid"
"""
),
encoding="utf-8",
)
out_of_tree = pytester.mkdir("out_of_tree")
@@ -1370,16 +1344,12 @@ def test_collect_symlink_dir(pytester: Pytester) -> None:
def test_collectignore_via_conftest(pytester: Pytester) -> None:
"""collect_ignore in parent conftest skips importing child (issue #4592)."""
tests = pytester.mkpydir("tests")
tests.joinpath("conftest.py").write_text(
"collect_ignore = ['ignore_me']", encoding="utf-8"
)
tests.joinpath("conftest.py").write_text("collect_ignore = ['ignore_me']")
ignore_me = tests.joinpath("ignore_me")
ignore_me.mkdir()
ignore_me.joinpath("__init__.py").touch()
ignore_me.joinpath("conftest.py").write_text(
"assert 0, 'should_not_be_called'", encoding="utf-8"
)
ignore_me.joinpath("conftest.py").write_text("assert 0, 'should_not_be_called'")
result = pytester.runpytest()
assert result.ret == ExitCode.NO_TESTS_COLLECTED
@@ -1388,9 +1358,9 @@ def test_collectignore_via_conftest(pytester: Pytester) -> None:
def test_collect_pkg_init_and_file_in_args(pytester: Pytester) -> None:
subdir = pytester.mkdir("sub")
init = subdir.joinpath("__init__.py")
init.write_text("def test_init(): pass", encoding="utf-8")
init.write_text("def test_init(): pass")
p = subdir.joinpath("test_file.py")
p.write_text("def test_file(): pass", encoding="utf-8")
p.write_text("def test_file(): pass")
# NOTE: without "-o python_files=*.py" this collects test_file.py twice.
# This changed/broke with "Add package scoped fixtures #2283" (2b1410895)
@@ -1417,7 +1387,7 @@ def test_collect_pkg_init_and_file_in_args(pytester: Pytester) -> None:
def test_collect_pkg_init_only(pytester: Pytester) -> None:
subdir = pytester.mkdir("sub")
init = subdir.joinpath("__init__.py")
init.write_text("def test_init(): pass", encoding="utf-8")
init.write_text("def test_init(): pass")
result = pytester.runpytest(str(init))
result.stdout.fnmatch_lines(["*no tests ran in*"])
@@ -1432,7 +1402,7 @@ def test_collect_sub_with_symlinks(use_pkg: bool, pytester: Pytester) -> None:
sub = pytester.mkdir("sub")
if use_pkg:
sub.joinpath("__init__.py").touch()
sub.joinpath("test_file.py").write_text("def test_file(): pass", encoding="utf-8")
sub.joinpath("test_file.py").write_text("def test_file(): pass")
# Create a broken symlink.
symlink_or_skip("test_doesnotexist.py", sub.joinpath("test_broken.py"))
@@ -1470,7 +1440,7 @@ def test_collector_respects_tbstyle(pytester: Pytester) -> None:
def test_does_not_eagerly_collect_packages(pytester: Pytester) -> None:
pytester.makepyfile("def test(): pass")
pydir = pytester.mkpydir("foopkg")
pydir.joinpath("__init__.py").write_text("assert False", encoding="utf-8")
pydir.joinpath("__init__.py").write_text("assert False")
result = pytester.runpytest()
assert result.ret == ExitCode.OK
+11 -20
View File
@@ -87,8 +87,7 @@ class TestParseIni:
[pytest]
addopts = --verbose
"""
),
encoding="utf-8",
)
)
config = pytester.parseconfig(tmp_path)
assert config.option.color == "no"
@@ -128,8 +127,7 @@ class TestParseIni:
""".format(
section=section
)
),
encoding="utf-8",
)
)
config = pytester.parseconfig()
assert config.getini("minversion") == "3.36"
@@ -152,8 +150,7 @@ class TestParseIni:
[pytest]
minversion = 2.0
"""
),
encoding="utf-8",
)
)
pytester.path.joinpath("pytest.ini").write_text(
textwrap.dedent(
@@ -161,16 +158,13 @@ class TestParseIni:
[pytest]
minversion = 1.5
"""
),
encoding="utf-8",
)
)
config = pytester.parseconfigure(sub)
assert config.getini("minversion") == "2.0"
def test_ini_parse_error(self, pytester: Pytester) -> None:
pytester.path.joinpath("pytest.ini").write_text(
"addopts = -x", encoding="utf-8"
)
pytester.path.joinpath("pytest.ini").write_text("addopts = -x")
result = pytester.runpytest()
assert result.ret != 0
result.stderr.fnmatch_lines("ERROR: *pytest.ini:1: no section header defined")
@@ -640,7 +634,7 @@ class TestConfigAPI:
def test_getconftest_pathlist(self, pytester: Pytester, tmp_path: Path) -> None:
somepath = tmp_path.joinpath("x", "y", "z")
p = tmp_path.joinpath("conftest.py")
p.write_text(f"mylist = {['.', str(somepath)]}", encoding="utf-8")
p.write_text(f"mylist = {['.', str(somepath)]}")
config = pytester.parseconfigure(p)
assert (
config._getconftest_pathlist("notexist", path=tmp_path, rootpath=tmp_path)
@@ -916,8 +910,7 @@ class TestConfigFromdictargs:
[pytest]
name = value
"""
),
encoding="utf-8",
)
)
inifilename = "../../foo/bar.ini"
@@ -934,8 +927,7 @@ class TestConfigFromdictargs:
name = wrong-value
should_not_be_set = true
"""
),
encoding="utf-8",
)
)
with MonkeyPatch.context() as mp:
mp.chdir(cwd)
@@ -1395,7 +1387,7 @@ class TestRootdir:
)
def test_with_ini(self, tmp_path: Path, name: str, contents: str) -> None:
inipath = tmp_path / name
inipath.write_text(contents, encoding="utf-8")
inipath.write_text(contents, "utf-8")
a = tmp_path / "a"
a.mkdir()
@@ -1454,7 +1446,7 @@ class TestRootdir:
) -> None:
p = tmp_path / name
p.touch()
p.write_text(contents, encoding="utf-8")
p.write_text(contents, "utf-8")
rootpath, inipath, ini_config = determine_setup(str(p), [str(tmp_path)])
assert rootpath == tmp_path
assert inipath == p
@@ -1550,8 +1542,7 @@ class TestOverrideIniArgs:
custom = 1.0""".format(
section=section
)
),
encoding="utf-8",
)
)
pytester.makeconftest(
"""
+36 -62
View File
@@ -1,3 +1,4 @@
import argparse
import os
import textwrap
from pathlib import Path
@@ -6,8 +7,6 @@ from typing import Dict
from typing import Generator
from typing import List
from typing import Optional
from typing import Sequence
from typing import Union
import pytest
from _pytest.config import ExitCode
@@ -25,18 +24,18 @@ def ConftestWithSetinitial(path) -> PytestPluginManager:
def conftest_setinitial(
conftest: PytestPluginManager,
args: Sequence[Union[str, Path]],
confcutdir: Optional[Path] = None,
conftest: PytestPluginManager, args, confcutdir: Optional["os.PathLike[str]"] = None
) -> None:
conftest._set_initial_conftests(
args=args,
pyargs=False,
noconftest=False,
rootpath=Path(args[0]),
confcutdir=confcutdir,
importmode="prepend",
)
class Namespace:
def __init__(self) -> None:
self.file_or_dir = args
self.confcutdir = os.fspath(confcutdir) if confcutdir is not None else None
self.noconftest = False
self.pyargs = False
self.importmode = "prepend"
namespace = cast(argparse.Namespace, Namespace())
conftest._set_initial_conftests(namespace, rootpath=Path(args[0]), testpaths_ini=[])
@pytest.mark.usefixtures("_sys_snapshot")
@@ -47,12 +46,8 @@ class TestConftestValueAccessGlobal:
) -> Generator[Path, None, None]:
tmp_path = tmp_path_factory.mktemp("basedir", numbered=True)
tmp_path.joinpath("adir/b").mkdir(parents=True)
tmp_path.joinpath("adir/conftest.py").write_text(
"a=1 ; Directory = 3", encoding="utf-8"
)
tmp_path.joinpath("adir/b/conftest.py").write_text(
"b=2 ; a = 1.5", encoding="utf-8"
)
tmp_path.joinpath("adir/conftest.py").write_text("a=1 ; Directory = 3")
tmp_path.joinpath("adir/b/conftest.py").write_text("b=2 ; a = 1.5")
if request.param == "inpackage":
tmp_path.joinpath("adir/__init__.py").touch()
tmp_path.joinpath("adir/b/__init__.py").touch()
@@ -127,12 +122,8 @@ class TestConftestValueAccessGlobal:
def test_conftest_in_nonpkg_with_init(tmp_path: Path, _sys_snapshot) -> None:
tmp_path.joinpath("adir-1.0/b").mkdir(parents=True)
tmp_path.joinpath("adir-1.0/conftest.py").write_text(
"a=1 ; Directory = 3", encoding="utf-8"
)
tmp_path.joinpath("adir-1.0/b/conftest.py").write_text(
"b=2 ; a = 1.5", encoding="utf-8"
)
tmp_path.joinpath("adir-1.0/conftest.py").write_text("a=1 ; Directory = 3")
tmp_path.joinpath("adir-1.0/b/conftest.py").write_text("b=2 ; a = 1.5")
tmp_path.joinpath("adir-1.0/b/__init__.py").touch()
tmp_path.joinpath("adir-1.0/__init__.py").touch()
ConftestWithSetinitial(tmp_path.joinpath("adir-1.0", "b"))
@@ -175,7 +166,7 @@ def test_conftest_global_import(pytester: Pytester) -> None:
sub = Path("sub")
sub.mkdir()
subconf = sub / "conftest.py"
subconf.write_text("y=4", encoding="utf-8")
subconf.write_text("y=4")
mod2 = conf._importconftest(subconf, importmode="prepend", rootpath=Path.cwd())
assert mod != mod2
assert mod2.y == 4
@@ -254,8 +245,7 @@ def test_conftest_confcutdir(pytester: Pytester) -> None:
def pytest_addoption(parser):
parser.addoption("--xyz", action="store_true")
"""
),
encoding="utf-8",
)
)
result = pytester.runpytest("-h", "--confcutdir=%s" % x, x)
result.stdout.fnmatch_lines(["*--xyz*"])
@@ -283,12 +273,9 @@ def test_installed_conftest_is_picked_up(pytester: Pytester, tmp_path: Path) ->
@pytest.fixture
def fix(): return None
"""
),
encoding="utf-8",
)
tmp_path.joinpath("foo", "test_it.py").write_text(
"def test_it(fix): pass", encoding="utf-8"
)
)
tmp_path.joinpath("foo", "test_it.py").write_text("def test_it(fix): pass")
result = pytester.runpytest("--pyargs", "foo")
assert result.ret == 0
@@ -413,8 +400,7 @@ def test_conftest_existing_junitxml(pytester: Pytester) -> None:
def pytest_addoption(parser):
parser.addoption("--xyz", action="store_true")
"""
),
encoding="utf-8",
)
)
pytester.makefile(ext=".xml", junit="") # Writes junit.xml
result = pytester.runpytest("-h", "--junitxml", "junit.xml")
@@ -425,7 +411,7 @@ def test_conftest_import_order(pytester: Pytester, monkeypatch: MonkeyPatch) ->
ct1 = pytester.makeconftest("")
sub = pytester.mkdir("sub")
ct2 = sub / "conftest.py"
ct2.write_text("", encoding="utf-8")
ct2.write_text("")
def impct(p, importmode, root):
return p
@@ -463,8 +449,7 @@ def test_fixture_dependency(pytester: Pytester) -> None:
def bar(foo):
return 'bar'
"""
),
encoding="utf-8",
)
)
subsub = sub.joinpath("subsub")
subsub.mkdir()
@@ -481,8 +466,7 @@ def test_fixture_dependency(pytester: Pytester) -> None:
def test_event_fixture(bar):
assert bar == 'sub bar'
"""
),
encoding="utf-8",
)
)
result = pytester.runpytest("sub")
result.stdout.fnmatch_lines(["*1 passed*"])
@@ -496,11 +480,10 @@ def test_conftest_found_with_double_dash(pytester: Pytester) -> None:
def pytest_addoption(parser):
parser.addoption("--hello-world", action="store_true")
"""
),
encoding="utf-8",
)
)
p = sub.joinpath("test_hello.py")
p.write_text("def test_hello(): pass", encoding="utf-8")
p.write_text("def test_hello(): pass")
result = pytester.runpytest(str(p) + "::test_hello", "-h")
result.stdout.fnmatch_lines(
"""
@@ -524,8 +507,7 @@ class TestConftestVisibility:
def fxtr():
return "from-package"
"""
),
encoding="utf-8",
)
)
package.joinpath("test_pkgroot.py").write_text(
textwrap.dedent(
@@ -533,8 +515,7 @@ class TestConftestVisibility:
def test_pkgroot(fxtr):
assert fxtr == "from-package"
"""
),
encoding="utf-8",
)
)
swc = package.joinpath("swc")
@@ -548,8 +529,7 @@ class TestConftestVisibility:
def fxtr():
return "from-swc"
"""
),
encoding="utf-8",
)
)
swc.joinpath("test_with_conftest.py").write_text(
textwrap.dedent(
@@ -557,8 +537,7 @@ class TestConftestVisibility:
def test_with_conftest(fxtr):
assert fxtr == "from-swc"
"""
),
encoding="utf-8",
)
)
snc = package.joinpath("snc")
@@ -571,8 +550,7 @@ class TestConftestVisibility:
assert fxtr == "from-package" # No local conftest.py, so should
# use value from parent dir's
"""
),
encoding="utf-8",
)
)
print("created directory structure:")
for x in pytester.path.glob("**/"):
@@ -638,7 +616,7 @@ def test_search_conftest_up_to_inifile(
root = pytester.path
src = root.joinpath("src")
src.mkdir()
src.joinpath("pytest.ini").write_text("[pytest]", encoding="utf-8")
src.joinpath("pytest.ini").write_text("[pytest]")
src.joinpath("conftest.py").write_text(
textwrap.dedent(
"""\
@@ -646,8 +624,7 @@ def test_search_conftest_up_to_inifile(
@pytest.fixture
def fix1(): pass
"""
),
encoding="utf-8",
)
)
src.joinpath("test_foo.py").write_text(
textwrap.dedent(
@@ -657,8 +634,7 @@ def test_search_conftest_up_to_inifile(
def test_2(out_of_reach):
pass
"""
),
encoding="utf-8",
)
)
root.joinpath("conftest.py").write_text(
textwrap.dedent(
@@ -667,8 +643,7 @@ def test_search_conftest_up_to_inifile(
@pytest.fixture
def out_of_reach(): pass
"""
),
encoding="utf-8",
)
)
args = [str(src)]
@@ -751,8 +726,7 @@ def test_required_option_help(pytester: Pytester) -> None:
def pytest_addoption(parser):
parser.addoption("--xyz", action="store_true", required=True)
"""
),
encoding="utf-8",
)
)
result = pytester.runpytest("-h", x)
result.stdout.no_fnmatch_line("*argument --xyz is required*")
+4 -30
View File
@@ -357,8 +357,7 @@ class TestDoctests:
>>> 1/0
'''
"""
),
encoding="utf-8",
)
)
result = pytester.runpytest("--doctest-modules")
result.stdout.fnmatch_lines(
@@ -449,8 +448,7 @@ class TestDoctests:
"""\
import asdalsdkjaslkdjasd
"""
),
encoding="utf-8",
)
)
pytester.maketxtfile(
"""
@@ -482,27 +480,6 @@ class TestDoctests:
reprec = pytester.inline_run(p, "--doctest-modules")
reprec.assertoutcome(failed=1)
@pytest.mark.skipif(
sys.version_info[:2] <= (3, 7), reason="Only Python 3.7 or less"
)
def test_doctest_cached_property(self, pytester: Pytester):
p = pytester.makepyfile(
"""
import functools
class Foo:
@functools.cached_property
def foo(self):
'''
>>> assert False, "Tacos!"
'''
...
"""
)
result = pytester.runpytest(p, "--doctest-modules")
result.assert_outcomes(failed=1)
assert "Tacos!" in result.stdout.str()
def test_doctestmodule_external_and_issue116(self, pytester: Pytester):
p = pytester.mkpydir("hello")
p.joinpath("__init__.py").write_text(
@@ -515,8 +492,7 @@ class TestDoctests:
2
'''
"""
),
encoding="utf-8",
)
)
result = pytester.runpytest(p, "--doctest-modules")
result.stdout.fnmatch_lines(
@@ -1590,9 +1566,7 @@ def test_warning_on_unwrap_of_broken_object(
def test_is_setup_py_not_named_setup_py(tmp_path: Path) -> None:
not_setup_py = tmp_path.joinpath("not_setup.py")
not_setup_py.write_text(
'from setuptools import setup; setup(name="foo")', encoding="utf-8"
)
not_setup_py.write_text('from setuptools import setup; setup(name="foo")')
assert not _is_setup_py(not_setup_py)
+6 -36
View File
@@ -28,7 +28,7 @@ from _pytest.stash import Stash
def schema() -> xmlschema.XMLSchema:
"""Return an xmlschema.XMLSchema object for the junit-10.xsd file."""
fn = Path(__file__).parent / "example_scripts/junit-10.xsd"
with fn.open(encoding="utf-8") as f:
with fn.open() as f:
return xmlschema.XMLSchema(f)
@@ -45,7 +45,7 @@ class RunAndParse:
xml_path = self.pytester.path.joinpath("junit.xml")
result = self.pytester.runpytest("--junitxml=%s" % xml_path, *args)
if family == "xunit2":
with xml_path.open(encoding="utf-8") as f:
with xml_path.open() as f:
self.schema.validate(f)
xmldoc = minidom.parse(str(xml_path))
return result, DomNode(xmldoc)
@@ -469,7 +469,7 @@ class TestPython:
self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
) -> None:
p = pytester.mkdir("sub").joinpath("test_hello.py")
p.write_text("def test_func(): 0/0", encoding="utf-8")
p.write_text("def test_func(): 0/0")
result, dom = run_and_parse(family=xunit_family)
assert result.ret
node = dom.find_first_by_tag("testsuite")
@@ -987,7 +987,7 @@ class TestNonPython:
return "custom item runtest failed"
"""
)
pytester.path.joinpath("myfile.xyz").write_text("hello", encoding="utf-8")
pytester.path.joinpath("myfile.xyz").write_text("hello")
result, dom = run_and_parse(family=xunit_family)
assert result.ret
node = dom.find_first_by_tag("testsuite")
@@ -1013,7 +1013,7 @@ def test_nullbyte(pytester: Pytester, junit_logging: str) -> None:
)
xmlf = pytester.path.joinpath("junit.xml")
pytester.runpytest("--junitxml=%s" % xmlf, "-o", "junit_logging=%s" % junit_logging)
text = xmlf.read_text(encoding="utf-8")
text = xmlf.read_text()
assert "\x00" not in text
if junit_logging == "system-out":
assert "#x00" in text
@@ -1035,7 +1035,7 @@ def test_nullbyte_replace(pytester: Pytester, junit_logging: str) -> None:
)
xmlf = pytester.path.joinpath("junit.xml")
pytester.runpytest("--junitxml=%s" % xmlf, "-o", "junit_logging=%s" % junit_logging)
text = xmlf.read_text(encoding="utf-8")
text = xmlf.read_text()
if junit_logging == "system-out":
assert "#x0" in text
if junit_logging == "no":
@@ -1228,36 +1228,6 @@ def test_record_property(pytester: Pytester, run_and_parse: RunAndParse) -> None
result.stdout.fnmatch_lines(["*= 1 passed in *"])
def test_record_property_on_test_and_teardown_failure(
pytester: Pytester, run_and_parse: RunAndParse
) -> None:
pytester.makepyfile(
"""
import pytest
@pytest.fixture
def other(record_property):
record_property("bar", 1)
yield
assert 0
def test_record(record_property, other):
record_property("foo", "<1")
assert 0
"""
)
result, dom = run_and_parse()
node = dom.find_first_by_tag("testsuite")
tnodes = node.find_by_tag("testcase")
for tnode in tnodes:
psnode = tnode.find_first_by_tag("properties")
assert psnode, f"testcase didn't had expected properties:\n{tnode}"
pnodes = psnode.find_by_tag("property")
pnodes[0].assert_attr(name="bar", value="1")
pnodes[1].assert_attr(name="foo", value="<1")
result.stdout.fnmatch_lines(["*= 1 failed, 1 error *"])
def test_record_property_same_name(
pytester: Pytester, run_and_parse: RunAndParse
) -> None:
+1 -2
View File
@@ -59,8 +59,7 @@ def test_link_resolve(pytester: Pytester) -> None:
def test_foo():
raise AssertionError()
"""
),
encoding="utf-8",
)
)
subst = subst_path_linux
-31
View File
@@ -262,34 +262,3 @@ def test_module_full_path_without_drive(pytester: Pytester) -> None:
"* 1 passed in *",
]
)
def test_very_long_cmdline_arg(pytester: Pytester) -> None:
"""
Regression test for #11394.
Note: we could not manage to actually reproduce the error with this code, we suspect
GitHub runners are configured to support very long paths, however decided to leave
the test in place in case this ever regresses in the future.
"""
pytester.makeconftest(
"""
import pytest
def pytest_addoption(parser):
parser.addoption("--long-list", dest="long_list", action="store", default="all", help="List of things")
@pytest.fixture(scope="module")
def specified_feeds(request):
list_string = request.config.getoption("--long-list")
return list_string.split(',')
"""
)
pytester.makepyfile(
"""
def test_foo(specified_feeds):
assert len(specified_feeds) == 100_000
"""
)
result = pytester.runpytest("--long-list", ",".join(["helloworld"] * 100_000))
result.stdout.fnmatch_lines("* 1 passed *")
+1 -36
View File
@@ -1130,41 +1130,6 @@ def test_mark_mro() -> None:
all_marks = get_unpacked_marks(C)
assert all_marks == [xfail("b").mark, xfail("a").mark, xfail("c").mark]
assert all_marks == [xfail("c").mark, xfail("a").mark, xfail("b").mark]
assert get_unpacked_marks(C, consider_mro=False) == [xfail("c").mark]
# @pytest.mark.issue("https://github.com/pytest-dev/pytest/issues/10447")
def test_mark_fixture_order_mro(pytester: Pytester):
"""This ensures we walk marks of the mro starting with the base classes
the action at a distance fixtures are taken as minimal example from a real project
"""
foo = pytester.makepyfile(
"""
import pytest
@pytest.fixture
def add_attr1(request):
request.instance.attr1 = object()
@pytest.fixture
def add_attr2(request):
request.instance.attr2 = request.instance.attr1
@pytest.mark.usefixtures('add_attr1')
class Parent:
pass
@pytest.mark.usefixtures('add_attr2')
class TestThings(Parent):
def test_attrs(self):
assert self.attr1 == self.attr2
"""
)
result = pytester.runpytest(foo)
result.assert_outcomes(passed=1)
+5 -9
View File
@@ -324,8 +324,7 @@ def test_importerror(pytester: Pytester) -> None:
x = 1
"""
),
encoding="utf-8",
)
)
pytester.path.joinpath("test_importerror.py").write_text(
textwrap.dedent(
@@ -333,8 +332,7 @@ def test_importerror(pytester: Pytester) -> None:
def test_importerror(monkeypatch):
monkeypatch.setattr('package.a.x', 2)
"""
),
encoding="utf-8",
)
)
result = pytester.runpytest()
result.stdout.fnmatch_lines(
@@ -436,13 +434,11 @@ def test_syspath_prepend_with_namespace_packages(
ns = d.joinpath("ns_pkg")
ns.mkdir()
ns.joinpath("__init__.py").write_text(
"__import__('pkg_resources').declare_namespace(__name__)", encoding="utf-8"
"__import__('pkg_resources').declare_namespace(__name__)"
)
lib = ns.joinpath(dirname)
lib.mkdir()
lib.joinpath("__init__.py").write_text(
"def check(): return %r" % dirname, encoding="utf-8"
)
lib.joinpath("__init__.py").write_text("def check(): return %r" % dirname)
monkeypatch.syspath_prepend("hello")
import ns_pkg.hello
@@ -461,5 +457,5 @@ def test_syspath_prepend_with_namespace_packages(
# Should invalidate caches via importlib.invalidate_caches.
modules_tmpdir = pytester.mkdir("modules_tmpdir")
monkeypatch.syspath_prepend(str(modules_tmpdir))
modules_tmpdir.joinpath("main_app.py").write_text("app = True", encoding="utf-8")
modules_tmpdir.joinpath("main_app.py").write_text("app = True")
from main_app import app # noqa: F401
+1 -8
View File
@@ -1,5 +1,4 @@
import argparse
import locale
import os
import shlex
import subprocess
@@ -290,11 +289,6 @@ class TestParser:
def test_argcomplete(pytester: Pytester, monkeypatch: MonkeyPatch) -> None:
try:
# New in Python 3.11, ignores utf-8 mode
encoding = locale.getencoding() # type: ignore[attr-defined]
except AttributeError:
encoding = locale.getpreferredencoding(False)
try:
bash_version = subprocess.run(
["bash", "--version"],
@@ -302,7 +296,6 @@ def test_argcomplete(pytester: Pytester, monkeypatch: MonkeyPatch) -> None:
stderr=subprocess.DEVNULL,
check=True,
text=True,
encoding=encoding,
).stdout
except (OSError, subprocess.CalledProcessError):
pytest.skip("bash is not available")
@@ -312,7 +305,7 @@ def test_argcomplete(pytester: Pytester, monkeypatch: MonkeyPatch) -> None:
script = str(pytester.path.joinpath("test_argcomplete"))
with open(str(script), "w", encoding="utf-8") as fp:
with open(str(script), "w") as fp:
# redirect output from argcomplete to stdin and stderr is not trivial
# http://stackoverflow.com/q/12589419/1307905
# so we use bash
+21 -147
View File
@@ -1,4 +1,3 @@
import errno
import os.path
import pickle
import sys
@@ -8,7 +7,6 @@ from textwrap import dedent
from types import ModuleType
from typing import Any
from typing import Generator
from typing import Iterator
import pytest
from _pytest.monkeypatch import MonkeyPatch
@@ -19,16 +17,13 @@ from _pytest.pathlib import fnmatch_ex
from _pytest.pathlib import get_extended_length_path_str
from _pytest.pathlib import get_lock_path
from _pytest.pathlib import import_path
from _pytest.pathlib import ImportMode
from _pytest.pathlib import ImportPathMismatchError
from _pytest.pathlib import insert_missing_modules
from _pytest.pathlib import maybe_delete_a_numbered_dir
from _pytest.pathlib import module_name_from_path
from _pytest.pathlib import resolve_package_path
from _pytest.pathlib import safe_exists
from _pytest.pathlib import symlink_or_skip
from _pytest.pathlib import visit
from _pytest.pytester import Pytester
from _pytest.tmpdir import TempPathFactory
@@ -105,13 +100,13 @@ class TestImportPath:
def setuptestfs(self, path: Path) -> None:
# print "setting up test fs for", repr(path)
samplefile = path / "samplefile"
samplefile.write_text("samplefile\n", encoding="utf-8")
samplefile.write_text("samplefile\n")
execfile = path / "execfile"
execfile.write_text("x=42", encoding="utf-8")
execfile.write_text("x=42")
execfilepy = path / "execfile.py"
execfilepy.write_text("x=42", encoding="utf-8")
execfilepy.write_text("x=42")
d = {1: 2, "hello": "world", "answer": 42}
path.joinpath("samplepickle").write_bytes(pickle.dumps(d, 1))
@@ -125,9 +120,9 @@ class TestImportPath:
otherdir.joinpath("__init__.py").touch()
module_a = otherdir / "a.py"
module_a.write_text("from .b import stuff as result\n", encoding="utf-8")
module_a.write_text("from .b import stuff as result\n")
module_b = otherdir / "b.py"
module_b.write_text('stuff="got it"\n', encoding="utf-8")
module_b.write_text('stuff="got it"\n')
module_c = otherdir / "c.py"
module_c.write_text(
dedent(
@@ -136,8 +131,7 @@ class TestImportPath:
import otherdir.a
value = otherdir.a.result
"""
),
encoding="utf-8",
)
)
module_d = otherdir / "d.py"
module_d.write_text(
@@ -147,8 +141,7 @@ class TestImportPath:
from otherdir import a
value2 = a.result
"""
),
encoding="utf-8",
)
)
def test_smoke_test(self, path1: Path) -> None:
@@ -287,36 +280,29 @@ class TestImportPath:
import_path(tmp_path / "invalid.py", root=tmp_path)
@pytest.fixture
def simple_module(
self, tmp_path: Path, request: pytest.FixtureRequest
) -> Iterator[Path]:
name = f"mymod_{request.node.name}"
fn = tmp_path / f"_src/tests/{name}.py"
def simple_module(self, tmp_path: Path) -> Path:
fn = tmp_path / "_src/tests/mymod.py"
fn.parent.mkdir(parents=True)
fn.write_text("def foo(x): return 40 + x", encoding="utf-8")
module_name = module_name_from_path(fn, root=tmp_path)
yield fn
sys.modules.pop(module_name, None)
fn.write_text("def foo(x): return 40 + x")
return fn
def test_importmode_importlib(
self, simple_module: Path, tmp_path: Path, request: pytest.FixtureRequest
) -> None:
def test_importmode_importlib(self, simple_module: Path, tmp_path: Path) -> None:
"""`importlib` mode does not change sys.path."""
module = import_path(simple_module, mode="importlib", root=tmp_path)
assert module.foo(2) == 42 # type: ignore[attr-defined]
assert str(simple_module.parent) not in sys.path
assert module.__name__ in sys.modules
assert module.__name__ == f"_src.tests.mymod_{request.node.name}"
assert module.__name__ == "_src.tests.mymod"
assert "_src" in sys.modules
assert "_src.tests" in sys.modules
def test_remembers_previous_imports(
def test_importmode_twice_is_different_module(
self, simple_module: Path, tmp_path: Path
) -> None:
"""`importlib` mode called remembers previous module (#10341, #10811)."""
"""`importlib` mode always returns a new module."""
module1 = import_path(simple_module, mode="importlib", root=tmp_path)
module2 = import_path(simple_module, mode="importlib", root=tmp_path)
assert module1 is module2
assert module1 is not module2
def test_no_meta_path_found(
self, simple_module: Path, monkeypatch: MonkeyPatch, tmp_path: Path
@@ -329,9 +315,6 @@ class TestImportPath:
# mode='importlib' fails if no spec is found to load the module
import importlib.util
# Force module to be re-imported.
del sys.modules[module.__name__]
monkeypatch.setattr(
importlib.util, "spec_from_file_location", lambda *args: None
)
@@ -464,7 +447,7 @@ def test_samefile_false_negatives(tmp_path: Path, monkeypatch: MonkeyPatch) -> N
return False, even when they are clearly equal.
"""
module_path = tmp_path.joinpath("my_module.py")
module_path.write_text("def foo(): return 42", encoding="utf-8")
module_path.write_text("def foo(): return 42")
monkeypatch.syspath_prepend(tmp_path)
with monkeypatch.context() as mp:
@@ -490,8 +473,7 @@ class TestImportLibMode:
class Data:
value: str
"""
),
encoding="utf-8",
)
)
module = import_path(fn, mode="importlib", root=tmp_path)
@@ -516,8 +498,7 @@ class TestImportLibMode:
s = pickle.dumps(_action)
return pickle.loads(s)
"""
),
encoding="utf-8",
)
)
module = import_path(fn, mode="importlib", root=tmp_path)
@@ -544,8 +525,7 @@ class TestImportLibMode:
class Data:
x: int = 42
"""
),
encoding="utf-8",
)
)
fn2 = tmp_path.joinpath("_src/m2/tests/test.py")
@@ -560,8 +540,7 @@ class TestImportLibMode:
class Data:
x: str = ""
"""
),
encoding="utf-8",
)
)
import pickle
@@ -589,14 +568,6 @@ class TestImportLibMode:
result = module_name_from_path(Path("/home/foo/test_foo.py"), Path("/bar"))
assert result == "home.foo.test_foo"
# Importing __init__.py files should return the package as module name.
result = module_name_from_path(tmp_path / "src/app/__init__.py", tmp_path)
assert result == "src.app"
# Unless __init__.py file is at the root, in which case we cannot have an empty module name.
result = module_name_from_path(tmp_path / "__init__.py", tmp_path)
assert result == "__init__"
def test_insert_missing_modules(
self, monkeypatch: MonkeyPatch, tmp_path: Path
) -> None:
@@ -615,100 +586,3 @@ class TestImportLibMode:
modules = {}
insert_missing_modules(modules, "")
assert modules == {}
def test_parent_contains_child_module_attribute(
self, monkeypatch: MonkeyPatch, tmp_path: Path
):
monkeypatch.chdir(tmp_path)
# Use 'xxx' and 'xxy' as parent names as they are unlikely to exist and
# don't end up being imported.
modules = {"xxx.tests.foo": ModuleType("xxx.tests.foo")}
insert_missing_modules(modules, "xxx.tests.foo")
assert sorted(modules) == ["xxx", "xxx.tests", "xxx.tests.foo"]
assert modules["xxx"].tests is modules["xxx.tests"]
assert modules["xxx.tests"].foo is modules["xxx.tests.foo"]
def test_importlib_package(self, monkeypatch: MonkeyPatch, tmp_path: Path):
"""
Importing a package using --importmode=importlib should not import the
package's __init__.py file more than once (#11306).
"""
monkeypatch.chdir(tmp_path)
monkeypatch.syspath_prepend(tmp_path)
package_name = "importlib_import_package"
tmp_path.joinpath(package_name).mkdir()
init = tmp_path.joinpath(f"{package_name}/__init__.py")
init.write_text(
dedent(
"""
from .singleton import Singleton
instance = Singleton()
"""
),
encoding="ascii",
)
singleton = tmp_path.joinpath(f"{package_name}/singleton.py")
singleton.write_text(
dedent(
"""
class Singleton:
INSTANCES = []
def __init__(self) -> None:
self.INSTANCES.append(self)
if len(self.INSTANCES) > 1:
raise RuntimeError("Already initialized")
"""
),
encoding="ascii",
)
mod = import_path(init, root=tmp_path, mode=ImportMode.importlib)
assert len(mod.instance.INSTANCES) == 1
def test_importlib_root_is_package(self, pytester: Pytester) -> None:
"""
Regression for importing a `__init__`.py file that is at the root
(#11417).
"""
pytester.makepyfile(__init__="")
pytester.makepyfile(
"""
def test_my_test():
assert True
"""
)
result = pytester.runpytest("--import-mode=importlib")
result.stdout.fnmatch_lines("* 1 passed *")
def test_safe_exists(tmp_path: Path) -> None:
d = tmp_path.joinpath("some_dir")
d.mkdir()
assert safe_exists(d) is True
f = tmp_path.joinpath("some_file")
f.touch()
assert safe_exists(f) is True
# Use unittest.mock() as a context manager to have a very narrow
# patch lifetime.
p = tmp_path.joinpath("some long filename" * 100)
with unittest.mock.patch.object(
Path,
"exists",
autospec=True,
side_effect=OSError(errno.ENAMETOOLONG, "name too long"),
):
assert safe_exists(p) is False
with unittest.mock.patch.object(
Path,
"exists",
autospec=True,
side_effect=ValueError("name too long"),
):
assert safe_exists(p) is False
+3 -9
View File
@@ -242,12 +242,8 @@ class TestPytestPluginManager:
mod = types.ModuleType("temp")
mod.__dict__["pytest_plugins"] = ["pytest_p1", "pytest_p2"]
pytestpm.consider_module(mod)
p1 = pytestpm.get_plugin("pytest_p1")
assert p1 is not None
assert p1.__name__ == "pytest_p1"
p2 = pytestpm.get_plugin("pytest_p2")
assert p2 is not None
assert p2.__name__ == "pytest_p2"
assert pytestpm.get_plugin("pytest_p1").__name__ == "pytest_p1"
assert pytestpm.get_plugin("pytest_p2").__name__ == "pytest_p2"
def test_consider_module_import_module(
self, pytester: Pytester, _config_for_test: Config
@@ -340,7 +336,6 @@ class TestPytestPluginManager:
len2 = len(pytestpm.get_plugins())
assert len1 == len2
plugin1 = pytestpm.get_plugin("pytest_hello")
assert plugin1 is not None
assert plugin1.__name__.endswith("pytest_hello")
plugin2 = pytestpm.get_plugin("pytest_hello")
assert plugin2 is plugin1
@@ -352,11 +347,10 @@ class TestPytestPluginManager:
pytest.raises(ImportError, pytestpm.import_plugin, "pytest_qweqwex.y")
pytester.syspathinsert()
pytester.mkpydir("pkg").joinpath("plug.py").write_text("x=3", encoding="utf-8")
pytester.mkpydir("pkg").joinpath("plug.py").write_text("x=3")
pluginname = "pkg.plug"
pytestpm.import_plugin(pluginname)
mod = pytestpm.get_plugin("pkg.plug")
assert mod is not None
assert mod.x == 3
def test_consider_conftest_deps(
+1 -1
View File
@@ -222,7 +222,7 @@ class TestInlineRunModulesCleanup:
result = pytester.inline_run(str(test_mod))
assert result.ret == ExitCode.OK
# rewrite module, now test should fail if module was re-imported
test_mod.write_text("def test_foo(): assert False", encoding="utf-8")
test_mod.write_text("def test_foo(): assert False")
result2 = pytester.inline_run(str(test_mod))
assert result2.ret == ExitCode.TESTS_FAILED
+1 -1
View File
@@ -410,7 +410,7 @@ class TestReportSerialization:
) -> None:
sub_dir = pytester.path.joinpath("ns")
sub_dir.mkdir()
sub_dir.joinpath("conftest.py").write_text("import unknown", encoding="utf-8")
sub_dir.joinpath("conftest.py").write_text("import unknown")
result = pytester.runpytest_subprocess(".")
result.stdout.fnmatch_lines(["E *Error: No module named 'unknown'"])
+6 -6
View File
@@ -265,9 +265,9 @@ def test_plugin_already_exists(pytester: Pytester) -> None:
def test_exclude(pytester: Pytester) -> None:
hellodir = pytester.mkdir("hello")
hellodir.joinpath("test_hello.py").write_text("x y syntaxerror", encoding="utf-8")
hellodir.joinpath("test_hello.py").write_text("x y syntaxerror")
hello2dir = pytester.mkdir("hello2")
hello2dir.joinpath("test_hello2.py").write_text("x y syntaxerror", encoding="utf-8")
hello2dir.joinpath("test_hello2.py").write_text("x y syntaxerror")
pytester.makepyfile(test_ok="def test_pass(): pass")
result = pytester.runpytest("--ignore=hello", "--ignore=hello2")
assert result.ret == 0
@@ -276,13 +276,13 @@ def test_exclude(pytester: Pytester) -> None:
def test_exclude_glob(pytester: Pytester) -> None:
hellodir = pytester.mkdir("hello")
hellodir.joinpath("test_hello.py").write_text("x y syntaxerror", encoding="utf-8")
hellodir.joinpath("test_hello.py").write_text("x y syntaxerror")
hello2dir = pytester.mkdir("hello2")
hello2dir.joinpath("test_hello2.py").write_text("x y syntaxerror", encoding="utf-8")
hello2dir.joinpath("test_hello2.py").write_text("x y syntaxerror")
hello3dir = pytester.mkdir("hallo3")
hello3dir.joinpath("test_hello3.py").write_text("x y syntaxerror", encoding="utf-8")
hello3dir.joinpath("test_hello3.py").write_text("x y syntaxerror")
subdir = pytester.mkdir("sub")
subdir.joinpath("test_hello4.py").write_text("x y syntaxerror", encoding="utf-8")
subdir.joinpath("test_hello4.py").write_text("x y syntaxerror")
pytester.makepyfile(test_ok="def test_pass(): pass")
result = pytester.runpytest("--ignore-glob=*h[ea]llo*")
assert result.ret == 0
+7 -14
View File
@@ -195,8 +195,7 @@ class TestEvaluation:
def pytest_markeval_namespace():
return {"arg": "root"}
"""
),
encoding="utf-8",
)
)
root.joinpath("test_root.py").write_text(
textwrap.dedent(
@@ -207,8 +206,7 @@ class TestEvaluation:
def test_root():
assert False
"""
),
encoding="utf-8",
)
)
foo = root.joinpath("foo")
foo.mkdir()
@@ -221,8 +219,7 @@ class TestEvaluation:
def pytest_markeval_namespace():
return {"arg": "foo"}
"""
),
encoding="utf-8",
)
)
foo.joinpath("test_foo.py").write_text(
textwrap.dedent(
@@ -233,8 +230,7 @@ class TestEvaluation:
def test_foo():
assert False
"""
),
encoding="utf-8",
)
)
bar = root.joinpath("bar")
bar.mkdir()
@@ -247,8 +243,7 @@ class TestEvaluation:
def pytest_markeval_namespace():
return {"arg": "bar"}
"""
),
encoding="utf-8",
)
)
bar.joinpath("test_bar.py").write_text(
textwrap.dedent(
@@ -259,8 +254,7 @@ class TestEvaluation:
def test_bar():
assert False
"""
),
encoding="utf-8",
)
)
reprec = pytester.inline_run("-vs", "--capture=no")
@@ -635,8 +629,7 @@ class TestXFail:
@pytest.mark.xfail(reason='unsupported feature', strict=%s)
def test_foo():
with open('foo_executed', 'w', encoding='utf-8'):
pass # make sure test executes
with open('foo_executed', 'w'): pass # make sure test executes
"""
% strict
)
+1 -1
View File
@@ -352,6 +352,6 @@ def test_one():
assert result.ret == 0
assert Path(stepwise_cache_file).exists()
with stepwise_cache_file.open(encoding="utf-8") as file_handle:
with stepwise_cache_file.open() as file_handle:
observed_value = file_handle.readlines()
assert [expected_value] == observed_value
+3 -5
View File
@@ -244,8 +244,7 @@ class TestTerminal:
def test_method(self):
pass
"""
),
encoding="utf-8",
)
)
result = pytester.runpytest("-vv")
assert result.ret == 0
@@ -1568,8 +1567,7 @@ class TestGenericReporting:
"""
def pytest_report_header(config, start_path):
return ["line1", str(start_path)]
""",
encoding="utf-8",
"""
)
result = pytester.runpytest("a")
result.stdout.fnmatch_lines(["*hello: 42*", "line1", str(pytester.path)])
@@ -1673,7 +1671,7 @@ def test_fdopen_kept_alive_issue124(pytester: Pytester) -> None:
import os, sys
k = []
def test_open_file_and_keep_alive(capfd):
stdout = os.fdopen(1, 'w', buffering=1, encoding='utf-8')
stdout = os.fdopen(1, 'w', 1)
k.append(stdout)
def test_close_kept_alive_file():
+1 -1
View File
@@ -561,7 +561,7 @@ def test_basetemp_with_read_only_files(pytester: Pytester) -> None:
def test(tmp_path):
fn = tmp_path / 'foo.txt'
fn.write_text('hello', encoding='utf-8')
fn.write_text('hello')
mode = os.stat(str(fn)).st_mode
os.chmod(str(fn), mode & ~stat.S_IREAD)
"""
+2 -2
View File
@@ -810,12 +810,12 @@ def test_resource_warning(pytester: Pytester, monkeypatch: pytest.MonkeyPatch) -
pytester.makepyfile(
"""
def open_file(p):
f = p.open("r", encoding="utf-8")
f = p.open("r")
assert p.read_text() == "hello"
def test_resource_warning(tmp_path):
p = tmp_path.joinpath("foo.txt")
p.write_text("hello", encoding="utf-8")
p.write_text("hello")
open_file(p)
"""
)
-4
View File
@@ -38,10 +38,6 @@ passenv =
setenv =
_PYTEST_TOX_DEFAULT_POSARGS={env:_PYTEST_TOX_POSARGS_DOCTESTING:} {env:_PYTEST_TOX_POSARGS_LSOF:} {env:_PYTEST_TOX_POSARGS_XDIST:}
# See https://docs.python.org/3/library/io.html#io-encoding-warning
# If we don't enable this, neither can any of our downstream users!
PYTHONWARNDEFAULTENCODING=1
# Configuration to run with coverage similar to CI, e.g.
# "tox -e py37-coverage".
coverage: _PYTEST_TOX_COVERAGE_RUN=coverage run -m