Compare commits

...

106 Commits

Author SHA1 Message Date
Bruno Oliveira
4645bcd449 Remove incorrect output in how-to/fixtures.rst 2022-08-31 14:53:40 -03:00
pytest bot
fadfb4f346 Prepare release version 7.1.3 2022-08-31 17:49:55 +00:00
Bruno Oliveira
ab96ea88e8 Merge pull request #10258 from pytest-dev/backport-10252-to-7.1.x
[7.1.x] Fix regendoc
2022-08-31 14:39:56 -03:00
Bruno Oliveira
fc0e024b11 [7.1.x] Fix regendoc 2022-08-31 17:36:03 +00:00
Bruno Oliveira
8f5088f412 Merge pull request #10249 from pytest-dev/backport-10231-to-7.1.x 2022-08-26 11:13:25 -03:00
Bruno Oliveira
aae93d6127 Ignore type-errors related to attr.asdict 2022-08-26 09:57:18 -03:00
Gergely Kalmár
71b79fcda5 [7.1.x] Ignore editable installation modules 2022-08-26 12:48:36 +00:00
Bruno Oliveira
89f7518cb1 Merge pull request #10222 from pytest-dev/backport-10171-to-7.1.x
[7.1.x] Update fixtures.rst w/ finalizer order
2022-08-15 15:07:46 -03:00
aizpurua23a
88fc45bd57 [7.1.x] Update fixtures.rst w/ finalizer order 2022-08-15 17:23:43 +00:00
Bruno Oliveira
d0b53d6ba7 Merge pull request #10221 from pytest-dev/backport-10217-to-7.1.x
[7.1.x] Add reference to the Where to patch docs in monkeypatch.setattr
2022-08-15 14:18:39 -03:00
Bruno Oliveira
9b2c6cedc7 [7.1.x] Add reference to the Where to patch docs in monkeypatch.setattr 2022-08-15 17:13:13 +00:00
Bruno Oliveira
13fd967f54 Merge pull request #10213 from pytest-dev/backport-10192-to-7.1.x 2022-08-13 16:41:29 -03:00
Sviatoslav Sydorenko
f5cdd18b6a [7.1.x] Mention monkeypatch.context() in the docs 2022-08-13 19:08:56 +00:00
Bruno Oliveira
d7f27c4687 Merge pull request #10144 from pytest-dev/backport-10143-to-7.1.x 2022-08-12 08:53:49 -03:00
Bruno Oliveira
7133c546f8 [7.1.x] Add monkeypatch.context() to how-to doc intro 2022-08-12 08:34:29 -03:00
github-actions[bot]
6f8c4fa742 [7.1.x] JUnit XML: Escape error messages in setup/teardown (#10207)
Co-authored-by: holesch <8659229+holesch@users.noreply.github.com>
2022-08-12 10:06:32 +00:00
Bruno Oliveira
121b69276a Merge pull request #10140 from pytest-dev/backport-10138-to-7.1.x
[7.1.x] Explicit note that tmpdir fixture is discouraged in favour of tmp_path #9937
2022-07-15 10:14:41 -03:00
Bruno Oliveira
b9d00fba13 Merge pull request #10132 from hroncok/python3.11.0b4 2022-07-15 09:53:20 -03:00
Wolfremium
da7daaac92 [7.1.x] Explicit note that tmpdir fixture is discouraged in favour of tmp_path #9937 2022-07-15 11:41:58 +00:00
Bruno Oliveira
0b65f0bb6f Merge pull request #10137 from pytest-dev/backport-10133-to-7.1.x 2022-07-14 21:06:01 -03:00
Nipunn Koorapati
9b3affc006 [7.1.x] Add typing for FixtureRequest.param 2022-07-14 23:37:36 +00:00
Bruno Oliveira
ffb9d2bcea Merge pull request #10124 from pytest-dev/backport-10123-to-7.1.x
[7.1.x] Fix mypy pre-commit run
2022-07-11 10:12:50 -03:00
Anthony Sottile
55189ebda6 [7.1.x] Fix mypy pre-commit run 2022-07-11 12:49:27 +00:00
Anthony Sottile
44bdd94c9e Merge pull request #10117 from pytest-dev/backport-10115
[7.1.x] replace atomicwrites with os.replace
2022-07-09 06:05:34 -04:00
Anthony Sottile
199367168b [7.1.x] replace atomicwrites with os.replace
(cherry picked from commit 7dc540f258)
2022-07-08 19:13:28 -07:00
github-actions[bot]
1eb8434529 [7.1.x] Remove europython training (#10108)
Co-authored-by: Florian Bruhin <me@the-compiler.org>
2022-07-06 11:27:46 +00:00
Anthony Sottile
3a975e8569 Merge pull request #10092 from pytest-dev/backport-10088-to-7.1.x
[7.1.x] Pass importmode to import_path in DoctestModule
2022-06-30 09:04:41 -04:00
Anthony Sottile
549e3136cc [7.1.x] Pass importmode to import_path in DoctestModule 2022-06-30 05:46:18 -07:00
Anthony Sottile
ec055cf772 Merge pull request #10091 from pytest-dev/backport-10090
[7.1.x] update does_not_raise docs now that pytest is 3.7+ only
2022-06-29 14:31:09 -04:00
Anthony Sottile
550a42d717 [7.1.x] update does_not_raise docs now that pytest is 3.7+ only
(cherry picked from commit 2941da0f2b)
2022-06-29 14:12:54 -04:00
Bruno Oliveira
9c6cf93717 Merge pull request #10087 from pytest-dev/backport-10081-to-7.1.x 2022-06-28 13:10:08 -03:00
Bruno Oliveira
9a16e69835 [7.1.x] Do not call tearDown for skipped unittest.TestCases with --pdb 2022-06-28 15:51:30 +00:00
Bruno Oliveira
bf765cc3c8 Merge pull request #10083 from pytest-dev/backport-10078-to-7.1.x 2022-06-27 10:17:09 -03:00
Bruno Oliveira
0e7a6a769e [7.1.x] Use PurePath directly instead of os.path.sep in rewrite.py 2022-06-27 13:00:17 +00:00
Bruno Oliveira
8ae6237b3e Merge pull request #10077 from pytest-dev/backport-10054-to-7.1.x
[7.1.x] Indicate support for a tuple of exceptions in xfail raises=
2022-06-26 10:54:32 -03:00
Bruno Oliveira
7127b56a7d Merge pull request #10075 from pytest-dev/backport-10056-to-7.1.x
[7.1.x] Added Docstring description for the Path property of FixtureRequest #9975
2022-06-26 10:54:22 -03:00
pre-commit-ci[bot]
b8ce513e95 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2022-06-26 12:56:42 +00:00
Bruno Oliveira
315695f53f [7.1.x] Indicate support for a tuple of exceptions in xfail raises= 2022-06-26 12:55:40 +00:00
pre-commit-ci[bot]
a1027af168 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2022-06-26 01:31:08 +00:00
Bruno Oliveira
9c3bc106dd [7.1.x] Added Docstring description for the Path property of FixtureRequest #9975 2022-06-25 23:56:46 +00:00
Bruno Oliveira
c19571f30d Merge pull request #10046 from pytest-dev/backport-10031-to-7.1.x
[7.1.x] Update training list
2022-06-14 07:56:59 -03:00
Bruno Oliveira
a749e08c42 Merge pull request #10044 from pytest-dev/backport-10043-to-7.1.x
[7.1.x] Mark pdb+expect tests as xfail for now
2022-06-14 07:53:12 -03:00
Bruno Oliveira
d89c13d670 [7.1.x] Update training list 2022-06-14 10:41:13 +00:00
Bruno Oliveira
be9f8b1d05 [7.1.x] Mark pdb+expect tests as xfail for now 2022-06-14 10:30:49 +00:00
Bruno Oliveira
afa3bbd0e9 [7.1.x] Fix representation of tuples in approx (#10041)
Co-authored-by: Zach OBrien <zachobrien99@gmail.com>
2022-06-14 07:30:11 -03:00
Zach OBrien
dc0812610d [7.1.x] Fix representation of tuples in approx 2022-06-14 09:56:12 +00:00
Bruno Oliveira
c2aa80132a Merge pull request #10028 from pytest-dev/backport-10026-to-7.1.x
[7.1.x] Update location of `usage.rst` to fix manpage compilation
2022-06-07 18:51:52 -03:00
Bruno Oliveira
55e7a71446 [7.1.x] Update location of usage.rst to fix manpage compilation 2022-06-07 21:51:21 +00:00
Bruno Oliveira
2b0899a4d1 Merge pull request #10015 from pytest-dev/backport-10013-to-7.1.x
[7.1.x] docs(monkeypatch): Fix autodoc reference links
2022-06-02 11:07:39 -03:00
Tony Narlock
eb55d4c945 [7.1.x] docs(monkeypatch): Fix autodoc reference links 2022-06-02 13:19:05 +00:00
Bruno Oliveira
4f9863abf8 Merge pull request #10009 from pytest-dev/backport-10007-to-7.1.x 2022-05-31 16:44:52 -03:00
Bruno Oliveira
f70e370e35 [7.1.x] Do not advertise that importlib will be default import mode 2022-05-31 19:26:38 +00:00
github-actions[bot]
835de75819 [7.1.x] Declutter doc entry page (#9991)
Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>
2022-05-24 10:42:44 -03:00
Bruno Oliveira
68f6121882 Merge pull request #9974 from pytest-dev/backport-9970-to-7.1.x 2022-05-17 08:46:47 -03:00
Bruno Oliveira
31759f7af4 Merge pull request #9973 from pytest-dev/backport-9968-to-7.1.x 2022-05-17 08:33:56 -03:00
Bruno Oliveira
796574f3b8 [7.1.x] Fix rst markup in TempdirFactory's docstring. 2022-05-17 11:23:53 +00:00
Bruno Oliveira
07ea99c5a0 Merge pull request #9972 from pytest-dev/backport-9967-to-7.1.x 2022-05-17 08:19:58 -03:00
Bruno Oliveira
5f18e9d1ab [7.1.x] Reorder the reference guides in the docs 2022-05-17 11:00:47 +00:00
Bruno Oliveira
643cc79759 [7.1.x] Remove "Python 2.7 and 3.4 Support" from globaltoc 2022-05-17 10:54:19 +00:00
Bruno Oliveira
ce4a11a364 Merge pull request #9965 from pytest-dev/backport-9957-to-7.1.x 2022-05-16 09:08:50 -03:00
Tim Hoffmann
f7e57ae839 [7.1.x] Remove docs on Python 2.7 and 3.4 Support 2022-05-16 08:18:57 +00:00
Bruno Oliveira
d3a27deb92 Merge pull request #9952 from pytest-dev/backport-9951-to-7.1.x 2022-05-12 09:55:34 -03:00
Bruno Oliveira
cb5fd75bea [7.1.x] doc: link to pytest-subtests 2022-05-12 12:37:10 +00:00
Bruno Oliveira
440db691ec Merge pull request #9943 from pytest-dev/backport-9925-to-7.1.x 2022-05-11 08:33:48 -03:00
Pax
dc36836dd2 [7.1.x] Add link to python docs on logging levels 2022-05-11 11:00:39 +00:00
Bruno Oliveira
85c83cd37d Merge pull request #9942 from pytest-dev/backport-9940-to-7.1.x 2022-05-10 19:03:50 -03:00
Bruno Oliveira
91d84e9256 [7.1.x] Move documentation contents from reference.rst to docstrings 2022-05-10 20:23:08 +00:00
Bruno Oliveira
ab014a1a0d Merge pull request #9939 from pytest-dev/backport-9938-to-7.1.x 2022-05-10 12:15:46 -03:00
Bruno Oliveira
0379346675 [7.1.x] Consistently add **Tutorial**: in front of how-to links in reference 2022-05-10 14:41:08 +00:00
github-actions[bot]
2f8ae29c17 [7.1.x] testing: fix Path.rglob("") failures in Python 3.11b1 (#9934)
Co-authored-by: Ran Benita <ran@unusedvar.com>
2022-05-09 13:38:22 +00:00
github-actions[bot]
a7d7676f7b [7.1.x] Clarify precision when using NUMBER option in --doctest-modules (#9923)
Co-authored-by: MatthewFlamm <39341281+MatthewFlamm@users.noreply.github.com>
2022-05-06 23:50:10 +00:00
Bruno Oliveira
df9df55749 Prepare release version 7.1.2 (#9881)
Co-authored-by: pytest bot <pytestbot@gmail.com>
2022-04-23 11:37:08 -03:00
pytest bot
2f2f1a601e Prepare release version 7.1.2 2022-04-23 11:33:44 +00:00
github-actions[bot]
5c04f3a1a2 [7.1.x] Fix wrong log_file docs (#9879)
Co-authored-by: Zac Hatfield-Dodds <zac.hatfield.dodds@gmail.com>
2022-04-22 18:40:15 +00:00
Bruno Oliveira
078733c005 Merge pull request #9872 from pytest-dev/backport-9871-to-7.1.x 2022-04-21 00:07:25 -03:00
Zac Hatfield-Dodds
3a7ead6bcf [7.1.x] fix: move 'import getpass' statement to try-clause 2022-04-21 02:46:24 +00:00
Bruno Oliveira
6d75333780 [7.1.x] Increase stacklevel to point at user's code (#9870)
Co-authored-by: Hugo van Kemenade <hugovk@users.noreply.github.com>
2022-04-20 14:38:00 -03:00
Hugo van Kemenade
ddbb998aed [7.1.x] Increase stacklevel to point at user's code 2022-04-20 16:09:01 +00:00
Bruno Oliveira
0ec5886ad5 Merge pull request #9855 from pytest-dev/backport-9854-to-7.1.x 2022-04-12 14:05:44 -03:00
Bruno Oliveira
f2469fca37 [7.1.x] Docs: link to easy issues in contributing guide 2022-04-12 16:42:32 +00:00
Bruno Oliveira
94ec0f8ad8 Merge pull request #9846 from pytest-dev/backport-9842-to-7.1.x 2022-04-09 11:25:28 -03:00
Anthony Sottile
5ef96fdb53 [7.1.x] fix comparison of dataclasses with InitVar 2022-04-09 00:48:09 +00:00
Bruno Oliveira
7a501fb313 Merge pull request #9844 from pytest-dev/backport-9843-to-7.1.x 2022-04-08 21:46:44 -03:00
Anthony Sottile
1769c66def [7.1.x] update pre-commit hooks 2022-04-08 20:21:19 -04:00
Bruno Oliveira
840c418de6 [7.1.x] temporarily pin jinja2 version for docs build 2022-04-08 23:35:13 +00:00
Bruno Oliveira
6461e2e385 Merge pull request #9805 from pytest-dev/backport-9804-to-7.1.x 2022-03-21 13:39:05 -03:00
Bruno Oliveira
b55b7f1ad4 [7.1.x] Change directories during some tests in test_collection.py 2022-03-21 16:13:52 +00:00
Bruno Oliveira
d9794ed3cf Merge pull request #9801 from pytest-dev/backport-9800-to-7.1.x 2022-03-21 08:38:29 -03:00
Bruno Oliveira
8b33683cbf [7.1.x] Fix CI for Python 3.11 2022-03-21 11:20:19 +00:00
Bruno Oliveira
1d2e50faa6 Merge pull request #9799 from pytest-dev/backport-9798-to-7.1.x 2022-03-20 22:55:24 -03:00
Bruno Oliveira
6820ab2bd4 Merge pull request #9795 from pytest-dev/backport-9794-to-7.1.x
[7.1.x] Split test/deploy workflows
2022-03-20 22:35:44 -03:00
Kian Eliasi
78356dc353 [7.1.x] Remove unnecessary numpy import 2022-03-21 01:35:22 +00:00
Bruno Oliveira
f1c27608ec [7.1.x] Split test/deploy workflows 2022-03-19 12:45:41 +00:00
Bruno Oliveira
0ceaa57d9d Merge pull request #9790 from pytest-dev/backport-9789-to-7.1.x
[7.1.x] [style]: fix typo in docstring
2022-03-18 19:01:32 -03:00
Bruno Oliveira
93fad3286b [7.1.x] [style]: fix typo in docstring 2022-03-18 21:59:16 +00:00
Ran Benita
b9462ed7d0 Merge pull request #9785 from pytest-dev/release-7.1.1
Prepare release 7.1.1
2022-03-17 23:17:11 +02:00
pytest bot
0ffe9e0742 Prepare release version 7.1.1 2022-03-17 20:21:30 +00:00
Ran Benita
6f2c1ec035 Merge pull request #9784 from pytest-dev/backport-9768-to-7.1.x
[7.1.x] testing: fix tests when run under `-v` or `-vv`
2022-03-17 22:13:28 +02:00
Ran Benita
a65c47a1a4 Merge pull request #9783 from pytest-dev/backport-9780-to-7.1.x
[7.1.x] config: restore pre-pytest 7.1.0 confcutdir exclusion behavior
2022-03-17 18:34:49 +02:00
pre-commit-ci[bot]
30d995ed25 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2022-03-17 16:27:25 +00:00
Ran Benita
10a14d1318 [7.1.x] testing: fix tests when run under -v or -vv 2022-03-17 16:26:18 +00:00
pre-commit-ci[bot]
f4cfc596c6 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2022-03-17 16:16:12 +00:00
Ran Benita
f1df8074b3 [7.1.x] config: restore pre-pytest 7.1.0 confcutdir exclusion behavior 2022-03-17 16:15:03 +00:00
Ran Benita
7d4d1ecde6 Merge pull request #9758 from pytest-dev/release-7.1.0
Prepare release 7.1.0
2022-03-13 16:58:05 +02:00
pre-commit-ci[bot]
1dbffcc0b4 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2022-03-13 13:44:48 +00:00
pytest bot
d53a5fb371 Prepare release version 7.1.0 2022-03-13 13:43:44 +00:00
80 changed files with 1107 additions and 628 deletions

56
.github/workflows/deploy.yml vendored Normal file
View File

@@ -0,0 +1,56 @@
name: deploy
on:
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:
deploy:
if: github.repository == 'pytest-dev/pytest'
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: write
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: "3.7"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install --upgrade build tox
- name: Build package
run: |
python -m build
- name: Publish package to PyPI
uses: pypa/gh-action-pypi-publish@master
with:
user: __token__
password: ${{ secrets.pypi_token }}
- name: Publish GitHub release notes
env:
GH_RELEASE_NOTES_TOKEN: ${{ github.token }}
run: |
sudo apt-get install pandoc
tox -e publish-gh-release-notes

View File

@@ -1,4 +1,4 @@
name: main
name: test
on:
push:
@@ -187,46 +187,3 @@ jobs:
fail_ci_if_error: true
files: ./coverage.xml
verbose: true
deploy:
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') && github.repository == 'pytest-dev/pytest'
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: write
needs: [build]
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: "3.7"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install --upgrade build tox
- name: Build package
run: |
python -m build
- name: Publish package to PyPI
uses: pypa/gh-action-pypi-publish@master
with:
user: __token__
password: ${{ secrets.pypi_token }}
- name: Publish GitHub release notes
env:
GH_RELEASE_NOTES_TOKEN: ${{ github.token }}
run: |
sudo apt-get install pandoc
tox -e publish-gh-release-notes

View File

@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/psf/black
rev: 22.1.0
rev: 22.3.0
hooks:
- id: black
args: [--safe, --quiet]
@@ -37,7 +37,7 @@ repos:
- flake8-typing-imports==1.12.0
- flake8-docstrings==1.5.0
- repo: https://github.com/asottile/reorder_python_imports
rev: v2.7.1
rev: v3.0.1
hooks:
- id: reorder-python-imports
args: ['--application-directories=.:src', --py37-plus]
@@ -67,7 +67,6 @@ repos:
- attrs>=19.2.0
- packaging
- tomli
- types-atomicwrites
- types-pkg_resources
- repo: local
hooks:
@@ -101,7 +100,7 @@ repos:
types: [python]
- id: py-path-deprecated
name: py.path usage is deprecated
exclude: docs|src/_pytest/deprecated.py|testing/deprecated_test.py
exclude: docs|src/_pytest/deprecated.py|testing/deprecated_test.py|src/_pytest/legacypath.py
language: pygrep
entry: \bpy\.path\.local
types: [python]

12
AUTHORS
View File

@@ -15,6 +15,7 @@ Alan Velasco
Alexander Johnson
Alexander King
Alexei Kozlenok
Alice Purcell
Allan Feldman
Aly Sivji
Amir Elkess
@@ -83,6 +84,7 @@ Damian Skrzypczak
Daniel Grana
Daniel Hahler
Daniel Nuri
Daniel Sánchez Castelló
Daniel Wandschneider
Daniele Procida
Danielle Jenkins
@@ -184,7 +186,9 @@ Katarzyna Król
Katerina Koukiou
Keri Volans
Kevin Cox
Kevin Hierro Carrasco
Kevin J. Foley
Kian Eliasi
Kian-Meng Ang
Kodi B. Arfer
Kojo Idrissa
@@ -245,6 +249,7 @@ Nicholas Murphy
Niclas Olofsson
Nicolas Delaby
Nikolay Kondratyev
Nipunn Koorapati
Olga Matoula
Oleg Pidsadnyi
Oleg Sushchenko
@@ -255,6 +260,8 @@ Ondřej Súkup
Oscar Benjamin
Parth Patel
Patrick Hayes
Paul Müller
Paul Reece
Pauli Virtanen
Pavel Karateev
Paweł Adamczak
@@ -300,6 +307,7 @@ Seth Junot
Shantanu Jain
Shubham Adep
Simon Gomizelj
Simon Holesch
Simon Kerr
Skylar Downes
Srinivas Reddy Thatiparthy
@@ -317,6 +325,7 @@ Taneli Hukkinen
Tanvi Mehta
Tarcisio Fischer
Tareq Alayan
Tatiana Ovary
Ted Xiao
Terje Runde
Thomas Grainger
@@ -327,12 +336,14 @@ Tom Dalton
Tom Viner
Tomáš Gavenčiak
Tomer Keren
Tony Narlock
Tor Colvin
Trevor Bekolay
Tyler Goodlet
Tzu-ping Chung
Vasily Kuznetsov
Victor Maryama
Victor Rodriguez
Victor Uriarte
Vidar T. Fauske
Virgil Dupras
@@ -353,5 +364,6 @@ Yoav Caspi
Yuval Shimon
Zac Hatfield-Dodds
Zachary Kneupper
Zachary OBrien
Zoltán Máté
Zsolt Cserna

View File

@@ -50,6 +50,8 @@ Fix bugs
--------
Look through the `GitHub issues for bugs <https://github.com/pytest-dev/pytest/labels/type:%20bug>`_.
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
to work on a particular issue, add a comment to that effect on the specific issue.

View File

@@ -20,8 +20,8 @@
:target: https://codecov.io/gh/pytest-dev/pytest
:alt: Code coverage Status
.. image:: https://github.com/pytest-dev/pytest/workflows/main/badge.svg
:target: https://github.com/pytest-dev/pytest/actions?query=workflow%3Amain
.. image:: https://github.com/pytest-dev/pytest/workflows/test/badge.svg
:target: https://github.com/pytest-dev/pytest/actions?query=workflow%3Atest
.. image:: https://results.pre-commit.ci/badge/github/pytest-dev/pytest/main.svg
:target: https://results.pre-commit.ci/latest/github/pytest-dev/pytest/main

View File

@@ -1,3 +0,0 @@
Fixed test output for some data types where ``-v`` would show less information.
Also, when showing diffs for sequences, ``-q`` would produce full diffs instead of the expected diff.

View File

@@ -1,3 +0,0 @@
The deprecation of raising :class:`unittest.SkipTest` to skip collection of
tests during the pytest collection phase is reverted - this is now a supported
feature again.

View File

@@ -1,15 +0,0 @@
As per our policy, the following features have been deprecated in the 6.X series and are now
removed:
* ``pytest._fillfuncargs`` function.
* ``pytest_warning_captured`` hook - use ``pytest_warning_recorded`` instead.
* ``-k -foobar`` syntax - use ``-k 'not foobar'`` instead.
* ``-k foobar:`` syntax.
* ``pytest.collect`` module - import from ``pytest`` directly.
For more information consult
`Deprecations and Removals <https://docs.pytest.org/en/latest/deprecations.html>`__ in the docs.

View File

@@ -1 +0,0 @@
pytest now avoids specialized assert formatting when it is detected that the default ``__eq__`` is overridden in ``attrs`` or ``dataclasses``.

View File

@@ -1 +0,0 @@
Dropped support for Python 3.6, which reached `end-of-life <https://devguide.python.org/#status-of-python-branches>`__ at 2021-12-23.

View File

@@ -1,10 +0,0 @@
Symbolic link components are no longer resolved in conftest paths.
This means that if a conftest appears twice in collection tree, using symlinks, it will be executed twice.
For example, given
tests/real/conftest.py
tests/real/test_it.py
tests/link -> tests/real
running ``pytest tests`` now imports the conftest twice, once as ``tests/real/conftest.py`` and once as ``tests/link/conftest.py``.
This is a fix to match a similar change made to test collection itself in pytest 6.0 (see :pull:`6523` for details).

View File

@@ -1 +0,0 @@
When ``-vv`` is given on command line, show skipping and xfail reasons in full instead of truncating them to fit the terminal width.

View File

@@ -1,3 +0,0 @@
Fixed count of selected tests on terminal collection summary when there were errors or skipped modules.
If there were errors or skipped modules on collection, pytest would mistakenly subtract those from the selected count.

View File

@@ -1,4 +0,0 @@
More information about the location of resources that led Python to raise :class:`ResourceWarning` can now
be obtained by enabling :mod:`tracemalloc`.
See :ref:`resource-warnings` for more information.

View File

@@ -1 +0,0 @@
Fixed regression where ``--import-mode=importlib`` used together with :envvar:`PYTHONPATH` or :confval:`pythonpath` would cause import errors in test suites.

View File

@@ -1,3 +0,0 @@
More types are now accepted in the ``ids`` argument to ``@pytest.mark.parametrize``.
Previously only `str`, `float`, `int` and `bool` were accepted;
now `bytes`, `complex`, `re.Pattern`, `Enum` and anything with a `__name__` are also accepted.

View File

@@ -1,3 +0,0 @@
:func:`pytest.approx` now raises a :class:`TypeError` when given an unordered sequence (such as :class:`set`).
Note that this implies that custom classes which only implement ``__iter__`` and ``__len__`` are no longer supported as they don't guarantee order.

View File

@@ -1 +0,0 @@
:fixture:`pytester` now requests a :fixture:`monkeypatch` fixture instead of creating one internally. This solves some issues with tests that involve pytest environment variables.

View File

@@ -1 +0,0 @@
Malformed ``pyproject.toml`` files now produce a clearer error message.

View File

@@ -17,7 +17,6 @@
<li><a href="{{ pathto('changelog') }}">Changelog</a></li>
<li><a href="{{ pathto('contributing') }}">Contributing</a></li>
<li><a href="{{ pathto('backwards-compatibility') }}">Backwards Compatibility</a></li>
<li><a href="{{ pathto('py27-py34-deprecation') }}">Python 2.7 and 3.4 Support</a></li>
<li><a href="{{ pathto('sponsor') }}">Sponsor</a></li>
<li><a href="{{ pathto('tidelift') }}">pytest for Enterprise</a></li>
<li><a href="{{ pathto('license') }}">License</a></li>
@@ -30,5 +29,3 @@
{%- endif %}
<hr>
<a href="{{ pathto('genindex') }}">Index</a>
<hr>

View File

@@ -6,6 +6,10 @@ Release announcements
:maxdepth: 2
release-7.1.3
release-7.1.2
release-7.1.1
release-7.1.0
release-7.0.1
release-7.0.0
release-7.0.0rc1

View File

@@ -0,0 +1,48 @@
pytest-7.1.0
=======================================
The pytest team is proud to announce the 7.1.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:
* Akuli
* Andrew Svetlov
* Anthony Sottile
* Brett Holman
* Bruno Oliveira
* Chris NeJame
* Dan Alvizu
* Elijah DeLee
* Emmanuel Arias
* Fabian Egli
* Florian Bruhin
* Gabor Szabo
* Hasan Ramezani
* Hugo van Kemenade
* Kian Meng, Ang
* Kojo Idrissa
* Masaru Tsuchiyama
* Olga Matoula
* P. L. Lim
* Ran Benita
* Tobias Deiminger
* Yuval Shimon
* eduardo naufel schettino
* Éric
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,18 @@
pytest-7.1.1
=======================================
pytest 7.1.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:
* Ran Benita
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,23 @@
pytest-7.1.2
=======================================
pytest 7.1.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:
* Anthony Sottile
* Bruno Oliveira
* Hugo van Kemenade
* Kian Eliasi
* Ran Benita
* Zac Hatfield-Dodds
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,28 @@
pytest-7.1.3
=======================================
pytest 7.1.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:
* Anthony Sottile
* Bruno Oliveira
* Gergely Kalmár
* Nipunn Koorapati
* Pax
* Sviatoslav Sydorenko
* Tim Hoffmann
* Tony Narlock
* Wolfremium
* Zach OBrien
* aizpurua23a
Happy testing,
The pytest Development Team

View File

@@ -77,3 +77,18 @@ Deprecation Roadmap
Features currently deprecated and removed in previous releases can be found in :ref:`deprecations`.
We track future deprecation and removal of features using milestones and the `deprecation <https://github.com/pytest-dev/pytest/issues?q=label%3A%22type%3A+deprecation%22>`_ and `removal <https://github.com/pytest-dev/pytest/labels/type%3A%20removal>`_ labels on GitHub.
Python version support
======================
Released pytest versions support all Python versions that are actively maintained at the time of the release:
============== ===================
pytest version min. Python version
============== ===================
7.1+ 3.7+
6.2 - 7.0 3.6+
5.0 - 6.1 3.5+
3.3 - 4.6 2.7, 3.4+
============== ===================

View File

@@ -40,32 +40,86 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
calls, which return a ``(out, err)`` namedtuple.
``out`` and ``err`` will be ``text`` objects.
capsysbinary -- .../_pytest/capture.py:895
Returns an instance of :class:`CaptureFixture[str] <pytest.CaptureFixture>`.
Example:
.. code-block:: python
def test_output(capsys):
print("hello")
captured = capsys.readouterr()
assert captured.out == "hello\n"
capsysbinary -- .../_pytest/capture.py:906
Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``.
The captured output is made available via ``capsysbinary.readouterr()``
method calls, which return a ``(out, err)`` namedtuple.
``out`` and ``err`` will be ``bytes`` objects.
capfd -- .../_pytest/capture.py:912
Returns an instance of :class:`CaptureFixture[bytes] <pytest.CaptureFixture>`.
Example:
.. code-block:: python
def test_output(capsysbinary):
print("hello")
captured = capsysbinary.readouterr()
assert captured.out == b"hello\n"
capfd -- .../_pytest/capture.py:934
Enable text capturing of writes to file descriptors ``1`` and ``2``.
The captured output is made available via ``capfd.readouterr()`` method
calls, which return a ``(out, err)`` namedtuple.
``out`` and ``err`` will be ``text`` objects.
capfdbinary -- .../_pytest/capture.py:929
Returns an instance of :class:`CaptureFixture[str] <pytest.CaptureFixture>`.
Example:
.. code-block:: python
def test_system_echo(capfd):
os.system('echo "hello"')
captured = capfd.readouterr()
assert captured.out == "hello\n"
capfdbinary -- .../_pytest/capture.py:962
Enable bytes capturing of writes to file descriptors ``1`` and ``2``.
The captured output is made available via ``capfd.readouterr()`` method
calls, which return a ``(out, err)`` namedtuple.
``out`` and ``err`` will be ``byte`` objects.
doctest_namespace [session scope] -- .../_pytest/doctest.py:731
Returns an instance of :class:`CaptureFixture[bytes] <pytest.CaptureFixture>`.
Example:
.. code-block:: python
def test_system_echo(capfdbinary):
os.system('echo "hello"')
captured = capfdbinary.readouterr()
assert captured.out == b"hello\n"
doctest_namespace [session scope] -- .../_pytest/doctest.py:735
Fixture that returns a :py:class:`dict` that will be injected into the
namespace of doctests.
pytestconfig [session scope] -- .../_pytest/fixtures.py:1365
Usually this fixture is used in conjunction with another ``autouse`` fixture:
.. code-block:: python
@pytest.fixture(autouse=True)
def add_np(doctest_namespace):
doctest_namespace["np"] = numpy
For more details: :ref:`doctest_namespace`.
pytestconfig [session scope] -- .../_pytest/fixtures.py:1344
Session-scoped fixture that returns the session's :class:`pytest.Config`
object.
@@ -117,10 +171,10 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
`pytest-xdist <https://github.com/pytest-dev/pytest-xdist>`__ plugin. See
:issue:`7767` for details.
tmpdir_factory [session scope] -- .../_pytest/legacypath.py:295
tmpdir_factory [session scope] -- .../_pytest/legacypath.py:302
Return a :class:`pytest.TempdirFactory` instance for the test session.
tmpdir -- .../_pytest/legacypath.py:302
tmpdir -- .../_pytest/legacypath.py:309
Return a temporary directory path object which is unique to each test
function invocation, created as a sub directory of the base temporary
directory.
@@ -132,9 +186,14 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
The returned object is a `legacy_path`_ object.
.. note::
These days, it is preferred to use ``tmp_path``.
:ref:`About the tmpdir and tmpdir_factory fixtures<tmpdir and tmpdir_factory>`.
.. _legacy_path: https://py.readthedocs.io/en/latest/path.html
caplog -- .../_pytest/logging.py:483
caplog -- .../_pytest/logging.py:487
Access and control log capturing.
Captured logs are available through the following properties/methods::
@@ -148,21 +207,26 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
monkeypatch -- .../_pytest/monkeypatch.py:29
A convenient fixture for monkey-patching.
The fixture provides these methods to modify objects, dictionaries or
os.environ::
The fixture provides these methods to modify objects, dictionaries, or
:data:`os.environ`:
monkeypatch.setattr(obj, name, value, raising=True)
monkeypatch.delattr(obj, name, raising=True)
monkeypatch.setitem(mapping, name, value)
monkeypatch.delitem(obj, name, raising=True)
monkeypatch.setenv(name, value, prepend=None)
monkeypatch.delenv(name, raising=True)
monkeypatch.syspath_prepend(path)
monkeypatch.chdir(path)
* :meth:`monkeypatch.setattr(obj, name, value, raising=True) <pytest.MonkeyPatch.setattr>`
* :meth:`monkeypatch.delattr(obj, name, raising=True) <pytest.MonkeyPatch.delattr>`
* :meth:`monkeypatch.setitem(mapping, name, value) <pytest.MonkeyPatch.setitem>`
* :meth:`monkeypatch.delitem(obj, name, raising=True) <pytest.MonkeyPatch.delitem>`
* :meth:`monkeypatch.setenv(name, value, prepend=None) <pytest.MonkeyPatch.setenv>`
* :meth:`monkeypatch.delenv(name, raising=True) <pytest.MonkeyPatch.delenv>`
* :meth:`monkeypatch.syspath_prepend(path) <pytest.MonkeyPatch.syspath_prepend>`
* :meth:`monkeypatch.chdir(path) <pytest.MonkeyPatch.chdir>`
* :meth:`monkeypatch.context() <pytest.MonkeyPatch.context>`
All modifications will be undone after the requesting test function or
fixture has finished. The ``raising`` parameter determines if a KeyError
or AttributeError will be raised if the set/deletion operation has no target.
fixture has finished. The ``raising`` parameter determines if a :class:`KeyError`
or :class:`AttributeError` will be raised if the set/deletion operation does not have the
specified target.
To undo modifications done by the fixture in a contained scope,
use :meth:`context() <pytest.MonkeyPatch.context>`.
recwarn -- .../_pytest/recwarn.py:29
Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions.
@@ -170,10 +234,10 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
See https://docs.python.org/library/how-to/capture-warnings.html for information
on warning categories.
tmp_path_factory [session scope] -- .../_pytest/tmpdir.py:183
tmp_path_factory [session scope] -- .../_pytest/tmpdir.py:184
Return a :class:`pytest.TempPathFactory` instance for the test session.
tmp_path -- .../_pytest/tmpdir.py:198
tmp_path -- .../_pytest/tmpdir.py:199
Return a temporary directory path object which is unique to each test
function invocation, created as a sub directory of the base temporary
directory.

View File

@@ -28,6 +28,168 @@ with advance notice in the **Deprecations** section of releases.
.. towncrier release notes start
pytest 7.1.3 (2022-08-31)
=========================
Bug Fixes
---------
- `#10060 <https://github.com/pytest-dev/pytest/issues/10060>`_: When running with ``--pdb``, ``TestCase.tearDown`` is no longer called for tests when the *class* has been skipped via ``unittest.skip`` or ``pytest.mark.skip``.
- `#10190 <https://github.com/pytest-dev/pytest/issues/10190>`_: Invalid XML characters in setup or teardown error messages are now properly escaped for JUnit XML reports.
- `#10230 <https://github.com/pytest-dev/pytest/issues/10230>`_: Ignore ``.py`` files created by ``pyproject.toml``-based editable builds introduced in `pip 21.3 <https://pip.pypa.io/en/stable/news/#v21-3>`__.
- `#3396 <https://github.com/pytest-dev/pytest/issues/3396>`_: Doctests now respect the ``--import-mode`` flag.
- `#9514 <https://github.com/pytest-dev/pytest/issues/9514>`_: Type-annotate ``FixtureRequest.param`` as ``Any`` as a stop gap measure until :issue:`8073` is fixed.
- `#9791 <https://github.com/pytest-dev/pytest/issues/9791>`_: Fixed a path handling code in ``rewrite.py`` that seems to work fine, but was incorrect and fails in some systems.
- `#9917 <https://github.com/pytest-dev/pytest/issues/9917>`_: Fixed string representation for :func:`pytest.approx` when used to compare tuples.
Improved Documentation
----------------------
- `#9937 <https://github.com/pytest-dev/pytest/issues/9937>`_: Explicit note that :fixture:`tmpdir` fixture is discouraged in favour of :fixture:`tmp_path`.
Trivial/Internal Changes
------------------------
- `#10114 <https://github.com/pytest-dev/pytest/issues/10114>`_: Replace `atomicwrites <https://github.com/untitaker/python-atomicwrites>`__ dependency on windows with `os.replace`.
pytest 7.1.2 (2022-04-23)
=========================
Bug Fixes
---------
- `#9726 <https://github.com/pytest-dev/pytest/issues/9726>`_: An unnecessary ``numpy`` import inside :func:`pytest.approx` was removed.
- `#9820 <https://github.com/pytest-dev/pytest/issues/9820>`_: Fix comparison of ``dataclasses`` with ``InitVar``.
- `#9869 <https://github.com/pytest-dev/pytest/issues/9869>`_: Increase ``stacklevel`` for the ``NODE_CTOR_FSPATH_ARG`` deprecation to point to the
user's code, not pytest.
- `#9871 <https://github.com/pytest-dev/pytest/issues/9871>`_: Fix a bizarre (and fortunately rare) bug where the `temp_path` fixture could raise
an internal error while attempting to get the current user's username.
pytest 7.1.1 (2022-03-17)
=========================
Bug Fixes
---------
- `#9767 <https://github.com/pytest-dev/pytest/issues/9767>`_: Fixed a regression in pytest 7.1.0 where some conftest.py files outside of the source tree (e.g. in the `site-packages` directory) were not picked up.
pytest 7.1.0 (2022-03-13)
=========================
Breaking Changes
----------------
- `#8838 <https://github.com/pytest-dev/pytest/issues/8838>`_: As per our policy, the following features have been deprecated in the 6.X series and are now
removed:
* ``pytest._fillfuncargs`` function.
* ``pytest_warning_captured`` hook - use ``pytest_warning_recorded`` instead.
* ``-k -foobar`` syntax - use ``-k 'not foobar'`` instead.
* ``-k foobar:`` syntax.
* ``pytest.collect`` module - import from ``pytest`` directly.
For more information consult
`Deprecations and Removals <https://docs.pytest.org/en/latest/deprecations.html>`__ in the docs.
- `#9437 <https://github.com/pytest-dev/pytest/issues/9437>`_: Dropped support for Python 3.6, which reached `end-of-life <https://devguide.python.org/#status-of-python-branches>`__ at 2021-12-23.
Improvements
------------
- `#5192 <https://github.com/pytest-dev/pytest/issues/5192>`_: Fixed test output for some data types where ``-v`` would show less information.
Also, when showing diffs for sequences, ``-q`` would produce full diffs instead of the expected diff.
- `#9362 <https://github.com/pytest-dev/pytest/issues/9362>`_: pytest now avoids specialized assert formatting when it is detected that the default ``__eq__`` is overridden in ``attrs`` or ``dataclasses``.
- `#9536 <https://github.com/pytest-dev/pytest/issues/9536>`_: When ``-vv`` is given on command line, show skipping and xfail reasons in full instead of truncating them to fit the terminal width.
- `#9644 <https://github.com/pytest-dev/pytest/issues/9644>`_: More information about the location of resources that led Python to raise :class:`ResourceWarning` can now
be obtained by enabling :mod:`tracemalloc`.
See :ref:`resource-warnings` for more information.
- `#9678 <https://github.com/pytest-dev/pytest/issues/9678>`_: More types are now accepted in the ``ids`` argument to ``@pytest.mark.parametrize``.
Previously only `str`, `float`, `int` and `bool` were accepted;
now `bytes`, `complex`, `re.Pattern`, `Enum` and anything with a `__name__` are also accepted.
- `#9692 <https://github.com/pytest-dev/pytest/issues/9692>`_: :func:`pytest.approx` now raises a :class:`TypeError` when given an unordered sequence (such as :class:`set`).
Note that this implies that custom classes which only implement ``__iter__`` and ``__len__`` are no longer supported as they don't guarantee order.
Bug Fixes
---------
- `#8242 <https://github.com/pytest-dev/pytest/issues/8242>`_: The deprecation of raising :class:`unittest.SkipTest` to skip collection of
tests during the pytest collection phase is reverted - this is now a supported
feature again.
- `#9493 <https://github.com/pytest-dev/pytest/issues/9493>`_: Symbolic link components are no longer resolved in conftest paths.
This means that if a conftest appears twice in collection tree, using symlinks, it will be executed twice.
For example, given
tests/real/conftest.py
tests/real/test_it.py
tests/link -> tests/real
running ``pytest tests`` now imports the conftest twice, once as ``tests/real/conftest.py`` and once as ``tests/link/conftest.py``.
This is a fix to match a similar change made to test collection itself in pytest 6.0 (see :pull:`6523` for details).
- `#9626 <https://github.com/pytest-dev/pytest/issues/9626>`_: Fixed count of selected tests on terminal collection summary when there were errors or skipped modules.
If there were errors or skipped modules on collection, pytest would mistakenly subtract those from the selected count.
- `#9645 <https://github.com/pytest-dev/pytest/issues/9645>`_: Fixed regression where ``--import-mode=importlib`` used together with :envvar:`PYTHONPATH` or :confval:`pythonpath` would cause import errors in test suites.
- `#9708 <https://github.com/pytest-dev/pytest/issues/9708>`_: :fixture:`pytester` now requests a :fixture:`monkeypatch` fixture instead of creating one internally. This solves some issues with tests that involve pytest environment variables.
- `#9730 <https://github.com/pytest-dev/pytest/issues/9730>`_: Malformed ``pyproject.toml`` files now produce a clearer error message.
pytest 7.0.1 (2022-02-11)
=========================
@@ -2497,7 +2659,8 @@ Important
This release is a Python3.5+ only release.
For more details, see our :std:doc:`Python 2.7 and 3.4 support plan <py27-py34-deprecation>`.
For more details, see our `Python 2.7 and 3.4 support plan
<https://docs.pytest.org/en/7.0.x/py27-py34-deprecation.html>`_.
Removals
--------
@@ -2721,7 +2884,11 @@ Features
- :issue:`6870`: New ``Config.invocation_args`` attribute containing the unchanged arguments passed to ``pytest.main()``.
Remark: while this is technically a new feature and according to our :ref:`policy <what goes into 4.6.x releases>` it should not have been backported, we have opened an exception in this particular case because it fixes a serious interaction with ``pytest-xdist``, so it can also be considered a bugfix.
Remark: while this is technically a new feature and according to our
`policy <https://docs.pytest.org/en/7.0.x/py27-py34-deprecation.html#what-goes-into-4-6-x-releases>`_
it should not have been backported, we have opened an exception in this
particular case because it fixes a serious interaction with ``pytest-xdist``,
so it can also be considered a bugfix.
Trivial/Internal Changes
------------------------
@@ -2893,7 +3060,8 @@ Important
The ``4.6.X`` series will be the last series to support **Python 2 and Python 3.4**.
For more details, see our :std:doc:`Python 2.7 and 3.4 support plan <py27-py34-deprecation>`.
For more details, see our `Python 2.7 and 3.4 support plan
<https://docs.pytest.org/en/7.0.x/py27-py34-deprecation.html>`_.
Features

View File

@@ -247,7 +247,7 @@ html_sidebars = {
html_domain_indices = True
# If false, no index is generated.
html_use_index = True
html_use_index = False
# If true, the index is split into individual pages for each letter.
# html_split_index = False
@@ -320,7 +320,9 @@ latex_domain_indices = False
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [("usage", "pytest", "pytest usage", ["holger krekel at merlinux eu"], 1)]
man_pages = [
("how-to/usage", "pytest", "pytest usage", ["holger krekel at merlinux eu"], 1)
]
# -- Options for Epub output ---------------------------------------------------

View File

@@ -85,7 +85,6 @@ Further topics
backwards-compatibility
deprecations
py27-py34-deprecation
contributing
development_guide

View File

@@ -657,20 +657,17 @@ Use :func:`pytest.raises` with the
:ref:`pytest.mark.parametrize ref` decorator to write parametrized tests
in which some tests raise exceptions and others do not.
It is helpful to define a no-op context manager ``does_not_raise`` to serve
as a complement to ``raises``. For example:
It may be helpful to use ``nullcontext`` as a complement to ``raises``.
For example:
.. code-block:: python
from contextlib import contextmanager
from contextlib import nullcontext as does_not_raise
import pytest
@contextmanager
def does_not_raise():
yield
@pytest.mark.parametrize(
"example_input,expectation",
[
@@ -687,22 +684,3 @@ as a complement to ``raises``. For example:
In the example above, the first three test cases should run unexceptionally,
while the fourth should raise ``ZeroDivisionError``.
If you're only supporting Python 3.7+, you can simply use ``nullcontext``
to define ``does_not_raise``:
.. code-block:: python
from contextlib import nullcontext as does_not_raise
Or, if you're supporting Python 3.3+ you can use:
.. code-block:: python
from contextlib import ExitStack as does_not_raise
Or, if desired, you can ``pip install contextlib2`` and use:
.. code-block:: python
from contextlib2 import nullcontext as does_not_raise

View File

@@ -155,7 +155,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
> assert [0, 1, 2] == [0, 1, 3]
E assert [0, 1, 2] == [0, 1, 3]
E At index 2 diff: 2 != 3
E Use -v to get the full diff
E Use -v to get more diff
failure_demo.py:63: AssertionError
______________ TestSpecialisedExplanations.test_eq_list_long _______________
@@ -168,7 +168,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
> assert a == b
E assert [0, 0, 0, 0, 0, 0, ...] == [0, 0, 0, 0, 0, 0, ...]
E At index 100 diff: 1 != 2
E Use -v to get the full diff
E Use -v to get more diff
failure_demo.py:68: AssertionError
_________________ TestSpecialisedExplanations.test_eq_dict _________________
@@ -215,7 +215,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
> assert [1, 2] == [1, 2, 3]
E assert [1, 2] == [1, 2, 3]
E Right contains one more item: 3
E Use -v to get the full diff
E Use -v to get more diff
failure_demo.py:77: AssertionError
_________________ TestSpecialisedExplanations.test_in_list _________________

View File

@@ -173,10 +173,9 @@ This layout prevents a lot of common pitfalls and has many benefits, which are b
`blog post by Ionel Cristian Mărieș <https://blog.ionelmc.ro/2014/05/25/python-packaging/#the-structure>`_.
.. note::
The new ``--import-mode=importlib`` (see :ref:`import-modes`) doesn't have
The ``--import-mode=importlib`` option (see :ref:`import-modes`) does not have
any of the drawbacks above because ``sys.path`` is not changed when importing
test modules, so users that run
into this issue are strongly encouraged to try it and report if the new option works well for them.
test modules, so users that run into this issue are strongly encouraged to try it.
The ``src`` directory layout is still strongly recommended however.

View File

@@ -45,10 +45,19 @@ these values:
* ``importlib``: new in pytest-6.0, this mode uses :mod:`importlib` to import test modules. This gives full control over the import process, and doesn't require changing :py:data:`sys.path`.
For this reason this doesn't require test module names to be unique, but also makes test
modules non-importable by each other.
For this reason this doesn't require test module names to be unique.
One drawback however is that test modules are non-importable by each other. Also, utility
modules in the tests directories are not automatically importable because the tests directory is no longer
added to :py:data:`sys.path`.
Initially we intended to make ``importlib`` the default in future releases, however it is clear now that
it has its own set of drawbacks so the default will remain ``prepend`` for the foreseeable future.
.. seealso::
The :confval:`pythonpath` configuration variable.
We intend to make ``importlib`` the default in future releases, depending on feedback.
``prepend`` and ``append`` import modes scenarios
-------------------------------------------------

View File

@@ -22,7 +22,7 @@ Install ``pytest``
.. code-block:: bash
$ pytest --version
pytest 7.0.1
pytest 7.1.3
.. _`simpletest`:

View File

@@ -201,7 +201,7 @@ if you run this module:
E '1'
E Extra items in the right set:
E '5'
E Use -v to get the full diff
E Use -v to get more diff
test_assert2.py:4: AssertionError
========================= short test summary info ==========================

View File

@@ -126,14 +126,17 @@ pytest also introduces new options:
in expected doctest output.
* ``NUMBER``: when enabled, floating-point numbers only need to match as far as
the precision you have written in the expected doctest output. For example,
the following output would only need to match to 2 decimal places::
the precision you have written in the expected doctest output. The numbers are
compared using :func:`pytest.approx` with relative tolerance equal to the
precision. For example, the following output would only need to match to 2
decimal places when comparing ``3.14`` to
``pytest.approx(math.pi, rel=10**-2)``::
>>> math.pi
3.14
If you wrote ``3.1416`` then the actual output would need to match to 4
decimal places; and so on.
If you wrote ``3.1416`` then the actual output would need to match to
approximately 4 decimal places; and so on.
This avoids false positives caused by limited floating-point precision, like
this::

View File

@@ -736,6 +736,87 @@ does offer some nuances for when you're in a pinch.
. [100%]
1 passed in 0.12s
Note on finalizer order
""""""""""""""""""""""""
Finalizers are executed in a first-in-last-out order.
For yield fixtures, the first teardown code to run is from the right-most fixture, i.e. the last test parameter.
.. code-block:: python
# content of test_finalizers.py
import pytest
def test_bar(fix_w_yield1, fix_w_yield2):
print("test_bar")
@pytest.fixture
def fix_w_yield1():
yield
print("after_yield_1")
@pytest.fixture
def fix_w_yield2():
yield
print("after_yield_2")
.. code-block:: pytest
$ pytest -s test_finalizers.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 1 item
test_finalizers.py test_bar
.after_yield_2
after_yield_1
============================ 1 passed in 0.12s =============================
For finalizers, the first fixture to run is last call to `request.addfinalizer`.
.. code-block:: python
# content of test_finalizers.py
from functools import partial
import pytest
@pytest.fixture
def fix_w_finalizers(request):
request.addfinalizer(partial(print, "finalizer_2"))
request.addfinalizer(partial(print, "finalizer_1"))
def test_bar(fix_w_finalizers):
print("test_bar")
.. code-block:: pytest
$ pytest -s test_finalizers.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 1 item
test_finalizers.py test_bar
.finalizer_1
finalizer_2
============================ 1 passed in 0.12s =============================
This is so because yield fixtures use `addfinalizer` behind the scenes: when the fixture executes, `addfinalizer` registers a function that resumes the generator, which in turn calls the teardown code.
.. _`safe teardowns`:
Safe teardowns
@@ -1332,13 +1413,15 @@ Running the above tests results in the following test IDs being used:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 11 items
collected 12 items
<Module test_anothersmtp.py>
<Function test_showhelo[smtp.gmail.com]>
<Function test_showhelo[mail.python.org]>
<Module test_emaillib.py>
<Function test_email_received>
<Module test_finalizers.py>
<Function test_bar>
<Module test_ids.py>
<Function test_a[spam]>
<Function test_a[ham]>
@@ -1350,7 +1433,7 @@ Running the above tests results in the following test IDs being used:
<Function test_ehlo[mail.python.org]>
<Function test_noop[mail.python.org]>
======================= 11 tests collected in 0.12s ========================
======================= 12 tests collected in 0.12s ========================
.. _`fixture-parametrize-marks`:

View File

@@ -180,8 +180,8 @@ logging records as they are emitted directly into the console.
You can specify the logging level for which log records with equal or higher
level are printed to the console by passing ``--log-cli-level``. This setting
accepts the logging level names as seen in python's documentation or an integer
as the logging level num.
accepts the logging level names or numeric values as seen in
:ref:`logging's documentation <python:levels>`.
Additionally, you can also specify ``--log-cli-format`` and
``--log-cli-date-format`` which mirror and default to ``--log-format`` and
@@ -198,11 +198,12 @@ option names are:
If you need to record the whole test suite logging calls to a file, you can pass
``--log-file=/path/to/log/file``. This log file is opened in write mode which
means that it will be overwritten at each run tests session.
Note that relative paths for the log-file location, whether passed on the CLI or declared in a
config file, are always resolved relative to the current working directory.
You can also specify the logging level for the log file by passing
``--log-file-level``. This setting accepts the logging level names as seen in
python's documentation(ie, uppercased level names) or an integer as the logging
level num.
``--log-file-level``. This setting accepts the logging level names or numeric
values as seen in :ref:`logging's documentation <python:levels>`.
Additionally, you can also specify ``--log-file-format`` and
``--log-file-date-format`` which are equal to ``--log-format`` and

View File

@@ -3,7 +3,7 @@
How to monkeypatch/mock modules and environments
================================================================
.. currentmodule:: _pytest.monkeypatch
.. currentmodule:: pytest
Sometimes tests need to invoke functionality which depends
on global settings or which invokes code which cannot be easily
@@ -14,17 +14,16 @@ environment variable, or to modify ``sys.path`` for importing.
The ``monkeypatch`` fixture provides these helper methods for safely patching and mocking
functionality in tests:
.. code-block:: python
* :meth:`monkeypatch.setattr(obj, name, value, raising=True) <pytest.MonkeyPatch.setattr>`
* :meth:`monkeypatch.delattr(obj, name, raising=True) <pytest.MonkeyPatch.delattr>`
* :meth:`monkeypatch.setitem(mapping, name, value) <pytest.MonkeyPatch.setitem>`
* :meth:`monkeypatch.delitem(obj, name, raising=True) <pytest.MonkeyPatch.delitem>`
* :meth:`monkeypatch.setenv(name, value, prepend=None) <pytest.MonkeyPatch.setenv>`
* :meth:`monkeypatch.delenv(name, raising=True) <pytest.MonkeyPatch.delenv>`
* :meth:`monkeypatch.syspath_prepend(path) <pytest.MonkeyPatch.syspath_prepend>`
* :meth:`monkeypatch.chdir(path) <pytest.MonkeyPatch.chdir>`
* :meth:`monkeypatch.context() <pytest.MonkeyPatch.context>`
monkeypatch.setattr(obj, name, value, raising=True)
monkeypatch.setattr("somemodule.obj.name", value, raising=True)
monkeypatch.delattr(obj, name, raising=True)
monkeypatch.setitem(mapping, name, value)
monkeypatch.delitem(obj, name, raising=True)
monkeypatch.setenv(name, value, prepend=None)
monkeypatch.delenv(name, raising=True)
monkeypatch.syspath_prepend(path)
monkeypatch.chdir(path)
All modifications will be undone after the requesting
test function or fixture has finished. The ``raising``
@@ -55,13 +54,16 @@ during a test.
5. Use :py:meth:`monkeypatch.syspath_prepend <MonkeyPatch.syspath_prepend>` to modify ``sys.path`` which will also
call ``pkg_resources.fixup_namespace_packages`` and :py:func:`importlib.invalidate_caches`.
6. Use :py:meth:`monkeypatch.context <MonkeyPatch.context>` to apply patches only in a specific scope, which can help
control teardown of complex fixtures or patches to the stdlib.
See the `monkeypatch blog post`_ for some introduction material
and a discussion of its motivation.
.. _`monkeypatch blog post`: https://tetamap.wordpress.com//2009/03/03/monkeypatching-in-unit-tests-done-right/
Simple example: monkeypatching functions
----------------------------------------
Monkeypatching functions
------------------------
Consider a scenario where you are working with user directories. In the context of
testing, you do not want your test to depend on the running user. ``monkeypatch``
@@ -436,7 +438,7 @@ separate fixtures for each potential mock and reference them in the needed tests
_ = app.create_connection_string()
.. currentmodule:: _pytest.monkeypatch
.. currentmodule:: pytest
API Reference
-------------

View File

@@ -84,7 +84,7 @@ Executing pytest normally gives us this output (we are skipping the header to fo
> assert fruits1 == fruits2
E AssertionError: assert ['banana', 'a...elon', 'kiwi'] == ['banana', 'a...elon', 'kiwi']
E At index 2 diff: 'grapes' != 'orange'
E Use -v to get the full diff
E Use -v to get more diff
test_verbosity_example.py:8: AssertionError
____________________________ test_numbers_fail _____________________________
@@ -99,7 +99,7 @@ Executing pytest normally gives us this output (we are skipping the header to fo
E {'1': 1, '2': 2, '3': 3, '4': 4}
E Right contains 4 more items:
E {'10': 10, '20': 20, '30': 30, '40': 40}
E Use -v to get the full diff
E Use -v to get more diff
test_verbosity_example.py:14: AssertionError
___________________________ test_long_text_fail ____________________________

View File

@@ -104,8 +104,10 @@ The ``tmpdir`` and ``tmpdir_factory`` fixtures
The ``tmpdir`` and ``tmpdir_factory`` fixtures are similar to ``tmp_path``
and ``tmp_path_factory``, but use/return legacy `py.path.local`_ objects
rather than standard :class:`pathlib.Path` objects. These days, prefer to
use ``tmp_path`` and ``tmp_path_factory``.
rather than standard :class:`pathlib.Path` objects.
.. note::
These days, it is preferred to use ``tmp_path`` and ``tmp_path_factory``.
See :fixture:`tmpdir <tmpdir>` :fixture:`tmpdir_factory <tmpdir_factory>`
API for details.

View File

@@ -27,12 +27,15 @@ Almost all ``unittest`` features are supported:
* ``setUpClass/tearDownClass``;
* ``setUpModule/tearDownModule``;
.. _`pytest-subtests`: https://github.com/pytest-dev/pytest-subtests
.. _`load_tests protocol`: https://docs.python.org/3/library/unittest.html#load-tests-protocol
Additionally, :ref:`subtests <python:subtests>` are supported by the
`pytest-subtests`_ plugin.
Up to this point pytest does not have support for the following features:
* `load_tests protocol`_;
* :ref:`subtests <python:subtests>`;
Benefits out of the box
-----------------------

View File

@@ -2,16 +2,11 @@
.. sidebar:: Next Open Trainings
- `PyConDE <https://2022.pycon.de/program/W93DBJ/>`__, April 11th 2022 (3h), Berlin, Germany
- `PyConIT <https://pycon.it/en/talk/pytest-simple-rapid-and-fun-testing-with-python>`__, June 3rd 2022 (4h), Florence, Italy
- `CH Open Workshop-Tage <https://workshoptage.ch/workshops/2022/pytest-professionelles-testen-nicht-nur-fuer-python/>`__ (German), September 8th 2022, Bern, Switzerland
- `Professional Testing with Python <https://python-academy.com/courses/python_course_testing.html>`_, via `Python Academy <https://www.python-academy.com/>`_, March 7th to 9th 2023 (3 day in-depth training), Remote and Leipzig, Germany
Also see :doc:`previous talks and blogposts <talks>`.
..
- `Europython <https://ep2022.europython.eu/>`__, July 11th to 17th (3h), Dublin, Ireland
- `CH Open Workshoptage <https://workshoptage.ch/>`__ (German), September 6th to 8th (1 day), Bern, Switzerland
.. _features:
pytest: helps you write better programs
@@ -27,8 +22,6 @@ scale to support complex functional testing for applications and libraries.
**PyPI package name**: :pypi:`pytest`
**Documentation as PDF**: `download latest <https://media.readthedocs.org/pdf/pytest/latest/pytest.pdf>`_
A quick example
---------------
@@ -104,11 +97,6 @@ Bugs/Requests
Please use the `GitHub issue tracker <https://github.com/pytest-dev/pytest/issues>`_ to submit bugs or request features.
Changelog
---------
Consult the :ref:`Changelog <changelog>` page for fixes and enhancements of each version.
Support pytest
--------------
@@ -141,13 +129,3 @@ Security
pytest has never been associated with a security vulnerability, but in any case, to report a
security vulnerability please use the `Tidelift security contact <https://tidelift.com/security>`_.
Tidelift will coordinate the fix and disclosure.
License
-------
Copyright Holger Krekel and others, 2004.
Distributed under the terms of the `MIT`_ license, pytest is free and open source software.
.. _`MIT`: https://github.com/pytest-dev/pytest/blob/main/LICENSE

View File

@@ -1,99 +0,0 @@
Python 2.7 and 3.4 support
==========================
It is demanding on the maintainers of an open source project to support many Python versions, as
there's extra cost of keeping code compatible between all versions, while holding back on
features only made possible on newer Python versions.
In case of Python 2 and 3, the difference between the languages makes it even more prominent,
because many new Python 3 features cannot be used in a Python 2/3 compatible code base.
Python 2.7 EOL has been reached :pep:`in 2020 <0373#maintenance-releases>`, with
the last release made in April, 2020.
Python 3.4 EOL has been reached :pep:`in 2019 <0429#release-schedule>`, with the last release made in March, 2019.
For those reasons, in Jun 2019 it was decided that **pytest 4.6** series will be the last to support Python 2.7 and 3.4.
What this means for general users
---------------------------------
Thanks to the `python_requires`_ setuptools option,
Python 2.7 and Python 3.4 users using a modern pip version
will install the last pytest 4.6.X version automatically even if 5.0 or later versions
are available on PyPI.
Users should ensure they are using the latest pip and setuptools versions for this to work.
Maintenance of 4.6.X versions
-----------------------------
Until January 2020, the pytest core team ported many bug-fixes from the main release into the
``4.6.x`` branch, with several 4.6.X releases being made along the year.
From now on, the core team will **no longer actively backport patches**, but the ``4.6.x``
branch will continue to exist so the community itself can contribute patches.
The core team will be happy to accept those patches, and make new 4.6.X releases **until mid-2020**
(but consider that date as a ballpark, after that date the team might still decide to make new releases
for critical bugs).
.. _`python_requires`: https://packaging.python.org/guides/distributing-packages-using-setuptools/#python-requires
Technical aspects
~~~~~~~~~~~~~~~~~
(This section is a transcript from :issue:`5275`).
In this section we describe the technical aspects of the Python 2.7 and 3.4 support plan.
.. _what goes into 4.6.x releases:
What goes into 4.6.X releases
+++++++++++++++++++++++++++++
New 4.6.X releases will contain bug fixes only.
When will 4.6.X releases happen
+++++++++++++++++++++++++++++++
New 4.6.X releases will happen after we have a few bugs in place to release, or if a few weeks have
passed (say a single bug has been fixed a month after the latest 4.6.X release).
No hard rules here, just ballpark.
Who will handle applying bug fixes
++++++++++++++++++++++++++++++++++
We core maintainers expect that people still using Python 2.7/3.4 and being affected by
bugs to step up and provide patches and/or port bug fixes from the active branches.
We will be happy to guide users interested in doing so, so please don't hesitate to ask.
**Backporting changes into 4.6**
Please follow these instructions:
#. ``git fetch --all --prune``
#. ``git checkout origin/4.6.x -b backport-XXXX`` # use the PR number here
#. Locate the merge commit on the PR, in the *merged* message, for example:
nicoddemus merged commit 0f8b462 into pytest-dev:features
#. ``git cherry-pick -m1 REVISION`` # use the revision you found above (``0f8b462``).
#. Open a PR targeting ``4.6.x``:
* Prefix the message with ``[4.6]`` so it is an obvious backport
* Delete the PR body, it usually contains a duplicate commit message.
**Providing new PRs to 4.6**
Fresh pull requests to ``4.6.x`` will be accepted provided that
the equivalent code in the active branches does not contain that bug (for example, a bug is specific
to Python 2 only).
Bug fixes that also happen in the mainstream version should be first fixed
there, and then backported as per instructions above.

View File

@@ -8,8 +8,8 @@ Reference guides
.. toctree::
:maxdepth: 1
fixtures
plugin_list
customize
reference
fixtures
customize
exit-codes
plugin_list

View File

@@ -92,7 +92,7 @@ pytest.param
pytest.raises
~~~~~~~~~~~~~
**Tutorial**: :ref:`assertraises`.
**Tutorial**: :ref:`assertraises`
.. autofunction:: pytest.raises(expected_exception: Exception [, *, match])
:with: excinfo
@@ -100,7 +100,7 @@ pytest.raises
pytest.deprecated_call
~~~~~~~~~~~~~~~~~~~~~~
**Tutorial**: :ref:`ensuring_function_triggers`.
**Tutorial**: :ref:`ensuring_function_triggers`
.. autofunction:: pytest.deprecated_call()
:with:
@@ -108,7 +108,7 @@ pytest.deprecated_call
pytest.register_assert_rewrite
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**Tutorial**: :ref:`assertion-rewriting`.
**Tutorial**: :ref:`assertion-rewriting`
.. autofunction:: pytest.register_assert_rewrite
@@ -123,7 +123,7 @@ pytest.warns
pytest.freeze_includes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**Tutorial**: :ref:`freezing-pytest`.
**Tutorial**: :ref:`freezing-pytest`
.. autofunction:: pytest.freeze_includes
@@ -143,7 +143,7 @@ fixtures or plugins.
pytest.mark.filterwarnings
~~~~~~~~~~~~~~~~~~~~~~~~~~
**Tutorial**: :ref:`filterwarnings`.
**Tutorial**: :ref:`filterwarnings`
Add warning filters to marked test items.
@@ -169,7 +169,7 @@ Add warning filters to marked test items.
pytest.mark.parametrize
~~~~~~~~~~~~~~~~~~~~~~~
:ref:`parametrize`.
**Tutorial**: :ref:`parametrize`
This mark has the same signature as :py:meth:`pytest.Metafunc.parametrize`; see there.
@@ -179,7 +179,7 @@ This mark has the same signature as :py:meth:`pytest.Metafunc.parametrize`; see
pytest.mark.skip
~~~~~~~~~~~~~~~~
:ref:`skip`.
**Tutorial**: :ref:`skip`
Unconditionally skip a test function.
@@ -193,7 +193,7 @@ Unconditionally skip a test function.
pytest.mark.skipif
~~~~~~~~~~~~~~~~~~
:ref:`skipif`.
**Tutorial**: :ref:`skipif`
Skip a test function if a condition is ``True``.
@@ -209,7 +209,7 @@ Skip a test function if a condition is ``True``.
pytest.mark.usefixtures
~~~~~~~~~~~~~~~~~~~~~~~
**Tutorial**: :ref:`usefixtures`.
**Tutorial**: :ref:`usefixtures`
Mark a test function as using the given fixture names.
@@ -231,7 +231,7 @@ Mark a test function as using the given fixture names.
pytest.mark.xfail
~~~~~~~~~~~~~~~~~~
**Tutorial**: :ref:`xfail`.
**Tutorial**: :ref:`xfail`
Marks a test function as *expected to fail*.
@@ -245,7 +245,7 @@ Marks a test function as *expected to fail*.
:keyword str reason:
Reason why the test function is marked as xfail.
:keyword Type[Exception] raises:
Exception subclass expected to be raised by the test function; other exceptions will fail the test.
Exception subclass (or tuple of subclasses) expected to be raised by the test function; other exceptions will fail the test.
:keyword bool run:
If the test function should actually be executed. If ``False``, the function will always xfail and will
not be executed (useful if a function is segfaulting).
@@ -297,7 +297,7 @@ When :meth:`Node.iter_markers <_pytest.nodes.Node.iter_markers>` or :meth:`Node.
Fixtures
--------
**Tutorial**: :ref:`fixture`.
**Tutorial**: :ref:`fixture`
Fixtures are requested by test functions or other fixtures by declaring them as argument names.
@@ -338,7 +338,7 @@ For more details, consult the full :ref:`fixtures docs <fixture>`.
config.cache
~~~~~~~~~~~~
**Tutorial**: :ref:`cache`.
**Tutorial**: :ref:`cache`
The ``config.cache`` object allows other plugins and fixtures
to store and retrieve values across test runs. To access it from fixtures
@@ -358,22 +358,11 @@ Under the hood, the cache plugin uses the simple
capsys
~~~~~~
:ref:`captures`.
**Tutorial**: :ref:`captures`
.. autofunction:: _pytest.capture.capsys()
:no-auto-options:
Returns an instance of :class:`CaptureFixture[str] <pytest.CaptureFixture>`.
Example:
.. code-block:: python
def test_output(capsys):
print("hello")
captured = capsys.readouterr()
assert captured.out == "hello\n"
.. autoclass:: pytest.CaptureFixture()
:members:
@@ -383,93 +372,48 @@ capsys
capsysbinary
~~~~~~~~~~~~
:ref:`captures`.
**Tutorial**: :ref:`captures`
.. autofunction:: _pytest.capture.capsysbinary()
:no-auto-options:
Returns an instance of :class:`CaptureFixture[bytes] <pytest.CaptureFixture>`.
Example:
.. code-block:: python
def test_output(capsysbinary):
print("hello")
captured = capsysbinary.readouterr()
assert captured.out == b"hello\n"
.. fixture:: capfd
capfd
~~~~~~
:ref:`captures`.
**Tutorial**: :ref:`captures`
.. autofunction:: _pytest.capture.capfd()
:no-auto-options:
Returns an instance of :class:`CaptureFixture[str] <pytest.CaptureFixture>`.
Example:
.. code-block:: python
def test_system_echo(capfd):
os.system('echo "hello"')
captured = capfd.readouterr()
assert captured.out == "hello\n"
.. fixture:: capfdbinary
capfdbinary
~~~~~~~~~~~~
:ref:`captures`.
**Tutorial**: :ref:`captures`
.. autofunction:: _pytest.capture.capfdbinary()
:no-auto-options:
Returns an instance of :class:`CaptureFixture[bytes] <pytest.CaptureFixture>`.
Example:
.. code-block:: python
def test_system_echo(capfdbinary):
os.system('echo "hello"')
captured = capfdbinary.readouterr()
assert captured.out == b"hello\n"
.. fixture:: doctest_namespace
doctest_namespace
~~~~~~~~~~~~~~~~~
:ref:`doctest`.
**Tutorial**: :ref:`doctest`
.. autofunction:: _pytest.doctest.doctest_namespace()
Usually this fixture is used in conjunction with another ``autouse`` fixture:
.. code-block:: python
@pytest.fixture(autouse=True)
def add_np(doctest_namespace):
doctest_namespace["np"] = numpy
For more details: :ref:`doctest_namespace`.
.. fixture:: request
request
~~~~~~~
:ref:`request example`.
**Example**: :ref:`request example`
The ``request`` fixture is a special fixture providing information of the requesting test function.
@@ -490,7 +434,7 @@ pytestconfig
record_property
~~~~~~~~~~~~~~~~~~~
**Tutorial**: :ref:`record_property example`.
**Tutorial**: :ref:`record_property example`
.. autofunction:: _pytest.junitxml.record_property()
@@ -500,7 +444,7 @@ record_property
record_testsuite_property
~~~~~~~~~~~~~~~~~~~~~~~~~
**Tutorial**: :ref:`record_testsuite_property example`.
**Tutorial**: :ref:`record_testsuite_property example`
.. autofunction:: _pytest.junitxml.record_testsuite_property()
@@ -510,7 +454,7 @@ record_testsuite_property
caplog
~~~~~~
:ref:`logging`.
**Tutorial**: :ref:`logging`
.. autofunction:: _pytest.logging.caplog()
:no-auto-options:
@@ -526,7 +470,7 @@ caplog
monkeypatch
~~~~~~~~~~~
:ref:`monkeypatching`.
**Tutorial**: :ref:`monkeypatching`
.. autofunction:: _pytest.monkeypatch.monkeypatch()
:no-auto-options:
@@ -600,19 +544,13 @@ recwarn
.. autoclass:: pytest.WarningsRecorder()
:members:
Each recorded warning is an instance of :class:`warnings.WarningMessage`.
.. note::
``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated
differently; see :ref:`ensuring_function_triggers`.
.. fixture:: tmp_path
tmp_path
~~~~~~~~
:ref:`tmp_path`
**Tutorial**: :ref:`tmp_path`
.. autofunction:: _pytest.tmpdir.tmp_path()
:no-auto-options:
@@ -623,7 +561,7 @@ tmp_path
tmp_path_factory
~~~~~~~~~~~~~~~~
:ref:`tmp_path_factory example`
**Tutorial**: :ref:`tmp_path_factory example`
.. _`tmp_path_factory factory api`:
@@ -638,7 +576,7 @@ tmp_path_factory
tmpdir
~~~~~~
:ref:`tmpdir and tmpdir_factory`
**Tutorial**: :ref:`tmpdir and tmpdir_factory`
.. autofunction:: _pytest.legacypath.LegacyTmpdirPlugin.tmpdir()
:no-auto-options:
@@ -649,7 +587,7 @@ tmpdir
tmpdir_factory
~~~~~~~~~~~~~~
:ref:`tmpdir and tmpdir_factory`
**Tutorial**: :ref:`tmpdir and tmpdir_factory`
``tmpdir_factory`` is an instance of :class:`~pytest.TempdirFactory`:
@@ -662,7 +600,7 @@ tmpdir_factory
Hooks
-----
:ref:`writing-plugins`.
**Tutorial**: :ref:`writing-plugins`
.. currentmodule:: _pytest.hookspec
@@ -1514,7 +1452,7 @@ passed multiple times. The expected format is ``name=value``. For example::
Sets a file name relative to the ``pytest.ini`` file where log messages should be written to, in addition
Sets a file name relative to the current working directory where log messages should be written to, in addition
to the other logging facilities that are active.
.. code-block:: ini

View File

@@ -5,3 +5,6 @@ sphinx-removed-in>=0.2.0
sphinx>=3.1,<4
sphinxcontrib-trio
sphinxcontrib-svg2pdfconverter
# XXX: sphinx<4 is broken with latest jinja2
jinja2<3.1

View File

@@ -17,6 +17,8 @@ Books
Talks and blog postings
---------------------------------------------
- Training: `pytest - simple, rapid and fun testing with Python <https://www.youtube.com/watch?v=ofPHJrAOaTE>`_, Florian Bruhin, PyConDE 2022
- `pytest: Simple, rapid and fun testing with Python, <https://youtu.be/cSJ-X3TbQ1c?t=15752>`_ (@ 4:22:32), Florian Bruhin, WeAreDevelopers World Congress 2021
- Webinar: `pytest: Test Driven Development für Python (German) <https://bruhin.software/ins-pytest/>`_, Florian Bruhin, via mylearning.ch, 2020

View File

@@ -1,4 +1,5 @@
import sys
from distutils.core import setup
if __name__ == "__main__":

View File

@@ -47,7 +47,6 @@ install_requires =
pluggy>=0.12,<2.0
py>=1.8.2
tomli>=1.0.0
atomicwrites>=1.0;sys_platform=="win32"
colorama;sys_platform=="win32"
importlib-metadata>=0.12;python_version<"3.8"
python_requires = >=3.7

View File

@@ -190,7 +190,7 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader)
return False
# For matching the name it must be as if it was a filename.
path = PurePath(os.path.sep.join(parts) + ".py")
path = PurePath(*parts).with_suffix(".py")
for pat in self.fnpats:
# if the pattern contains subdirectories ("tests/**.py" for example) we can't bail out based
@@ -281,7 +281,9 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader)
else:
from importlib.resources.readers import FileReader
return FileReader(types.SimpleNamespace(path=self._rewritten_names[name]))
return FileReader( # type:ignore[no-any-return]
types.SimpleNamespace(path=self._rewritten_names[name])
)
def _write_pyc_fp(
@@ -302,53 +304,29 @@ def _write_pyc_fp(
fp.write(marshal.dumps(co))
if sys.platform == "win32":
from atomicwrites import atomic_write
def _write_pyc(
state: "AssertionState",
co: types.CodeType,
source_stat: os.stat_result,
pyc: Path,
) -> bool:
try:
with atomic_write(os.fspath(pyc), mode="wb", overwrite=True) as fp:
_write_pyc_fp(fp, source_stat, co)
except OSError as e:
state.trace(f"error writing pyc file at {pyc}: {e}")
# we ignore any failure to write the cache file
# there are many reasons, permission-denied, pycache dir being a
# file etc.
return False
return True
else:
def _write_pyc(
state: "AssertionState",
co: types.CodeType,
source_stat: os.stat_result,
pyc: Path,
) -> bool:
proc_pyc = f"{pyc}.{os.getpid()}"
try:
fp = open(proc_pyc, "wb")
except OSError as e:
state.trace(f"error writing pyc file at {proc_pyc}: errno={e.errno}")
return False
try:
def _write_pyc(
state: "AssertionState",
co: types.CodeType,
source_stat: os.stat_result,
pyc: Path,
) -> bool:
proc_pyc = f"{pyc}.{os.getpid()}"
try:
with open(proc_pyc, "wb") as fp:
_write_pyc_fp(fp, source_stat, co)
os.rename(proc_pyc, pyc)
except OSError as e:
state.trace(f"error writing pyc file at {pyc}: {e}")
# we ignore any failure to write the cache file
# there are many reasons, permission-denied, pycache dir being a
# file etc.
return False
finally:
fp.close()
return True
except OSError as e:
state.trace(f"error writing pyc file at {proc_pyc}: errno={e.errno}")
return False
try:
os.replace(proc_pyc, pyc)
except OSError as e:
state.trace(f"error writing pyc file at {pyc}: {e}")
# we ignore any failure to write the cache file
# there are many reasons, permission-denied, pycache dir being a
# file etc.
return False
return True
def _rewrite_test(fn: Path, config: Config) -> Tuple[os.stat_result, types.CodeType]:

View File

@@ -437,8 +437,10 @@ def _compare_eq_cls(left: Any, right: Any, verbose: int) -> List[str]:
if not has_default_eq(left):
return []
if isdatacls(left):
all_fields = left.__dataclass_fields__
fields_to_check = [field for field, info in all_fields.items() if info.compare]
import dataclasses
all_fields = dataclasses.fields(left)
fields_to_check = [info.name for info in all_fields if info.compare]
elif isattrs(left):
all_fields = left.__attrs_attrs__
fields_to_check = [field.name for field in all_fields if getattr(field, "eq")]

View File

@@ -876,11 +876,22 @@ class CaptureFixture(Generic[AnyStr]):
@fixture
def capsys(request: SubRequest) -> Generator[CaptureFixture[str], None, None]:
"""Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``.
r"""Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``.
The captured output is made available via ``capsys.readouterr()`` method
calls, which return a ``(out, err)`` namedtuple.
``out`` and ``err`` will be ``text`` objects.
Returns an instance of :class:`CaptureFixture[str] <pytest.CaptureFixture>`.
Example:
.. code-block:: python
def test_output(capsys):
print("hello")
captured = capsys.readouterr()
assert captured.out == "hello\n"
"""
capman = request.config.pluginmanager.getplugin("capturemanager")
capture_fixture = CaptureFixture[str](SysCapture, request, _ispytest=True)
@@ -893,11 +904,22 @@ def capsys(request: SubRequest) -> Generator[CaptureFixture[str], None, None]:
@fixture
def capsysbinary(request: SubRequest) -> Generator[CaptureFixture[bytes], None, None]:
"""Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``.
r"""Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``.
The captured output is made available via ``capsysbinary.readouterr()``
method calls, which return a ``(out, err)`` namedtuple.
``out`` and ``err`` will be ``bytes`` objects.
Returns an instance of :class:`CaptureFixture[bytes] <pytest.CaptureFixture>`.
Example:
.. code-block:: python
def test_output(capsysbinary):
print("hello")
captured = capsysbinary.readouterr()
assert captured.out == b"hello\n"
"""
capman = request.config.pluginmanager.getplugin("capturemanager")
capture_fixture = CaptureFixture[bytes](SysCaptureBinary, request, _ispytest=True)
@@ -910,11 +932,22 @@ def capsysbinary(request: SubRequest) -> Generator[CaptureFixture[bytes], None,
@fixture
def capfd(request: SubRequest) -> Generator[CaptureFixture[str], None, None]:
"""Enable text capturing of writes to file descriptors ``1`` and ``2``.
r"""Enable text capturing of writes to file descriptors ``1`` and ``2``.
The captured output is made available via ``capfd.readouterr()`` method
calls, which return a ``(out, err)`` namedtuple.
``out`` and ``err`` will be ``text`` objects.
Returns an instance of :class:`CaptureFixture[str] <pytest.CaptureFixture>`.
Example:
.. code-block:: python
def test_system_echo(capfd):
os.system('echo "hello"')
captured = capfd.readouterr()
assert captured.out == "hello\n"
"""
capman = request.config.pluginmanager.getplugin("capturemanager")
capture_fixture = CaptureFixture[str](FDCapture, request, _ispytest=True)
@@ -927,11 +960,23 @@ def capfd(request: SubRequest) -> Generator[CaptureFixture[str], None, None]:
@fixture
def capfdbinary(request: SubRequest) -> Generator[CaptureFixture[bytes], None, None]:
"""Enable bytes capturing of writes to file descriptors ``1`` and ``2``.
r"""Enable bytes capturing of writes to file descriptors ``1`` and ``2``.
The captured output is made available via ``capfd.readouterr()`` method
calls, which return a ``(out, err)`` namedtuple.
``out`` and ``err`` will be ``byte`` objects.
Returns an instance of :class:`CaptureFixture[bytes] <pytest.CaptureFixture>`.
Example:
.. code-block:: python
def test_system_echo(capfdbinary):
os.system('echo "hello"')
captured = capfdbinary.readouterr()
assert captured.out == b"hello\n"
"""
capman = request.config.pluginmanager.getplugin("capturemanager")
capture_fixture = CaptureFixture[bytes](FDCaptureBinary, request, _ispytest=True)

View File

@@ -538,11 +538,7 @@ class PytestPluginManager(PluginManager):
"""
if self._confcutdir is None:
return True
try:
path.relative_to(self._confcutdir)
except ValueError:
return False
return True
return path not in self._confcutdir.parents
def _try_load_conftest(
self, anchor: Path, importmode: Union[str, ImportMode], rootpath: Path
@@ -837,7 +833,8 @@ def _iter_rewritable_modules(package_files: Iterable[str]) -> Iterator[str]:
if is_simple_module:
module_name, _ = os.path.splitext(fn)
# we ignore "setup.py" at the root of the distribution
if module_name != "setup":
# as well as editable installation finder modules made by setuptools
if module_name != "setup" and not module_name.startswith("__editable__"):
seen_some = True
yield module_name
elif is_package:

View File

@@ -542,7 +542,11 @@ class DoctestModule(pytest.Module):
)
else:
try:
module = import_path(self.path, root=self.config.rootpath)
module = import_path(
self.path,
root=self.config.rootpath,
mode=self.config.getoption("importmode"),
)
except ImportError:
if self.config.getvalue("doctest_ignore_import_errors"):
pytest.skip("unable to import module %r" % self.path)
@@ -730,5 +734,16 @@ def _get_report_choice(key: str) -> int:
@pytest.fixture(scope="session")
def doctest_namespace() -> Dict[str, Any]:
"""Fixture that returns a :py:class:`dict` that will be injected into the
namespace of doctests."""
namespace of doctests.
Usually this fixture is used in conjunction with another ``autouse`` fixture:
.. code-block:: python
@pytest.fixture(autouse=True)
def add_np(doctest_namespace):
doctest_namespace["np"] = numpy
For more details: :ref:`doctest_namespace`.
"""
return dict()

View File

@@ -350,7 +350,7 @@ def reorder_items_atscope(
return items_done
def get_direct_param_fixture_func(request):
def get_direct_param_fixture_func(request: "FixtureRequest") -> Any:
return request.param
@@ -412,6 +412,15 @@ class FixtureRequest:
self._arg2fixturedefs = fixtureinfo.name2fixturedefs.copy()
self._arg2index: Dict[str, int] = {}
self._fixturemanager: FixtureManager = pyfuncitem.session._fixturemanager
# Notes on the type of `param`:
# -`request.param` is only defined in parametrized fixtures, and will raise
# AttributeError otherwise. Python typing has no notion of "undefined", so
# this cannot be reflected in the type.
# - Technically `param` is only (possibly) defined on SubRequest, not
# FixtureRequest, but the typing of that is still in flux so this cheats.
# - In the future we might consider using a generic for the param type, but
# for now just using Any.
self.param: Any
@property
def scope(self) -> "_ScopeName":
@@ -491,6 +500,7 @@ class FixtureRequest:
@property
def path(self) -> Path:
"""Path where the test function was collected."""
if self.scope not in ("function", "class", "module", "package"):
raise AttributeError(f"path not available in {self.scope}-scoped context")
# TODO: Remove ignore once _pyfuncitem is properly typed.

View File

@@ -231,7 +231,7 @@ class _NodeReporter:
msg = f'failed on teardown with "{reason}"'
else:
msg = f'failed on setup with "{reason}"'
self._add_simple("error", msg, str(report.longrepr))
self._add_simple("error", bin_xml_escape(msg), str(report.longrepr))
def append_skipped(self, report: TestReport) -> None:
if hasattr(report, "wasxfail"):

View File

@@ -270,8 +270,15 @@ class LegacyTestdirPlugin:
@final
@attr.s(init=False, auto_attribs=True)
class TempdirFactory:
"""Backward compatibility wrapper that implements :class:``_pytest.compat.LEGACY_PATH``
for :class:``TempPathFactory``."""
"""Backward compatibility wrapper that implements :class:`py.path.local`
for :class:`TempPathFactory`.
.. note::
These days, it is preferred to use ``tmp_path_factory``.
:ref:`About the tmpdir and tmpdir_factory fixtures<tmpdir and tmpdir_factory>`.
"""
_tmppath_factory: TempPathFactory
@@ -282,11 +289,11 @@ class TempdirFactory:
self._tmppath_factory = tmppath_factory
def mktemp(self, basename: str, numbered: bool = True) -> LEGACY_PATH:
"""Same as :meth:`TempPathFactory.mktemp`, but returns a ``_pytest.compat.LEGACY_PATH`` object."""
"""Same as :meth:`TempPathFactory.mktemp`, but returns a :class:`py.path.local` object."""
return legacy_path(self._tmppath_factory.mktemp(basename, numbered).resolve())
def getbasetemp(self) -> LEGACY_PATH:
"""Backward compat wrapper for ``_tmppath_factory.getbasetemp``."""
"""Same as :meth:`TempPathFactory.getbasetemp`, but returns a :class:`py.path.local` object."""
return legacy_path(self._tmppath_factory.getbasetemp().resolve())
@@ -312,6 +319,11 @@ class LegacyTmpdirPlugin:
The returned object is a `legacy_path`_ object.
.. note::
These days, it is preferred to use ``tmp_path``.
:ref:`About the tmpdir and tmpdir_factory fixtures<tmpdir and tmpdir_factory>`.
.. _legacy_path: https://py.readthedocs.io/en/latest/path.html
"""
return legacy_path(tmp_path)

View File

@@ -29,21 +29,26 @@ V = TypeVar("V")
def monkeypatch() -> Generator["MonkeyPatch", None, None]:
"""A convenient fixture for monkey-patching.
The fixture provides these methods to modify objects, dictionaries or
os.environ::
The fixture provides these methods to modify objects, dictionaries, or
:data:`os.environ`:
monkeypatch.setattr(obj, name, value, raising=True)
monkeypatch.delattr(obj, name, raising=True)
monkeypatch.setitem(mapping, name, value)
monkeypatch.delitem(obj, name, raising=True)
monkeypatch.setenv(name, value, prepend=None)
monkeypatch.delenv(name, raising=True)
monkeypatch.syspath_prepend(path)
monkeypatch.chdir(path)
* :meth:`monkeypatch.setattr(obj, name, value, raising=True) <pytest.MonkeyPatch.setattr>`
* :meth:`monkeypatch.delattr(obj, name, raising=True) <pytest.MonkeyPatch.delattr>`
* :meth:`monkeypatch.setitem(mapping, name, value) <pytest.MonkeyPatch.setitem>`
* :meth:`monkeypatch.delitem(obj, name, raising=True) <pytest.MonkeyPatch.delitem>`
* :meth:`monkeypatch.setenv(name, value, prepend=None) <pytest.MonkeyPatch.setenv>`
* :meth:`monkeypatch.delenv(name, raising=True) <pytest.MonkeyPatch.delenv>`
* :meth:`monkeypatch.syspath_prepend(path) <pytest.MonkeyPatch.syspath_prepend>`
* :meth:`monkeypatch.chdir(path) <pytest.MonkeyPatch.chdir>`
* :meth:`monkeypatch.context() <pytest.MonkeyPatch.context>`
All modifications will be undone after the requesting test function or
fixture has finished. The ``raising`` parameter determines if a KeyError
or AttributeError will be raised if the set/deletion operation has no target.
fixture has finished. The ``raising`` parameter determines if a :class:`KeyError`
or :class:`AttributeError` will be raised if the set/deletion operation does not have the
specified target.
To undo modifications done by the fixture in a contained scope,
use :meth:`context() <pytest.MonkeyPatch.context>`.
"""
mpatch = MonkeyPatch()
yield mpatch
@@ -182,16 +187,40 @@ class MonkeyPatch:
value: object = notset,
raising: bool = True,
) -> None:
"""Set attribute value on target, memorizing the old value.
"""
Set attribute value on target, memorizing the old value.
For convenience you can specify a string as ``target`` which
For example:
.. code-block:: python
import os
monkeypatch.setattr(os, "getcwd", lambda: "/")
The code above replaces the :func:`os.getcwd` function by a ``lambda`` which
always returns ``"/"``.
For convenience, you can specify a string as ``target`` which
will be interpreted as a dotted import path, with the last part
being the attribute name. For example,
``monkeypatch.setattr("os.getcwd", lambda: "/")``
would set the ``getcwd`` function of the ``os`` module.
being the attribute name:
Raises AttributeError if the attribute does not exist, unless
.. code-block:: python
monkeypatch.setattr("os.getcwd", lambda: "/")
Raises :class:`AttributeError` if the attribute does not exist, unless
``raising`` is set to False.
**Where to patch**
``monkeypatch.setattr`` works by (temporarily) changing the object that a name points to with another one.
There can be many names pointing to any individual object, so for patching to work you must ensure
that you patch the name used by the system under test.
See the section :ref:`Where to patch <python:where-to-patch>` in the :mod:`unittest.mock`
docs for a complete explanation, which is meant for :func:`unittest.mock.patch` but
applies to ``monkeypatch.setattr`` as well.
"""
__tracebackhide__ = True
import inspect
@@ -353,11 +382,14 @@ class MonkeyPatch:
There is generally no need to call `undo()`, since it is
called automatically during tear-down.
Note that the same `monkeypatch` fixture is used across a
single test function invocation. If `monkeypatch` is used both by
the test function itself and one of the test fixtures,
calling `undo()` will undo all of the changes made in
both functions.
.. note::
The same `monkeypatch` fixture is used across a
single test function invocation. If `monkeypatch` is used both by
the test function itself and one of the test fixtures,
calling `undo()` will undo all of the changes made in
both functions.
Prefer to use :meth:`context() <pytest.MonkeyPatch.context>` instead.
"""
for obj, name, value in reversed(self._setattr):
if value is not notset:

View File

@@ -111,7 +111,7 @@ def _imply_path(
NODE_CTOR_FSPATH_ARG.format(
node_type_name=node_type.__name__,
),
stacklevel=3,
stacklevel=6,
)
if path is not None:
if fspath is not None:

View File

@@ -133,9 +133,11 @@ class ApproxBase:
# raise if there are any non-numeric elements in the sequence.
def _recursive_list_map(f, x):
if isinstance(x, list):
return [_recursive_list_map(f, xi) for xi in x]
def _recursive_sequence_map(f, x):
"""Recursively map a function over a sequence of arbitary depth"""
if isinstance(x, (list, tuple)):
seq_type = type(x)
return seq_type(_recursive_sequence_map(f, xi) for xi in x)
else:
return f(x)
@@ -144,7 +146,9 @@ class ApproxNumpy(ApproxBase):
"""Perform approximate comparisons where the expected value is numpy array."""
def __repr__(self) -> str:
list_scalars = _recursive_list_map(self._approx_scalar, self.expected.tolist())
list_scalars = _recursive_sequence_map(
self._approx_scalar, self.expected.tolist()
)
return f"approx({list_scalars!r})"
def _repr_compare(self, other_side: "ndarray") -> List[str]:
@@ -164,7 +168,7 @@ class ApproxNumpy(ApproxBase):
return value
np_array_shape = self.expected.shape
approx_side_as_list = _recursive_list_map(
approx_side_as_seq = _recursive_sequence_map(
self._approx_scalar, self.expected.tolist()
)
@@ -179,7 +183,7 @@ class ApproxNumpy(ApproxBase):
max_rel_diff = -math.inf
different_ids = []
for index in itertools.product(*(range(i) for i in np_array_shape)):
approx_value = get_value_from_nested_list(approx_side_as_list, index)
approx_value = get_value_from_nested_list(approx_side_as_seq, index)
other_value = get_value_from_nested_list(other_side, index)
if approx_value != other_value:
abs_diff = abs(approx_value.expected - other_value)
@@ -194,7 +198,7 @@ class ApproxNumpy(ApproxBase):
(
str(index),
str(get_value_from_nested_list(other_side, index)),
str(get_value_from_nested_list(approx_side_as_list, index)),
str(get_value_from_nested_list(approx_side_as_seq, index)),
)
for index in different_ids
]
@@ -319,7 +323,6 @@ class ApproxSequenceLike(ApproxBase):
def _repr_compare(self, other_side: Sequence[float]) -> List[str]:
import math
import numpy as np
if len(self.expected) != len(other_side):
return [
@@ -327,7 +330,7 @@ class ApproxSequenceLike(ApproxBase):
f"Lengths: {len(self.expected)} and {len(other_side)}",
]
approx_side_as_map = _recursive_list_map(self._approx_scalar, self.expected)
approx_side_as_map = _recursive_sequence_map(self._approx_scalar, self.expected)
number_of_elements = len(approx_side_as_map)
max_abs_diff = -math.inf
@@ -340,7 +343,7 @@ class ApproxSequenceLike(ApproxBase):
abs_diff = abs(approx_value.expected - other_value)
max_abs_diff = max(max_abs_diff, abs_diff)
if other_value == 0.0:
max_rel_diff = np.inf
max_rel_diff = math.inf
else:
max_rel_diff = max(max_rel_diff, abs_diff / abs(other_value))
different_ids.append(i)
@@ -573,7 +576,7 @@ def approx(expected, rel=None, abs=None, nan_ok: bool = False) -> ApproxBase:
>>> {'a': 0.1 + 0.2, 'b': 0.2 + 0.4} == approx({'a': 0.3, 'b': 0.6})
True
The comparision will be true if both mappings have the same keys and their
The comparison will be true if both mappings have the same keys and their
respective values match the expected tolerances.
**Tolerances**

View File

@@ -158,7 +158,14 @@ def warns(
class WarningsRecorder(warnings.catch_warnings):
"""A context manager to record raised warnings.
Each recorded warning is an instance of :class:`warnings.WarningMessage`.
Adapted from `warnings.catch_warnings`.
.. note::
``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated
differently; see :ref:`ensuring_function_triggers`.
"""
def __init__(self, *, _ispytest: bool = False) -> None:

View File

@@ -455,7 +455,7 @@ def _report_to_json(report: BaseReport) -> Dict[str, Any]:
def serialize_repr_entry(
entry: Union[ReprEntry, ReprEntryNative]
) -> Dict[str, Any]:
data = attr.asdict(entry)
data = attr.asdict(entry) # type:ignore[arg-type]
for key, value in data.items():
if hasattr(value, "__dict__"):
data[key] = attr.asdict(value)
@@ -463,7 +463,7 @@ def _report_to_json(report: BaseReport) -> Dict[str, Any]:
return entry_data
def serialize_repr_traceback(reprtraceback: ReprTraceback) -> Dict[str, Any]:
result = attr.asdict(reprtraceback)
result = attr.asdict(reprtraceback) # type:ignore[arg-type]
result["reprentries"] = [
serialize_repr_entry(x) for x in reprtraceback.reprentries
]
@@ -473,7 +473,7 @@ def _report_to_json(report: BaseReport) -> Dict[str, Any]:
reprcrash: Optional[ReprFileLocation],
) -> Optional[Dict[str, Any]]:
if reprcrash is not None:
return attr.asdict(reprcrash)
return attr.asdict(reprcrash) # type:ignore[arg-type]
else:
return None

View File

@@ -158,9 +158,10 @@ class TempPathFactory:
def get_user() -> Optional[str]:
"""Return the current user name, or None if getuser() does not work
in the current environment (see #1010)."""
import getpass
try:
# In some exotic environments, getpass may not be importable.
import getpass
return getpass.getuser()
except (ImportError, KeyError):
return None

View File

@@ -316,7 +316,10 @@ class TestCaseFunction(Function):
# Arguably we could always postpone tearDown(), but this changes the moment where the
# TestCase instance interacts with the results object, so better to only do it
# when absolutely needed.
if self.config.getoption("usepdb") and not _is_skipped(self.obj):
# We need to consider if the test itself is skipped, or the whole class.
assert isinstance(self.parent, UnitTestCase)
skipped = _is_skipped(self.obj) or _is_skipped(self.parent.obj)
if self.config.getoption("usepdb") and not skipped:
self._explicit_tearDown = self._testcase.tearDown
setattr(self._testcase, "tearDown", lambda *args: None)

View File

@@ -0,0 +1,12 @@
from dataclasses import dataclass
from dataclasses import InitVar
@dataclass
class Foo:
init_only: InitVar[int]
real_attr: int
def test_demonstrate():
assert Foo(1, 2) == Foo(1, 3)

View File

@@ -2,12 +2,14 @@ import operator
from contextlib import contextmanager
from decimal import Decimal
from fractions import Fraction
from math import sqrt
from operator import eq
from operator import ne
from typing import Optional
import pytest
from _pytest.pytester import Pytester
from _pytest.python_api import _recursive_sequence_map
from pytest import approx
inf, nan = float("inf"), float("nan")
@@ -92,9 +94,7 @@ SOME_INT = r"[0-9]+\s*"
class TestApprox:
def test_error_messages(self, assert_approx_raises_regex):
np = pytest.importorskip("numpy")
def test_error_messages_native_dtypes(self, assert_approx_raises_regex):
assert_approx_raises_regex(
2.0,
1.0,
@@ -135,6 +135,34 @@ class TestApprox:
],
)
assert_approx_raises_regex(
(1, 2.2, 4),
(1, 3.2, 4),
[
r" comparison failed. Mismatched elements: 1 / 3:",
rf" Max absolute difference: {SOME_FLOAT}",
rf" Max relative difference: {SOME_FLOAT}",
r" Index \| Obtained\s+\| Expected ",
rf" 1 \| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}",
],
)
# Specific test for comparison with 0.0 (relative diff will be 'inf')
assert_approx_raises_regex(
[0.0],
[1.0],
[
r" comparison failed. Mismatched elements: 1 / 1:",
rf" Max absolute difference: {SOME_FLOAT}",
r" Max relative difference: inf",
r" Index \| Obtained\s+\| Expected ",
rf"\s*0\s*\| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}",
],
)
def test_error_messages_numpy_dtypes(self, assert_approx_raises_regex):
np = pytest.importorskip("numpy")
a = np.linspace(0, 100, 20)
b = np.linspace(0, 100, 20)
a[10] += 0.5
@@ -175,18 +203,6 @@ class TestApprox:
)
# Specific test for comparison with 0.0 (relative diff will be 'inf')
assert_approx_raises_regex(
[0.0],
[1.0],
[
r" comparison failed. Mismatched elements: 1 / 1:",
rf" Max absolute difference: {SOME_FLOAT}",
r" Max relative difference: inf",
r" Index \| Obtained\s+\| Expected ",
rf"\s*0\s*\| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}",
],
)
assert_approx_raises_regex(
np.array([0.0]),
np.array([1.0]),
@@ -876,3 +892,31 @@ class TestApprox:
"""pytest.approx() should raise an error on unordered sequences (#9692)."""
with pytest.raises(TypeError, match="only supports ordered sequences"):
assert {1, 2, 3} == approx({1, 2, 3})
class TestRecursiveSequenceMap:
def test_map_over_scalar(self):
assert _recursive_sequence_map(sqrt, 16) == 4
def test_map_over_empty_list(self):
assert _recursive_sequence_map(sqrt, []) == []
def test_map_over_list(self):
assert _recursive_sequence_map(sqrt, [4, 16, 25, 676]) == [2, 4, 5, 26]
def test_map_over_tuple(self):
assert _recursive_sequence_map(sqrt, (4, 16, 25, 676)) == (2, 4, 5, 26)
def test_map_over_nested_lists(self):
assert _recursive_sequence_map(sqrt, [4, [25, 64], [[49]]]) == [
2,
[5, 8],
[[7]],
]
def test_map_over_mixed_sequence(self):
assert _recursive_sequence_map(sqrt, [4, (25, 64), [(49)]]) == [
2,
(5, 8),
[(7)],
]

View File

@@ -82,13 +82,9 @@ class TestRaises:
def test_does_not_raise(self, pytester: Pytester) -> None:
pytester.makepyfile(
"""
from contextlib import contextmanager
from contextlib import nullcontext as does_not_raise
import pytest
@contextmanager
def does_not_raise():
yield
@pytest.mark.parametrize('example_input,expectation', [
(3, does_not_raise()),
(2, does_not_raise()),
@@ -107,13 +103,9 @@ class TestRaises:
def test_does_not_raise_does_raise(self, pytester: Pytester) -> None:
pytester.makepyfile(
"""
from contextlib import contextmanager
from contextlib import nullcontext as does_not_raise
import pytest
@contextmanager
def does_not_raise():
yield
@pytest.mark.parametrize('example_input,expectation', [
(0, does_not_raise()),
(1, pytest.raises(ZeroDivisionError)),

View File

@@ -882,6 +882,13 @@ class TestAssert_reprcompare_dataclass:
result.assert_outcomes(failed=1, passed=0)
result.stdout.no_re_match_line(".*Differing attributes.*")
def test_data_classes_with_initvar(self, pytester: Pytester) -> None:
p = pytester.copy_example("dataclasses/test_compare_initvar.py")
# issue 9820
result = pytester.runpytest(p, "-vv")
result.assert_outcomes(failed=1, passed=0)
result.stdout.no_re_match_line(".*AttributeError.*")
class TestAssert_reprcompare_attrsclass:
def test_attrs(self) -> None:

View File

@@ -204,16 +204,8 @@ class TestAssertionRewrite:
def f4() -> None:
assert sys == 42 # type: ignore[comparison-overlap]
verbose = request.config.getoption("verbose")
msg = getmsg(f4, {"sys": sys})
if verbose > 0:
assert msg == (
"assert <module 'sys' (built-in)> == 42\n"
" +<module 'sys' (built-in)>\n"
" -42"
)
else:
assert msg == "assert sys == 42"
assert msg == "assert sys == 42"
def f5() -> None:
assert cls == 42 # type: ignore[name-defined] # noqa: F821
@@ -224,20 +216,7 @@ class TestAssertionRewrite:
msg = getmsg(f5, {"cls": X})
assert msg is not None
lines = msg.splitlines()
if verbose > 1:
assert lines == [
f"assert {X!r} == 42",
f" +{X!r}",
" -42",
]
elif verbose > 0:
assert lines == [
"assert <class 'test_...e.<locals>.X'> == 42",
f" +{X!r}",
" -42",
]
else:
assert lines == ["assert cls == 42"]
assert lines == ["assert cls == 42"]
def test_assertrepr_compare_same_width(self, request) -> None:
"""Should use same width/truncation with same initial width."""
@@ -279,14 +258,11 @@ class TestAssertionRewrite:
msg = getmsg(f, {"cls": Y})
assert msg is not None
lines = msg.splitlines()
if request.config.getoption("verbose") > 0:
assert lines == ["assert 3 == 2", " +3", " -2"]
else:
assert lines == [
"assert 3 == 2",
" + where 3 = Y.foo",
" + where Y = cls()",
]
assert lines == [
"assert 3 == 2",
" + where 3 = Y.foo",
" + where Y = cls()",
]
def test_assert_already_has_message(self) -> None:
def f():
@@ -663,10 +639,7 @@ class TestAssertionRewrite:
assert len(values) == 11
msg = getmsg(f)
if request.config.getoption("verbose") > 0:
assert msg == "assert 10 == 11\n +10\n -11"
else:
assert msg == "assert 10 == 11\n + where 10 = len([0, 1, 2, 3, 4, 5, ...])"
assert msg == "assert 10 == 11\n + where 10 = len([0, 1, 2, 3, 4, 5, ...])"
def test_custom_reprcompare(self, monkeypatch) -> None:
def my_reprcompare1(op, left, right) -> str:
@@ -732,10 +705,7 @@ class TestAssertionRewrite:
msg = getmsg(f)
assert msg is not None
lines = util._format_lines([msg])
if request.config.getoption("verbose") > 0:
assert lines == ["assert 0 == 1\n +0\n -1"]
else:
assert lines == ["assert 0 == 1\n + where 1 = \\n{ \\n~ \\n}.a"]
assert lines == ["assert 0 == 1\n + where 1 = \\n{ \\n~ \\n}.a"]
def test_custom_repr_non_ascii(self) -> None:
def f() -> None:
@@ -1039,7 +1009,7 @@ class TestAssertionRewriteHookDetails:
)
assert pytester.runpytest().ret == 0
def test_write_pyc(self, pytester: Pytester, tmp_path, monkeypatch) -> None:
def test_write_pyc(self, pytester: Pytester, tmp_path) -> None:
from _pytest.assertion.rewrite import _write_pyc
from _pytest.assertion import AssertionState
@@ -1051,27 +1021,8 @@ class TestAssertionRewriteHookDetails:
co = compile("1", "f.py", "single")
assert _write_pyc(state, co, os.stat(source_path), pycpath)
if sys.platform == "win32":
from contextlib import contextmanager
@contextmanager
def atomic_write_failed(fn, mode="r", overwrite=False):
e = OSError()
e.errno = 10
raise e
yield
monkeypatch.setattr(
_pytest.assertion.rewrite, "atomic_write", atomic_write_failed
)
else:
def raise_oserror(*args):
raise OSError()
monkeypatch.setattr("os.rename", raise_oserror)
assert not _write_pyc(state, co, os.stat(source_path), pycpath)
with mock.patch.object(os, "replace", side_effect=OSError):
assert not _write_pyc(state, co, os.stat(source_path), pycpath)
def test_resources_provider_for_loader(self, pytester: Pytester) -> None:
"""

View File

@@ -651,7 +651,7 @@ class Test_getinitialnodes:
for parent in col.listchain():
assert parent.config is config
def test_pkgfile(self, pytester: Pytester) -> None:
def test_pkgfile(self, pytester: Pytester, monkeypatch: MonkeyPatch) -> None:
"""Verify nesting when a module is within a package.
The parent chain should match: Module<x.py> -> Package<subdir> -> Session.
Session's parent should always be None.
@@ -660,7 +660,8 @@ class Test_getinitialnodes:
subdir = tmp_path.joinpath("subdir")
x = ensure_file(subdir / "x.py")
ensure_file(subdir / "__init__.py")
with subdir.cwd():
with monkeypatch.context() as mp:
mp.chdir(subdir)
config = pytester.parseconfigure(x)
col = pytester.getnode(config, x)
assert col is not None
@@ -1188,8 +1189,7 @@ def test_collect_with_chdir_during_import(pytester: Pytester) -> None:
"""
% (str(subdir),)
)
with pytester.path.cwd():
result = pytester.runpytest()
result = pytester.runpytest()
result.stdout.fnmatch_lines(["*1 passed in*"])
assert result.ret == 0
@@ -1200,8 +1200,7 @@ def test_collect_with_chdir_during_import(pytester: Pytester) -> None:
testpaths = .
"""
)
with pytester.path.cwd():
result = pytester.runpytest("--collect-only")
result = pytester.runpytest("--collect-only")
result.stdout.fnmatch_lines(["collected 1 item"])
@@ -1224,7 +1223,8 @@ def test_collect_pyargs_with_testpaths(
)
)
monkeypatch.setenv("PYTHONPATH", str(pytester.path), prepend=os.pathsep)
with root.cwd():
with monkeypatch.context() as mp:
mp.chdir(root)
result = pytester.runpytest_subprocess()
result.stdout.fnmatch_lines(["*1 passed in*"])

View File

@@ -837,6 +837,9 @@ class TestConfigAPI:
(["src/bar/__init__.py"], ["bar"]),
(["src/bar/__init__.py", "setup.py"], ["bar"]),
(["source/python/bar/__init__.py", "setup.py"], ["bar"]),
# editable installation finder modules
(["__editable___xyz_finder.py"], []),
(["bar/__init__.py", "__editable___xyz_finder.py"], ["bar"]),
],
)
def test_iter_rewritable_modules(self, names, expected) -> None:

View File

@@ -252,6 +252,34 @@ def test_conftest_confcutdir(pytester: Pytester) -> None:
result.stdout.no_fnmatch_line("*warning: could not load initial*")
def test_installed_conftest_is_picked_up(pytester: Pytester, tmp_path: Path) -> None:
"""When using `--pyargs` to run tests in an installed packages (located e.g.
in a site-packages in the PYTHONPATH), conftest files in there are picked
up.
Regression test for #9767.
"""
# pytester dir - the source tree.
# tmp_path - the simulated site-packages dir (not in source tree).
pytester.syspathinsert(tmp_path)
pytester.makepyprojecttoml("[tool.pytest.ini_options]")
tmp_path.joinpath("foo").mkdir()
tmp_path.joinpath("foo", "__init__.py").touch()
tmp_path.joinpath("foo", "conftest.py").write_text(
textwrap.dedent(
"""\
import pytest
@pytest.fixture
def fix(): return None
"""
)
)
tmp_path.joinpath("foo", "test_it.py").write_text("def test_it(fix): pass")
result = pytester.runpytest("--pyargs", "foo")
assert result.ret == 0
def test_conftest_symlink(pytester: Pytester) -> None:
"""`conftest.py` discovery follows normal path resolution and does not resolve symlinks."""
# Structure:
@@ -525,7 +553,7 @@ class TestConftestVisibility:
)
)
print("created directory structure:")
for x in pytester.path.rglob(""):
for x in pytester.path.glob("**/"):
print(" " + str(x.relative_to(pytester.path)))
return {"runner": runner, "package": package, "swc": swc, "snc": snc}

View File

@@ -353,6 +353,7 @@ class TestPDB:
result = pytester.runpytest_subprocess("--pdb", ".")
result.stdout.fnmatch_lines(["-> import unknown"])
@pytest.mark.xfail(reason="#10042")
def test_pdb_interaction_capturing_simple(self, pytester: Pytester) -> None:
p1 = pytester.makepyfile(
"""
@@ -521,6 +522,7 @@ class TestPDB:
assert "BdbQuit" not in rest
assert "UNEXPECTED EXCEPTION" not in rest
@pytest.mark.xfail(reason="#10042")
def test_pdb_interaction_capturing_twice(self, pytester: Pytester) -> None:
p1 = pytester.makepyfile(
"""
@@ -556,6 +558,7 @@ class TestPDB:
assert "1 failed" in rest
self.flush(child)
@pytest.mark.xfail(reason="#10042")
def test_pdb_with_injected_do_debug(self, pytester: Pytester) -> None:
"""Simulates pdbpp, which injects Pdb into do_debug, and uses
self.__class__ in do_continue.
@@ -1000,6 +1003,7 @@ class TestDebuggingBreakpoints:
assert "reading from stdin while output" not in rest
TestPDB.flush(child)
@pytest.mark.xfail(reason="#10042")
def test_pdb_not_altered(self, pytester: Pytester) -> None:
p1 = pytester.makepyfile(
"""
@@ -1159,6 +1163,7 @@ def test_quit_with_swallowed_SystemExit(pytester: Pytester) -> None:
@pytest.mark.parametrize("fixture", ("capfd", "capsys"))
@pytest.mark.xfail(reason="#10042")
def test_pdb_suspends_fixture_capturing(pytester: Pytester, fixture: str) -> None:
"""Using "-s" with pytest should suspend/resume fixture capturing."""
p1 = pytester.makepyfile(

View File

@@ -113,6 +113,28 @@ class TestDoctests:
reprec = pytester.inline_run(p)
reprec.assertoutcome(failed=1)
def test_importmode(self, pytester: Pytester):
p = pytester.makepyfile(
**{
"namespacepkg/innerpkg/__init__.py": "",
"namespacepkg/innerpkg/a.py": """
def some_func():
return 42
""",
"namespacepkg/innerpkg/b.py": """
from namespacepkg.innerpkg.a import some_func
def my_func():
'''
>>> my_func()
42
'''
return some_func()
""",
}
)
reprec = pytester.inline_run(p, "--doctest-modules", "--import-mode=importlib")
reprec.assertoutcome(passed=1)
def test_new_pattern(self, pytester: Pytester):
p = pytester.maketxtfile(
xdoc="""
@@ -201,7 +223,11 @@ class TestDoctests:
"Traceback (most recent call last):",
' File "*/doctest.py", line *, in __run',
" *",
*((" *^^^^*",) if sys.version_info >= (3, 11) else ()),
*(
(" *^^^^*",)
if (3, 11, 0, "beta", 4) > sys.version_info >= (3, 11)
else ()
),
' File "<doctest test_doctest_unexpected_exception.txt[1]>", line 1, in <module>',
"ZeroDivisionError: division by zero",
"*/test_doctest_unexpected_exception.txt:2: UnexpectedException",

View File

@@ -1625,6 +1625,28 @@ def test_escaped_skipreason_issue3533(
snode.assert_attr(message="1 <> 2")
def test_escaped_setup_teardown_error(
pytester: Pytester, run_and_parse: RunAndParse
) -> None:
pytester.makepyfile(
"""
import pytest
@pytest.fixture()
def my_setup():
raise Exception("error: \033[31mred\033[m")
def test_esc(my_setup):
pass
"""
)
_, dom = run_and_parse()
node = dom.find_first_by_tag("testcase")
snode = node.find_first_by_tag("error")
assert "#x1B[31mred#x1B[m" in snode["message"]
assert "#x1B[31mred#x1B[m" in snode.text
@parametrize_families
def test_logging_passing_tests_disabled_does_not_log_test_output(
pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str

View File

@@ -47,7 +47,7 @@ def test_wrap_session_notify_exception(ret_exc, pytester: Pytester) -> None:
end_lines = (
result.stdout.lines[-4:]
if sys.version_info >= (3, 11)
if (3, 11, 0, "beta", 4) > sys.version_info >= (3, 11)
else result.stdout.lines[-3:]
)
@@ -57,7 +57,7 @@ def test_wrap_session_notify_exception(ret_exc, pytester: Pytester) -> None:
'INTERNALERROR> raise SystemExit("boom")',
*(
("INTERNALERROR> ^^^^^^^^^^^^^^^^^^^^^^^^",)
if sys.version_info >= (3, 11)
if (3, 11, 0, "beta", 4) > sys.version_info >= (3, 11)
else ()
),
"INTERNALERROR> SystemExit: boom",
@@ -68,7 +68,7 @@ def test_wrap_session_notify_exception(ret_exc, pytester: Pytester) -> None:
'INTERNALERROR> raise ValueError("boom")',
*(
("INTERNALERROR> ^^^^^^^^^^^^^^^^^^^^^^^^",)
if sys.version_info >= (3, 11)
if (3, 11, 0, "beta", 4) > sys.version_info >= (3, 11)
else ()
),
"INTERNALERROR> ValueError: boom",

View File

@@ -1241,12 +1241,15 @@ def test_pdb_teardown_called(pytester: Pytester, monkeypatch: MonkeyPatch) -> No
@pytest.mark.parametrize("mark", ["@unittest.skip", "@pytest.mark.skip"])
def test_pdb_teardown_skipped(
def test_pdb_teardown_skipped_for_functions(
pytester: Pytester, monkeypatch: MonkeyPatch, mark: str
) -> None:
"""With --pdb, setUp and tearDown should not be called for skipped tests."""
"""
With --pdb, setUp and tearDown should not be called for tests skipped
via a decorator (#7215).
"""
tracked: List[str] = []
monkeypatch.setattr(pytest, "test_pdb_teardown_skipped", tracked, raising=False)
monkeypatch.setattr(pytest, "track_pdb_teardown_skipped", tracked, raising=False)
pytester.makepyfile(
"""
@@ -1256,10 +1259,10 @@ def test_pdb_teardown_skipped(
class MyTestCase(unittest.TestCase):
def setUp(self):
pytest.test_pdb_teardown_skipped.append("setUp:" + self.id())
pytest.track_pdb_teardown_skipped.append("setUp:" + self.id())
def tearDown(self):
pytest.test_pdb_teardown_skipped.append("tearDown:" + self.id())
pytest.track_pdb_teardown_skipped.append("tearDown:" + self.id())
{mark}("skipped for reasons")
def test_1(self):
@@ -1274,6 +1277,43 @@ def test_pdb_teardown_skipped(
assert tracked == []
@pytest.mark.parametrize("mark", ["@unittest.skip", "@pytest.mark.skip"])
def test_pdb_teardown_skipped_for_classes(
pytester: Pytester, monkeypatch: MonkeyPatch, mark: str
) -> None:
"""
With --pdb, setUp and tearDown should not be called for tests skipped
via a decorator on the class (#10060).
"""
tracked: List[str] = []
monkeypatch.setattr(pytest, "track_pdb_teardown_skipped", tracked, raising=False)
pytester.makepyfile(
"""
import unittest
import pytest
{mark}("skipped for reasons")
class MyTestCase(unittest.TestCase):
def setUp(self):
pytest.track_pdb_teardown_skipped.append("setUp:" + self.id())
def tearDown(self):
pytest.track_pdb_teardown_skipped.append("tearDown:" + self.id())
def test_1(self):
pass
""".format(
mark=mark
)
)
result = pytester.runpytest_inprocess("--pdb")
result.stdout.fnmatch_lines("* 1 skipped in *")
assert tracked == []
def test_async_support(pytester: Pytester) -> None:
pytest.importorskip("unittest.async_case")