Compare commits

...

90 Commits
6.2.0 ... 6.2.x

Author SHA1 Message Date
James Frost
aa6f97d6cd Add html_baseurl to sphinx conf.py (#12364) (#12396)
This is used to set the <link rel="canonical" href="X"> tag that points to the canonical version of the webpage. Including this indicates to search engines which version to include in their indexes, and should prevent older versions showing up.

Fixes #12363
2024-05-30 08:09:40 -03:00
Brylie Christopher Oxley
20f1c95be2 Change example to reference from within same page (#10189) 2022-08-11 08:30:09 -03:00
Bruno Oliveira
c8ae63db7f [6.2.x] docs(monkeypatch): Fix autodoc reference links (#10017)
* docs(monkeypatch): Fix autodoc reference links (#10013)

Close #10014

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-06-02 11:07:06 -03:00
pre-commit-ci[bot]
195c1fcda4 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2022-06-02 13:41:07 +00:00
Tony Narlock
6956a00056 docs(monkeypatch): Fix autodoc reference links (#10013)
Close #10014
2022-06-02 08:36:46 -05:00
Bruno Oliveira
4e7a69d45a Merge pull request #9572 from mgorny/backport-8664
Backport 8664 to 6.2.x
2022-01-29 09:49:22 -03:00
Ran Benita
ea35fd299a Merge pull request #8664 from hroncok/test_unittest-ignore_DeprecationWarning-from-twisted
Ignore DeprecationWarnings in test_trial_error

(cherry picked from commit 850a844779)
2022-01-29 09:05:13 -03:00
Bruno Oliveira
c30ffc5b46 Merge pull request #9571 from mgorny/backport-9417 2022-01-29 08:58:49 -03:00
Bruno Oliveira
741c8482d8 Merge pull request #9417 from nicoddemus/fix-py3.10.1-9413
(cherry picked from commit 47df71d23f)
2022-01-29 07:58:49 -03:00
Bruno Oliveira
a6726abbe2 Merge pull request #9573 from mgorny/backport-9512
Backport #9512 to 6.2.x
2022-01-29 07:57:46 -03:00
Bruno Oliveira
8c1016884f ci: remove linting job - covered be pre-commit.ci (#9247) (#9574)
(cherry picked from commit 69b0a7b306)

Co-authored-by: Ran Benita <ran@unusedvar.com>
2022-01-29 07:56:46 -03:00
Ran Benita
981ca24d40 ci: remove linting job - covered be pre-commit.ci (#9247)
(cherry picked from commit 69b0a7b306)
2022-01-29 07:58:00 +01:00
Ran Benita
2a22b81240 Merge pull request #9512 from bluetech/pluggy-private
testing: avoid private pluggy attributes in test
(cherry picked from commit a6310c20c1)
2022-01-29 07:54:45 +01:00
Ran Benita
cca9b90905 Merge pull request #9180 from dybi/patch-3
Further fixes in fixtures' examples
2021-10-09 19:14:29 +03:00
Piotr Dybowski
2476da4445 Further fixes in fixtures' examples 2021-10-08 15:45:23 +02:00
Piotr Dybowski
c642e6d550 Fix example in fixture documentation (sending_user, receiving_user) (#9179) 2021-10-08 11:41:40 +03:00
Bruno Oliveira
2cf78187a8 Merge pull request #9085 from The-Compiler/remove-workshoptage-6.2
[6.2] Remove finished open training
2021-09-10 08:26:25 -03:00
Florian Bruhin
8f9779d43c Remove finished open training
(cherry picked from commit 1c1dcf27c8)
2021-09-09 22:32:13 +02:00
Bruno Oliveira
16664a1b72 Merge pull request #9057 from pytest-dev/release-6.2.5 2021-08-30 14:40:59 -03:00
Bruno Oliveira
1569fac603 Fix CHANGELOG header 2021-08-29 11:09:45 -03:00
pytest bot
a3599cacb5 Prepare release version 6.2.5 2021-08-29 14:05:45 +00:00
Bruno Oliveira
27613b8d70 Merge pull request #9056 from nicoddemus/backport-9053
[6.2.x] Merge pull request #9053 from nicoddemus/change-8494-to-trivial
2021-08-29 11:00:28 -03:00
Bruno Oliveira
cef74be094 Merge pull request #9053 from nicoddemus/change-8494-to-trivial 2021-08-29 10:52:35 -03:00
Bruno Oliveira
83dc953669 Merge pull request #9051 from nicoddemus/backport-9047 2021-08-27 14:59:44 -03:00
Bruno Oliveira
fb38e8d097 Merge pull request #9047 from nicoddemus/changelog-9040
Add changelog for #9040 (pluggy 1.0)
2021-08-27 14:58:55 -03:00
Bruno Oliveira
d74baf4a52 Merge pull request #9042 from nicoddemus/backport-9040 2021-08-27 09:04:50 -03:00
Miro Hrončok
d9b8f7cf0a Backport #8896 2021-08-27 07:43:30 -03:00
Ronny Pfannschmidt
69212d15fa Merge pull request #8425 from RonnyPfannschmidt/main-fixes
reconfigure everything for main
2021-08-26 18:07:13 -03:00
Bruno Oliveira
44d3282bb7 Merge pull request #9040 from nicoddemus/bump-pluggy 2021-08-26 14:56:00 -03:00
Bruno Oliveira
fafe00aaee Merge pull request #9024 from pytest-dev/raining-update-2-6.4.x
[6.2.x] doc: Update trainings
2021-08-19 14:30:26 -03:00
Florian Bruhin
477225c18a doc: Update trainings
(cherry picked from commit e643385399)
2021-08-19 18:55:52 +02:00
Bruno Oliveira
4e51b9200f Merge pull request #8932 from guettli/patch-1
contact.rst: Added github discussions.
2021-07-23 07:08:28 -03:00
Thomas Güttler
b0fb02297e contact.rst: Added github discussions. 2021-07-22 10:28:53 +02:00
Brylie Christopher Oxley
e47a352c49 Fix "external plugins" link (#8806) 2021-06-28 14:32:31 +02:00
Bruno Oliveira
767b3b2cc0 Merge pull request #8794 from The-Compiler/6.2.x-open-trainings 2021-06-24 16:02:02 -03:00
Florian Bruhin
4eae23a633 doc: Add new open trainings
(cherry picked from commit 61336bb385)
2021-06-24 20:22:09 +02:00
Bruno Oliveira
6989435490 Merge pull request #8774 from hroncok/6.2.x-setuptools-typo
[6.2] Remove duplicate '>=' in setup.cfg
2021-06-17 11:11:04 -03:00
Sylvain Bellemare
b178175e95 Remove duplicate '>=' in setup.cfg
Cherrypicked from 3d831225bb
2021-06-17 12:56:59 +02:00
Bruno Oliveira
de7c19a373 Merge pull request #8762 from asztalosdani/patch-1
Typo fixed
2021-06-14 10:55:21 -03:00
Daniel Asztalos
82611bdc1e Typo fixed 2021-06-14 15:48:06 +02:00
Bruno Oliveira
f0e12d4d61 Merge pull request #8755 from nicoddemus/backport-8754
[6.2.x] Merge pull request #8754 from nicoddemus/fix-deprecation-docs
2021-06-12 13:56:55 -03:00
Bruno Oliveira
3293758545 Merge pull request #8754 from nicoddemus/fix-deprecation-docs 2021-06-12 13:45:50 -03:00
Anthony Sottile
4c41b7e88b Merge pull request #8746 from haugenj/patch-1
Fix typo in fixture.rst
2021-06-09 14:56:28 -07:00
Jason Haugen
74495a2b7f Fix typo in fixture.rst
Example referenced the wrong function name
2021-06-09 13:48:16 -05:00
Florian Bruhin
a5e67a26b0 Switch to irc.libera.chat (#8723)
(cherry picked from commit 06ff9c3a97)
2021-06-01 18:04:47 +02:00
Anthony Sottile
8e0b08d5da Merge pull request #8702 from The-Compiler/remove-training-2-6.2.x
[6.2.x] doc: Remove training sidebar
2021-05-28 07:59:34 -07:00
MapleCCC
5ad0d1acb9 Update index.rst (#8708) 2021-05-27 21:44:33 +02:00
Florian Bruhin
89b6b2ee2b doc: Remove training sidebar
(cherry picked from commit 54e4aad88f)
2021-05-26 14:15:58 +02:00
Bruno Oliveira
ddf7d88a51 Merge pull request #8689 from bluetech/backport-8681
[6.2.x] ci: few security/permissions improvements
2021-05-18 20:23:29 -03:00
Ran Benita
733f8027d2 Merge pull request #8681 from bluetech/ci-permissions
ci: few security/permissions improvements
(cherry picked from commit ff6d29717c)

Conflicts:
      .github/workflows/main.yml
      .github/workflows/prepare-release-pr.yml
      .github/workflows/update-plugin-list.yml
      scripts/prepare-release-pr.py
      scripts/report-coverage.sh
2021-05-18 22:26:28 +03:00
Bruno Oliveira
8317542c9e Merge pull request #8639 from mgorny/backport-8494-8555 2021-05-06 16:01:16 -03:00
Michał Górny
48f78f9ba0 Update setup-cfg-fmt to 1.17.0 2021-05-06 16:49:09 +02:00
Florian Bruhin
1e1955dbb7 Merge pull request #8555 from The-Compiler/py310-fix
Fix Python 3.10 test issues

(cherry picked from commit adc197424d)
2021-05-05 22:33:57 +02:00
Bruno Oliveira
ca68875eab Merge pull request #8494 from nicoddemus/python-3.10
(cherry picked from commit c1bdff9e69)
2021-05-05 22:30:20 +02:00
Anthony Sottile
15a45388fa Merge pull request #8632 from pytest-dev/release-6.2.4
Prepare release 6.2.4
2021-05-04 09:23:48 -07:00
pytest bot
017dd1ccd6 Prepare release version 6.2.4 2021-05-04 08:33:43 -07:00
Anthony Sottile
18569f44c1 Merge pull request #8629 from asottile/backport_8540
[6.2.x] Merge pull request #8540 from hauntsaninja/assert310
2021-05-04 08:13:02 -07:00
Bruno Oliveira
d8d6812bdf Merge pull request #8540 from hauntsaninja/assert310
(cherry picked from commit af31c60db1)
2021-05-04 07:52:27 -07:00
Bruno Oliveira
a5061484d4 Merge pull request #8607 from cmaurer/patch-1
Update fixture.rst
2021-04-28 09:54:22 -03:00
Christian Maurer
69ea076d55 Update fixture.rst
Availability was misspelled.  It was `availabiility`
2021-04-28 07:01:45 -05:00
Florian Bruhin
40cb2f5b54 Backport training update (#8557) 2021-04-15 20:02:50 +02:00
Ran Benita
724e22cb00 Merge pull request #8519 from pytest-dev/release-6.2.3
Prepare release 6.2.3
2021-04-04 00:43:05 +03:00
pytest bot
3a2fd96305 Prepare release version 6.2.3 2021-04-03 21:41:18 +00:00
Ran Benita
138b19a930 Merge pull request #8517 from bluetech/backport-mktmp
[6.2.x] Fix minor temporary directory security issue
2021-04-04 00:34:15 +03:00
Ran Benita
822686e880 tmpdir: prevent using a non-private root temp directory
pytest uses a root temp directory named `/tmp/pytest-of-<username>`. The
name is predictable, and the directory might already exists from a
previous run, so that's allowed.

This makes it possible for my_user to pre-create
`/tmp/pytest-of-another_user`, thus giving my_user control of
another_user's tempdir.

Prevent this scenario by adding a couple of safety checks. I believe
they are sufficient.

Testing the first check requires changing the owner, which requires
root permissions, so can't be unit-tested easily, but I checked it
manually.
2021-04-04 00:04:50 +03:00
Ran Benita
9dc54f79b0 tmpdir: fix temporary directories created with world-readable permissions
(Written for a Unix system, but might be applicable to Windows as well).

pytest creates a root temporary directory under /tmp, named
`pytest-of-<username>`, and creates tmp_path's and other under it.
/tmp is shared between all users of the system.

This root temporary directory was created with 0o777&~umask permissions,
which usually becomes 0o755, meaning any user in the system could list
and read the files, which is undesirable.

Use 0o700 permissions instead. Also for subdirectories, because the root
dir is adjustable.
2021-04-04 00:00:42 +03:00
Ran Benita
93dbae24e1 pathlib: inline ensure_reset_dir()
This is only used in TempPathFactory.getbasetemp(). We'll be wanting
further control/care there, so move it into there.
2021-04-03 23:39:37 +03:00
Ran Benita
02fdbe2e76 pathlib: remove useless temporary variable 2021-04-03 23:39:32 +03:00
Bruno Oliveira
12e7db85af Merge pull request #8285 from nicoddemus/backport-8280
[6.2.x] Doc: Move the module declaration to index.rst
2021-01-27 09:13:27 -03:00
Bruno Oliveira
56e4392444 Merge pull request #8280 from xuhdev/module
Doc: Move the module declaration to index.rst
2021-01-27 09:07:33 -03:00
Bruno Oliveira
8220eca963 Merge pull request #8275 from pytest-dev/release-6.2.2
Prepare release 6.2.2
2021-01-25 11:52:23 -03:00
pytest bot
b9c98762f5 Prepare release version 6.2.2 2021-01-25 12:30:53 +00:00
Bruno Oliveira
8003fd23b9 Merge pull request #8259 from nicoddemus/backport-8250
[6.2.x] Fix faulthandler for Twisted Logger when used with "--capture=no"
2021-01-20 10:14:34 -03:00
Bruno Oliveira
8d605b9b26 Merge pull request #8250 from daq-tools/fix-twisted-capture 2021-01-20 09:47:10 -03:00
Bruno Oliveira
14e0c3e105 Merge pull request #8225 from The-Compiler/training-update (#8226)
doc: Add note about training early bird discount
2021-01-05 20:49:50 +01:00
Bruno Oliveira
45facc16c8 Merge pull request #8224 from nicoddemus/backport-8220
[6.2.x] DOC: Mark pytest module
2021-01-05 13:31:19 -03:00
Bruno Oliveira
99fe887d7c Merge pull request #8220 from xuhdev/module-doc
DOC: Mark pytest module
2021-01-05 13:26:19 -03:00
Bruno Oliveira
8dbf9dc1aa Merge pull request #8167 from nicoddemus/backport-8166
[6.2.x] Add Changelog to setup.cfg (#8166)
2020-12-17 13:59:14 -03:00
Adam Johnson
baaee2148d Add Changelog to setup.cfg (#8166)
Co-authored-by: Thomas Grainger <tagrain@gmail.com>
Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
2020-12-17 13:56:18 -03:00
Bruno Oliveira
f7d1ab870f Merge pull request #8163 from bluetech/backport-8152
[6.2.x] terminal: fix "(<Skipped instance>)" skip reason in test status line
2020-12-17 08:31:53 -03:00
Ran Benita
b8201c280e Merge pull request #8152 from bluetech/empty-skip
terminal: fix "(<Skipped instance>)" skip reason in test status line
(cherry picked from commit 02e69e5cdc)
2020-12-17 12:59:01 +02:00
Bruno Oliveira
1f0c50b475 Merge pull request #8160 from nicoddemus/backport-7381
[6.2.x] Clarify fixture execution order and provide visual aids (#7381)
2020-12-16 14:02:02 -03:00
Chris NeJame
da82e1853c Clarify fixture execution order and provide visual aids (#7381)
Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
Co-authored-by: Ran Benita <ran@unusedvar.com>
2020-12-16 13:54:52 -03:00
Bruno Oliveira
a566eb9c70 Merge pull request #8149 from pytest-dev/release-6.2.1
Prepare release 6.2.1
2020-12-15 12:39:06 -03:00
pytest bot
d3971c30f4 Prepare release version 6.2.1 2020-12-15 13:06:34 +00:00
Bruno Oliveira
780044b64a Merge pull request #8147 from nicoddemus/backport-8137
[6.2.x] python_api: handle array-like args in approx() #8137
2020-12-15 09:08:58 -03:00
Jakob van Santen
8354995abc python_api: handle array-like args in approx() (#8137) 2020-12-15 08:50:11 -03:00
Bruno Oliveira
8b8b1214f4 Merge pull request #8135 from nicoddemus/backport-8123
[6.2] Merge pull request #8123 from nicoddemus/import-mismatch-unc
2020-12-13 10:50:49 -03:00
Bruno Oliveira
f854cf66f4 Merge pull request #8123 from nicoddemus/import-mismatch-unc
Compare also paths on Windows when considering ImportPathMismatchError
2020-12-13 10:35:59 -03:00
Ran Benita
c475106f12 Merge pull request #8130 from pytest-dev/release-6.2.0
Prepare release 6.2.0
2020-12-12 23:21:28 +02:00
88 changed files with 3107 additions and 658 deletions

View File

@@ -3,7 +3,7 @@ name: main
on:
push:
branches:
- master
- main
- "[0-9]+.[0-9]+.x"
tags:
- "[0-9]+.[0-9]+.[0-9]+"
@@ -11,13 +11,18 @@ on:
pull_request:
branches:
- master
- main
- "[0-9]+.[0-9]+.x"
# Set permissions at the job level.
permissions: {}
jobs:
build:
runs-on: ${{ matrix.os }}
timeout-minutes: 30
permissions:
contents: read
strategy:
fail-fast: false
@@ -27,6 +32,8 @@ jobs:
"windows-py37",
"windows-py37-pluggy",
"windows-py38",
"windows-py39",
"windows-py310",
"ubuntu-py36",
"ubuntu-py37",
@@ -34,6 +41,7 @@ jobs:
"ubuntu-py37-freeze",
"ubuntu-py38",
"ubuntu-py39",
"ubuntu-py310",
"ubuntu-pypy3",
"macos-py37",
@@ -56,12 +64,20 @@ jobs:
- name: "windows-py37-pluggy"
python: "3.7"
os: windows-latest
tox_env: "py37-pluggymaster-xdist"
tox_env: "py37-pluggymain-xdist"
- name: "windows-py38"
python: "3.8"
os: windows-latest
tox_env: "py38-unittestextras"
use_coverage: true
- name: "windows-py39"
python: "3.9"
os: windows-latest
tox_env: "py39-xdist"
- name: "windows-py310"
python: "3.10.1"
os: windows-latest
tox_env: "py310-xdist"
- name: "ubuntu-py36"
python: "3.6"
@@ -75,7 +91,7 @@ jobs:
- name: "ubuntu-py37-pluggy"
python: "3.7"
os: ubuntu-latest
tox_env: "py37-pluggymaster-xdist"
tox_env: "py37-pluggymain-xdist"
- name: "ubuntu-py37-freeze"
python: "3.7"
os: ubuntu-latest
@@ -88,6 +104,10 @@ jobs:
python: "3.9"
os: ubuntu-latest
tox_env: "py39-xdist"
- name: "ubuntu-py310"
python: "3.10.1"
os: ubuntu-latest
tox_env: "py310-xdist"
- name: "ubuntu-pypy3"
python: "pypy3"
os: ubuntu-latest
@@ -122,10 +142,13 @@ jobs:
- uses: actions/checkout@v2
with:
fetch-depth: 0
persist-credentials: false
- name: Set up Python ${{ matrix.python }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
@@ -154,28 +177,13 @@ jobs:
CODECOV_NAME: ${{ matrix.name }}
run: bash scripts/report-coverage.sh -F GHA,${{ runner.os }}
linting:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- name: set PY
run: echo "name=PY::$(python -c 'import hashlib, sys;print(hashlib.sha256(sys.version.encode()+sys.executable.encode()).hexdigest())')" >> $GITHUB_ENV
- uses: actions/cache@v2
with:
path: ~/.cache/pre-commit
key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install tox
- run: tox -e linting
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]
@@ -183,25 +191,31 @@ jobs:
- 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 wheel setuptools tox
- name: Build package
run: |
python setup.py sdist bdist_wheel
- 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: ${{ secrets.release_notes }}
GH_RELEASE_NOTES_TOKEN: ${{ github.token }}
run: |
sudo apt-get install pandoc
tox -e publish-gh-release-notes

View File

@@ -7,9 +7,15 @@ on:
issue_comment:
types: [created, edited]
# Set permissions at the job level.
permissions: {}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: write
issues: write
if: (github.event.comment && startsWith(github.event.comment.body, '@pytestbot please')) || (github.event.issue && !github.event.comment && startsWith(github.event.issue.body, '@pytestbot please'))
@@ -17,15 +23,18 @@ jobs:
- uses: actions/checkout@v2
with:
fetch-depth: 0
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: "3.8"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install --upgrade setuptools tox
- name: Prepare release
run: |
tox -e release-on-comment -- $GITHUB_EVENT_PATH ${{ secrets.chatops }}
tox -e release-on-comment -- $GITHUB_EVENT_PATH ${{ github.token }}

View File

@@ -39,11 +39,10 @@ repos:
- id: pyupgrade
args: [--py36-plus]
- repo: https://github.com/asottile/setup-cfg-fmt
rev: v1.11.0
rev: v1.17.0
hooks:
- id: setup-cfg-fmt
# TODO: when upgrading setup-cfg-fmt this can be removed
args: [--max-py-version=3.9]
args: [--max-py-version=3.10]
- repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.6.0
hooks:

View File

@@ -21,6 +21,7 @@ Anders Hovmöller
Andras Mitzki
Andras Tim
Andrea Cimatoribus
Andreas Motl
Andreas Zeidler
Andrey Paramonov
Andrzej Klajnert
@@ -56,6 +57,7 @@ Charles Cloud
Charles Machalow
Charnjit SiNGH (CCSJ)
Chris Lamb
Chris NeJame
Christian Boelsen
Christian Fetzer
Christian Neumüller
@@ -135,6 +137,7 @@ Iwan Briquemont
Jaap Broekhuizen
Jakob van Santen
Jakub Mitoraj
James Frost
Jan Balster
Janne Vanhala
Jason R. Coombs
@@ -238,6 +241,7 @@ Petter Strandmark
Philipp Loose
Pieter Mulder
Piotr Banaszkiewicz
Piotr Dybowski
Piotr Helm
Prakhar Gurunani
Prashant Anand
@@ -271,6 +275,7 @@ Sankt Petersbug
Segev Finer
Serhii Mozghovyi
Seth Junot
Shantanu Jain
Shubham Adep
Simon Gomizelj
Simon Kerr
@@ -298,6 +303,7 @@ Tom Dalton
Tom Viner
Tomáš Gavenčiak
Tomer Keren
Tony Narlock
Tor Colvin
Trevor Bekolay
Tyler Goodlet

View File

@@ -236,7 +236,7 @@ Here is a simple overview, with pytest-specific bits:
$ cd pytest
# now, create your own branch off "master":
$ git checkout -b your-bugfix-branch-name master
$ git checkout -b your-bugfix-branch-name main
Given we have "major.minor.micro" version numbers, bug fixes will usually
be released in micro releases whereas features will be released in
@@ -318,7 +318,7 @@ Here is a simple overview, with pytest-specific bits:
compare: your-branch-name
base-fork: pytest-dev/pytest
base: master
base: main
Writing Tests

View File

@@ -1,4 +1,4 @@
.. image:: https://docs.pytest.org/en/stable/_static/pytest1.png
.. image:: https://github.com/pytest-dev/pytest/raw/main/doc/en/img/pytest_logo_curves.svg
:target: https://docs.pytest.org/en/stable/
:align: center
:alt: pytest
@@ -15,7 +15,7 @@
.. image:: https://img.shields.io/pypi/pyversions/pytest.svg
:target: https://pypi.org/project/pytest/
.. image:: https://codecov.io/gh/pytest-dev/pytest/branch/master/graph/badge.svg
.. image:: https://codecov.io/gh/pytest-dev/pytest/branch/main/graph/badge.svg
:target: https://codecov.io/gh/pytest-dev/pytest
:alt: Code coverage Status
@@ -25,6 +25,10 @@
.. image:: https://github.com/pytest-dev/pytest/workflows/main/badge.svg
:target: https://github.com/pytest-dev/pytest/actions?query=workflow%3Amain
.. image:: https://results.pre-commit.ci/badge/github/pytest-dev/pytest/main.svg
:target: https://results.pre-commit.ci/latest/github/pytest-dev/pytest/master
:alt: pre-commit.ci status
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/psf/black
@@ -149,8 +153,8 @@ Tidelift will coordinate the fix and disclosure.
License
-------
Copyright Holger Krekel and others, 2004-2020.
Copyright Holger Krekel and others, 2004-2021.
Distributed under the terms of the `MIT`_ license, pytest is free and open source software.
.. _`MIT`: https://github.com/pytest-dev/pytest/blob/master/LICENSE
.. _`MIT`: https://github.com/pytest-dev/pytest/blob/main/LICENSE

1
changelog/12363.doc.rst Normal file
View File

@@ -0,0 +1 @@
The documentation webpages now links to a canonical version to reduce outdated documentation in search engine results.

View File

@@ -6,6 +6,11 @@ Release announcements
:maxdepth: 2
release-6.2.5
release-6.2.4
release-6.2.3
release-6.2.2
release-6.2.1
release-6.2.0
release-6.1.2
release-6.1.1

View File

@@ -0,0 +1,20 @@
pytest-6.2.1
=======================================
pytest 6.2.1 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
Thanks to all of the contributors to this release:
* Bruno Oliveira
* Jakob van Santen
* Ran Benita
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,21 @@
pytest-6.2.2
=======================================
pytest 6.2.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:
* Adam Johnson
* Bruno Oliveira
* Chris NeJame
* Ran Benita
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,19 @@
pytest-6.2.3
=======================================
pytest 6.2.3 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
Thanks to all of the contributors to this release:
* Bruno Oliveira
* Ran Benita
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,22 @@
pytest-6.2.4
=======================================
pytest 6.2.4 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
* Christian Maurer
* Florian Bruhin
* Ran Benita
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,30 @@
pytest-6.2.5
=======================================
pytest 6.2.5 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
* Brylie Christopher Oxley
* Daniel Asztalos
* Florian Bruhin
* Jason Haugen
* MapleCCC
* Michał Górny
* Miro Hrončok
* Ran Benita
* Ronny Pfannschmidt
* Sylvain Bellemare
* Thomas Güttler
Happy testing,
The pytest Development Team

View File

@@ -31,7 +31,7 @@ you will see the return value of the function call:
$ pytest test_assert1.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 1 item
@@ -188,7 +188,7 @@ if you run this module:
$ pytest test_assert2.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 1 item

View File

@@ -86,7 +86,7 @@ If you then run it with ``--lf``:
$ pytest --lf
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 2 items
@@ -133,7 +133,7 @@ of ``FF`` and dots):
$ pytest --ff
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 50 items
@@ -277,7 +277,7 @@ You can always peek at the content of the cache using the
$ pytest --cache-show
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
cachedir: $PYTHON_PREFIX/.pytest_cache
@@ -358,7 +358,7 @@ filtering:
$ pytest --cache-show example/*
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
cachedir: $PYTHON_PREFIX/.pytest_cache

View File

@@ -83,7 +83,7 @@ of the failing function and hide the other one:
$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 2 items

View File

@@ -28,6 +28,80 @@ with advance notice in the **Deprecations** section of releases.
.. towncrier release notes start
pytest 6.2.5 (2021-08-29)
=========================
Trivial/Internal Changes
------------------------
- `#8494 <https://github.com/pytest-dev/pytest/issues/8494>`_: Python 3.10 is now supported.
- `#9040 <https://github.com/pytest-dev/pytest/issues/9040>`_: Enable compatibility with ``pluggy 1.0`` or later.
pytest 6.2.4 (2021-05-04)
=========================
Bug Fixes
---------
- `#8539 <https://github.com/pytest-dev/pytest/issues/8539>`_: Fixed assertion rewriting on Python 3.10.
pytest 6.2.3 (2021-04-03)
=========================
Bug Fixes
---------
- `#8414 <https://github.com/pytest-dev/pytest/issues/8414>`_: pytest used to create directories under ``/tmp`` with world-readable
permissions. This means that any user in the system was able to read
information written by tests in temporary directories (such as those created by
the ``tmp_path``/``tmpdir`` fixture). Now the directories are created with
private permissions.
pytest used silenty use a pre-existing ``/tmp/pytest-of-<username>`` directory,
even if owned by another user. This means another user could pre-create such a
directory and gain control of another user's temporary directory. Now such a
condition results in an error.
pytest 6.2.2 (2021-01-25)
=========================
Bug Fixes
---------
- `#8152 <https://github.com/pytest-dev/pytest/issues/8152>`_: Fixed "(<Skipped instance>)" being shown as a skip reason in the verbose test summary line when the reason is empty.
- `#8249 <https://github.com/pytest-dev/pytest/issues/8249>`_: Fix the ``faulthandler`` plugin for occasions when running with ``twisted.logger`` and using ``pytest --capture=no``.
pytest 6.2.1 (2020-12-15)
=========================
Bug Fixes
---------
- `#7678 <https://github.com/pytest-dev/pytest/issues/7678>`_: Fixed bug where ``ImportPathMismatchError`` would be raised for files compiled in
the host and loaded later from an UNC mounted path (Windows).
- `#8132 <https://github.com/pytest-dev/pytest/issues/8132>`_: Fixed regression in ``approx``: in 6.2.0 ``approx`` no longer raises
``TypeError`` when dealing with non-numeric types, falling back to normal comparison.
Before 6.2.0, array types like tf.DeviceArray fell through to the scalar case,
and happened to compare correctly to a scalar if they had only one element.
After 6.2.0, these types began failing, because they inherited neither from
standard Python number hierarchy nor from ``numpy.ndarray``.
``approx`` now converts arguments to ``numpy.ndarray`` if they expose the array
protocol and are not scalars. This treats array-like objects like numpy arrays,
regardless of size.
pytest 6.2.0 (2020-12-12)
=========================

View File

@@ -235,6 +235,9 @@ html_show_sourcelink = False
# Output file base name for HTML help builder.
htmlhelp_basename = "pytestdoc"
# The base URL which points to the root of the HTML documentation. It is used
# to indicate the location of document using the canonical link relation (#12363).
html_baseurl = "https://docs.pytest.org/en/stable/"
# -- Options for LaTeX output --------------------------------------------------

View File

@@ -7,21 +7,22 @@ Contact channels
- `pytest issue tracker`_ to report bugs or suggest features (for version
2.0 and above).
- `pytest discussions`_ at github for general questions.
- `pytest on stackoverflow.com <http://stackoverflow.com/search?q=pytest>`_
to post questions with the tag ``pytest``. New Questions will usually
to post precise questions with the tag ``pytest``. New Questions will usually
be seen by pytest users or developers and answered quickly.
- `Testing In Python`_: a mailing list for Python testing tools and discussion.
- `pytest-dev at python.org (mailing list)`_ pytest specific announcements and discussions.
- `pytest-commit at python.org (mailing list)`_: for commits and new issues
- :doc:`contribution guide <contributing>` for help on submitting pull
requests to GitHub.
- ``#pylib`` on irc.freenode.net IRC channel for random questions.
- ``#pytest`` `on irc.libera.chat <ircs://irc.libera.chat:6697/#pytest>`_ IRC
channel for random questions (using an IRC client, `via webchat
<https://web.libera.chat/#pytest>`_, or `via Matrix
<https://matrix.to/#/%23pytest:libera.chat>`_).
- private mail to Holger.Krekel at gmail com if you want to communicate sensitive issues
@@ -32,6 +33,8 @@ Contact channels
.. _`pytest issue tracker`: https://github.com/pytest-dev/pytest/issues
.. _`old issue tracker`: http://bitbucket.org/hpk42/py-trunk/issues/
.. _`pytest discussions`: https://github.com/pytest-dev/pytest/discussions
.. _`merlinux.eu`: http://merlinux.eu
.. _`get an account`:

View File

@@ -4,4 +4,4 @@ Development Guide
The contributing guidelines are to be found :ref:`here <contributing>`.
The release procedure for pytest is documented on
`GitHub <https://github.com/pytest-dev/pytest/blob/master/RELEASING.rst>`_.
`GitHub <https://github.com/pytest-dev/pytest/blob/main/RELEASING.rst>`_.

View File

@@ -29,7 +29,7 @@ then you can just invoke ``pytest`` directly:
$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 1 item
@@ -58,7 +58,7 @@ and functions, including from test modules:
$ pytest --doctest-modules
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 2 items

View File

@@ -0,0 +1,132 @@
<svg xmlns="http://www.w3.org/2000/svg" width="572" height="542">
<style>
text {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
dominant-baseline: middle;
text-anchor: middle;
fill: #062886;
font-size: medium;
}
ellipse.fixture, rect.test {
fill: #eeffcc;
stroke: #007020;
stroke-width: 2;
}
text.fixture {
color: #06287e;
}
circle.class, circle.module, circle.package {
fill: #c3e0ec;
stroke: #0e84b5;
stroke-width: 2;
}
text.class, text.module, text.package {
fill: #0e84b5;
}
line, path {
stroke: black;
stroke-width: 2;
fill: none;
}
</style>
<!-- main scope -->
<circle class="package" r="270" cx="286" cy="271" />
<!-- scope name -->
<defs>
<path d="M 26,271 A 260 260 0 0 1 546 271" id="testp"/>
</defs>
<text class="package">
<textPath href="#testp" startOffset="50%">tests</textPath>
</text>
<!-- subpackage -->
<circle class="package" r="140" cx="186" cy="271" />
<!-- scope name -->
<defs>
<path d="M 56,271 A 130 130 0 0 1 316 271" id="subpackage"/>
</defs>
<text class="package">
<textPath href="#subpackage" startOffset="50%">subpackage</textPath>
</text>
<!-- test_subpackage.py -->
<circle class="module" r="90" cx="186" cy="311" />
<!-- scope name -->
<defs>
<path d="M 106,311 A 80 80 0 0 1 266 311" id="testSubpackage"/>
</defs>
<text class="module">
<textPath href="#testSubpackage" startOffset="50%">test_subpackage.py</textPath>
</text>
<!-- innermost -->
<line x1="186" x2="186" y1="271" y2="351"/>
<!-- mid -->
<path d="M 186 351 L 136 351 L 106 331 L 106 196" />
<!-- order -->
<path d="M 186 351 L 256 351 L 316 291 L 316 136" />
<!-- top -->
<path d="M 186 351 L 186 391 L 231 436 L 331 436" />
<ellipse class="fixture" rx="50" ry="25" cx="186" cy="271" />
<text x="186" y="271">innermost</text>
<rect class="test" width="110" height="50" x="131" y="326" />
<text x="186" y="351">test_order</text>
<ellipse class="fixture" rx="50" ry="25" cx="126" cy="196" />
<text x="126" y="196">mid</text>
<!-- scope order number -->
<mask id="testSubpackageOrderMask">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<circle fill="black" stroke="white" stroke-width="2" r="90" cx="186" cy="311" />
</mask>
<circle class="module" r="15" cx="96" cy="311" mask="url(#testSubpackageOrderMask)"/>
<text class="module" x="96" y="311">1</text>
<!-- scope order number -->
<mask id="subpackageOrderMask">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<circle fill="black" stroke="white" stroke-width="2" r="140" cx="186" cy="271" />
</mask>
<circle class="module" r="15" cx="46" cy="271" mask="url(#subpackageOrderMask)"/>
<text class="module" x="46" y="271">2</text>
<!-- scope order number -->
<mask id="testsOrderMask">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<circle fill="black" stroke="white" stroke-width="2" r="270" cx="286" cy="271" />
</mask>
<circle class="module" r="15" cx="16" cy="271" mask="url(#testsOrderMask)"/>
<text class="module" x="16" y="271">3</text>
<!-- test_top.py -->
<circle class="module" r="85" cx="441" cy="271" />
<!-- scope name -->
<defs>
<path d="M 366,271 A 75 75 0 0 1 516 271" id="testTop"/>
</defs>
<text class="module">
<textPath href="#testTop" startOffset="50%">test_top.py</textPath>
</text>
<!-- innermost -->
<line x1="441" x2="441" y1="306" y2="236"/>
<!-- order -->
<path d="M 441 306 L 376 306 L 346 276 L 346 136" />
<!-- top -->
<path d="M 441 306 L 441 411 L 411 436 L 331 436" />
<ellipse class="fixture" rx="50" ry="25" cx="441" cy="236" />
<text x="441" y="236">innermost</text>
<rect class="test" width="110" height="50" x="386" y="281" />
<text x="441" y="306">test_order</text>
<!-- scope order number -->
<mask id="testTopOrderMask">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<circle fill="black" stroke="white" stroke-width="2" r="85" cx="441" cy="271" />
</mask>
<circle class="module" r="15" cx="526" cy="271" mask="url(#testTopOrderMask)"/>
<text class="module" x="526" y="271">1</text>
<!-- scope order number -->
<circle class="module" r="15" cx="556" cy="271" mask="url(#testsOrderMask)"/>
<text class="module" x="556" y="271">2</text>
<ellipse class="fixture" rx="50" ry="25" cx="331" cy="436" />
<text x="331" y="436">top</text>
<ellipse class="fixture" rx="50" ry="25" cx="331" cy="136" />
<text x="331" y="136">order</text>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@@ -0,0 +1,142 @@
<svg xmlns="http://www.w3.org/2000/svg" width="587" height="382">
<style>
text {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
dominant-baseline: middle;
text-anchor: middle;
fill: #062886;
font-size: medium;
alignment-baseline: center;
}
ellipse.fixture, rect.test {
fill: #eeffcc;
stroke: #007020;
stroke-width: 2;
}
text.fixture {
color: #06287e;
}
circle.class, circle.module, circle.package, circle.plugin {
fill: #c3e0ec;
stroke: #0e84b5;
stroke-width: 2;
}
text.class, text.module, text.package, text.plugin {
fill: #0e84b5;
}
line, path {
stroke: black;
stroke-width: 2;
fill: none;
}
</style>
<!-- plugin_a.py scope -->
<circle class="plugin" r="85" cx="486" cy="86" />
<!-- plugin name -->
<defs>
<path d="M 411,86 A 75 75 0 0 1 561 86" id="pluginA"/>
</defs>
<text class="plugin">
<textPath href="#pluginA" startOffset="50%">plugin_a</textPath>
</text>
<!-- scope order number -->
<mask id="pluginAOrderMask">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<circle fill="black" stroke="white" stroke-width="2" r="85" cx="486" cy="86" />
</mask>
<circle class="module" r="15" cx="571" cy="86" mask="url(#pluginAOrderMask)"/>
<text class="module" x="571" y="86">4</text>
<!-- plugin_b.py scope -->
<circle class="plugin" r="85" cx="486" cy="296" />
<!-- plugin name -->
<defs>
<path d="M 411,296 A 75 75 0 0 1 561 296" id="pluginB"/>
</defs>
<text class="plugin">
<textPath href="#pluginB" startOffset="50%">plugin_b</textPath>
</text>
<!-- scope order number -->
<mask id="pluginBOrderMask">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<circle fill="black" stroke="white" stroke-width="2" r="85" cx="486" cy="296" />
</mask>
<circle class="module" r="15" cx="571" cy="296" mask="url(#pluginBOrderMask)"/>
<text class="module" x="571" y="296">4</text>
<!-- main scope -->
<circle class="package" r="190" cx="191" cy="191" />
<!-- scope name -->
<defs>
<path d="M 11,191 A 180 180 0 0 1 371 191" id="testp"/>
</defs>
<text class="package">
<textPath href="#testp" startOffset="50%">tests</textPath>
</text>
<!-- scope order number -->
<mask id="mainOrderMask">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<circle fill="black" stroke="white" stroke-width="2" r="190" cx="191" cy="191" />
</mask>
<circle class="module" r="15" cx="381" cy="191" mask="url(#mainOrderMask)"/>
<text class="module" x="381" y="191">3</text>
<!-- subpackage -->
<circle class="package" r="140" cx="191" cy="231" />
<!-- scope name -->
<defs>
<path d="M 61,231 A 130 130 0 0 1 321 231" id="subpackage"/>
</defs>
<text class="package">
<textPath href="#subpackage" startOffset="50%">subpackage</textPath>
</text>
<!-- scope order number -->
<mask id="subpackageOrderMask">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<circle fill="black" stroke="white" stroke-width="2" r="140" cx="191" cy="231" />
</mask>
<circle class="module" r="15" cx="331" cy="231" mask="url(#subpackageOrderMask)"/>
<text class="module" x="331" y="231">2</text>
<!-- test_subpackage.py -->
<circle class="module" r="90" cx="191" cy="271" />
<!-- scope name -->
<defs>
<path d="M 111,271 A 80 80 0 0 1 271 271" id="testSubpackage"/>
</defs>
<text class="module">
<textPath href="#testSubpackage" startOffset="50%">test_subpackage.py</textPath>
</text>
<!-- scope order number -->
<mask id="testSubpackageOrderMask">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<circle fill="black" stroke="white" stroke-width="2" r="90" cx="191" cy="271" />
</mask>
<circle class="module" r="15" cx="281" cy="271" mask="url(#testSubpackageOrderMask)"/>
<text class="module" x="281" y="271">1</text>
<!-- innermost -->
<line x1="191" x2="191" y1="231" y2="311"/>
<!-- mid -->
<path d="M 191 306 L 101 306 L 91 296 L 91 156 L 101 146 L 191 146" />
<!-- order -->
<path d="M 191 316 L 91 316 L 81 306 L 81 61 L 91 51 L 191 51" />
<!-- a_fix -->
<path d="M 191 306 L 291 306 L 301 296 L 301 96 L 311 86 L 486 86" />
<!-- b_fix -->
<path d="M 191 316 L 316 316 L 336 296 L 486 296" />
<ellipse class="fixture" rx="50" ry="25" cx="191" cy="231" />
<text x="191" y="231">inner</text>
<rect class="test" width="110" height="50" x="136" y="286" />
<text x="191" y="311">test_order</text>
<ellipse class="fixture" rx="50" ry="25" cx="191" cy="146" />
<text x="191" y="146">mid</text>
<ellipse class="fixture" rx="50" ry="25" cx="191" cy="51" />
<text x="191" y="51">order</text>
<ellipse class="fixture" rx="50" ry="25" cx="486" cy="86" />
<text x="486" y="86">a_fix</text>
<ellipse class="fixture" rx="50" ry="25" cx="486" cy="296" />
<text x="486" y="296">b_fix</text>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -1,38 +0,0 @@
import pytest
# fixtures documentation order example
order = []
@pytest.fixture(scope="session")
def s1():
order.append("s1")
@pytest.fixture(scope="module")
def m1():
order.append("m1")
@pytest.fixture
def f1(f3):
order.append("f1")
@pytest.fixture
def f3():
order.append("f3")
@pytest.fixture(autouse=True)
def a1():
order.append("a1")
@pytest.fixture
def f2():
order.append("f2")
def test_order(f1, m1, f2, s1):
assert order == ["s1", "m1", "a1", "f3", "f1", "f2"]

View File

@@ -0,0 +1,45 @@
import pytest
@pytest.fixture
def order():
return []
@pytest.fixture
def a(order):
order.append("a")
@pytest.fixture
def b(a, order):
order.append("b")
@pytest.fixture(autouse=True)
def c(b, order):
order.append("c")
@pytest.fixture
def d(b, order):
order.append("d")
@pytest.fixture
def e(d, order):
order.append("e")
@pytest.fixture
def f(e, order):
order.append("f")
@pytest.fixture
def g(f, c, order):
order.append("g")
def test_order_and_g(g, order):
assert order == ["a", "b", "c", "d", "e", "f", "g"]

View File

@@ -0,0 +1,64 @@
<svg xmlns="http://www.w3.org/2000/svg" width="252" height="682">
<style>
text {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
dominant-baseline: middle;
text-anchor: middle;
fill: #062886;
font-size: medium;
}
ellipse.fixture, rect.test {
fill: #eeffcc;
stroke: #007020;
stroke-width: 2;
}
text.fixture {
color: #06287e;
}
circle.class {
fill: #c3e0ec;
stroke: #0e84b5;
stroke-width: 2;
}
text.class {
fill: #0e84b5;
}
path, line {
stroke: black;
stroke-width: 2;
fill: none;
}
rect.autouse {
fill: #ca7f3d;
}
</style>
<path d="M126,586 L26,506 L26,236" />
<path d="M226,446 L226,236 L126,166" />
<line x1="126" x2="126" y1="656" y2="516" />
<line x1="126" x2="226" y1="516" y2="446" />
<line x1="226" x2="126" y1="446" y2="376" />
<line x1="126" x2="126" y1="376" y2="166" />
<line x1="26" x2="126" y1="236" y2="166" />
<line x1="126" x2="126" y1="166" y2="26" />
<line x1="126" x2="126" y1="96" y2="26" />
<rect class="autouse" width="251" height="40" x="0" y="286" />
<text x="126" y="306">autouse</text>
<ellipse class="fixture" rx="50" ry="25" cx="126" cy="26" />
<text x="126" y="26">order</text>
<ellipse class="fixture" rx="25" ry="25" cx="126" cy="96" />
<text x="126" y="96">a</text>
<ellipse class="fixture" rx="25" ry="25" cx="126" cy="166" />
<text x="126" y="166">b</text>
<ellipse class="fixture" rx="25" ry="25" cx="26" cy="236" />
<text x="26" y="236">c</text>
<ellipse class="fixture" rx="25" ry="25" cx="126" cy="376" />
<text x="126" y="376">d</text>
<ellipse class="fixture" rx="25" ry="25" cx="226" cy="446" />
<text x="226" y="446">e</text>
<ellipse class="fixture" rx="25" ry="25" cx="126" cy="516" />
<text x="126" y="516">f</text>
<ellipse class="fixture" rx="25" ry="25" cx="126" cy="586" />
<text x="126" y="586">g</text>
<rect class="test" width="110" height="50" x="71" y="631" />
<text x="126" y="656">test_order</text>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,31 @@
import pytest
@pytest.fixture(scope="class")
def order():
return []
@pytest.fixture(scope="class", autouse=True)
def c1(order):
order.append("c1")
@pytest.fixture(scope="class")
def c2(order):
order.append("c2")
@pytest.fixture(scope="class")
def c3(order, c1):
order.append("c3")
class TestClassWithC1Request:
def test_order(self, order, c1, c3):
assert order == ["c1", "c3"]
class TestClassWithoutC1Request:
def test_order(self, order, c2):
assert order == ["c1", "c2"]

View File

@@ -0,0 +1,76 @@
<svg xmlns="http://www.w3.org/2000/svg" width="862" height="402">
<style>
text {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
dominant-baseline: middle;
text-anchor: middle;
fill: #062886;
font-size: medium;
}
ellipse.fixture, rect.test {
fill: #eeffcc;
stroke: #007020;
stroke-width: 2;
}
text.fixture {
color: #06287e;
}
circle.class {
fill: #c3e0ec;
stroke: #0e84b5;
stroke-width: 2;
}
text.class {
fill: #0e84b5;
}
line {
stroke: black;
stroke-width: 2;
}
rect.autouse {
fill: #ca7f3d;
}
</style>
<!-- TestWithC1Request -->
<circle class="class" r="200" cx="221" cy="201" />
<line x1="221" x2="221" y1="61" y2="316"/>
<ellipse class="fixture" rx="50" ry="25" cx="221" cy="61" />
<text x="221" y="61">order</text>
<ellipse class="fixture" rx="25" ry="25" cx="221" cy="131" />
<text x="221" y="131">c1</text>
<ellipse class="fixture" rx="25" ry="25" cx="221" cy="271" />
<text x="221" y="271">c3</text>
<rect class="test" width="110" height="50" x="166" y="316" />
<text x="221" y="341">test_order</text>
<!-- scope name -->
<defs>
<path d="M31,201 A 190 190 0 0 1 411 201" id="testClassWith"/>
</defs>
<text class="class">
<textPath href="#testClassWith" startOffset="50%">TestWithC1Request</textPath>
</text>
<!-- TestWithoutC1Request -->
<circle class="class" r="200" cx="641" cy="201" />
<line x1="641" x2="641" y1="61" y2="316"/>
<ellipse class="fixture" rx="50" ry="25" cx="641" cy="61" />
<text x="641" y="61">order</text>
<ellipse class="fixture" rx="25" ry="25" cx="641" cy="131" />
<text x="641" y="131">c1</text>
<ellipse class="fixture" rx="25" ry="25" cx="641" cy="271" />
<text x="641" y="271">c2</text>
<rect class="test" width="110" height="50" x="586" y="316" />
<text x="641" y="341">test_order</text>
<!-- scope name -->
<defs>
<path d="M451,201 A 190 190 0 0 1 831 201" id="testClassWithout"/>
</defs>
<text class="class">
<textPath href="#testClassWithout" startOffset="50%">TestWithoutC1Request</textPath>
</text>
<rect class="autouse" width="862" height="40" x="1" y="181" />
<rect width="10" height="100" class="autouse" x="426" y="151" />
<text x="431" y="201">autouse</text>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,36 @@
import pytest
@pytest.fixture
def order():
return []
@pytest.fixture
def c1(order):
order.append("c1")
@pytest.fixture
def c2(order):
order.append("c2")
class TestClassWithAutouse:
@pytest.fixture(autouse=True)
def c3(self, order, c2):
order.append("c3")
def test_req(self, order, c1):
assert order == ["c2", "c3", "c1"]
def test_no_req(self, order):
assert order == ["c2", "c3"]
class TestClassWithoutAutouse:
def test_req(self, order, c1):
assert order == ["c1"]
def test_no_req(self, order):
assert order == []

View File

@@ -0,0 +1,100 @@
<svg xmlns="http://www.w3.org/2000/svg" width="862" height="502">
<style>
text {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
dominant-baseline: middle;
text-anchor: middle;
fill: #062886;
font-size: medium;
}
ellipse.fixture, rect.test {
fill: #eeffcc;
stroke: #007020;
stroke-width: 2;
}
text.fixture {
color: #06287e;
}
circle.class {
fill: #c3e0ec;
stroke: #0e84b5;
stroke-width: 2;
}
text.class {
fill: #0e84b5;
}
line {
stroke: black;
stroke-width: 2;
}
rect.autouse {
fill: #ca7f3d;
}
</style>
<!-- TestWithAutouse -->
<circle class="class" r="250" cx="251" cy="251" />
<!-- scope name -->
<defs>
<path d="M11,251 A 240 240 0 0 1 491 251" id="testClassWith"/>
</defs>
<text class="class">
<textPath href="#testClassWith" startOffset="50%">TestWithAutouse</textPath>
</text>
<mask id="autouseScope">
<circle fill="white" r="249" cx="251" cy="251" />
</mask>
<!-- TestWithAutouse.test_req -->
<line x1="176" x2="176" y1="76" y2="426"/>
<ellipse class="fixture" rx="50" ry="25" cx="176" cy="76" />
<text x="176" y="76">order</text>
<ellipse class="fixture" rx="25" ry="25" cx="176" cy="146" />
<text x="176" y="146">c2</text>
<ellipse class="fixture" rx="25" ry="25" cx="176" cy="216" />
<text x="176" y="216">c3</text>
<ellipse class="fixture" rx="25" ry="25" cx="176" cy="356" />
<text x="176" y="356">c1</text>
<rect class="test" width="100" height="50" x="126" y="401" />
<text x="176" y="426">test_req</text>
<!-- TestWithAutouse.test_no_req -->
<line x1="326" x2="326" y1="76" y2="346"/>
<ellipse class="fixture" rx="50" ry="25" cx="326" cy="76" />
<text x="326" y="76">order</text>
<ellipse class="fixture" rx="25" ry="25" cx="326" cy="146" />
<text x="326" y="146">c2</text>
<ellipse class="fixture" rx="25" ry="25" cx="326" cy="216" />
<text x="326" y="216">c3</text>
<rect class="test" width="120" height="50" x="266" y="331" />
<text x="326" y="356">test_no_req</text>
<rect class="autouse" width="500" height="40" x="1" y="266" mask="url(#autouseScope)"/>
<text x="261" y="286">autouse</text>
<!-- TestWithoutAutouse -->
<circle class="class" r="170" cx="691" cy="251" />
<!-- scope name -->
<defs>
<path d="M 531,251 A 160 160 0 0 1 851 251" id="testClassWithout"/>
</defs>
<text class="class">
<textPath href="#testClassWithout" startOffset="50%">TestWithoutAutouse</textPath>
</text>
<!-- TestWithoutAutouse.test_req -->
<line x1="616" x2="616" y1="181" y2="321"/>
<ellipse class="fixture" rx="50" ry="25" cx="616" cy="181" />
<text x="616" y="181">order</text>
<ellipse class="fixture" rx="25" ry="25" cx="616" cy="251" />
<text x="616" y="251">c1</text>
<rect class="test" width="100" height="50" x="566" y="296" />
<text x="616" y="321">test_req</text>
<!-- TestWithoutAutouse.test_no_req -->
<line x1="766" x2="766" y1="181" y2="251"/>
<ellipse class="fixture" rx="50" ry="25" cx="766" cy="181" />
<text x="766" y="181">order</text>
<rect class="test" width="120" height="50" x="706" y="226" />
<text x="766" y="251">test_no_req</text>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -0,0 +1,45 @@
import pytest
@pytest.fixture
def order():
return []
@pytest.fixture
def a(order):
order.append("a")
@pytest.fixture
def b(a, order):
order.append("b")
@pytest.fixture
def c(a, b, order):
order.append("c")
@pytest.fixture
def d(c, b, order):
order.append("d")
@pytest.fixture
def e(d, b, order):
order.append("e")
@pytest.fixture
def f(e, order):
order.append("f")
@pytest.fixture
def g(f, c, order):
order.append("g")
def test_order(g, order):
assert order == ["a", "b", "c", "d", "e", "f", "g"]

View File

@@ -0,0 +1,60 @@
<svg xmlns="http://www.w3.org/2000/svg" width="252" height="612">
<style>
text {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
dominant-baseline: middle;
text-anchor: middle;
fill: #062886;
font-size: medium;
}
ellipse.fixture, rect.test {
fill: #eeffcc;
stroke: #007020;
stroke-width: 2;
}
text.fixture {
color: #06287e;
}
circle.class {
fill: #c3e0ec;
stroke: #0e84b5;
stroke-width: 2;
}
text.class {
fill: #0e84b5;
}
path, line {
stroke: black;
stroke-width: 2;
fill: none;
}
</style>
<path d="M126,516 L26,436 L26,236" />
<path d="M226,376 L226,236 L126,166" />
<line x1="126" x2="126" y1="586" y2="446" />
<line x1="126" x2="226" y1="446" y2="376" />
<line x1="226" x2="126" y1="376" y2="306" />
<line x1="126" x2="26" y1="306" y2="236" />
<line x1="126" x2="126" y1="306" y2="166" />
<line x1="26" x2="126" y1="236" y2="166" />
<line x1="126" x2="126" y1="166" y2="26" />
<line x1="126" x2="126" y1="96" y2="26" />
<ellipse class="fixture" rx="50" ry="25" cx="126" cy="26" />
<text x="126" y="26">order</text>
<ellipse class="fixture" rx="25" ry="25" cx="126" cy="96" />
<text x="126" y="96">a</text>
<ellipse class="fixture" rx="25" ry="25" cx="126" cy="166" />
<text x="126" y="166">b</text>
<ellipse class="fixture" rx="25" ry="25" cx="26" cy="236" />
<text x="26" y="236">c</text>
<ellipse class="fixture" rx="25" ry="25" cx="126" cy="306" />
<text x="126" y="306">d</text>
<ellipse class="fixture" rx="25" ry="25" cx="226" cy="376" />
<text x="226" y="376">e</text>
<ellipse class="fixture" rx="25" ry="25" cx="126" cy="446" />
<text x="126" y="446">f</text>
<ellipse class="fixture" rx="25" ry="25" cx="126" cy="516" />
<text x="126" y="516">g</text>
<rect class="test" width="110" height="50" x="71" y="561" />
<text x="126" y="586">test_order</text>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,51 @@
<svg xmlns="http://www.w3.org/2000/svg" width="112" height="612">
<style>
text {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
dominant-baseline: middle;
text-anchor: middle;
fill: #062886;
font-size: medium;
}
ellipse.fixture, rect.test {
fill: #eeffcc;
stroke: #007020;
stroke-width: 2;
}
text.fixture {
color: #06287e;
}
circle.class {
fill: #c3e0ec;
stroke: #0e84b5;
stroke-width: 2;
}
text.class {
fill: #0e84b5;
}
path, line {
stroke: black;
stroke-width: 2;
fill: none;
}
</style>
<line x1="56" x2="56" y1="611" y2="26" />
<ellipse class="fixture" rx="50" ry="25" cx="56" cy="26" />
<text x="56" y="26">order</text>
<ellipse class="fixture" rx="25" ry="25" cx="56" cy="96" />
<text x="56" y="96">a</text>
<ellipse class="fixture" rx="25" ry="25" cx="56" cy="166" />
<text x="56" y="166">b</text>
<ellipse class="fixture" rx="25" ry="25" cx="56" cy="236" />
<text x="56" y="236">c</text>
<ellipse class="fixture" rx="25" ry="25" cx="56" cy="306" />
<text x="56" y="306">d</text>
<ellipse class="fixture" rx="25" ry="25" cx="56" cy="376" />
<text x="56" y="376">e</text>
<ellipse class="fixture" rx="25" ry="25" cx="56" cy="446" />
<text x="56" y="446">f</text>
<ellipse class="fixture" rx="25" ry="25" cx="56" cy="516" />
<text x="56" y="516">g</text>
<rect class="test" width="110" height="50" x="1" y="561" />
<text x="56" y="586">test_order</text>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,60 @@
<svg xmlns="http://www.w3.org/2000/svg" width="252" height="542">
<style>
text {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
dominant-baseline: middle;
text-anchor: middle;
fill: #062886;
font-size: medium;
}
ellipse.fixture, rect.test {
fill: #eeffcc;
stroke: #007020;
stroke-width: 2;
}
text.fixture {
color: #06287e;
}
circle.class {
fill: #c3e0ec;
stroke: #0e84b5;
stroke-width: 2;
}
text.class {
fill: #0e84b5;
}
path, line {
stroke: black;
stroke-width: 2;
fill: none;
}
</style>
<path d="M126,446 L26,376 L26,236" />
<path d="M226,306 L126,236 L126,166" />
<line x1="126" x2="126" y1="516" y2="446" />
<line x1="226" x2="226" y1="376" y2="306" />
<line x1="226" x2="226" y1="306" y2="236" />
<line x1="226" x2="126" y1="236" y2="166" />
<line x1="126" x2="226" y1="446" y2="376" />
<line x1="26" x2="126" y1="236" y2="166" />
<line x1="126" x2="126" y1="166" y2="96" />
<line x1="126" x2="126" y1="96" y2="26" />
<ellipse class="fixture" rx="50" ry="25" cx="126" cy="26" />
<text x="126" y="26">order</text>
<ellipse class="fixture" rx="25" ry="25" cx="126" cy="96" />
<text x="126" y="96">a</text>
<ellipse class="fixture" rx="25" ry="25" cx="126" cy="166" />
<text x="126" y="166">b</text>
<ellipse class="fixture" rx="25" ry="25" cx="26" cy="236" />
<text x="26" y="236">c</text>
<ellipse class="fixture" rx="25" ry="25" cx="226" cy="236" />
<text x="226" y="236">d</text>
<ellipse class="fixture" rx="25" ry="25" cx="226" cy="306" />
<text x="226" y="306">e</text>
<ellipse class="fixture" rx="25" ry="25" cx="226" cy="376" />
<text x="226" y="376">f</text>
<ellipse class="fixture" rx="25" ry="25" cx="126" cy="446" />
<text x="126" y="446">g</text>
<rect class="test" width="110" height="50" x="71" y="491" />
<text x="126" y="516">test_order</text>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,36 @@
import pytest
@pytest.fixture(scope="session")
def order():
return []
@pytest.fixture
def func(order):
order.append("function")
@pytest.fixture(scope="class")
def cls(order):
order.append("class")
@pytest.fixture(scope="module")
def mod(order):
order.append("module")
@pytest.fixture(scope="package")
def pack(order):
order.append("package")
@pytest.fixture(scope="session")
def sess(order):
order.append("session")
class TestClass:
def test_order(self, func, cls, mod, pack, sess, order):
assert order == ["session", "package", "module", "class", "function"]

View File

@@ -0,0 +1,55 @@
<svg xmlns="http://www.w3.org/2000/svg" width="262" height="537">
<style>
text {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
dominant-baseline: middle;
text-anchor: middle;
fill: #062886;
font-size: medium;
}
ellipse.fixture, rect.test {
fill: #eeffcc;
stroke: #007020;
stroke-width: 2;
}
text.fixture {
color: #06287e;
}
circle.class {
fill: #c3e0ec;
stroke: #0e84b5;
stroke-width: 2;
}
text.class {
fill: #0e84b5;
}
line {
stroke: black;
stroke-width: 2;
}
</style>
<!-- TestClass -->
<circle class="class" r="130" cx="131" cy="406" />
<line x1="131" x2="131" y1="21" y2="446"/>
<ellipse class="fixture" rx="50" ry="25" cx="131" cy="26" />
<text x="131" y="26">order</text>
<ellipse class="fixture" rx="50" ry="25" cx="131" cy="96" />
<text x="131" y="96">sess</text>
<ellipse class="fixture" rx="50" ry="25" cx="131" cy="166" />
<text x="131" y="166">pack</text>
<ellipse class="fixture" rx="50" ry="25" cx="131" cy="236" />
<text x="131" y="236">mod</text>
<ellipse class="fixture" rx="50" ry="25" cx="131" cy="306" />
<text x="131" y="306">cls</text>
<ellipse class="fixture" rx="50" ry="25" cx="131" cy="376" />
<text x="131" y="376">func</text>
<rect class="test" width="110" height="50" x="76" y="421" />
<text x="131" y="446">test_order</text>
<!-- scope name -->
<defs>
<path d="M131,526 A 120 120 0 0 1 136 286" id="testClass"/>
</defs>
<text class="class">
<textPath href="#testClass" startOffset="50%">TestClass</textPath>
</text>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,29 @@
import pytest
@pytest.fixture
def order():
return []
@pytest.fixture
def outer(order, inner):
order.append("outer")
class TestOne:
@pytest.fixture
def inner(self, order):
order.append("one")
def test_order(self, order, outer):
assert order == ["one", "outer"]
class TestTwo:
@pytest.fixture
def inner(self, order):
order.append("two")
def test_order(self, order, outer):
assert order == ["two", "outer"]

View File

@@ -0,0 +1,115 @@
<svg xmlns="http://www.w3.org/2000/svg" width="562" height="532">
<style>
text {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
dominant-baseline: middle;
text-anchor: middle;
fill: #062886;
font-size: medium;
}
ellipse.fixture, rect.test {
fill: #eeffcc;
stroke: #007020;
stroke-width: 2;
}
text.fixture {
color: #06287e;
}
circle.class {
fill: #c3e0ec;
stroke: #0e84b5;
stroke-width: 2;
}
circle.module {
fill: #c3e0ec;
stroke: #0e84b5;
stroke-width: 2;
}
text.class, text.module {
fill: #0e84b5;
}
line, path {
stroke: black;
stroke-width: 2;
fill: none;
}
</style>
<!-- main scope -->
<circle class="module" r="265" cx="281" cy="266" />
<!-- scope name -->
<defs>
<path d="M 26,266 A 255 255 0 0 1 536 266" id="testModule"/>
</defs>
<text class="module">
<textPath href="#testModule" startOffset="50%">test_fixtures_request_different_scope.py</textPath>
</text>
<!-- TestOne -->
<circle class="class" r="100" cx="141" cy="266" />
<!-- inner -->
<line x1="141" x2="141" y1="231" y2="301"/>
<!-- order -->
<path d="M 141 296 L 201 296 L 211 286 L 211 146 L 221 136 L 281 136" />
<!-- outer -->
<path d="M 141 306 L 201 306 L 211 316 L 211 386 L 221 396 L 281 396" />
<ellipse class="fixture" rx="50" ry="25" cx="141" cy="231" />
<text x="141" y="231">inner</text>
<rect class="test" width="110" height="50" x="86" y="276" />
<text x="141" y="301">test_order</text>
<!-- scope name -->
<defs>
<path d="M 51,266 A 90 90 0 0 1 231 266" id="testOne"/>
</defs>
<text class="class">
<textPath href="#testOne" startOffset="50%">TestOne</textPath>
</text>
<!-- scope order number -->
<mask id="testOneOrderMask">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<circle fill="black" stroke="white" stroke-width="2" r="100" cx="141" cy="266" />
</mask>
<circle class="module" r="15" cx="41" cy="266" mask="url(#testOneOrderMask)"/>
<text class="module" x="41" y="266">1</text>
<!-- scope order number -->
<mask id="testMainOrderMask">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<circle fill="black" stroke="white" stroke-width="2" r="265" cx="281" cy="266" />
</mask>
<circle class="module" r="15" cx="16" cy="266" mask="url(#testMainOrderMask)"/>
<text class="module" x="16" y="266">2</text>
<!-- TestTwo -->
<circle class="class" r="100" cx="421" cy="266" />
<!-- inner -->
<line x1="421" x2="421" y1="231" y2="301"/>
<!-- order -->
<path d="M 421 296 L 361 296 L 351 286 L 351 146 L 341 136 L 281 136" />
<!-- outer -->
<path d="M 421 306 L 361 306 L 351 316 L 351 386 L 341 396 L 281 396" />
<ellipse class="fixture" rx="50" ry="25" cx="421" cy="231" />
<text x="421" y="231">inner</text>
<rect class="test" width="110" height="50" x="366" y="276" />
<text x="421" y="301">test_order</text>
<!-- scope name -->
<defs>
<path d="M 331,266 A 90 90 0 0 1 511 266" id="testTwo"/>
</defs>
<text class="class">
<textPath href="#testTwo" startOffset="50%">TestTwo</textPath>
</text>
<!-- scope order number -->
<mask id="testTwoOrderMask">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<circle fill="black" stroke="white" stroke-width="2" r="100" cx="421" cy="266" />
</mask>
<circle class="module" r="15" cx="521" cy="266" mask="url(#testTwoOrderMask)"/>
<text class="module" x="521" y="266">1</text>
<!-- scope order number -->
<circle class="module" r="15" cx="546" cy="266" mask="url(#testMainOrderMask)"/>
<text class="module" x="546" y="266">2</text>
<ellipse class="fixture" rx="50" ry="25" cx="281" cy="396" />
<text x="281" y="396">outer</text>
<ellipse class="fixture" rx="50" ry="25" cx="281" cy="136" />
<text x="281" y="136">order</text>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -45,7 +45,7 @@ You can then restrict a test run to only run tests marked with ``webtest``:
$ pytest -v -m webtest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collecting ... collected 4 items / 3 deselected / 1 selected
@@ -60,7 +60,7 @@ Or the inverse, running all tests except the webtest ones:
$ pytest -v -m "not webtest"
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collecting ... collected 4 items / 1 deselected / 3 selected
@@ -82,7 +82,7 @@ tests based on their module, class, method, or function name:
$ pytest -v test_server.py::TestClass::test_method
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collecting ... collected 1 item
@@ -97,7 +97,7 @@ You can also select on the class:
$ pytest -v test_server.py::TestClass
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collecting ... collected 1 item
@@ -112,7 +112,7 @@ Or select multiple nodes:
$ pytest -v test_server.py::TestClass test_server.py::test_send_http
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collecting ... collected 2 items
@@ -156,7 +156,7 @@ The expression matching is now case-insensitive.
$ pytest -v -k http # running with the above defined example module
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collecting ... collected 4 items / 3 deselected / 1 selected
@@ -171,7 +171,7 @@ And you can also run all tests except the ones that match the keyword:
$ pytest -k "not send_http" -v
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collecting ... collected 4 items / 1 deselected / 3 selected
@@ -188,7 +188,7 @@ Or to select "http" and "quick" tests:
$ pytest -k "http or quick" -v
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collecting ... collected 4 items / 2 deselected / 2 selected
@@ -397,7 +397,7 @@ the test needs:
$ pytest -E stage2
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 1 item
@@ -412,7 +412,7 @@ and here is one that specifies exactly the environment needed:
$ pytest -E stage1
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 1 item
@@ -605,7 +605,7 @@ then you will see two tests skipped and two executed tests as expected:
$ pytest -rs # this option reports skip reasons
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 4 items
@@ -622,7 +622,7 @@ Note that if you specify a platform via the marker-command line option like this
$ pytest -m linux
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 4 items / 3 deselected / 1 selected
@@ -686,7 +686,7 @@ We can now use the ``-m option`` to select one set:
$ pytest -m interface --tb=short
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 4 items / 2 deselected / 2 selected
@@ -713,7 +713,7 @@ or to select both "event" and "interface" tests:
$ pytest -m "interface or event" --tb=short
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 4 items / 1 deselected / 3 selected

View File

@@ -29,7 +29,7 @@ now execute the test specification:
nonpython $ pytest test_simple.yaml
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR/nonpython
collected 2 items
@@ -66,7 +66,7 @@ consulted when reporting in ``verbose`` mode:
nonpython $ pytest -v
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR/nonpython
collecting ... collected 2 items
@@ -92,7 +92,7 @@ interesting to just look at the collection tree:
nonpython $ pytest --collect-only
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR/nonpython
collected 2 items

View File

@@ -160,7 +160,7 @@ objects, they are still using the default pytest representation:
$ pytest test_time.py --collect-only
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 8 items
@@ -225,7 +225,7 @@ this is a fully self-contained example which you can run with:
$ pytest test_scenarios.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 4 items
@@ -240,7 +240,7 @@ If you just collect tests you'll also nicely see 'advanced' and 'basic' as varia
$ pytest --collect-only test_scenarios.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 4 items
@@ -319,7 +319,7 @@ Let's first see how it looks like at collection time:
$ pytest test_backends.py --collect-only
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 2 items
@@ -418,7 +418,7 @@ The result of this test will be successful:
$ pytest -v test_indirect_list.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collecting ... collected 1 item
@@ -508,11 +508,12 @@ Running it results in some skips if we don't have all the python interpreters in
.. code-block:: pytest
. $ pytest -rs -q multipython.py
ssssssssssss...ssssssssssss [100%]
sssssssssssssssssssssssssss [100%]
========================= short test summary info ==========================
SKIPPED [12] multipython.py:29: 'python3.5' not found
SKIPPED [12] multipython.py:29: 'python3.7' not found
3 passed, 24 skipped in 0.12s
SKIPPED [9] multipython.py:29: 'python3.5' not found
SKIPPED [9] multipython.py:29: 'python3.6' not found
SKIPPED [9] multipython.py:29: 'python3.7' not found
27 skipped in 0.12s
Indirect parametrization of optional implementations/imports
--------------------------------------------------------------------
@@ -572,7 +573,7 @@ If you run this with reporting for skips enabled:
$ pytest -rs test_module.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 2 items
@@ -634,16 +635,16 @@ Then run ``pytest`` with verbose mode and with only the ``basic`` marker:
$ pytest -v -m basic
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collecting ... collected 14 items / 11 deselected / 3 selected
collecting ... collected 24 items / 21 deselected / 3 selected
test_pytest_param_example.py::test_eval[1+7-8] PASSED [ 33%]
test_pytest_param_example.py::test_eval[basic_2+4] PASSED [ 66%]
test_pytest_param_example.py::test_eval[basic_6*9] XFAIL [100%]
=============== 2 passed, 11 deselected, 1 xfailed in 0.12s ================
=============== 2 passed, 21 deselected, 1 xfailed in 0.12s ================
As the result:

View File

@@ -147,7 +147,7 @@ The test collection would look like this:
$ pytest --collect-only
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, configfile: pytest.ini
collected 2 items
@@ -209,7 +209,7 @@ You can always peek at the collection tree without running tests like this:
. $ pytest --collect-only pythoncollection.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, configfile: pytest.ini
collected 3 items
@@ -291,7 +291,7 @@ file will be left out:
$ pytest --collect-only
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, configfile: pytest.ini
collected 0 items

View File

@@ -9,7 +9,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
assertion $ pytest failure_demo.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR/assertion
collected 44 items

View File

@@ -175,7 +175,7 @@ directory with the above conftest.py:
$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 0 items
@@ -240,7 +240,7 @@ and when running it will see a skipped "slow" test:
$ pytest -rs # "-rs" means report details on the little 's'
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 2 items
@@ -257,7 +257,7 @@ Or run it including the ``slow`` marked test:
$ pytest --runslow
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 2 items
@@ -401,7 +401,7 @@ which will add the string to the test header accordingly:
$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
project deps: mylib-1.1
rootdir: $REGENDOC_TMPDIR
@@ -430,7 +430,7 @@ which will add info only when run with "--v":
$ pytest -v
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
cachedir: $PYTHON_PREFIX/.pytest_cache
info1: did you know that ...
did you?
@@ -445,7 +445,7 @@ and nothing when run plainly:
$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 0 items
@@ -485,7 +485,7 @@ Now we can profile which test functions execute the slowest:
$ pytest --durations=3
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 3 items
@@ -591,7 +591,7 @@ If we run this:
$ pytest -rx
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 4 items
@@ -675,7 +675,7 @@ We can run this:
$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 7 items
@@ -794,7 +794,7 @@ and run them:
$ pytest test_module.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 2 items
@@ -901,7 +901,7 @@ and run it:
$ pytest -s test_module.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 3 items

File diff suppressed because it is too large Load Diff

View File

@@ -168,7 +168,7 @@ pytest for a long time offered a pytest_configure and a pytest_sessionstart
hook which are often used to setup global resources. This suffers from
several problems:
1. in distributed testing the master process would setup test resources
1. in distributed testing the managing process would setup test resources
that are never needed because it only co-ordinates the test run
activities of the worker processes.

View File

@@ -28,7 +28,7 @@ Install ``pytest``
.. code-block:: bash
$ pytest --version
pytest 6.2.0
pytest 6.2.5
.. _`simpletest`:
@@ -53,7 +53,7 @@ Thats it. You can now execute the test function:
$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 1 item

View File

@@ -2,7 +2,7 @@
.. sidebar:: Next Open Trainings
- `Professional testing with Python <https://www.python-academy.com/courses/specialtopics/python_course_testing.html>`_, via Python Academy, February 1-3 2021, Leipzig (Germany) and remote.
- `Professional Testing with Python <https://www.python-academy.com/courses/specialtopics/python_course_testing.html>`_, via `Python Academy <https://www.python-academy.com/>`_, February 1st to 3rd, 2022, Leipzig (Germany) and remote.
Also see `previous talks and blogposts <talks.html>`_.
@@ -11,6 +11,7 @@
pytest: helps you write better programs
=======================================
.. module:: pytest
The ``pytest`` framework makes it easy to write small tests, yet
scales to support complex functional testing for applications and libraries.
@@ -34,7 +35,7 @@ To execute it:
$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 1 item
@@ -71,7 +72,7 @@ Features
- Python 3.6+ and PyPy 3
- Rich plugin architecture, with over 315+ `external plugins <http://plugincompat.herokuapp.com>`_ and thriving community
- Rich plugin architecture, with over 315+ `external plugins <https://docs.pytest.org/en/latest/reference/plugin_list.html>`_ and thriving community
Documentation
@@ -128,8 +129,8 @@ Tidelift will coordinate the fix and disclosure.
License
-------
Copyright Holger Krekel and others, 2004-2020.
Copyright Holger Krekel and others, 2004-2021.
Distributed under the terms of the `MIT`_ license, pytest is free and open source software.
.. _`MIT`: https://github.com/pytest-dev/pytest/blob/master/LICENSE
.. _`MIT`: https://github.com/pytest-dev/pytest/blob/main/LICENSE

View File

@@ -29,4 +29,4 @@ Distributed under the terms of the `MIT`_ license, pytest is free and open sourc
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
.. _`MIT`: https://github.com/pytest-dev/pytest/blob/master/LICENSE
.. _`MIT`: https://github.com/pytest-dev/pytest/blob/main/LICENSE

View File

@@ -2,7 +2,7 @@
Monkeypatching/mocking 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
@@ -434,7 +434,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

@@ -56,7 +56,7 @@ them in turn:
$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 3 items
@@ -134,7 +134,7 @@ Let's run this:
$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 3 items

View File

@@ -920,11 +920,8 @@ TestReport
_Result
~~~~~~~
Result used within :ref:`hook wrappers <hookwrapper>`.
Result object used within :ref:`hook wrappers <hookwrapper>`, see :py:class:`_Result in the pluggy documentation <pluggy._callers._Result>` for more information.
.. autoclass:: pluggy.callers._Result
.. automethod:: pluggy.callers._Result.get_result
.. automethod:: pluggy.callers._Result.force_result
Global Variables
----------------

View File

@@ -1,4 +1,5 @@
pallets-sphinx-themes
pluggy>=1.0
pygments-pytest>=1.1.0
sphinx-removed-in>=0.2.0
sphinx>=3.1,<4

View File

@@ -373,7 +373,7 @@ Running it with the report-on-xfail option gives this output:
example $ pytest -rx xfail_demo.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR/example
collected 7 items

View File

@@ -39,7 +39,7 @@ Running this would result in a passed test except for the last
$ pytest test_tmp_path.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 1 item
@@ -109,7 +109,7 @@ Running this would result in a passed test except for the last
$ pytest test_tmpdir.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 1 item

View File

@@ -137,7 +137,7 @@ the ``self.db`` values in the traceback:
$ pytest test_unittest_db.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 2 items

View File

@@ -218,7 +218,7 @@ Example:
$ pytest -ra
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 6 items
@@ -276,7 +276,7 @@ More than one character can be used, so for example to only see failed and skipp
$ pytest -rfs
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 6 items
@@ -312,7 +312,7 @@ captured output:
$ pytest -rpP
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 6 items

View File

@@ -28,7 +28,7 @@ Running pytest now produces this output:
$ pytest test_show_warnings.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 1 item
@@ -173,8 +173,6 @@ DeprecationWarning and PendingDeprecationWarning
------------------------------------------------
By default pytest will display ``DeprecationWarning`` and ``PendingDeprecationWarning`` warnings from
user code and third-party libraries, as recommended by `PEP-0565 <https://www.python.org/dev/peps/pep-0565>`_.
This helps users keep their code modern and avoid breakages when deprecated warnings are effectively removed.
@@ -230,27 +228,8 @@ that a certain function call triggers a ``DeprecationWarning`` or
This test will fail if ``myfunction`` does not issue a deprecation warning
when called with a ``17`` argument.
By default, ``DeprecationWarning`` and ``PendingDeprecationWarning`` will not be
caught when using :func:`pytest.warns` or :ref:`recwarn <recwarn>` because
the default Python warnings filters hide
them. If you wish to record them in your own code, use
``warnings.simplefilter('always')``:
.. code-block:: python
import warnings
import pytest
def test_deprecation(recwarn):
warnings.simplefilter("always")
myfunction(17)
assert len(recwarn) == 1
assert recwarn.pop(DeprecationWarning)
The :ref:`recwarn <recwarn>` fixture automatically ensures to reset the warnings
filter at the end of the test, so no global state is leaked.
.. _`asserting warnings`:
@@ -317,9 +296,9 @@ additional information:
Alternatively, you can examine raised warnings in detail using the
:ref:`recwarn <recwarn>` fixture (see below).
.. note::
``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated
differently; see :ref:`ensuring_function_triggers`.
The :ref:`recwarn <recwarn>` fixture automatically ensures to reset the warnings
filter at the end of the test, so no global state is leaked.
.. _`recording warnings`:

View File

@@ -442,7 +442,7 @@ Additionally it is possible to copy examples for an example folder before runnin
$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, configfile: pytest.ini
collected 2 items

View File

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

View File

@@ -26,6 +26,8 @@ filterwarnings = [
# produced by older pyparsing<=2.2.0.
"default:Using or importing the ABCs:DeprecationWarning:pyparsing.*",
"default:the imp module is deprecated in favour of importlib:DeprecationWarning:nose.*",
# distutils is deprecated in 3.10, scheduled for removal in 3.12
"ignore:The distutils package is deprecated:DeprecationWarning",
# produced by python3.6/site.py itself (3.6.7 on Travis, could not trigger it with 3.6.8)."
"ignore:.*U.*mode is deprecated:DeprecationWarning:(?!(pytest|_pytest))",
# produced by pytest-xdist
@@ -40,6 +42,14 @@ filterwarnings = [
"default:invalid escape sequence:DeprecationWarning",
# ignore use of unregistered marks, because we use many to test the implementation
"ignore::_pytest.warning_types.PytestUnknownMarkWarning",
# https://github.com/benjaminp/six/issues/341
"ignore:_SixMetaPathImporter\\.exec_module\\(\\) not found; falling back to load_module\\(\\):ImportWarning",
# https://github.com/benjaminp/six/pull/352
"ignore:_SixMetaPathImporter\\.find_spec\\(\\) not found; falling back to find_module\\(\\):ImportWarning",
# https://github.com/pypa/setuptools/pull/2517
"ignore:VendorImporter\\.find_spec\\(\\) not found; falling back to find_module\\(\\):ImportWarning",
# https://github.com/pytest-dev/execnet/pull/127
"ignore:isSet\\(\\) is deprecated, use is_set\\(\\) instead:DeprecationWarning",
]
pytester_example_dir = "testing/example_scripts"
markers = [

View File

@@ -23,8 +23,8 @@ Then the appropriate version will be obtained based on the given branch name:
After that, it will create a release using the `release` tox environment, and push a new PR.
**Secret**: currently the secret is defined in the @pytestbot account, which the core maintainers
have access to. There we created a new secret named `chatops` with write access to the repository.
**Token**: currently the token from the GitHub Actions is used, pushed with
`pytest bot <pytestbot@gmail.com>` commit author.
"""
import argparse
import json

View File

@@ -1,18 +1,30 @@
#!/usr/bin/env bash
set -e
set -euo pipefail
set -x
if [ -z "$TOXENV" ]; then
# Install coverage.
if [[ -z ${TOXENV+x} || -z $TOXENV ]]; then
python -m pip install coverage
else
# Add last TOXENV to $PATH.
PATH="$PWD/.tox/${TOXENV##*,}/bin:$PATH"
fi
# Run coverage.
python -m coverage combine
python -m coverage xml
python -m coverage report -m
# Download and verify latest Codecov bash uploader.
# Set --connect-timeout to work around https://github.com/curl/curl/issues/4461
curl -S -L --connect-timeout 5 --retry 6 -s https://codecov.io/bash -o codecov-upload.sh
bash codecov-upload.sh -Z -X fix -f coverage.xml "$@"
curl --silent --show-error --location --connect-timeout 5 --retry 6 -o codecov https://codecov.io/bash
VERSION=$(grep --only-matching 'VERSION=\"[0-9\.]*\"' codecov | cut -d'"' -f2)
if command -v sha256sum; then
sha256sum --check --strict --ignore-missing --quiet <(curl --silent "https://raw.githubusercontent.com/codecov/codecov-bash/${VERSION}/SHA256SUM")
else
shasum --algorithm 256 --check --strict --ignore-missing --quiet <(curl --silent "https://raw.githubusercontent.com/codecov/codecov-bash/${VERSION}/SHA256SUM")
fi
# Upload coverage.
bash codecov -Z -X fix -f coverage.xml "$@"

View File

@@ -21,11 +21,14 @@ classifiers =
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Topic :: Software Development :: Libraries
Topic :: Software Development :: Testing
Topic :: Utilities
keywords = test, unittest
project_urls =
Changelog=https://docs.pytest.org/en/stable/changelog.html
Twitter=https://twitter.com/pytestdotorg
Source=https://github.com/pytest-dev/pytest
Tracker=https://github.com/pytest-dev/pytest/issues
@@ -42,7 +45,7 @@ install_requires =
attrs>=19.2.0
iniconfig
packaging
pluggy>=0.12,<1.0.0a1
pluggy>=0.12,<2.0
py>=1.8.2
toml
atomicwrites>=1.0;sys_platform=="win32"
@@ -52,7 +55,7 @@ python_requires = >=3.6
package_dir =
=src
setup_requires =
setuptools>=>=42.0
setuptools>=42.0
setuptools-scm>=3.4
zip_safe = no

View File

@@ -673,12 +673,9 @@ class AssertionRewriter(ast.NodeVisitor):
if not mod.body:
# Nothing to do.
return
# Insert some special imports at the top of the module but after any
# docstrings and __future__ imports.
aliases = [
ast.alias("builtins", "@py_builtins"),
ast.alias("_pytest.assertion.rewrite", "@pytest_ar"),
]
# We'll insert some special imports at the top of the module, but after any
# docstrings and __future__ imports, so first figure out where that is.
doc = getattr(mod, "docstring", None)
expect_docstring = doc is None
if doc is not None and self.is_rewrite_disabled(doc):
@@ -710,10 +707,27 @@ class AssertionRewriter(ast.NodeVisitor):
lineno = item.decorator_list[0].lineno
else:
lineno = item.lineno
# Now actually insert the special imports.
if sys.version_info >= (3, 10):
aliases = [
ast.alias("builtins", "@py_builtins", lineno=lineno, col_offset=0),
ast.alias(
"_pytest.assertion.rewrite",
"@pytest_ar",
lineno=lineno,
col_offset=0,
),
]
else:
aliases = [
ast.alias("builtins", "@py_builtins"),
ast.alias("_pytest.assertion.rewrite", "@pytest_ar"),
]
imports = [
ast.Import([alias], lineno=lineno, col_offset=0) for alias in aliases
]
mod.body[pos:pos] = imports
# Collect asserts.
nodes: List[ast.AST] = [mod]
while nodes:

View File

@@ -69,7 +69,12 @@ class FaultHandlerHooks:
@staticmethod
def _get_stderr_fileno():
try:
return sys.stderr.fileno()
fileno = sys.stderr.fileno()
# The Twisted Logger will return an invalid file descriptor since it is not backed
# by an FD. So, let's also forward this to the same code path as with pytest-xdist.
if fileno == -1:
raise AttributeError()
return fileno
except (AttributeError, io.UnsupportedOperation):
# pytest-xdist monkeypatches sys.stderr with an object that is not an actual file.
# https://docs.python.org/3/library/faulthandler.html#issue-with-file-descriptors

View File

@@ -38,7 +38,7 @@ class OutcomeException(BaseException):
self.pytrace = pytrace
def __repr__(self) -> str:
if self.msg:
if self.msg is not None:
return self.msg
return f"<{self.__class__.__name__} instance>"

View File

@@ -64,13 +64,6 @@ def get_lock_path(path: _AnyPurePath) -> _AnyPurePath:
return path.joinpath(".lock")
def ensure_reset_dir(path: Path) -> None:
"""Ensure the given path is an empty directory."""
if path.exists():
rm_rf(path)
path.mkdir()
def on_rm_rf_error(func, path: str, exc, *, start_path: Path) -> bool:
"""Handle known read-only errors during rmtree.
@@ -214,7 +207,7 @@ def _force_symlink(
pass
def make_numbered_dir(root: Path, prefix: str) -> Path:
def make_numbered_dir(root: Path, prefix: str, mode: int = 0o700) -> Path:
"""Create a directory with an increased number as suffix for the given prefix."""
for i in range(10):
# try up to 10 times to create the folder
@@ -222,7 +215,7 @@ def make_numbered_dir(root: Path, prefix: str) -> Path:
new_number = max_existing + 1
new_path = root.joinpath(f"{prefix}{new_number}")
try:
new_path.mkdir()
new_path.mkdir(mode=mode)
except Exception:
pass
else:
@@ -354,13 +347,13 @@ def cleanup_numbered_dir(
def make_numbered_dir_with_cleanup(
root: Path, prefix: str, keep: int, lock_timeout: float
root: Path, prefix: str, keep: int, lock_timeout: float, mode: int,
) -> Path:
"""Create a numbered dir with a cleanup lock and remove old ones."""
e = None
for i in range(10):
try:
p = make_numbered_dir(root, prefix)
p = make_numbered_dir(root, prefix, mode)
lock_path = create_cleanup_lock(p)
register_cleanup_lock_removal(lock_path)
except Exception as exc:
@@ -543,7 +536,7 @@ def import_path(
module_file = module_file[: -(len(os.path.sep + "__init__.py"))]
try:
is_same = os.path.samefile(str(path), module_file)
is_same = _is_same(str(path), module_file)
except FileNotFoundError:
is_same = False
@@ -553,6 +546,20 @@ def import_path(
return mod
# Implement a special _is_same function on Windows which returns True if the two filenames
# compare equal, to circumvent os.path.samefile returning False for mounts in UNC (#7678).
if sys.platform.startswith("win"):
def _is_same(f1: str, f2: str) -> bool:
return Path(f1) == Path(f2) or os.path.samefile(f1, f2)
else:
def _is_same(f1: str, f2: str) -> bool:
return os.path.samefile(f1, f2)
def resolve_package_path(path: Path) -> Optional[Path]:
"""Return the Python package path by looking for the last
directory upwards which still contains an __init__.py.

View File

@@ -1426,7 +1426,7 @@ class Pytester:
:rtype: RunResult
"""
__tracebackhide__ = True
p = make_numbered_dir(root=self.path, prefix="runpytest-")
p = make_numbered_dir(root=self.path, prefix="runpytest-", mode=0o700)
args = ("--basetemp=%s" % p,) + args
plugins = [x for x in self.plugins if isinstance(x, str)]
if plugins:
@@ -1445,7 +1445,7 @@ class Pytester:
The pexpect child is returned.
"""
basetemp = self.path / "temp-pexpect"
basetemp.mkdir()
basetemp.mkdir(mode=0o700)
invoke = " ".join(map(str, self._getpytestargs()))
cmd = f"{invoke} --basetemp={basetemp} {string}"
return self.spawn(cmd, expect_timeout=expect_timeout)

View File

@@ -15,9 +15,14 @@ from typing import overload
from typing import Pattern
from typing import Tuple
from typing import Type
from typing import TYPE_CHECKING
from typing import TypeVar
from typing import Union
if TYPE_CHECKING:
from numpy import ndarray
import _pytest._code
from _pytest.compat import final
from _pytest.compat import STRING_TYPES
@@ -232,10 +237,11 @@ class ApproxScalar(ApproxBase):
def __eq__(self, actual) -> bool:
"""Return whether the given value is equal to the expected value
within the pre-specified tolerance."""
if _is_numpy_array(actual):
asarray = _as_numpy_array(actual)
if asarray is not None:
# Call ``__eq__()`` manually to prevent infinite-recursion with
# numpy<1.13. See #3748.
return all(self.__eq__(a) for a in actual.flat)
return all(self.__eq__(a) for a in asarray.flat)
# Short-circuit exact equality.
if actual == self.expected:
@@ -521,6 +527,7 @@ def approx(expected, rel=None, abs=None, nan_ok: bool = False) -> ApproxBase:
elif isinstance(expected, Mapping):
cls = ApproxMapping
elif _is_numpy_array(expected):
expected = _as_numpy_array(expected)
cls = ApproxNumpy
elif (
isinstance(expected, Iterable)
@@ -536,16 +543,30 @@ def approx(expected, rel=None, abs=None, nan_ok: bool = False) -> ApproxBase:
def _is_numpy_array(obj: object) -> bool:
"""Return true if the given object is a numpy array.
"""
Return true if the given object is implicitly convertible to ndarray,
and numpy is already imported.
"""
return _as_numpy_array(obj) is not None
A special effort is made to avoid importing numpy unless it's really necessary.
def _as_numpy_array(obj: object) -> Optional["ndarray"]:
"""
Return an ndarray if the given object is implicitly convertible to ndarray,
and numpy is already imported, otherwise None.
"""
import sys
np: Any = sys.modules.get("numpy")
if np is not None:
return isinstance(obj, np.ndarray)
return False
# avoid infinite recursion on numpy scalars, which have __array__
if np.isscalar(obj):
return None
elif isinstance(obj, np.ndarray):
return obj
elif hasattr(obj, "__array__") or hasattr("obj", "__array_interface__"):
return np.asarray(obj)
return None
# builtin pytest.raises helper

View File

@@ -1400,4 +1400,6 @@ def _get_raw_skip_reason(report: TestReport) -> str:
_, _, reason = report.longrepr
if reason.startswith("Skipped: "):
reason = reason[len("Skipped: ") :]
elif reason == "Skipped":
reason = ""
return reason

View File

@@ -1,6 +1,7 @@
"""Support for providing temporary directories to test functions."""
import os
import re
import sys
import tempfile
from pathlib import Path
from typing import Optional
@@ -8,10 +9,10 @@ from typing import Optional
import attr
import py
from .pathlib import ensure_reset_dir
from .pathlib import LOCK_TIMEOUT
from .pathlib import make_numbered_dir
from .pathlib import make_numbered_dir_with_cleanup
from .pathlib import rm_rf
from _pytest.compat import final
from _pytest.config import Config
from _pytest.deprecated import check_ispytest
@@ -90,20 +91,22 @@ class TempPathFactory:
basename = self._ensure_relative_to_basetemp(basename)
if not numbered:
p = self.getbasetemp().joinpath(basename)
p.mkdir()
p.mkdir(mode=0o700)
else:
p = make_numbered_dir(root=self.getbasetemp(), prefix=basename)
p = make_numbered_dir(root=self.getbasetemp(), prefix=basename, mode=0o700)
self._trace("mktemp", p)
return p
def getbasetemp(self) -> Path:
"""Return base temporary directory."""
"""Return the base temporary directory, creating it if needed."""
if self._basetemp is not None:
return self._basetemp
if self._given_basetemp is not None:
basetemp = self._given_basetemp
ensure_reset_dir(basetemp)
if basetemp.exists():
rm_rf(basetemp)
basetemp.mkdir(mode=0o700)
basetemp = basetemp.resolve()
else:
from_env = os.environ.get("PYTEST_DEBUG_TEMPROOT")
@@ -112,14 +115,37 @@ class TempPathFactory:
# use a sub-directory in the temproot to speed-up
# make_numbered_dir() call
rootdir = temproot.joinpath(f"pytest-of-{user}")
rootdir.mkdir(exist_ok=True)
rootdir.mkdir(mode=0o700, exist_ok=True)
# Because we use exist_ok=True with a predictable name, make sure
# we are the owners, to prevent any funny business (on unix, where
# temproot is usually shared).
# Also, to keep things private, fixup any world-readable temp
# rootdir's permissions. Historically 0o755 was used, so we can't
# just error out on this, at least for a while.
if sys.platform != "win32":
uid = os.getuid()
rootdir_stat = rootdir.stat()
# getuid shouldn't fail, but cpython defines such a case.
# Let's hope for the best.
if uid != -1:
if rootdir_stat.st_uid != uid:
raise OSError(
f"The temporary directory {rootdir} is not owned by the current user. "
"Fix this and try again."
)
if (rootdir_stat.st_mode & 0o077) != 0:
os.chmod(rootdir, rootdir_stat.st_mode & ~0o077)
basetemp = make_numbered_dir_with_cleanup(
prefix="pytest-", root=rootdir, keep=3, lock_timeout=LOCK_TIMEOUT
prefix="pytest-",
root=rootdir,
keep=3,
lock_timeout=LOCK_TIMEOUT,
mode=0o700,
)
assert basetemp is not None, basetemp
self._basetemp = t = basetemp
self._trace("new basetemp", t)
return t
self._basetemp = basetemp
self._trace("new basetemp", basetemp)
return basetemp
@final

View File

@@ -1173,7 +1173,7 @@ def test_usage_error_code(pytester: Pytester) -> None:
assert result.ret == ExitCode.USAGE_ERROR
@pytest.mark.filterwarnings("default")
@pytest.mark.filterwarnings("default::pytest.PytestUnhandledCoroutineWarning")
def test_warn_on_async_function(pytester: Pytester) -> None:
# In the below we .close() the coroutine only to avoid
# "RuntimeWarning: coroutine 'test_2' was never awaited"
@@ -1206,7 +1206,7 @@ def test_warn_on_async_function(pytester: Pytester) -> None:
)
@pytest.mark.filterwarnings("default")
@pytest.mark.filterwarnings("default::pytest.PytestUnhandledCoroutineWarning")
def test_warn_on_async_gen_function(pytester: Pytester) -> None:
pytester.makepyfile(
test_async="""

View File

@@ -447,6 +447,36 @@ class TestApprox:
assert a12 != approx(a21)
assert a21 != approx(a12)
def test_numpy_array_protocol(self):
"""
array-like objects such as tensorflow's DeviceArray are handled like ndarray.
See issue #8132
"""
np = pytest.importorskip("numpy")
class DeviceArray:
def __init__(self, value, size):
self.value = value
self.size = size
def __array__(self):
return self.value * np.ones(self.size)
class DeviceScalar:
def __init__(self, value):
self.value = value
def __array__(self):
return np.array(self.value)
expected = 1
actual = 1 + 1e-6
assert approx(expected) == DeviceArray(actual, size=1)
assert approx(expected) == DeviceArray(actual, size=2)
assert approx(expected) == DeviceScalar(actual)
assert approx(DeviceScalar(expected)) == actual
assert approx(DeviceScalar(expected)) == DeviceScalar(actual)
def test_doctests(self, mocked_doctest_runner) -> None:
import doctest

View File

@@ -1210,7 +1210,7 @@ def test_unorderable_types(testdir):
assert result.ret == ExitCode.NO_TESTS_COLLECTED
@pytest.mark.filterwarnings("default")
@pytest.mark.filterwarnings("default::pytest.PytestCollectionWarning")
def test_dont_collect_non_function_callable(testdir):
"""Test for issue https://github.com/pytest-dev/pytest/issues/331

View File

@@ -1212,7 +1212,7 @@ def test_collect_symlink_dir(pytester: Pytester) -> None:
"""A symlinked directory is collected."""
dir = pytester.mkdir("dir")
dir.joinpath("test_it.py").write_text("def test_it(): pass", "utf-8")
pytester.path.joinpath("symlink_dir").symlink_to(dir)
symlink_or_skip(pytester.path.joinpath("symlink_dir"), dir)
result = pytester.runpytest()
result.assert_outcomes(passed=2)

View File

@@ -290,7 +290,7 @@ class TestParseIni:
result = pytester.runpytest()
result.stdout.no_fnmatch_line("*PytestConfigWarning*")
@pytest.mark.filterwarnings("default")
@pytest.mark.filterwarnings("default::pytest.PytestConfigWarning")
def test_disable_warnings_plugin_disables_config_warnings(
self, pytester: Pytester
) -> None:
@@ -1247,9 +1247,19 @@ def test_load_initial_conftest_last_ordering(_config_for_test):
m = My()
pm.register(m)
hc = pm.hook.pytest_load_initial_conftests
values = hc._nonwrappers + hc._wrappers
expected = ["_pytest.config", m.__module__, "_pytest.capture", "_pytest.warnings"]
assert [x.function.__module__ for x in values] == expected
hookimpls = [
(
hookimpl.function.__module__,
"wrapper" if hookimpl.hookwrapper else "nonwrapper",
)
for hookimpl in hc.get_hookimpls()
]
assert hookimpls == [
("_pytest.config", "nonwrapper"),
(m.__module__, "nonwrapper"),
("_pytest.capture", "wrapper"),
("_pytest.warnings", "wrapper"),
]
def test_get_plugin_specs_as_list() -> None:
@@ -1780,7 +1790,7 @@ def test_invocation_args(pytester: Pytester) -> None:
)
def test_config_blocked_default_plugins(pytester: Pytester, plugin: str) -> None:
if plugin == "debugging":
# Fixed in xdist master (after 1.27.0).
# Fixed in xdist (after 1.27.0).
# https://github.com/pytest-dev/pytest-xdist/pull/422
try:
import xdist # noqa: F401

View File

@@ -1,3 +1,4 @@
import io
import sys
import pytest
@@ -135,3 +136,27 @@ def test_already_initialized(faulthandler_timeout: int, pytester: Pytester) -> N
result.stdout.no_fnmatch_line(warning_line)
result.stdout.fnmatch_lines("*1 passed*")
assert result.ret == 0
def test_get_stderr_fileno_invalid_fd() -> None:
"""Test for faulthandler being able to handle invalid file descriptors for stderr (#8249)."""
from _pytest.faulthandler import FaultHandlerHooks
class StdErrWrapper(io.StringIO):
"""
Mimic ``twisted.logger.LoggingFile`` to simulate returning an invalid file descriptor.
https://github.com/twisted/twisted/blob/twisted-20.3.0/src/twisted/logger/_io.py#L132-L139
"""
def fileno(self):
return -1
wrapper = StdErrWrapper()
with pytest.MonkeyPatch.context() as mp:
mp.setattr("sys.stderr", wrapper)
# Even when the stderr wrapper signals an invalid file descriptor,
# ``_get_stderr_fileno()`` should return the real one.
assert FaultHandlerHooks._get_stderr_fileno() == 2

View File

@@ -7,6 +7,7 @@ from textwrap import dedent
import py
import pytest
from _pytest.monkeypatch import MonkeyPatch
from _pytest.pathlib import bestrelpath
from _pytest.pathlib import commonpath
from _pytest.pathlib import ensure_deletable
@@ -414,3 +415,23 @@ def test_visit_ignores_errors(tmpdir) -> None:
"bar",
"foo",
]
@pytest.mark.skipif(not sys.platform.startswith("win"), reason="Windows only")
def test_samefile_false_negatives(tmp_path: Path, monkeypatch: MonkeyPatch) -> None:
"""
import_file() should not raise ImportPathMismatchError if the paths are exactly
equal on Windows. It seems directories mounted as UNC paths make os.path.samefile
return False, even when they are clearly equal.
"""
module_path = tmp_path.joinpath("my_module.py")
module_path.write_text("def foo(): return 42")
monkeypatch.syspath_prepend(tmp_path)
with monkeypatch.context() as mp:
# Forcibly make os.path.samefile() return False here to ensure we are comparing
# the paths too. Using a context to narrow the patch as much as possible given
# this is an important system function.
mp.setattr(os.path, "samefile", lambda x, y: False)
module = import_path(module_path)
assert getattr(module, "foo")() == 42

View File

@@ -27,6 +27,17 @@ def test_recwarn_functional(pytester: Pytester) -> None:
reprec.assertoutcome(passed=1)
@pytest.mark.filterwarnings("")
def test_recwarn_captures_deprecation_warning(recwarn: WarningsRecorder) -> None:
"""
Check that recwarn can capture DeprecationWarning by default
without custom filterwarnings (see #8666).
"""
warnings.warn(DeprecationWarning("some deprecation"))
assert len(recwarn) == 1
assert recwarn.pop(DeprecationWarning)
class TestWarningsRecorderChecker:
def test_recording(self) -> None:
rec = WarningsRecorder(_ispytest=True)

View File

@@ -1128,19 +1128,30 @@ def test_errors_in_xfail_skip_expressions(pytester: Pytester) -> None:
markline = markline[5:]
elif sys.version_info >= (3, 8) or hasattr(sys, "pypy_version_info"):
markline = markline[4:]
result.stdout.fnmatch_lines(
[
if sys.version_info[:2] >= (3, 10):
expected = [
"*ERROR*test_nameerror*",
"*evaluating*skipif*condition*",
"*asd*",
"*ERROR*test_syntax*",
"*evaluating*xfail*condition*",
" syntax error",
markline,
"SyntaxError: invalid syntax",
"*1 pass*2 errors*",
"",
"During handling of the above exception, another exception occurred:",
]
)
else:
expected = [
"*ERROR*test_nameerror*",
]
expected += [
"*evaluating*skipif*condition*",
"*asd*",
"*ERROR*test_syntax*",
"*evaluating*xfail*condition*",
" syntax error",
markline,
"SyntaxError: invalid syntax",
"*1 pass*2 errors*",
]
result.stdout.fnmatch_lines(expected)
def test_xfail_skipif_with_globals(pytester: Pytester) -> None:

View File

@@ -366,6 +366,26 @@ class TestTerminal:
@pytest.mark.xfail(reason="")
def test_4():
assert False
@pytest.mark.skip
def test_5():
pass
@pytest.mark.xfail
def test_6():
pass
def test_7():
pytest.skip()
def test_8():
pytest.skip("888 is great")
def test_9():
pytest.xfail()
def test_10():
pytest.xfail("It's 🕙 o'clock")
"""
)
result = pytester.runpytest("-v")
@@ -375,6 +395,12 @@ class TestTerminal:
"test_verbose_skip_reason.py::test_2 XPASS (456) *",
"test_verbose_skip_reason.py::test_3 XFAIL (789) *",
"test_verbose_skip_reason.py::test_4 XFAIL *",
"test_verbose_skip_reason.py::test_5 SKIPPED (unconditional skip) *",
"test_verbose_skip_reason.py::test_6 XPASS *",
"test_verbose_skip_reason.py::test_7 SKIPPED *",
"test_verbose_skip_reason.py::test_8 SKIPPED (888 is great) *",
"test_verbose_skip_reason.py::test_9 XFAIL *",
"test_verbose_skip_reason.py::test_10 XFAIL (It's 🕙 o'clock) *",
]
)
@@ -1592,7 +1618,7 @@ def test_terminal_summary(pytester: Pytester) -> None:
)
@pytest.mark.filterwarnings("default")
@pytest.mark.filterwarnings("default::UserWarning")
def test_terminal_summary_warnings_are_displayed(pytester: Pytester) -> None:
"""Test that warnings emitted during pytest_terminal_summary are displayed.
(#1305).
@@ -1629,7 +1655,7 @@ def test_terminal_summary_warnings_are_displayed(pytester: Pytester) -> None:
assert stdout.count("=== warnings summary ") == 2
@pytest.mark.filterwarnings("default")
@pytest.mark.filterwarnings("default::UserWarning")
def test_terminal_summary_warnings_header_once(pytester: Pytester) -> None:
pytester.makepyfile(
"""

View File

@@ -8,7 +8,7 @@ if sys.version_info < (3, 8):
pytest.skip("threadexception plugin needs Python>=3.8", allow_module_level=True)
@pytest.mark.filterwarnings("default")
@pytest.mark.filterwarnings("default::pytest.PytestUnhandledThreadExceptionWarning")
def test_unhandled_thread_exception(pytester: Pytester) -> None:
pytester.makepyfile(
test_it="""
@@ -42,7 +42,7 @@ def test_unhandled_thread_exception(pytester: Pytester) -> None:
)
@pytest.mark.filterwarnings("default")
@pytest.mark.filterwarnings("default::pytest.PytestUnhandledThreadExceptionWarning")
def test_unhandled_thread_exception_in_setup(pytester: Pytester) -> None:
pytester.makepyfile(
test_it="""
@@ -78,7 +78,7 @@ def test_unhandled_thread_exception_in_setup(pytester: Pytester) -> None:
)
@pytest.mark.filterwarnings("default")
@pytest.mark.filterwarnings("default::pytest.PytestUnhandledThreadExceptionWarning")
def test_unhandled_thread_exception_in_teardown(pytester: Pytester) -> None:
pytester.makepyfile(
test_it="""

View File

@@ -445,3 +445,44 @@ def test_basetemp_with_read_only_files(pytester: Pytester) -> None:
# running a second time and ensure we don't crash
result = pytester.runpytest("--basetemp=tmp")
assert result.ret == 0
@pytest.mark.skipif(not hasattr(os, "getuid"), reason="checks unix permissions")
def test_tmp_path_factory_create_directory_with_safe_permissions(
tmp_path: Path, monkeypatch,
) -> None:
"""Verify that pytest creates directories under /tmp with private permissions."""
# Use the test's tmp_path as the system temproot (/tmp).
monkeypatch.setenv("PYTEST_DEBUG_TEMPROOT", str(tmp_path))
tmp_factory = TempPathFactory(None, lambda *args: None, _ispytest=True)
basetemp = tmp_factory.getbasetemp()
# No world-readable permissions.
assert (basetemp.stat().st_mode & 0o077) == 0
# Parent too (pytest-of-foo).
assert (basetemp.parent.stat().st_mode & 0o077) == 0
@pytest.mark.skipif(not hasattr(os, "getuid"), reason="checks unix permissions")
def test_tmp_path_factory_fixes_up_world_readable_permissions(
tmp_path: Path, monkeypatch,
) -> None:
"""Verify that if a /tmp/pytest-of-foo directory already exists with
world-readable permissions, it is fixed.
pytest used to mkdir with such permissions, that's why we fix it up.
"""
# Use the test's tmp_path as the system temproot (/tmp).
monkeypatch.setenv("PYTEST_DEBUG_TEMPROOT", str(tmp_path))
tmp_factory = TempPathFactory(None, lambda *args: None, _ispytest=True)
basetemp = tmp_factory.getbasetemp()
# Before - simulate bad perms.
os.chmod(basetemp.parent, 0o777)
assert (basetemp.parent.stat().st_mode & 0o077) != 0
tmp_factory = TempPathFactory(None, lambda *args: None, _ispytest=True)
basetemp = tmp_factory.getbasetemp()
# After - fixed.
assert (basetemp.parent.stat().st_mode & 0o077) == 0

View File

@@ -533,7 +533,9 @@ class TestTrialUnittest:
# will crash both at test time and at teardown
"""
)
result = testdir.runpytest("-vv", "-oconsole_output_style=classic")
result = testdir.runpytest(
"-vv", "-oconsole_output_style=classic", "-W", "ignore::DeprecationWarning"
)
result.stdout.fnmatch_lines(
[
"test_trial_error.py::TC::test_four FAILED",

View File

@@ -8,7 +8,7 @@ if sys.version_info < (3, 8):
pytest.skip("unraisableexception plugin needs Python>=3.8", allow_module_level=True)
@pytest.mark.filterwarnings("default")
@pytest.mark.filterwarnings("default::pytest.PytestUnraisableExceptionWarning")
def test_unraisable(pytester: Pytester) -> None:
pytester.makepyfile(
test_it="""
@@ -40,7 +40,7 @@ def test_unraisable(pytester: Pytester) -> None:
)
@pytest.mark.filterwarnings("default")
@pytest.mark.filterwarnings("default::pytest.PytestUnraisableExceptionWarning")
def test_unraisable_in_setup(pytester: Pytester) -> None:
pytester.makepyfile(
test_it="""
@@ -76,7 +76,7 @@ def test_unraisable_in_setup(pytester: Pytester) -> None:
)
@pytest.mark.filterwarnings("default")
@pytest.mark.filterwarnings("default::pytest.PytestUnraisableExceptionWarning")
def test_unraisable_in_teardown(pytester: Pytester) -> None:
pytester.makepyfile(
test_it="""

View File

@@ -38,7 +38,7 @@ def pyfile_with_warnings(testdir: Testdir, request: FixtureRequest) -> str:
return str(test_file)
@pytest.mark.filterwarnings("default")
@pytest.mark.filterwarnings("default::UserWarning", "default::RuntimeWarning")
def test_normal_flow(testdir, pyfile_with_warnings):
"""Check that the warnings section is displayed."""
result = testdir.runpytest(pyfile_with_warnings)
@@ -55,7 +55,7 @@ def test_normal_flow(testdir, pyfile_with_warnings):
)
@pytest.mark.filterwarnings("always")
@pytest.mark.filterwarnings("always::UserWarning")
def test_setup_teardown_warnings(testdir):
testdir.makepyfile(
"""
@@ -123,7 +123,7 @@ def test_ignore(testdir, pyfile_with_warnings, method):
assert WARNINGS_SUMMARY_HEADER not in result.stdout.str()
@pytest.mark.filterwarnings("always")
@pytest.mark.filterwarnings("always::UserWarning")
def test_unicode(testdir):
testdir.makepyfile(
"""
@@ -182,7 +182,7 @@ def test_filterwarnings_mark(testdir, default_config):
testdir.makeini(
"""
[pytest]
filterwarnings = always
filterwarnings = always::RuntimeWarning
"""
)
testdir.makepyfile(
@@ -202,7 +202,9 @@ def test_filterwarnings_mark(testdir, default_config):
warnings.warn(RuntimeWarning())
"""
)
result = testdir.runpytest("-W always" if default_config == "cmdline" else "")
result = testdir.runpytest(
"-W always::RuntimeWarning" if default_config == "cmdline" else ""
)
result.stdout.fnmatch_lines(["*= 1 failed, 2 passed, 1 warning in *"])
@@ -217,7 +219,7 @@ def test_non_string_warning_argument(testdir):
warnings.warn(UserWarning(1, 'foo'))
"""
)
result = testdir.runpytest("-W", "always")
result = testdir.runpytest("-W", "always::UserWarning")
result.stdout.fnmatch_lines(["*= 1 passed, 1 warning in *"])
@@ -236,7 +238,7 @@ def test_filterwarnings_mark_registration(testdir):
assert result.ret == 0
@pytest.mark.filterwarnings("always")
@pytest.mark.filterwarnings("always::UserWarning")
def test_warning_captured_hook(testdir):
testdir.makeconftest(
"""
@@ -297,7 +299,7 @@ def test_warning_captured_hook(testdir):
assert collected_result[3] is None, str(collected)
@pytest.mark.filterwarnings("always")
@pytest.mark.filterwarnings("always::UserWarning")
def test_collection_warnings(testdir):
"""Check that we also capture warnings issued during test collection (#3251)."""
testdir.makepyfile(
@@ -321,7 +323,7 @@ def test_collection_warnings(testdir):
)
@pytest.mark.filterwarnings("always")
@pytest.mark.filterwarnings("always::UserWarning")
def test_mark_regex_escape(testdir):
"""@pytest.mark.filterwarnings should not try to escape regex characters (#3936)"""
testdir.makepyfile(
@@ -337,7 +339,7 @@ def test_mark_regex_escape(testdir):
assert WARNINGS_SUMMARY_HEADER not in result.stdout.str()
@pytest.mark.filterwarnings("default")
@pytest.mark.filterwarnings("default::pytest.PytestWarning")
@pytest.mark.parametrize("ignore_pytest_warnings", ["no", "ini", "cmdline"])
def test_hide_pytest_internal_warnings(testdir, ignore_pytest_warnings):
"""Make sure we can ignore internal pytest warnings using a warnings filter."""
@@ -383,7 +385,7 @@ def test_option_precedence_cmdline_over_ini(testdir, ignore_on_cmdline):
testdir.makeini(
"""
[pytest]
filterwarnings = error
filterwarnings = error::UserWarning
"""
)
testdir.makepyfile(
@@ -578,7 +580,7 @@ def test_warnings_checker_twice():
@pytest.mark.filterwarnings("ignore::pytest.PytestExperimentalApiWarning")
@pytest.mark.filterwarnings("always")
@pytest.mark.filterwarnings("always::UserWarning")
def test_group_warnings_by_message(testdir):
testdir.copy_example("warnings/test_group_warnings_by_message.py")
result = testdir.runpytest()
@@ -610,7 +612,7 @@ def test_group_warnings_by_message(testdir):
@pytest.mark.filterwarnings("ignore::pytest.PytestExperimentalApiWarning")
@pytest.mark.filterwarnings("always")
@pytest.mark.filterwarnings("always::UserWarning")
def test_group_warnings_by_message_summary(testdir):
testdir.copy_example("warnings/test_group_warnings_by_message_summary")
testdir.syspathinsert()

View File

@@ -9,8 +9,9 @@ envlist =
py37
py38
py39
py310
pypy3
py37-{pexpect,xdist,unittestextras,numpy,pluggymaster}
py37-{pexpect,xdist,unittestextras,numpy,pluggymain}
doctesting
plugins
py37-freeze
@@ -46,7 +47,7 @@ deps =
doctesting: PyYAML
numpy: numpy>=1.19.4
pexpect: pexpect>=4.8.0
pluggymaster: git+https://github.com/pytest-dev/pluggy.git@master
pluggymain: pluggy @ git+https://github.com/pytest-dev/pluggy.git
pygments>=2.7.2
unittestextras: twisted
unittestextras: asynctest