Compare commits
295 Commits
7.4.x
...
graingert-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
662fc9c012 | ||
|
|
db62b80e8d | ||
|
|
4821a41967 | ||
|
|
ff12e083c5 | ||
|
|
6e40458fc1 | ||
|
|
e74a6da699 | ||
|
|
5e6dc297c2 | ||
|
|
5ba3351b52 | ||
|
|
f34091f0ec | ||
|
|
dc2080f3be | ||
|
|
cdddd6d695 | ||
|
|
dd68f9c95a | ||
|
|
3ab70cd561 | ||
|
|
23825f2983 | ||
|
|
fb3a46dd8a | ||
|
|
3d6d93d0c2 | ||
|
|
2401d76655 | ||
|
|
af9b1dcc24 | ||
|
|
696859fc43 | ||
|
|
e966dcd93c | ||
|
|
54623f0f33 | ||
|
|
9bbfe995ee | ||
|
|
d015bc1b8f | ||
|
|
b73b4c464c | ||
|
|
1a16bac131 | ||
|
|
81192ca85f | ||
|
|
486a9ed057 | ||
|
|
4ae102c003 | ||
|
|
c614590ec9 | ||
|
|
d2b214220f | ||
|
|
a38ad254ef | ||
|
|
9f22d3281c | ||
|
|
9a58e6283d | ||
|
|
8bac8d7807 | ||
|
|
5e081162df | ||
|
|
8062743f6b | ||
|
|
8b7f94f145 | ||
|
|
5ace48ca5b | ||
|
|
e7caaa0b3e | ||
|
|
a47fcb4873 | ||
|
|
ab8f5ce7f4 | ||
|
|
f43a8db618 | ||
|
|
6c2feb75d2 | ||
|
|
fcb8e73288 | ||
|
|
241f2a890e | ||
|
|
39f9306357 | ||
|
|
e0d04bdfab | ||
|
|
1949b09fd3 | ||
|
|
3d1c52f203 | ||
|
|
a60c23c3d8 | ||
|
|
24a6ee1ffd | ||
|
|
e2acc1a99b | ||
|
|
71f265f1f3 | ||
|
|
7259e8db98 | ||
|
|
dd7beb39d6 | ||
|
|
6ad9499c9c | ||
|
|
2ed2e9208d | ||
|
|
ab63ebb3dc | ||
|
|
b3a981d385 | ||
|
|
48b0395648 | ||
|
|
9c11275553 | ||
|
|
e5c81fa41a | ||
|
|
0a06db0729 | ||
|
|
5936a79fdb | ||
|
|
28ccf476b9 | ||
|
|
333e4eba6b | ||
|
|
e787d2ed48 | ||
|
|
f6b6478868 | ||
|
|
3ce63bc768 | ||
|
|
faa8f2ea08 | ||
|
|
194a782e38 | ||
|
|
bc71561ad9 | ||
|
|
574e0f45d9 | ||
|
|
b8906b29a7 | ||
|
|
d2b5177dd6 | ||
|
|
65c01f531b | ||
|
|
82bd63d318 | ||
|
|
d4872f5df7 | ||
|
|
8d815ca55b | ||
|
|
8032d21271 | ||
|
|
9c8937b480 | ||
|
|
0d8b87f161 | ||
|
|
65c73a09e7 | ||
|
|
917ce9aa01 | ||
|
|
4e3a0874df | ||
|
|
ba0da81f88 | ||
|
|
f08782d8d0 | ||
|
|
5b528bd131 | ||
|
|
e4794b26b2 | ||
|
|
ab6cae2126 | ||
|
|
afb8d66e42 | ||
|
|
76ba7db6ce | ||
|
|
941b203c94 | ||
|
|
19d6b12b2a | ||
|
|
17e8f2b3fc | ||
|
|
ff23347f1f | ||
|
|
00fedcc439 | ||
|
|
77f7f59b2a | ||
|
|
7500fe44b2 | ||
|
|
23b899f31f | ||
|
|
43d1398fc7 | ||
|
|
03832fa31e | ||
|
|
8f36fd5454 | ||
|
|
cada6c105a | ||
|
|
3f446b68fd | ||
|
|
050f402816 | ||
|
|
d1722d5c18 | ||
|
|
4deb38b2ed | ||
|
|
370eacd3ca | ||
|
|
010e1742d8 | ||
|
|
6e5f10b28f | ||
|
|
dd5ae0c3b8 | ||
|
|
fc653d0d12 | ||
|
|
a357c7abc8 | ||
|
|
61133ba83d | ||
|
|
049eec8474 | ||
|
|
37bb186175 | ||
|
|
3cb3cd1a08 | ||
|
|
7a625481da | ||
|
|
0ddfdfcc04 | ||
|
|
87bfc83aa0 | ||
|
|
ebd571bb18 | ||
|
|
176d728b7b | ||
|
|
d4fb6ac9f7 | ||
|
|
15fadd8c5c | ||
|
|
73d754bd74 | ||
|
|
10056865d2 | ||
|
|
1827d8d5f9 | ||
|
|
9e164fc4fe | ||
|
|
c0cf822ca1 | ||
|
|
ec1053cc16 | ||
|
|
d2dc8a70b5 | ||
|
|
b9cb87d862 | ||
|
|
e938580257 | ||
|
|
47c0fc3d78 | ||
|
|
556e075d23 | ||
|
|
3ad3fc6b8f | ||
|
|
09b78737a5 | ||
|
|
b2186e2455 | ||
|
|
18bc6c9a0e | ||
|
|
24c9aa6c30 | ||
|
|
9c67b7aeb6 | ||
|
|
1cc58ed67f | ||
|
|
84a342e27c | ||
|
|
e8a8a5f320 | ||
|
|
1c04a92503 | ||
|
|
cc0adf6bf3 | ||
|
|
b8b74331b4 | ||
|
|
4797deab99 | ||
|
|
c9163402e0 | ||
|
|
01ac13a77d | ||
|
|
485c555812 | ||
|
|
a21fb87a90 | ||
|
|
c754da10d2 | ||
|
|
71e627aa8f | ||
|
|
d3552ef4c0 | ||
|
|
448563caaa | ||
|
|
e8aa906e06 | ||
|
|
12054a4972 | ||
|
|
430ad145c1 | ||
|
|
13e2b00258 | ||
|
|
4e42421ebf | ||
|
|
161ba87300 | ||
|
|
f2b6040e9e | ||
|
|
e3247834e2 | ||
|
|
0b4a557087 | ||
|
|
ffdcce67f4 | ||
|
|
1ded74739b | ||
|
|
497a1d798a | ||
|
|
1de0923e83 | ||
|
|
7c30f674c5 | ||
|
|
396bfbf30b | ||
|
|
02ba39bfcd | ||
|
|
29010d23a6 | ||
|
|
de1f6f58ba | ||
|
|
cfda801ebf | ||
|
|
c5262b0c42 | ||
|
|
ff6e110161 | ||
|
|
0e0ed2af95 | ||
|
|
a3fbf24389 | ||
|
|
bf451d47a1 | ||
|
|
578fbe3dfd | ||
|
|
a668719626 | ||
|
|
04e0db7e48 | ||
|
|
32f480814c | ||
|
|
40ed678885 | ||
|
|
fb55615d5e | ||
|
|
9d0ddb4625 | ||
|
|
01f38aca44 | ||
|
|
78d81ef865 | ||
|
|
b41acaea12 | ||
|
|
7008385253 | ||
|
|
b25a3adff5 | ||
|
|
ecfab4dc8b | ||
|
|
2c80de532f | ||
|
|
7967b2e710 | ||
|
|
f1c9570a0e | ||
|
|
b20e7f6d0c | ||
|
|
b91d5a3112 | ||
|
|
4e75bff71a | ||
|
|
99ab8ae884 | ||
|
|
782cacf86b | ||
|
|
a3b4220d76 | ||
|
|
fd7a4d2429 | ||
|
|
b73ec8e5d1 | ||
|
|
d790e96765 | ||
|
|
d4265448a5 | ||
|
|
db37e34613 | ||
|
|
c5b13099e6 | ||
|
|
97ed533f63 | ||
|
|
f5a9aa0b84 | ||
|
|
901316b3f4 | ||
|
|
b81003f6fb | ||
|
|
ddd773ecb1 | ||
|
|
44604f49cd | ||
|
|
2203897086 | ||
|
|
119cec0279 | ||
|
|
b5bc53e441 | ||
|
|
084d756ae6 | ||
|
|
d7dbadbffc | ||
|
|
cb732f7f49 | ||
|
|
7775e494b1 | ||
|
|
1f5058e972 | ||
|
|
6baf9f2d31 | ||
|
|
c4876c7106 | ||
|
|
6badb6f01e | ||
|
|
4517af1e28 | ||
|
|
3d0dedb5ec | ||
|
|
7b7bd304aa | ||
|
|
2706271f66 | ||
|
|
8ac3c645fa | ||
|
|
18e87c9831 | ||
|
|
6995257cf4 | ||
|
|
bea56b30af | ||
|
|
b847084224 | ||
|
|
024e62e6d2 | ||
|
|
f9410fddcd | ||
|
|
561f1a993b | ||
|
|
b77d0deaf5 | ||
|
|
2f7415cfbc | ||
|
|
ba60649680 | ||
|
|
679bd6f2ed | ||
|
|
a50ea1b8e7 | ||
|
|
0353a94cd1 | ||
|
|
0311fc3384 | ||
|
|
7fdc8391e2 | ||
|
|
15524f34d2 | ||
|
|
2d48171e88 | ||
|
|
7022fb455d | ||
|
|
a1b37022af | ||
|
|
9279ea2882 | ||
|
|
165fbbd12a | ||
|
|
f617bab0a2 | ||
|
|
f4e3b4ad98 | ||
|
|
81cfb3fc87 | ||
|
|
dd5f3773d9 | ||
|
|
e132d6488f | ||
|
|
4719d998d8 | ||
|
|
5332656906 | ||
|
|
a14fc10cac | ||
|
|
05d7e60904 | ||
|
|
4ebb2b94c2 | ||
|
|
9859c110cc | ||
|
|
5eaf8207eb | ||
|
|
518ca37cae | ||
|
|
45f1a462d5 | ||
|
|
4cd95eeabf | ||
|
|
1306b84241 | ||
|
|
fe51121f39 | ||
|
|
c8b1790ee7 | ||
|
|
2870157234 | ||
|
|
1b7896f83d | ||
|
|
a4a189ad99 | ||
|
|
57d352460c | ||
|
|
a14745550a | ||
|
|
bd88a6412d | ||
|
|
264e7ac327 | ||
|
|
a2feb6bd00 | ||
|
|
246ceb84bd | ||
|
|
2fd160110c | ||
|
|
2998b596b4 | ||
|
|
7f73722c4a | ||
|
|
f7542f6292 | ||
|
|
0de2aa93f0 | ||
|
|
7759a9d3c2 | ||
|
|
d86df89a92 | ||
|
|
24fd292878 | ||
|
|
a1b10b552a | ||
|
|
aa9cc7e8b4 | ||
|
|
5c286d19d8 | ||
|
|
faac197fcf | ||
|
|
5227279c15 | ||
|
|
a7e0ae2455 | ||
|
|
12efc58479 | ||
|
|
3f71680ac0 |
2
.github/workflows/backport.yml
vendored
2
.github/workflows/backport.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: true
|
||||
|
||||
64
.github/workflows/deploy.yml
vendored
64
.github/workflows/deploy.yml
vendored
@@ -1,29 +1,26 @@
|
||||
name: deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
# These tags are protected, see:
|
||||
# https://github.com/pytest-dev/pytest/settings/tag_protection
|
||||
- "[0-9]+.[0-9]+.[0-9]+"
|
||||
- "[0-9]+.[0-9]+.[0-9]+rc[0-9]+"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Release version'
|
||||
required: true
|
||||
default: '1.2.3'
|
||||
|
||||
|
||||
# Set permissions at the job level.
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
|
||||
deploy:
|
||||
if: github.repository == 'pytest-dev/pytest'
|
||||
|
||||
package:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
permissions:
|
||||
contents: write
|
||||
env:
|
||||
SETUPTOOLS_SCM_PRETEND_VERSION: ${{ github.event.inputs.version }}
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
@@ -31,6 +28,18 @@ jobs:
|
||||
- name: Build and Check Package
|
||||
uses: hynek/build-and-inspect-python-package@v1.5
|
||||
|
||||
deploy:
|
||||
if: github.repository == 'pytest-dev/pytest'
|
||||
needs: [package]
|
||||
runs-on: ubuntu-latest
|
||||
environment: deploy
|
||||
timeout-minutes: 30
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Download Package
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
@@ -38,14 +47,35 @@ jobs:
|
||||
path: dist
|
||||
|
||||
- name: Publish package to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
uses: pypa/gh-action-pypi-publish@v1.8.10
|
||||
|
||||
- name: Push tag
|
||||
run: |
|
||||
git config user.name "pytest bot"
|
||||
git config user.email "pytestbot@gmail.com"
|
||||
git tag --annotate --message=v${{ github.event.inputs.version }} v${{ github.event.inputs.version }} ${{ github.sha }}
|
||||
git push origin v${{ github.event.inputs.version }}
|
||||
|
||||
release-notes:
|
||||
|
||||
# todo: generate the content in the build job
|
||||
# the goal being of using a github action script to push the release data
|
||||
# after success instead of creating a complete python/tox env
|
||||
needs: [deploy]
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
password: ${{ secrets.pypi_token }}
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.7"
|
||||
python-version: "3.11"
|
||||
|
||||
- name: Install tox
|
||||
run: |
|
||||
|
||||
2
.github/workflows/prepare-release-pr.yml
vendored
2
.github/workflows/prepare-release-pr.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
94
.github/workflows/test.yml
vendored
94
.github/workflows/test.yml
vendored
@@ -27,7 +27,19 @@ concurrency:
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
package:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- name: Build and Check Package
|
||||
uses: hynek/build-and-inspect-python-package@v1.5
|
||||
|
||||
build:
|
||||
needs: [package]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 45
|
||||
permissions:
|
||||
@@ -37,48 +49,41 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
name: [
|
||||
"windows-py37",
|
||||
"windows-py37-pluggy",
|
||||
"windows-py38",
|
||||
"windows-py38-pluggy",
|
||||
"windows-py39",
|
||||
"windows-py310",
|
||||
"windows-py311",
|
||||
"windows-py312",
|
||||
|
||||
"ubuntu-py37",
|
||||
"ubuntu-py37-pluggy",
|
||||
"ubuntu-py37-freeze",
|
||||
"ubuntu-py38",
|
||||
"ubuntu-py38-pluggy",
|
||||
"ubuntu-py38-freeze",
|
||||
"ubuntu-py39",
|
||||
"ubuntu-py310",
|
||||
"ubuntu-py311",
|
||||
"ubuntu-py312",
|
||||
"ubuntu-pypy3",
|
||||
|
||||
"macos-py37",
|
||||
"macos-py38",
|
||||
"macos-py39",
|
||||
"macos-py310",
|
||||
"macos-py312",
|
||||
|
||||
"docs",
|
||||
"doctesting",
|
||||
"plugins",
|
||||
]
|
||||
|
||||
include:
|
||||
- name: "windows-py37"
|
||||
python: "3.7"
|
||||
os: windows-latest
|
||||
tox_env: "py37-numpy"
|
||||
- name: "windows-py37-pluggy"
|
||||
python: "3.7"
|
||||
os: windows-latest
|
||||
tox_env: "py37-pluggymain-pylib-xdist"
|
||||
- name: "windows-py38"
|
||||
python: "3.8"
|
||||
os: windows-latest
|
||||
tox_env: "py38-unittestextras"
|
||||
use_coverage: true
|
||||
- name: "windows-py38-pluggy"
|
||||
python: "3.8"
|
||||
os: windows-latest
|
||||
tox_env: "py38-pluggymain-pylib-xdist"
|
||||
- name: "windows-py39"
|
||||
python: "3.9"
|
||||
os: windows-latest
|
||||
@@ -96,23 +101,19 @@ jobs:
|
||||
os: windows-latest
|
||||
tox_env: "py312"
|
||||
|
||||
- name: "ubuntu-py37"
|
||||
python: "3.7"
|
||||
os: ubuntu-latest
|
||||
tox_env: "py37-lsof-numpy-pexpect"
|
||||
use_coverage: true
|
||||
- name: "ubuntu-py37-pluggy"
|
||||
python: "3.7"
|
||||
os: ubuntu-latest
|
||||
tox_env: "py37-pluggymain-pylib-xdist"
|
||||
- name: "ubuntu-py37-freeze"
|
||||
python: "3.7"
|
||||
os: ubuntu-latest
|
||||
tox_env: "py37-freeze"
|
||||
- name: "ubuntu-py38"
|
||||
python: "3.8"
|
||||
os: ubuntu-latest
|
||||
tox_env: "py38-xdist"
|
||||
tox_env: "py38-lsof-numpy-pexpect"
|
||||
use_coverage: true
|
||||
- name: "ubuntu-py38-pluggy"
|
||||
python: "3.8"
|
||||
os: ubuntu-latest
|
||||
tox_env: "py38-pluggymain-pylib-xdist"
|
||||
- name: "ubuntu-py38-freeze"
|
||||
python: "3.8"
|
||||
os: ubuntu-latest
|
||||
tox_env: "py38-freeze"
|
||||
- name: "ubuntu-py39"
|
||||
python: "3.9"
|
||||
os: ubuntu-latest
|
||||
@@ -132,14 +133,14 @@ jobs:
|
||||
tox_env: "py312"
|
||||
use_coverage: true
|
||||
- name: "ubuntu-pypy3"
|
||||
python: "pypy-3.7"
|
||||
python: "pypy-3.8"
|
||||
os: ubuntu-latest
|
||||
tox_env: "pypy3-xdist"
|
||||
|
||||
- name: "macos-py37"
|
||||
python: "3.7"
|
||||
- name: "macos-py38"
|
||||
python: "3.8"
|
||||
os: macos-latest
|
||||
tox_env: "py37-xdist"
|
||||
tox_env: "py38-xdist"
|
||||
- name: "macos-py39"
|
||||
python: "3.9"
|
||||
os: macos-latest
|
||||
@@ -159,22 +160,24 @@ jobs:
|
||||
os: ubuntu-latest
|
||||
tox_env: "plugins"
|
||||
|
||||
- name: "docs"
|
||||
python: "3.7"
|
||||
os: ubuntu-latest
|
||||
tox_env: "docs"
|
||||
- name: "doctesting"
|
||||
python: "3.7"
|
||||
python: "3.8"
|
||||
os: ubuntu-latest
|
||||
tox_env: "doctesting"
|
||||
use_coverage: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Download Package
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: Packages
|
||||
path: dist
|
||||
|
||||
- name: Set up Python ${{ matrix.python }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
@@ -188,11 +191,13 @@ jobs:
|
||||
|
||||
- name: Test without coverage
|
||||
if: "! matrix.use_coverage"
|
||||
run: "tox -e ${{ matrix.tox_env }}"
|
||||
shell: bash
|
||||
run: tox run -e ${{ matrix.tox_env }} --installpkg `find dist/*.tar.gz`
|
||||
|
||||
- name: Test with coverage
|
||||
if: "matrix.use_coverage"
|
||||
run: "tox -e ${{ matrix.tox_env }}-coverage"
|
||||
shell: bash
|
||||
run: tox run -e ${{ matrix.tox_env }}-coverage --installpkg `find dist/*.tar.gz`
|
||||
|
||||
- name: Generate coverage report
|
||||
if: "matrix.use_coverage"
|
||||
@@ -206,10 +211,3 @@ jobs:
|
||||
fail_ci_if_error: true
|
||||
files: ./coverage.xml
|
||||
verbose: true
|
||||
|
||||
check-package:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build and Check Package
|
||||
uses: hynek/build-and-inspect-python-package@v1.5
|
||||
|
||||
14
.github/workflows/update-plugin-list.yml
vendored
14
.github/workflows/update-plugin-list.yml
vendored
@@ -20,19 +20,27 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.8
|
||||
python-version: "3.11"
|
||||
cache: pip
|
||||
- name: requests-cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pytest-plugin-list/
|
||||
key: plugins-http-cache-${{ github.run_id }} # Can use time based key as well
|
||||
restore-keys: plugins-http-cache-
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install packaging requests tabulate[widechars] tqdm
|
||||
pip install packaging requests tabulate[widechars] tqdm requests-cache platformdirs
|
||||
|
||||
|
||||
- name: Update Plugin List
|
||||
run: python scripts/update-plugin-list.py
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
repos:
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 23.3.0
|
||||
rev: 23.9.1
|
||||
hooks:
|
||||
- id: black
|
||||
args: [--safe, --quiet]
|
||||
- repo: https://github.com/asottile/blacken-docs
|
||||
rev: 1.14.0
|
||||
rev: 1.16.0
|
||||
hooks:
|
||||
- id: blacken-docs
|
||||
additional_dependencies: [black==23.1.0]
|
||||
additional_dependencies: [black==23.7.0]
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.4.0
|
||||
rev: v4.5.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
@@ -21,7 +21,7 @@ repos:
|
||||
exclude: _pytest/(debugging|hookspec).py
|
||||
language_version: python3
|
||||
- repo: https://github.com/PyCQA/autoflake
|
||||
rev: v2.1.1
|
||||
rev: v2.2.1
|
||||
hooks:
|
||||
- id: autoflake
|
||||
name: autoflake
|
||||
@@ -29,7 +29,7 @@ repos:
|
||||
language: python
|
||||
files: \.py$
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 6.0.0
|
||||
rev: 6.1.0
|
||||
hooks:
|
||||
- id: flake8
|
||||
language_version: python3
|
||||
@@ -37,17 +37,17 @@ repos:
|
||||
- flake8-typing-imports==1.12.0
|
||||
- flake8-docstrings==1.5.0
|
||||
- repo: https://github.com/asottile/reorder-python-imports
|
||||
rev: v3.10.0
|
||||
rev: v3.12.0
|
||||
hooks:
|
||||
- id: reorder-python-imports
|
||||
args: ['--application-directories=.:src', --py37-plus]
|
||||
args: ['--application-directories=.:src', --py38-plus]
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.7.0
|
||||
rev: v3.15.0
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py37-plus]
|
||||
args: [--py38-plus]
|
||||
- repo: https://github.com/asottile/setup-cfg-fmt
|
||||
rev: v2.3.0
|
||||
rev: v2.5.0
|
||||
hooks:
|
||||
- id: setup-cfg-fmt
|
||||
args: ["--max-py-version=3.12", "--include-version-classifiers"]
|
||||
@@ -56,7 +56,7 @@ repos:
|
||||
hooks:
|
||||
- id: python-use-type-annotations
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v1.3.0
|
||||
rev: v1.5.1
|
||||
hooks:
|
||||
- id: mypy
|
||||
files: ^(src/|testing/)
|
||||
|
||||
19
AUTHORS
19
AUTHORS
@@ -11,6 +11,7 @@ Adam Johnson
|
||||
Adam Stewart
|
||||
Adam Uhlir
|
||||
Ahn Ki-Wook
|
||||
Akhilesh Ramakrishnan
|
||||
Akiomi Kamakura
|
||||
Alan Velasco
|
||||
Alessio Izzo
|
||||
@@ -56,6 +57,7 @@ Ben Gartner
|
||||
Ben Webb
|
||||
Benjamin Peterson
|
||||
Bernard Pratz
|
||||
Bo Wu
|
||||
Bob Ippolito
|
||||
Brian Dorsey
|
||||
Brian Larsen
|
||||
@@ -129,6 +131,7 @@ Eric Hunsberger
|
||||
Eric Liu
|
||||
Eric Siegerman
|
||||
Erik Aronesty
|
||||
Erik Hasse
|
||||
Erik M. Bray
|
||||
Evan Kepner
|
||||
Evgeny Seliverstov
|
||||
@@ -140,6 +143,7 @@ Feng Ma
|
||||
Florian Bruhin
|
||||
Florian Dahlitz
|
||||
Floris Bruynooghe
|
||||
Fraser Stark
|
||||
Gabriel Landau
|
||||
Gabriel Reis
|
||||
Garvit Shubham
|
||||
@@ -166,6 +170,8 @@ Ian Bicking
|
||||
Ian Lesperance
|
||||
Ilya Konstantinov
|
||||
Ionuț Turturică
|
||||
Isaac Virshup
|
||||
Israel Fruchter
|
||||
Itxaso Aizpurua
|
||||
Iwan Briquemont
|
||||
Jaap Broekhuizen
|
||||
@@ -229,6 +235,7 @@ Maho
|
||||
Maik Figura
|
||||
Mandeep Bhutani
|
||||
Manuel Krebber
|
||||
Marc Mueller
|
||||
Marc Schlaich
|
||||
Marcelo Duarte Trevisani
|
||||
Marcin Bachry
|
||||
@@ -259,8 +266,10 @@ Michal Wajszczuk
|
||||
Michał Zięba
|
||||
Mickey Pashov
|
||||
Mihai Capotă
|
||||
Mihail Milushev
|
||||
Mike Hoyle (hoylemd)
|
||||
Mike Lundy
|
||||
Milan Lesnek
|
||||
Miro Hrončok
|
||||
Nathaniel Compton
|
||||
Nathaniel Waisbrot
|
||||
@@ -310,6 +319,7 @@ Raphael Pierzina
|
||||
Rafal Semik
|
||||
Raquel Alegre
|
||||
Ravi Chandra
|
||||
Reagan Lee
|
||||
Robert Holt
|
||||
Roberto Aldera
|
||||
Roberto Polli
|
||||
@@ -320,7 +330,9 @@ Ronny Pfannschmidt
|
||||
Ross Lawley
|
||||
Ruaridh Williamson
|
||||
Russel Winder
|
||||
Ryan Puddephatt
|
||||
Ryan Wooden
|
||||
Sadra Barikbin
|
||||
Saiprasad Kale
|
||||
Samuel Colvin
|
||||
Samuel Dion-Girardeau
|
||||
@@ -329,16 +341,20 @@ Samuele Pedroni
|
||||
Sanket Duthade
|
||||
Sankt Petersbug
|
||||
Saravanan Padmanaban
|
||||
Sean Malloy
|
||||
Segev Finer
|
||||
Serhii Mozghovyi
|
||||
Seth Junot
|
||||
Shantanu Jain
|
||||
Sharad Nair
|
||||
Shubham Adep
|
||||
Simon Blanchard
|
||||
Simon Gomizelj
|
||||
Simon Holesch
|
||||
Simon Kerr
|
||||
Skylar Downes
|
||||
Srinivas Reddy Thatiparthy
|
||||
Stefaan Lippens
|
||||
Stefan Farmbauer
|
||||
Stefan Scherfke
|
||||
Stefan Zimmermann
|
||||
@@ -352,6 +368,7 @@ Tadek Teleżyński
|
||||
Takafumi Arakaki
|
||||
Taneli Hukkinen
|
||||
Tanvi Mehta
|
||||
Tanya Agarwal
|
||||
Tarcisio Fischer
|
||||
Tareq Alayan
|
||||
Tatiana Ovary
|
||||
@@ -370,7 +387,9 @@ Tomer Keren
|
||||
Tony Narlock
|
||||
Tor Colvin
|
||||
Trevor Bekolay
|
||||
Tushar Sadhwani
|
||||
Tyler Goodlet
|
||||
Tyler Smart
|
||||
Tzu-ping Chung
|
||||
Vasily Kuznetsov
|
||||
Victor Maryama
|
||||
|
||||
@@ -50,7 +50,7 @@ Fix bugs
|
||||
--------
|
||||
|
||||
Look through the `GitHub issues for bugs <https://github.com/pytest-dev/pytest/labels/type:%20bug>`_.
|
||||
See also the `"status: easy" issues <https://github.com/pytest-dev/pytest/labels/status%3A%20easy>`_
|
||||
See also the `"good first issue" issues <https://github.com/pytest-dev/pytest/labels/good%20first%20issue>`_
|
||||
that are friendly to new contributors.
|
||||
|
||||
:ref:`Talk <contact>` to developers to find out how you can fix specific bugs. To indicate that you are going
|
||||
@@ -197,11 +197,12 @@ Short version
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
#. Fork the repository.
|
||||
#. Fetch tags from upstream if necessary (if you cloned only main `git fetch --tags https://github.com/pytest-dev/pytest`).
|
||||
#. Enable and install `pre-commit <https://pre-commit.com>`_ to ensure style-guides and code checks are followed.
|
||||
#. Follow **PEP-8** for naming and `black <https://github.com/psf/black>`_ for formatting.
|
||||
#. Tests are run using ``tox``::
|
||||
|
||||
tox -e linting,py37
|
||||
tox -e linting,py39
|
||||
|
||||
The test environments above are usually enough to cover most cases locally.
|
||||
|
||||
@@ -236,6 +237,7 @@ Here is a simple overview, with pytest-specific bits:
|
||||
|
||||
$ git clone git@github.com:YOUR_GITHUB_USERNAME/pytest.git
|
||||
$ cd pytest
|
||||
$ git fetch --tags https://github.com/pytest-dev/pytest
|
||||
# now, create your own branch off "main":
|
||||
|
||||
$ git checkout -b your-bugfix-branch-name main
|
||||
@@ -272,24 +274,24 @@ Here is a simple overview, with pytest-specific bits:
|
||||
|
||||
#. Run all the tests
|
||||
|
||||
You need to have Python 3.7 available in your system. Now
|
||||
You need to have Python 3.8 or later available in your system. Now
|
||||
running tests is as simple as issuing this command::
|
||||
|
||||
$ tox -e linting,py37
|
||||
$ tox -e linting,py39
|
||||
|
||||
This command will run tests via the "tox" tool against Python 3.7
|
||||
This command will run tests via the "tox" tool against Python 3.9
|
||||
and also perform "lint" coding-style checks.
|
||||
|
||||
#. You can now edit your local working copy and run the tests again as necessary. Please follow PEP-8 for naming.
|
||||
|
||||
You can pass different options to ``tox``. For example, to run tests on Python 3.7 and pass options to pytest
|
||||
You can pass different options to ``tox``. For example, to run tests on Python 3.9 and pass options to pytest
|
||||
(e.g. enter pdb on failure) to pytest you can do::
|
||||
|
||||
$ tox -e py37 -- --pdb
|
||||
$ tox -e py39 -- --pdb
|
||||
|
||||
Or to only run tests in a particular test module on Python 3.7::
|
||||
Or to only run tests in a particular test module on Python 3.9::
|
||||
|
||||
$ tox -e py37 -- testing/test_config.py
|
||||
$ tox -e py39 -- testing/test_config.py
|
||||
|
||||
|
||||
When committing, ``pre-commit`` will re-format the files if necessary.
|
||||
|
||||
@@ -100,7 +100,7 @@ Features
|
||||
- Can run `unittest <https://docs.pytest.org/en/stable/how-to/unittest.html>`_ (or trial),
|
||||
`nose <https://docs.pytest.org/en/stable/how-to/nose.html>`_ test suites out of the box
|
||||
|
||||
- Python 3.7+ or PyPy3
|
||||
- Python 3.8+ or PyPy3
|
||||
|
||||
- Rich plugin architecture, with over 850+ `external plugins <https://docs.pytest.org/en/latest/reference/plugin_list.html>`_ and thriving community
|
||||
|
||||
|
||||
@@ -133,14 +133,12 @@ Releasing
|
||||
|
||||
Both automatic and manual processes described above follow the same steps from this point onward.
|
||||
|
||||
#. After all tests pass and the PR has been approved, tag the release commit
|
||||
in the ``release-MAJOR.MINOR.PATCH`` branch and push it. This will publish to PyPI::
|
||||
#. After all tests pass and the PR has been approved, trigger the ``deploy`` job
|
||||
in https://github.com/pytest-dev/pytest/actions/workflows/deploy.yml, using the ``release-MAJOR.MINOR.PATCH`` branch
|
||||
as source.
|
||||
|
||||
git fetch upstream
|
||||
git tag MAJOR.MINOR.PATCH upstream/release-MAJOR.MINOR.PATCH
|
||||
git push upstream MAJOR.MINOR.PATCH
|
||||
|
||||
Wait for the deploy to complete, then make sure it is `available on PyPI <https://pypi.org/project/pytest>`_.
|
||||
This job will require approval from ``pytest-dev/core``, after which it will publish to PyPI
|
||||
and tag the repository.
|
||||
|
||||
#. Merge the PR. **Make sure it's not squash-merged**, so that the tagged commit ends up in the main branch.
|
||||
|
||||
|
||||
2
changelog/10441.feature.rst
Normal file
2
changelog/10441.feature.rst
Normal file
@@ -0,0 +1,2 @@
|
||||
Added :func:`ExceptionInfo.group_contains() <pytest.ExceptionInfo.group_contains>`, an assertion
|
||||
helper that tests if an `ExceptionGroup` contains a matching exception.
|
||||
2
changelog/10447.bugfix.rst
Normal file
2
changelog/10447.bugfix.rst
Normal file
@@ -0,0 +1,2 @@
|
||||
markers are now considered in the reverse mro order to ensure base class markers are considered first
|
||||
this resolves a regression.
|
||||
1
changelog/10465.deprecation.rst
Normal file
1
changelog/10465.deprecation.rst
Normal file
@@ -0,0 +1 @@
|
||||
Test functions returning a value other than None will now issue a :class:`pytest.PytestWarning` instead of :class:`pytest.PytestRemovedIn8Warning`, meaning this will stay a warning instead of becoming an error in the future.
|
||||
2
changelog/10617.feature.rst
Normal file
2
changelog/10617.feature.rst
Normal file
@@ -0,0 +1,2 @@
|
||||
Added more comprehensive set assertion rewrites for comparisons other than equality ``==``, with
|
||||
the following operations now providing better failure messages: ``!=``, ``<=``, ``>=``, ``<``, and ``>``.
|
||||
2
changelog/10701.bugfix.rst
Normal file
2
changelog/10701.bugfix.rst
Normal file
@@ -0,0 +1,2 @@
|
||||
:meth:`pytest.WarningsRecorder.pop` will return the most-closely-matched warning in the list,
|
||||
rather than the first warning which is an instance of the requested type.
|
||||
@@ -1 +0,0 @@
|
||||
Terminal Reporting: Fixed bug when running in ``--tb=line`` mode where ``pytest.fail(pytrace=False)`` tests report ``None``.
|
||||
@@ -1 +0,0 @@
|
||||
Update test log report annotation to named tuple and fixed inconsistency in docs for :hook:`pytest_report_teststatus` hook.
|
||||
@@ -1,2 +0,0 @@
|
||||
Added :func:`ExceptionInfo.from_exception() <pytest.ExceptionInfo.from_exception>`, a simpler way to create an :class:`~pytest.ExceptionInfo` from an exception.
|
||||
This can replace :func:`ExceptionInfo.from_exc_info() <pytest.ExceptionInfo.from_exc_info()>` for most uses.
|
||||
@@ -1,5 +0,0 @@
|
||||
When an exception traceback to be displayed is completely filtered out (by mechanisms such as ``__tracebackhide__``, internal frames, and similar), now only the exception string and the following message are shown:
|
||||
|
||||
"All traceback entries are hidden. Pass `--full-trace` to see hidden and internal frames.".
|
||||
|
||||
Previously, the last frame of the traceback was shown, even though it was hidden.
|
||||
@@ -1,3 +0,0 @@
|
||||
Improved verbose output (``-vv``) of ``skip`` and ``xfail`` reasons by performing text wrapping while leaving a clear margin for progress output.
|
||||
|
||||
Added :func:`TerminalReporter.wrap_write() <pytest.TerminalReporter.wrap_write>` as a helper for that.
|
||||
@@ -1 +0,0 @@
|
||||
Added handling of ``%f`` directive to print microseconds in log format options, such as ``log-date-format``.
|
||||
@@ -1 +0,0 @@
|
||||
Added underlying exception to cache provider path creation and write warning messages.
|
||||
1
changelog/11011.doc.rst
Normal file
1
changelog/11011.doc.rst
Normal file
@@ -0,0 +1 @@
|
||||
Added a warning about modifying the root logger during tests when using ``caplog``.
|
||||
@@ -1 +0,0 @@
|
||||
Added warning when :confval:`testpaths` is set, but paths are not found by glob. In this case, pytest will fall back to searching from the current directory.
|
||||
@@ -1 +0,0 @@
|
||||
Enhanced the CLI flag for ``-c`` to now include ``--config-file`` to make it clear that this flag applies to the usage of a custom config file.
|
||||
@@ -1,3 +0,0 @@
|
||||
When `--confcutdir` is not specified, and there is no config file present, the conftest cutoff directory (`--confcutdir`) is now set to the :ref:`rootdir`.
|
||||
Previously in such cases, `conftest.py` files would be probed all the way to the root directory of the filesystem.
|
||||
If you are badly affected by this change, consider adding an empty config file to your desired cutoff directory, or explicitly set `--confcutdir`.
|
||||
@@ -1 +0,0 @@
|
||||
Fixed the ``--last-failed`` whole-file skipping functionality ("skipped N files") for :ref:`non-python test files <non-python tests>`.
|
||||
@@ -1,7 +0,0 @@
|
||||
The :confval:`norecursedir` check is now performed in a :hook:`pytest_ignore_collect` implementation, so plugins can affect it.
|
||||
|
||||
If after updating to this version you see that your `norecursedir` setting is not being respected,
|
||||
it means that a conftest or a plugin you use has a bad `pytest_ignore_collect` implementation.
|
||||
Most likely, your hook returns `False` for paths it does not want to ignore,
|
||||
which ends the processing and doesn't allow other plugins, including pytest itself, to ignore the path.
|
||||
The fix is to return `None` instead of `False` for paths your hook doesn't want to ignore.
|
||||
1
changelog/11091.doc.rst
Normal file
1
changelog/11091.doc.rst
Normal file
@@ -0,0 +1 @@
|
||||
Updated documentation and tests to refer to hyphonated options: replaced ``--junitxml`` with ``--junit-xml`` and ``--collectonly`` with ``--collect-only``.
|
||||
@@ -1,3 +0,0 @@
|
||||
Fixed a regression in pytest 7.3.2 which caused to :confval:`testpaths` to be considered for loading initial conftests,
|
||||
even when it was not utilized (e.g. when explicit paths were given on the command line).
|
||||
Now the ``testpaths`` are only considered when they are in use.
|
||||
6
changelog/11122.improvement.rst
Normal file
6
changelog/11122.improvement.rst
Normal file
@@ -0,0 +1,6 @@
|
||||
``pluggy>=1.2.0`` is now required.
|
||||
|
||||
pytest now uses "new-style" hook wrappers internally, available since pluggy 1.2.0.
|
||||
See `pluggy's 1.2.0 changelog <https://pluggy.readthedocs.io/en/latest/changelog.html#pluggy-1-2-0-2023-06-21>`_ and the :ref:`updated docs <hookwrapper>` for details.
|
||||
|
||||
Plugins which want to use new-style wrappers can do so if they require this version of pytest or later.
|
||||
11
changelog/11137.breaking.rst
Normal file
11
changelog/11137.breaking.rst
Normal file
@@ -0,0 +1,11 @@
|
||||
:class:`pytest.Package` is no longer a :class:`pytest.Module` or :class:`pytest.File`.
|
||||
|
||||
The ``Package`` collector node designates a Python package, that is, a directory with an `__init__.py` file.
|
||||
Previously ``Package`` was a subtype of ``pytest.Module`` (which represents a single Python module),
|
||||
the module being the `__init__.py` file.
|
||||
This has been deemed a design mistake (see :issue:`11137` and :issue:`7777` for details).
|
||||
|
||||
The ``path`` property of ``Package`` nodes now points to the package directory instead of the ``__init__.py`` file.
|
||||
|
||||
Note that a ``Module`` node for ``__init__.py`` (which is not a ``Package``) may still exist,
|
||||
if it is picked up during collection (e.g. if you configured :confval:`python_files` to include ``__init__.py`` files).
|
||||
1
changelog/11146.bugfix.rst
Normal file
1
changelog/11146.bugfix.rst
Normal file
@@ -0,0 +1 @@
|
||||
- Prevent constants at the top of file from being detected as docstrings.
|
||||
1
changelog/11151.breaking.rst
Normal file
1
changelog/11151.breaking.rst
Normal file
@@ -0,0 +1 @@
|
||||
Dropped support for Python 3.7, which `reached end-of-life on 2023-06-27 <https://devguide.python.org/versions/>`__.
|
||||
2
changelog/11208.trivial.rst
Normal file
2
changelog/11208.trivial.rst
Normal file
@@ -0,0 +1,2 @@
|
||||
The (internal) ``FixtureDef.cached_result`` type has changed.
|
||||
Now the third item ``cached_result[2]``, when set, is an exception instance instead of an exception triplet.
|
||||
1
changelog/11216.improvement.rst
Normal file
1
changelog/11216.improvement.rst
Normal file
@@ -0,0 +1 @@
|
||||
If a test is skipped from inside an :ref:`xunit setup fixture <classic xunit>`, the test summary now shows the test location instead of the fixture location.
|
||||
5
changelog/11218.trivial.rst
Normal file
5
changelog/11218.trivial.rst
Normal file
@@ -0,0 +1,5 @@
|
||||
(This entry is meant to assist plugins which access private pytest internals to instantiate ``FixtureRequest`` objects.)
|
||||
|
||||
:class:`~pytest.FixtureRequest` is now an abstract class which can't be instantiated directly.
|
||||
A new concrete ``TopRequest`` subclass of ``FixtureRequest`` has been added for the ``request`` fixture in test functions,
|
||||
as counterpart to the existing ``SubRequest`` subclass for the ``request`` fixture in fixture functions.
|
||||
1
changelog/11227.improvement.rst
Normal file
1
changelog/11227.improvement.rst
Normal file
@@ -0,0 +1 @@
|
||||
Allow :func:`pytest.raises` ``match`` argument to match against `PEP-678 <https://peps.python.org/pep-0678/>` ``__notes__``.
|
||||
1
changelog/11239.bugfix.rst
Normal file
1
changelog/11239.bugfix.rst
Normal file
@@ -0,0 +1 @@
|
||||
Fixed ``:=`` in asserts impacting unrelated test cases.
|
||||
1
changelog/11255.bugfix.rst
Normal file
1
changelog/11255.bugfix.rst
Normal file
@@ -0,0 +1 @@
|
||||
Fixed crash on `parametrize(..., scope="package")` without a package present.
|
||||
2
changelog/11277.bugfix.rst
Normal file
2
changelog/11277.bugfix.rst
Normal file
@@ -0,0 +1,2 @@
|
||||
Fixed a bug that when there are multiple fixtures for an indirect parameter,
|
||||
the scope of the highest-scope fixture is picked for the parameter set, instead of that of the one with the narrowest scope.
|
||||
2
changelog/11314.improvement.rst
Normal file
2
changelog/11314.improvement.rst
Normal file
@@ -0,0 +1,2 @@
|
||||
Logging to a file using the ``--log-file`` option will use ``--log-level``, ``--log-format`` and ``--log-date-format`` as fallback
|
||||
if ``--log-file-level``, ``--log-file-format`` and ``--log-file-date-format`` are not provided respectively.
|
||||
3
changelog/11315.trivial.rst
Normal file
3
changelog/11315.trivial.rst
Normal file
@@ -0,0 +1,3 @@
|
||||
The :fixture:`pytester` fixture now uses the :fixture:`monkeypatch` fixture to manage the current working directory.
|
||||
If you use ``pytester`` in combination with :func:`monkeypatch.undo() <pytest.MonkeyPatch.undo>`, the CWD might get restored.
|
||||
Use :func:`monkeypatch.context() <pytest.MonkeyPatch.context>` instead.
|
||||
2
changelog/11333.trivial.rst
Normal file
2
changelog/11333.trivial.rst
Normal file
@@ -0,0 +1,2 @@
|
||||
Corrected the spelling of ``Config.ArgsSource.INVOCATION_DIR``.
|
||||
The previous spelling ``INCOVATION_DIR`` remains as an alias.
|
||||
1
changelog/11353.trivial.rst
Normal file
1
changelog/11353.trivial.rst
Normal file
@@ -0,0 +1 @@
|
||||
pluggy>=1.3.0 is now required. This adds typing to :class:`~pytest.PytestPluginManager`.
|
||||
1
changelog/11439.bugfix.rst
Normal file
1
changelog/11439.bugfix.rst
Normal file
@@ -0,0 +1 @@
|
||||
Handle an edge case where :data:`sys.stderr` might already be closed when :ref:`faulthandler` is tearing down.
|
||||
1
changelog/11447.improvement.rst
Normal file
1
changelog/11447.improvement.rst
Normal file
@@ -0,0 +1 @@
|
||||
:func:`pytest.deprecated_call` now also considers warnings of type :class:`FutureWarning`.
|
||||
4
changelog/11456.bugfix.rst
Normal file
4
changelog/11456.bugfix.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
Parametrized tests now *really do* ensure that the ids given to each input are unique - for
|
||||
example, ``a, a, a0`` now results in ``a1, a2, a0`` instead of the previous (buggy) ``a0, a1, a0``.
|
||||
This necessarily means changing nodeids where these were previously colliding, and for
|
||||
readability adds an underscore when non-unique ids end in a number.
|
||||
@@ -1 +0,0 @@
|
||||
Fixed traceback entries hidden with ``__tracebackhide__ = True`` still being shown for chained exceptions (parts after "... the above exception ..." message).
|
||||
3
changelog/3664.deprecation.rst
Normal file
3
changelog/3664.deprecation.rst
Normal file
@@ -0,0 +1,3 @@
|
||||
Applying a mark to a fixture function now issues a warning: marks in fixtures never had any effect, but it is a common user error to apply a mark to a fixture (for example ``usefixtures``) and expect it to work.
|
||||
|
||||
This will become an error in the future.
|
||||
22
changelog/7363.breaking.rst
Normal file
22
changelog/7363.breaking.rst
Normal file
@@ -0,0 +1,22 @@
|
||||
**PytestRemovedIn8Warning deprecation warnings are now errors by default.**
|
||||
|
||||
Following our plan to remove deprecated features with as little disruption as
|
||||
possible, all warnings of type ``PytestRemovedIn8Warning`` now generate errors
|
||||
instead of warning messages by default.
|
||||
|
||||
**The affected features will be effectively removed in pytest 8.1**, so please consult the
|
||||
:ref:`deprecations` section in the docs for directions on how to update existing code.
|
||||
|
||||
In the pytest ``8.0.X`` series, it is possible to change the errors back into warnings as a
|
||||
stopgap measure by adding this to your ``pytest.ini`` file:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[pytest]
|
||||
filterwarnings =
|
||||
ignore::pytest.PytestRemovedIn8Warning
|
||||
|
||||
But this will stop working when pytest ``8.1`` is released.
|
||||
|
||||
**If you have concerns** about the removal of a specific feature, please add a
|
||||
comment to :issue:`7363`.
|
||||
1
changelog/7469.feature.rst
Normal file
1
changelog/7469.feature.rst
Normal file
@@ -0,0 +1 @@
|
||||
:class:`~pytest.FixtureDef` is now exported as ``pytest.FixtureDef`` for typing purposes.
|
||||
@@ -1 +0,0 @@
|
||||
Fix writing non-encodable text to log file when using ``--debug``.
|
||||
1
changelog/7966.bugfix.rst
Normal file
1
changelog/7966.bugfix.rst
Normal file
@@ -0,0 +1 @@
|
||||
Removes unhelpful error message from assertion rewrite mechanism when exceptions raised in __iter__ methods, and instead treats them as un-iterable.
|
||||
@@ -1,3 +0,0 @@
|
||||
:func:`_pytest.logging.LogCaptureFixture.set_level` and :func:`_pytest.logging.LogCaptureFixture.at_level`
|
||||
will temporarily enable the requested ``level`` if ``level`` was disabled globally via
|
||||
``logging.disable(LEVEL)``.
|
||||
5
changelog/8976.breaking.rst
Normal file
5
changelog/8976.breaking.rst
Normal file
@@ -0,0 +1,5 @@
|
||||
Running `pytest pkg/__init__.py` now collects the `pkg/__init__.py` file (module) only.
|
||||
Previously, it collected the entire `pkg` package, including other test files in the directory, but excluding tests in the `__init__.py` file itself
|
||||
(unless :confval:`python_files` was changed to allow `__init__.py` file).
|
||||
|
||||
To collect the entire package, specify just the directory: `pytest pkg`.
|
||||
1
changelog/9036.bugfix.rst
Normal file
1
changelog/9036.bugfix.rst
Normal file
@@ -0,0 +1 @@
|
||||
``pytest.warns`` and similar functions now capture warnings when an exception is raised inside a ``with`` block.
|
||||
@@ -1 +0,0 @@
|
||||
Improve Documentation for `caplog.set_level`.
|
||||
7
changelog/9288.breaking.rst
Normal file
7
changelog/9288.breaking.rst
Normal file
@@ -0,0 +1,7 @@
|
||||
:func:`pytest.warns <warns>` now re-emits unmatched warnings when the context
|
||||
closes -- previously it would consume all warnings, hiding those that were not
|
||||
matched by the function.
|
||||
|
||||
While this is a new feature, we decided to announce this as a breaking change
|
||||
because many test suites are configured to error-out on warnings, and will
|
||||
therefore fail on the newly-re-emitted warnings.
|
||||
@@ -14,7 +14,7 @@ Each file should be named like ``<ISSUE>.<TYPE>.rst``, where
|
||||
``<ISSUE>`` is an issue number, and ``<TYPE>`` is one of:
|
||||
|
||||
* ``feature``: new user facing features, like new command-line options and new behavior.
|
||||
* ``improvement``: improvement of existing functionality, usually without requiring user intervention (for example, new fields being written in ``--junitxml``, improved colors in terminal, etc).
|
||||
* ``improvement``: improvement of existing functionality, usually without requiring user intervention (for example, new fields being written in ``--junit-xml``, improved colors in terminal, etc).
|
||||
* ``bugfix``: fixes a bug.
|
||||
* ``doc``: documentation improvement, like rewording an entire session or adding missing docs.
|
||||
* ``deprecation``: feature deprecation.
|
||||
|
||||
@@ -6,6 +6,9 @@ Release announcements
|
||||
:maxdepth: 2
|
||||
|
||||
|
||||
release-7.4.2
|
||||
release-7.4.1
|
||||
release-7.4.0
|
||||
release-7.3.2
|
||||
release-7.3.1
|
||||
release-7.3.0
|
||||
|
||||
49
doc/en/announce/release-7.4.0.rst
Normal file
49
doc/en/announce/release-7.4.0.rst
Normal file
@@ -0,0 +1,49 @@
|
||||
pytest-7.4.0
|
||||
=======================================
|
||||
|
||||
The pytest team is proud to announce the 7.4.0 release!
|
||||
|
||||
This release contains new features, improvements, and bug fixes,
|
||||
the full list of changes is available in the changelog:
|
||||
|
||||
https://docs.pytest.org/en/stable/changelog.html
|
||||
|
||||
For complete documentation, please visit:
|
||||
|
||||
https://docs.pytest.org/en/stable/
|
||||
|
||||
As usual, you can upgrade from PyPI via:
|
||||
|
||||
pip install -U pytest
|
||||
|
||||
Thanks to all of the contributors to this release:
|
||||
|
||||
* Adam J. Stewart
|
||||
* Alessio Izzo
|
||||
* Alex
|
||||
* Alex Lambson
|
||||
* Brian Larsen
|
||||
* Bruno Oliveira
|
||||
* Bryan Ricker
|
||||
* Chris Mahoney
|
||||
* Facundo Batista
|
||||
* Florian Bruhin
|
||||
* Jarrett Keifer
|
||||
* Kenny Y
|
||||
* Miro Hrončok
|
||||
* Ran Benita
|
||||
* Roberto Aldera
|
||||
* Ronny Pfannschmidt
|
||||
* Sergey Kim
|
||||
* Stefanie Molin
|
||||
* Vijay Arora
|
||||
* Ville Skyttä
|
||||
* Zac Hatfield-Dodds
|
||||
* bzoracler
|
||||
* leeyueh
|
||||
* nondescryptid
|
||||
* theirix
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
20
doc/en/announce/release-7.4.1.rst
Normal file
20
doc/en/announce/release-7.4.1.rst
Normal file
@@ -0,0 +1,20 @@
|
||||
pytest-7.4.1
|
||||
=======================================
|
||||
|
||||
pytest 7.4.1 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
|
||||
|
||||
Thanks to all of the contributors to this release:
|
||||
|
||||
* Bruno Oliveira
|
||||
* Florian Bruhin
|
||||
* Ran Benita
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
18
doc/en/announce/release-7.4.2.rst
Normal file
18
doc/en/announce/release-7.4.2.rst
Normal file
@@ -0,0 +1,18 @@
|
||||
pytest-7.4.2
|
||||
=======================================
|
||||
|
||||
pytest 7.4.2 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
|
||||
|
||||
Thanks to all of the contributors to this release:
|
||||
|
||||
* Bruno Oliveira
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
@@ -87,6 +87,7 @@ Released pytest versions support all Python versions that are actively maintaine
|
||||
============== ===================
|
||||
pytest version min. Python version
|
||||
============== ===================
|
||||
8.0+ 3.8+
|
||||
7.1+ 3.7+
|
||||
6.2 - 7.0 3.6+
|
||||
5.0 - 6.1 3.5+
|
||||
|
||||
@@ -22,7 +22,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||
cachedir: .pytest_cache
|
||||
rootdir: /home/sweet/project
|
||||
collected 0 items
|
||||
cache -- .../_pytest/cacheprovider.py:510
|
||||
cache -- .../_pytest/cacheprovider.py:532
|
||||
Return a cache object that can persist state between testing sessions.
|
||||
|
||||
cache.get(key, default)
|
||||
@@ -105,7 +105,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == "hello\n"
|
||||
|
||||
doctest_namespace [session scope] -- .../_pytest/doctest.py:737
|
||||
doctest_namespace [session scope] -- .../_pytest/doctest.py:757
|
||||
Fixture that returns a :py:class:`dict` that will be injected into the
|
||||
namespace of doctests.
|
||||
|
||||
@@ -119,7 +119,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||
|
||||
For more details: :ref:`doctest_namespace`.
|
||||
|
||||
pytestconfig [session scope] -- .../_pytest/fixtures.py:1360
|
||||
pytestconfig [session scope] -- .../_pytest/fixtures.py:1353
|
||||
Session-scoped fixture that returns the session's :class:`pytest.Config`
|
||||
object.
|
||||
|
||||
@@ -196,7 +196,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||
|
||||
.. _legacy_path: https://py.readthedocs.io/en/latest/path.html
|
||||
|
||||
caplog -- .../_pytest/logging.py:498
|
||||
caplog -- .../_pytest/logging.py:570
|
||||
Access and control log capturing.
|
||||
|
||||
Captured logs are available through the following properties/methods::
|
||||
|
||||
@@ -28,6 +28,140 @@ with advance notice in the **Deprecations** section of releases.
|
||||
|
||||
.. towncrier release notes start
|
||||
|
||||
pytest 7.4.2 (2023-09-07)
|
||||
=========================
|
||||
|
||||
Bug Fixes
|
||||
---------
|
||||
|
||||
- `#11237 <https://github.com/pytest-dev/pytest/issues/11237>`_: Fix doctest collection of `functools.cached_property` objects.
|
||||
|
||||
|
||||
- `#11306 <https://github.com/pytest-dev/pytest/issues/11306>`_: Fixed bug using ``--importmode=importlib`` which would cause package ``__init__.py`` files to be imported more than once in some cases.
|
||||
|
||||
|
||||
- `#11367 <https://github.com/pytest-dev/pytest/issues/11367>`_: Fixed bug where `user_properties` where not being saved in the JUnit XML file if a fixture failed during teardown.
|
||||
|
||||
|
||||
- `#11394 <https://github.com/pytest-dev/pytest/issues/11394>`_: Fixed crash when parsing long command line arguments that might be interpreted as files.
|
||||
|
||||
|
||||
|
||||
Improved Documentation
|
||||
----------------------
|
||||
|
||||
- `#11391 <https://github.com/pytest-dev/pytest/issues/11391>`_: Improved disclaimer on pytest plugin reference page to better indicate this is an automated, non-curated listing.
|
||||
|
||||
|
||||
pytest 7.4.1 (2023-09-02)
|
||||
=========================
|
||||
|
||||
Bug Fixes
|
||||
---------
|
||||
|
||||
- `#10337 <https://github.com/pytest-dev/pytest/issues/10337>`_: Fixed bug where fake intermediate modules generated by ``--import-mode=importlib`` would not include the
|
||||
child modules as attributes of the parent modules.
|
||||
|
||||
|
||||
- `#10702 <https://github.com/pytest-dev/pytest/issues/10702>`_: Fixed error assertion handling in :func:`pytest.approx` when ``None`` is an expected or received value when comparing dictionaries.
|
||||
|
||||
|
||||
- `#10811 <https://github.com/pytest-dev/pytest/issues/10811>`_: Fixed issue when using ``--import-mode=importlib`` together with ``--doctest-modules`` that caused modules
|
||||
to be imported more than once, causing problems with modules that have import side effects.
|
||||
|
||||
|
||||
pytest 7.4.0 (2023-06-23)
|
||||
=========================
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
- `#10901 <https://github.com/pytest-dev/pytest/issues/10901>`_: Added :func:`ExceptionInfo.from_exception() <pytest.ExceptionInfo.from_exception>`, a simpler way to create an :class:`~pytest.ExceptionInfo` from an exception.
|
||||
This can replace :func:`ExceptionInfo.from_exc_info() <pytest.ExceptionInfo.from_exc_info()>` for most uses.
|
||||
|
||||
|
||||
|
||||
Improvements
|
||||
------------
|
||||
|
||||
- `#10872 <https://github.com/pytest-dev/pytest/issues/10872>`_: Update test log report annotation to named tuple and fixed inconsistency in docs for :hook:`pytest_report_teststatus` hook.
|
||||
|
||||
|
||||
- `#10907 <https://github.com/pytest-dev/pytest/issues/10907>`_: When an exception traceback to be displayed is completely filtered out (by mechanisms such as ``__tracebackhide__``, internal frames, and similar), now only the exception string and the following message are shown:
|
||||
|
||||
"All traceback entries are hidden. Pass `--full-trace` to see hidden and internal frames.".
|
||||
|
||||
Previously, the last frame of the traceback was shown, even though it was hidden.
|
||||
|
||||
|
||||
- `#10940 <https://github.com/pytest-dev/pytest/issues/10940>`_: Improved verbose output (``-vv``) of ``skip`` and ``xfail`` reasons by performing text wrapping while leaving a clear margin for progress output.
|
||||
|
||||
Added ``TerminalReporter.wrap_write()`` as a helper for that.
|
||||
|
||||
|
||||
- `#10991 <https://github.com/pytest-dev/pytest/issues/10991>`_: Added handling of ``%f`` directive to print microseconds in log format options, such as ``log-date-format``.
|
||||
|
||||
|
||||
- `#11005 <https://github.com/pytest-dev/pytest/issues/11005>`_: Added the underlying exception to the cache provider's path creation and write warning messages.
|
||||
|
||||
|
||||
- `#11013 <https://github.com/pytest-dev/pytest/issues/11013>`_: Added warning when :confval:`testpaths` is set, but paths are not found by glob. In this case, pytest will fall back to searching from the current directory.
|
||||
|
||||
|
||||
- `#11043 <https://github.com/pytest-dev/pytest/issues/11043>`_: When `--confcutdir` is not specified, and there is no config file present, the conftest cutoff directory (`--confcutdir`) is now set to the :ref:`rootdir <rootdir>`.
|
||||
Previously in such cases, `conftest.py` files would be probed all the way to the root directory of the filesystem.
|
||||
If you are badly affected by this change, consider adding an empty config file to your desired cutoff directory, or explicitly set `--confcutdir`.
|
||||
|
||||
|
||||
- `#11081 <https://github.com/pytest-dev/pytest/issues/11081>`_: The :confval:`norecursedirs` check is now performed in a :hook:`pytest_ignore_collect` implementation, so plugins can affect it.
|
||||
|
||||
If after updating to this version you see that your `norecursedirs` setting is not being respected,
|
||||
it means that a conftest or a plugin you use has a bad `pytest_ignore_collect` implementation.
|
||||
Most likely, your hook returns `False` for paths it does not want to ignore,
|
||||
which ends the processing and doesn't allow other plugins, including pytest itself, to ignore the path.
|
||||
The fix is to return `None` instead of `False` for paths your hook doesn't want to ignore.
|
||||
|
||||
|
||||
- `#8711 <https://github.com/pytest-dev/pytest/issues/8711>`_: :func:`caplog.set_level() <pytest.LogCaptureFixture.set_level>` and :func:`caplog.at_level() <pytest.LogCaptureFixture.at_level>`
|
||||
will temporarily enable the requested ``level`` if ``level`` was disabled globally via
|
||||
``logging.disable(LEVEL)``.
|
||||
|
||||
|
||||
|
||||
Bug Fixes
|
||||
---------
|
||||
|
||||
- `#10831 <https://github.com/pytest-dev/pytest/issues/10831>`_: Terminal Reporting: Fixed bug when running in ``--tb=line`` mode where ``pytest.fail(pytrace=False)`` tests report ``None``.
|
||||
|
||||
|
||||
- `#11068 <https://github.com/pytest-dev/pytest/issues/11068>`_: Fixed the ``--last-failed`` whole-file skipping functionality ("skipped N files") for :ref:`non-python test files <non-python tests>`.
|
||||
|
||||
|
||||
- `#11104 <https://github.com/pytest-dev/pytest/issues/11104>`_: Fixed a regression in pytest 7.3.2 which caused to :confval:`testpaths` to be considered for loading initial conftests,
|
||||
even when it was not utilized (e.g. when explicit paths were given on the command line).
|
||||
Now the ``testpaths`` are only considered when they are in use.
|
||||
|
||||
|
||||
- `#1904 <https://github.com/pytest-dev/pytest/issues/1904>`_: Fixed traceback entries hidden with ``__tracebackhide__ = True`` still being shown for chained exceptions (parts after "... the above exception ..." message).
|
||||
|
||||
|
||||
- `#7781 <https://github.com/pytest-dev/pytest/issues/7781>`_: Fix writing non-encodable text to log file when using ``--debug``.
|
||||
|
||||
|
||||
|
||||
Improved Documentation
|
||||
----------------------
|
||||
|
||||
- `#9146 <https://github.com/pytest-dev/pytest/issues/9146>`_: Improved documentation for :func:`caplog.set_level() <pytest.LogCaptureFixture.set_level>`.
|
||||
|
||||
|
||||
|
||||
Trivial/Internal Changes
|
||||
------------------------
|
||||
|
||||
- `#11031 <https://github.com/pytest-dev/pytest/issues/11031>`_: Enhanced the CLI flag for ``-c`` to now include ``--config-file`` to make it clear that this flag applies to the usage of a custom config file.
|
||||
|
||||
|
||||
pytest 7.3.2 (2023-06-10)
|
||||
=========================
|
||||
|
||||
|
||||
@@ -15,12 +15,10 @@
|
||||
#
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
# The short X.Y version.
|
||||
import ast
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
from textwrap import dedent
|
||||
from typing import List
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from _pytest import __version__ as version
|
||||
@@ -451,25 +449,6 @@ def setup(app: "sphinx.application.Sphinx") -> None:
|
||||
|
||||
configure_logging(app)
|
||||
|
||||
# Make Sphinx mark classes with "final" when decorated with @final.
|
||||
# We need this because we import final from pytest._compat, not from
|
||||
# typing (for Python < 3.8 compat), so Sphinx doesn't detect it.
|
||||
# To keep things simple we accept any `@final` decorator.
|
||||
# Ref: https://github.com/pytest-dev/pytest/pull/7780
|
||||
import sphinx.pycode.ast
|
||||
import sphinx.pycode.parser
|
||||
|
||||
original_is_final = sphinx.pycode.parser.VariableCommentPicker.is_final
|
||||
|
||||
def patched_is_final(self, decorators: List[ast.expr]) -> bool:
|
||||
if original_is_final(self, decorators):
|
||||
return True
|
||||
return any(
|
||||
sphinx.pycode.ast.unparse(decorator) == "final" for decorator in decorators
|
||||
)
|
||||
|
||||
sphinx.pycode.parser.VariableCommentPicker.is_final = patched_is_final
|
||||
|
||||
# legacypath.py monkey-patches pytest.Testdir in. Import the file so
|
||||
# that autodoc can discover references to it.
|
||||
import _pytest.legacypath # noqa: F401
|
||||
|
||||
@@ -380,6 +380,25 @@ conflicts (such as :class:`pytest.File` now taking ``path`` instead of
|
||||
``fspath``, as :ref:`outlined above <node-ctor-fspath-deprecation>`), a
|
||||
deprecation warning is now raised.
|
||||
|
||||
Applying a mark to a fixture function
|
||||
-------------------------------------
|
||||
|
||||
.. deprecated:: 7.4
|
||||
|
||||
Applying a mark to a fixture function never had any effect, but it is a common user error.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.mark.usefixtures("clean_database")
|
||||
@pytest.fixture
|
||||
def user() -> User:
|
||||
...
|
||||
|
||||
Users expected in this case that the ``usefixtures`` mark would have its intended effect of using the ``clean_database`` fixture when ``user`` was invoked, when in fact it has no effect at all.
|
||||
|
||||
Now pytest will issue a warning when it encounters this problem, and will raise an error in the future versions.
|
||||
|
||||
|
||||
Backward compatibilities in ``Parser.addoption``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -467,12 +486,42 @@ The ``yield_fixture`` function/decorator
|
||||
It has been so for a very long time, so can be search/replaced safely.
|
||||
|
||||
|
||||
Removed Features
|
||||
----------------
|
||||
Removed Features and Breaking Changes
|
||||
-------------------------------------
|
||||
|
||||
As stated in our :ref:`backwards-compatibility` policy, deprecated features are removed only in major releases after
|
||||
an appropriate period of deprecation has passed.
|
||||
|
||||
Some breaking changes which could not be deprecated are also listed.
|
||||
|
||||
|
||||
:class:`pytest.Package` is no longer a :class:`pytest.Module` or :class:`pytest.File`
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionchanged:: 8.0
|
||||
|
||||
The ``Package`` collector node designates a Python package, that is, a directory with an `__init__.py` file.
|
||||
Previously ``Package`` was a subtype of ``pytest.Module`` (which represents a single Python module),
|
||||
the module being the `__init__.py` file.
|
||||
This has been deemed a design mistake (see :issue:`11137` and :issue:`7777` for details).
|
||||
|
||||
The ``path`` property of ``Package`` nodes now points to the package directory instead of the ``__init__.py`` file.
|
||||
|
||||
Note that a ``Module`` node for ``__init__.py`` (which is not a ``Package``) may still exist,
|
||||
if it is picked up during collection (e.g. if you configured :confval:`python_files` to include ``__init__.py`` files).
|
||||
|
||||
|
||||
Collecting ``__init__.py`` files no longer collects package
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionremoved:: 8.0
|
||||
|
||||
Running `pytest pkg/__init__.py` now collects the `pkg/__init__.py` file (module) only.
|
||||
Previously, it collected the entire `pkg` package, including other test files in the directory, but excluding tests in the `__init__.py` file itself
|
||||
(unless :confval:`python_files` was changed to allow `__init__.py` file).
|
||||
|
||||
To collect the entire package, specify just the directory: `pytest pkg`.
|
||||
|
||||
|
||||
The ``pytest.collect`` module
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -596,7 +645,7 @@ By using ``legacy`` you will keep using the legacy/xunit1 format when upgrading
|
||||
pytest 6.0, where the default format will be ``xunit2``.
|
||||
|
||||
In order to let users know about the transition, pytest will issue a warning in case
|
||||
the ``--junitxml`` option is given in the command line but ``junit_family`` is not explicitly
|
||||
the ``--junit-xml`` option is given in the command line but ``junit_family`` is not explicitly
|
||||
configured in ``pytest.ini``.
|
||||
|
||||
Services known to support the ``xunit2`` format:
|
||||
|
||||
@@ -136,7 +136,7 @@ Or select multiple nodes:
|
||||
|
||||
Node IDs for failing tests are displayed in the test summary info
|
||||
when running pytest with the ``-rf`` option. You can also
|
||||
construct Node IDs from the output of ``pytest --collectonly``.
|
||||
construct Node IDs from the output of ``pytest --collect-only``.
|
||||
|
||||
Using ``-k expr`` to select tests based on their name
|
||||
-------------------------------------------------------
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
"""
|
||||
module containing a parametrized tests testing cross-python
|
||||
serialization via the pickle module.
|
||||
"""
|
||||
"""Module containing a parametrized tests testing cross-python serialization
|
||||
via the pickle module."""
|
||||
import shutil
|
||||
import subprocess
|
||||
import textwrap
|
||||
|
||||
import pytest
|
||||
|
||||
pythonlist = ["python3.5", "python3.6", "python3.7"]
|
||||
|
||||
pythonlist = ["python3.9", "python3.10", "python3.11"]
|
||||
|
||||
|
||||
@pytest.fixture(params=pythonlist)
|
||||
@@ -43,7 +42,7 @@ class Python:
|
||||
)
|
||||
)
|
||||
)
|
||||
subprocess.check_call((self.pythonpath, str(dumpfile)))
|
||||
subprocess.run((self.pythonpath, str(dumpfile)), check=True)
|
||||
|
||||
def load_and_is_true(self, expression):
|
||||
loadfile = self.picklefile.with_name("load.py")
|
||||
@@ -63,7 +62,7 @@ class Python:
|
||||
)
|
||||
)
|
||||
print(loadfile)
|
||||
subprocess.check_call((self.pythonpath, str(loadfile)))
|
||||
subprocess.run((self.pythonpath, str(loadfile)), check=True)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("obj", [42, {}, {1: 3}])
|
||||
|
||||
@@ -12,7 +12,7 @@ class YamlFile(pytest.File):
|
||||
# We need a yaml parser, e.g. PyYAML.
|
||||
import yaml
|
||||
|
||||
raw = yaml.safe_load(self.path.open())
|
||||
raw = yaml.safe_load(self.path.open(encoding="utf-8"))
|
||||
for name, spec in sorted(raw.items()):
|
||||
yield YamlItem.from_parent(self, name=name, spec=spec)
|
||||
|
||||
|
||||
@@ -483,8 +483,8 @@ argument sets to use for each test function. Let's run it:
|
||||
FAILED test_parametrize.py::TestClass::test_equals[1-2] - assert 1 == 2
|
||||
1 failed, 2 passed in 0.12s
|
||||
|
||||
Indirect parametrization with multiple fixtures
|
||||
--------------------------------------------------------------
|
||||
Parametrization with multiple fixtures
|
||||
--------------------------------------
|
||||
|
||||
Here is a stripped down real-life example of using parametrized
|
||||
testing for testing serialization of objects between different python
|
||||
@@ -509,8 +509,8 @@ Running it results in some skips if we don't have all the python interpreters in
|
||||
SKIPPED [9] multipython.py:69: 'python3.7' not found
|
||||
27 skipped in 0.12s
|
||||
|
||||
Indirect parametrization of optional implementations/imports
|
||||
--------------------------------------------------------------------
|
||||
Parametrization of optional implementations/imports
|
||||
---------------------------------------------------
|
||||
|
||||
If you want to compare the outcomes of several implementations of a given
|
||||
API, you can write test functions that receive the already imported implementations
|
||||
@@ -657,13 +657,16 @@ Use :func:`pytest.raises` with the
|
||||
:ref:`pytest.mark.parametrize ref` decorator to write parametrized tests
|
||||
in which some tests raise exceptions and others do not.
|
||||
|
||||
It may be helpful to use ``nullcontext`` as a complement to ``raises``.
|
||||
``contextlib.nullcontext`` can be used to test cases that are not expected to
|
||||
raise exceptions but that should result in some value. The value is given as the
|
||||
``enter_result`` parameter, which will be available as the ``with`` statement’s
|
||||
target (``e`` in the example below).
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from contextlib import nullcontext as does_not_raise
|
||||
from contextlib import nullcontext
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -671,16 +674,17 @@ For example:
|
||||
@pytest.mark.parametrize(
|
||||
"example_input,expectation",
|
||||
[
|
||||
(3, does_not_raise()),
|
||||
(2, does_not_raise()),
|
||||
(1, does_not_raise()),
|
||||
(3, nullcontext(2)),
|
||||
(2, nullcontext(3)),
|
||||
(1, nullcontext(6)),
|
||||
(0, pytest.raises(ZeroDivisionError)),
|
||||
],
|
||||
)
|
||||
def test_division(example_input, expectation):
|
||||
"""Test how much I know division."""
|
||||
with expectation:
|
||||
assert (6 / example_input) is not None
|
||||
with expectation as e:
|
||||
assert (6 / example_input) == e
|
||||
|
||||
In the example above, the first three test cases should run unexceptionally,
|
||||
while the fourth should raise ``ZeroDivisionError``.
|
||||
In the example above, the first three test cases should run without any
|
||||
exceptions, while the fourth should raise a``ZeroDivisionError`` exception,
|
||||
which is expected by pytest.
|
||||
|
||||
@@ -808,16 +808,15 @@ case we just write some information out to a ``failures`` file:
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
|
||||
@pytest.hookimpl(wrapper=True, tryfirst=True)
|
||||
def pytest_runtest_makereport(item, call):
|
||||
# execute all other hooks to obtain the report object
|
||||
outcome = yield
|
||||
rep = outcome.get_result()
|
||||
rep = yield
|
||||
|
||||
# we only look at actual failing test calls, not setup/teardown
|
||||
if rep.when == "call" and rep.failed:
|
||||
mode = "a" if os.path.exists("failures") else "w"
|
||||
with open("failures", mode) as f:
|
||||
with open("failures", mode, encoding="utf-8") as f:
|
||||
# let's also access a fixture for the fun of it
|
||||
if "tmp_path" in item.fixturenames:
|
||||
extra = " ({})".format(item.funcargs["tmp_path"])
|
||||
@@ -826,6 +825,8 @@ case we just write some information out to a ``failures`` file:
|
||||
|
||||
f.write(rep.nodeid + extra + "\n")
|
||||
|
||||
return rep
|
||||
|
||||
|
||||
if you then have failing tests:
|
||||
|
||||
@@ -899,16 +900,17 @@ here is a little example implemented via a local plugin:
|
||||
phase_report_key = StashKey[Dict[str, CollectReport]]()
|
||||
|
||||
|
||||
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
|
||||
@pytest.hookimpl(wrapper=True, tryfirst=True)
|
||||
def pytest_runtest_makereport(item, call):
|
||||
# execute all other hooks to obtain the report object
|
||||
outcome = yield
|
||||
rep = outcome.get_result()
|
||||
rep = yield
|
||||
|
||||
# store test results for each phase of a call, which can
|
||||
# be "setup", "call", "teardown"
|
||||
item.stash.setdefault(phase_report_key, {})[rep.when] = rep
|
||||
|
||||
return rep
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def something(request):
|
||||
@@ -1088,4 +1090,4 @@ application with standard ``pytest`` command-line options:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
./app_main --pytest --verbose --tb=long --junitxml=results.xml test-suite/
|
||||
./app_main --pytest --verbose --tb=long --junit=xml=results.xml test-suite/
|
||||
|
||||
@@ -34,7 +34,7 @@ a function/method call.
|
||||
|
||||
**Assert** is where we look at that resulting state and check if it looks how
|
||||
we'd expect after the dust has settled. It's where we gather evidence to say the
|
||||
behavior does or does not aligns with what we expect. The ``assert`` in our test
|
||||
behavior does or does not align with what we expect. The ``assert`` in our test
|
||||
is where we take that measurement/observation and apply our judgement to it. If
|
||||
something should be green, we'd say ``assert thing == "green"``.
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ Get Started
|
||||
Install ``pytest``
|
||||
----------------------------------------
|
||||
|
||||
``pytest`` requires: Python 3.7+ or PyPy3.
|
||||
``pytest`` requires: Python 3.8+ or PyPy3.
|
||||
|
||||
1. Run the following command in your command line:
|
||||
|
||||
@@ -22,7 +22,7 @@ Install ``pytest``
|
||||
.. code-block:: bash
|
||||
|
||||
$ pytest --version
|
||||
pytest 7.3.2
|
||||
pytest 7.4.2
|
||||
|
||||
.. _`simpletest`:
|
||||
|
||||
@@ -97,6 +97,30 @@ Use the :ref:`raises <assertraises>` helper to assert that some code raises an e
|
||||
with pytest.raises(SystemExit):
|
||||
f()
|
||||
|
||||
You can also use the context provided by :ref:`raises <assertraises>` to
|
||||
assert that an expected exception is part of a raised ``ExceptionGroup``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_exceptiongroup.py
|
||||
import pytest
|
||||
|
||||
|
||||
def f():
|
||||
raise ExceptionGroup(
|
||||
"Group message",
|
||||
[
|
||||
RuntimeError(),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def test_exception_in_group():
|
||||
with pytest.raises(ExceptionGroup) as excinfo:
|
||||
f()
|
||||
assert excinfo.group_contains(RuntimeError)
|
||||
assert not excinfo.group_contains(TypeError)
|
||||
|
||||
Execute the test function with “quiet” reporting mode:
|
||||
|
||||
.. code-block:: pytest
|
||||
|
||||
@@ -54,14 +54,13 @@ operators. (See :ref:`tbreportdemo`). This allows you to use the
|
||||
idiomatic python constructs without boilerplate code while not losing
|
||||
introspection information.
|
||||
|
||||
However, if you specify a message with the assertion like this:
|
||||
If a message is specified with the assertion like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
assert a % 2 == 0, "value was odd, should be even"
|
||||
|
||||
then no assertion introspection takes places at all and the message
|
||||
will be simply shown in the traceback.
|
||||
it is printed alongside the assertion introspection in the traceback.
|
||||
|
||||
See :ref:`assert-details` for more information on assertion introspection.
|
||||
|
||||
@@ -116,10 +115,56 @@ that a regular expression matches on the string representation of an exception
|
||||
with pytest.raises(ValueError, match=r".* 123 .*"):
|
||||
myfunc()
|
||||
|
||||
The regexp parameter of the ``match`` method is matched with the ``re.search``
|
||||
The regexp parameter of the ``match`` parameter is matched with the ``re.search``
|
||||
function, so in the above example ``match='123'`` would have worked as
|
||||
well.
|
||||
|
||||
You can also use the :func:`excinfo.group_contains() <pytest.ExceptionInfo.group_contains>`
|
||||
method to test for exceptions returned as part of an ``ExceptionGroup``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def test_exception_in_group():
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
raise ExceptionGroup(
|
||||
"Group message",
|
||||
[
|
||||
RuntimeError("Exception 123 raised"),
|
||||
],
|
||||
)
|
||||
assert excinfo.group_contains(RuntimeError, match=r".* 123 .*")
|
||||
assert not excinfo.group_contains(TypeError)
|
||||
|
||||
The optional ``match`` keyword parameter works the same way as for
|
||||
:func:`pytest.raises`.
|
||||
|
||||
By default ``group_contains()`` will recursively search for a matching
|
||||
exception at any level of nested ``ExceptionGroup`` instances. You can
|
||||
specify a ``depth`` keyword parameter if you only want to match an
|
||||
exception at a specific level; exceptions contained directly in the top
|
||||
``ExceptionGroup`` would match ``depth=1``.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def test_exception_in_group_at_given_depth():
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
raise ExceptionGroup(
|
||||
"Group message",
|
||||
[
|
||||
RuntimeError(),
|
||||
ExceptionGroup(
|
||||
"Nested group",
|
||||
[
|
||||
TypeError(),
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
assert excinfo.group_contains(RuntimeError, depth=1)
|
||||
assert excinfo.group_contains(TypeError, depth=2)
|
||||
assert not excinfo.group_contains(RuntimeError, depth=2)
|
||||
assert not excinfo.group_contains(TypeError, depth=1)
|
||||
|
||||
There's an alternate form of the :func:`pytest.raises` function where you pass
|
||||
a function that will be executed with the given ``*args`` and ``**kwargs`` and
|
||||
assert that the given exception is raised:
|
||||
|
||||
@@ -176,14 +176,21 @@ with more recent files coming first.
|
||||
Behavior when no tests failed in the last run
|
||||
---------------------------------------------
|
||||
|
||||
When no tests failed in the last run, or when no cached ``lastfailed`` data was
|
||||
found, ``pytest`` can be configured either to run all of the tests or no tests,
|
||||
using the ``--last-failed-no-failures`` option, which takes one of the following values:
|
||||
The ``--lfnf/--last-failed-no-failures`` option governs the behavior of ``--last-failed``.
|
||||
Determines whether to execute tests when there are no previously (known)
|
||||
failures or when no cached ``lastfailed`` data was found.
|
||||
|
||||
There are two options:
|
||||
|
||||
* ``all``: when there are no known test failures, runs all tests (the full test suite). This is the default.
|
||||
* ``none``: when there are no known test failures, just emits a message stating this and exit successfully.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest --last-failed --last-failed-no-failures all # run all tests (default behavior)
|
||||
pytest --last-failed --last-failed-no-failures none # run no tests and exit
|
||||
pytest --last-failed --last-failed-no-failures all # runs the full test suite (default behavior)
|
||||
pytest --last-failed --last-failed-no-failures none # runs no tests and exits successfully
|
||||
|
||||
The new config.cache object
|
||||
--------------------------------
|
||||
|
||||
@@ -135,10 +135,6 @@ Warning about unraisable exceptions and unhandled thread exceptions
|
||||
|
||||
.. versionadded:: 6.2
|
||||
|
||||
.. note::
|
||||
|
||||
These features only work on Python>=3.8.
|
||||
|
||||
Unhandled exceptions are exceptions that are raised in a situation in which
|
||||
they cannot propagate to a caller. The most common case is an exception raised
|
||||
in a :meth:`__del__ <object.__del__>` implementation.
|
||||
|
||||
@@ -1698,7 +1698,7 @@ and declare its use in a test module via a ``usefixtures`` marker:
|
||||
class TestDirectoryInit:
|
||||
def test_cwd_starts_empty(self):
|
||||
assert os.listdir(os.getcwd()) == []
|
||||
with open("myfile", "w") as f:
|
||||
with open("myfile", "w", encoding="utf-8") as f:
|
||||
f.write("hello")
|
||||
|
||||
def test_cwd_again_starts_empty(self):
|
||||
@@ -1752,8 +1752,7 @@ into an ini-file:
|
||||
def my_fixture_that_sadly_wont_use_my_other_fixture():
|
||||
...
|
||||
|
||||
Currently this will not generate any error or warning, but this is intended
|
||||
to be handled by :issue:`3664`.
|
||||
This generates a deprecation warning, and will become an error in Pytest 8.
|
||||
|
||||
.. _`override fixtures`:
|
||||
|
||||
|
||||
@@ -172,6 +172,13 @@ the records for the ``setup`` and ``call`` stages during teardown like so:
|
||||
|
||||
The full API is available at :class:`pytest.LogCaptureFixture`.
|
||||
|
||||
.. warning::
|
||||
|
||||
The ``caplog`` fixture adds a handler to the root logger to capture logs. If the root logger is
|
||||
modified during a test, for example with ``logging.config.dictConfig``, this handler may be
|
||||
removed and cause no logs to be captured. To avoid this, ensure that any root logger configuration
|
||||
only adds to the existing handlers.
|
||||
|
||||
|
||||
.. _live_logs:
|
||||
|
||||
|
||||
@@ -47,8 +47,7 @@ Unsupported idioms / known issues
|
||||
- nose imports test modules with the same import path (e.g.
|
||||
``tests.test_mode``) but different file system paths
|
||||
(e.g. ``tests/test_mode.py`` and ``other/tests/test_mode.py``)
|
||||
by extending sys.path/import semantics. pytest does not do that
|
||||
but there is discussion in :issue:`268` for adding some support. Note that
|
||||
by extending sys.path/import semantics. pytest does not do that. Note that
|
||||
`nose2 choose to avoid this sys.path/import hackery <https://nose2.readthedocs.io/en/latest/differences.html#test-discovery-and-loading>`_.
|
||||
|
||||
If you place a conftest.py file in the root directory of your project
|
||||
@@ -66,16 +65,34 @@ Unsupported idioms / known issues
|
||||
|
||||
- no nose-configuration is recognized.
|
||||
|
||||
- ``yield``-based methods are unsupported as of pytest 4.1.0. They are
|
||||
- ``yield``-based methods are
|
||||
fundamentally incompatible with pytest because they don't support fixtures
|
||||
properly since collection and test execution are separated.
|
||||
|
||||
Here is a table comparing the default supported naming conventions for both
|
||||
nose and pytest.
|
||||
|
||||
========= ========================== ======= =====
|
||||
what default naming convention pytest nose
|
||||
========= ========================== ======= =====
|
||||
module ``test*.py`` ✅
|
||||
module ``test_*.py`` ✅ ✅
|
||||
module ``*_test.py`` ✅
|
||||
module ``*_tests.py``
|
||||
class ``*(unittest.TestCase)`` ✅ ✅
|
||||
method ``test_*`` ✅ ✅
|
||||
class ``Test*`` ✅
|
||||
method ``test_*`` ✅
|
||||
function ``test_*`` ✅
|
||||
========= ========================== ======= =====
|
||||
|
||||
|
||||
Migrating from nose to pytest
|
||||
------------------------------
|
||||
|
||||
`nose2pytest <https://github.com/pytest-dev/nose2pytest>`_ is a Python script
|
||||
and pytest plugin to help convert Nose-based tests into pytest-based tests.
|
||||
Specifically, the script transforms nose.tools.assert_* function calls into
|
||||
Specifically, the script transforms ``nose.tools.assert_*`` function calls into
|
||||
raw assert statements, while preserving format of original arguments
|
||||
as much as possible.
|
||||
|
||||
|
||||
@@ -478,7 +478,7 @@ integration servers, use this invocation:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest --junitxml=path
|
||||
pytest --junit-xml=path
|
||||
|
||||
to create an XML file at ``path``.
|
||||
|
||||
|
||||
@@ -197,6 +197,14 @@ the test. You can also skip based on the version number of a library:
|
||||
The version will be read from the specified
|
||||
module's ``__version__`` attribute.
|
||||
|
||||
Sometimes importing a module can fail due to an exception, if you want to
|
||||
only skip if the module does not exist pass ModuleNotFoundError as the
|
||||
``exc`` kwarg:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
docutils = pytest.importorskip("docutils", exc=ModuleNotFoundError)
|
||||
|
||||
Summary
|
||||
~~~~~~~
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@ created in the `base temporary directory`_.
|
||||
d = tmp_path / "sub"
|
||||
d.mkdir()
|
||||
p = d / "hello.txt"
|
||||
p.write_text(CONTENT)
|
||||
assert p.read_text() == CONTENT
|
||||
p.write_text(CONTENT, encoding="utf-8")
|
||||
assert p.read_text(encoding="utf-8") == CONTENT
|
||||
assert len(list(tmp_path.iterdir())) == 1
|
||||
assert 0
|
||||
|
||||
@@ -51,8 +51,8 @@ Running this would result in a passed test except for the last
|
||||
d = tmp_path / "sub"
|
||||
d.mkdir()
|
||||
p = d / "hello.txt"
|
||||
p.write_text(CONTENT)
|
||||
assert p.read_text() == CONTENT
|
||||
p.write_text(CONTENT, encoding="utf-8")
|
||||
assert p.read_text(encoding="utf-8") == CONTENT
|
||||
assert len(list(tmp_path.iterdir())) == 1
|
||||
> assert 0
|
||||
E assert 0
|
||||
|
||||
@@ -207,10 +207,10 @@ creation of a per-test temporary directory:
|
||||
@pytest.fixture(autouse=True)
|
||||
def initdir(self, tmp_path, monkeypatch):
|
||||
monkeypatch.chdir(tmp_path) # change to pytest-provided temporary directory
|
||||
tmp_path.joinpath("samplefile.ini").write_text("# testdata")
|
||||
tmp_path.joinpath("samplefile.ini").write_text("# testdata", encoding="utf-8")
|
||||
|
||||
def test_method(self):
|
||||
with open("samplefile.ini") as f:
|
||||
with open("samplefile.ini", encoding="utf-8") as f:
|
||||
s = f.read()
|
||||
assert "testdata" in s
|
||||
|
||||
|
||||
@@ -44,23 +44,34 @@ Use ``""`` instead of ``''`` in expression when running this on Windows
|
||||
|
||||
.. _nodeids:
|
||||
|
||||
**Run tests by node ids**
|
||||
**Run tests by collection arguments**
|
||||
|
||||
Each collected test is assigned a unique ``nodeid`` which consist of the module filename followed
|
||||
by specifiers like class names, function names and parameters from parametrization, separated by ``::`` characters.
|
||||
Pass the module filename relative to the working directory, followed by specifiers like the class name and function name
|
||||
separated by ``::`` characters, and parameters from parameterization enclosed in ``[]``.
|
||||
|
||||
To run a specific test within a module:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest test_mod.py::test_func
|
||||
pytest tests/test_mod.py::test_func
|
||||
|
||||
|
||||
Another example specifying a test method in the command line:
|
||||
To run all tests in a class:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest test_mod.py::TestClass::test_method
|
||||
pytest tests/test_mod.py::TestClass
|
||||
|
||||
Specifying a specific test method:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest tests/test_mod.py::TestClass::test_method
|
||||
|
||||
Specifying a specific parametrization of a test:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest tests/test_mod.py::test_func[x1,y2]
|
||||
|
||||
**Run tests by marker expressions**
|
||||
|
||||
@@ -173,7 +184,8 @@ You can invoke ``pytest`` from Python code directly:
|
||||
|
||||
this acts as if you would call "pytest" from the command line.
|
||||
It will not raise :class:`SystemExit` but return the :ref:`exit code <exit-codes>` instead.
|
||||
You can pass in options and arguments:
|
||||
If you don't pass it any arguments, ``main`` reads the arguments from the command line arguments of the process (:data:`sys.argv`), which may be undesirable.
|
||||
You can pass in options and arguments explicitly:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ The remaining hook functions will not be called in this case.
|
||||
|
||||
.. _`hookwrapper`:
|
||||
|
||||
hookwrapper: executing around other hooks
|
||||
hook wrappers: executing around other hooks
|
||||
-------------------------------------------------
|
||||
|
||||
.. currentmodule:: _pytest.core
|
||||
@@ -69,10 +69,8 @@ which yields exactly once. When pytest invokes hooks it first executes
|
||||
hook wrappers and passes the same arguments as to the regular hooks.
|
||||
|
||||
At the yield point of the hook wrapper pytest will execute the next hook
|
||||
implementations and return their result to the yield point in the form of
|
||||
a :py:class:`Result <pluggy._Result>` instance which encapsulates a result or
|
||||
exception info. The yield point itself will thus typically not raise
|
||||
exceptions (unless there are bugs).
|
||||
implementations and return their result to the yield point, or will
|
||||
propagate an exception if they raised.
|
||||
|
||||
Here is an example definition of a hook wrapper:
|
||||
|
||||
@@ -81,26 +79,35 @@ Here is an example definition of a hook wrapper:
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.hookimpl(hookwrapper=True)
|
||||
@pytest.hookimpl(wrapper=True)
|
||||
def pytest_pyfunc_call(pyfuncitem):
|
||||
do_something_before_next_hook_executes()
|
||||
|
||||
outcome = yield
|
||||
# outcome.excinfo may be None or a (cls, val, tb) tuple
|
||||
# If the outcome is an exception, will raise the exception.
|
||||
res = yield
|
||||
|
||||
res = outcome.get_result() # will raise if outcome was exception
|
||||
new_res = post_process_result(res)
|
||||
|
||||
post_process_result(res)
|
||||
# Override the return value to the plugin system.
|
||||
return new_res
|
||||
|
||||
outcome.force_result(new_res) # to override the return value to the plugin system
|
||||
The hook wrapper needs to return a result for the hook, or raise an exception.
|
||||
|
||||
Note that hook wrappers don't return results themselves, they merely
|
||||
perform tracing or other side effects around the actual hook implementations.
|
||||
If the result of the underlying hook is a mutable object, they may modify
|
||||
that result but it's probably better to avoid it.
|
||||
In many cases, the wrapper only needs to perform tracing or other side effects
|
||||
around the actual hook implementations, in which case it can return the result
|
||||
value of the ``yield``. The simplest (though useless) hook wrapper is
|
||||
``return (yield)``.
|
||||
|
||||
In other cases, the wrapper wants the adjust or adapt the result, in which case
|
||||
it can return a new value. If the result of the underlying hook is a mutable
|
||||
object, the wrapper may modify that result, but it's probably better to avoid it.
|
||||
|
||||
If the hook implementation failed with an exception, the wrapper can handle that
|
||||
exception using a ``try-catch-finally`` around the ``yield``, by propagating it,
|
||||
supressing it, or raising a different exception entirely.
|
||||
|
||||
For more information, consult the
|
||||
:ref:`pluggy documentation about hookwrappers <pluggy:hookwrappers>`.
|
||||
:ref:`pluggy documentation about hook wrappers <pluggy:hookwrappers>`.
|
||||
|
||||
.. _plugin-hookorder:
|
||||
|
||||
@@ -130,11 +137,14 @@ after others, i.e. the position in the ``N``-sized list of functions:
|
||||
|
||||
|
||||
# Plugin 3
|
||||
@pytest.hookimpl(hookwrapper=True)
|
||||
@pytest.hookimpl(wrapper=True)
|
||||
def pytest_collection_modifyitems(items):
|
||||
# will execute even before the tryfirst one above!
|
||||
outcome = yield
|
||||
# will execute after all non-hookwrappers executed
|
||||
try:
|
||||
return (yield)
|
||||
finally:
|
||||
# will execute after all non-wrappers executed
|
||||
...
|
||||
|
||||
Here is the order of execution:
|
||||
|
||||
@@ -149,12 +159,11 @@ Here is the order of execution:
|
||||
Plugin1).
|
||||
|
||||
4. Plugin3's pytest_collection_modifyitems then executing the code after the yield
|
||||
point. The yield receives a :py:class:`Result <pluggy._Result>` instance which encapsulates
|
||||
the result from calling the non-wrappers. Wrappers shall not modify the result.
|
||||
point. The yield receives the result from calling the non-wrappers, or raises
|
||||
an exception if the non-wrappers raised.
|
||||
|
||||
It's possible to use ``tryfirst`` and ``trylast`` also in conjunction with
|
||||
``hookwrapper=True`` in which case it will influence the ordering of hookwrappers
|
||||
among each other.
|
||||
It's possible to use ``tryfirst`` and ``trylast`` also on hook wrappers
|
||||
in which case it will influence the ordering of hook wrappers among each other.
|
||||
|
||||
|
||||
Declaring new hooks
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
.. sidebar:: Next Open Trainings
|
||||
|
||||
- `pytest tips and tricks for a better testsuite <https://ep2023.europython.eu/session/pytest-tips-and-tricks-for-a-better-testsuite>`_, at `Europython 2023 <https://ep2023.europython.eu/>`_, July 18th (3h), Prague/Remote
|
||||
- `Professional Testing with Python <https://python-academy.com/courses/python_course_testing.html>`_, via `Python Academy <https://www.python-academy.com/>`_, March 5th to 7th 2024 (3 day in-depth training), Leipzig/Remote
|
||||
- `Professional Testing with Python <https://python-academy.com/courses/python_course_testing.html>`_, via `Python Academy <https://www.python-academy.com/>`_, **March 5th to 7th 2024** (3 day in-depth training), **Leipzig, Germany / Remote**
|
||||
|
||||
Also see :doc:`previous talks and blogposts <talks>`.
|
||||
|
||||
@@ -18,7 +17,7 @@ The ``pytest`` framework makes it easy to write small, readable tests, and can
|
||||
scale to support complex functional testing for applications and libraries.
|
||||
|
||||
|
||||
``pytest`` requires: Python 3.7+ or PyPy3.
|
||||
``pytest`` requires: Python 3.8+ or PyPy3.
|
||||
|
||||
**PyPI package name**: :pypi:`pytest`
|
||||
|
||||
@@ -77,7 +76,7 @@ Features
|
||||
|
||||
- Can run :ref:`unittest <unittest>` (including trial) and :ref:`nose <noseintegration>` test suites out of the box
|
||||
|
||||
- Python 3.7+ or PyPy 3
|
||||
- Python 3.8+ or PyPy 3
|
||||
|
||||
- Rich plugin architecture, with over 800+ :ref:`external plugins <plugin-list>` and thriving community
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -82,6 +82,8 @@ pytest.exit
|
||||
pytest.main
|
||||
~~~~~~~~~~~
|
||||
|
||||
**Tutorial**: :ref:`pytest.main-usage`
|
||||
|
||||
.. autofunction:: pytest.main
|
||||
|
||||
pytest.param
|
||||
@@ -235,7 +237,7 @@ pytest.mark.xfail
|
||||
|
||||
Marks a test function as *expected to fail*.
|
||||
|
||||
.. py:function:: pytest.mark.xfail(condition=None, *, reason=None, raises=None, run=True, strict=False)
|
||||
.. py:function:: pytest.mark.xfail(condition=None, *, reason=None, raises=None, run=True, strict=xfail_strict)
|
||||
|
||||
:type condition: bool or str
|
||||
:param condition:
|
||||
@@ -247,10 +249,10 @@ Marks a test function as *expected to fail*.
|
||||
:keyword Type[Exception] raises:
|
||||
Exception subclass (or tuple of subclasses) expected to be raised by the test function; other exceptions will fail the test.
|
||||
:keyword bool run:
|
||||
If the test function should actually be executed. If ``False``, the function will always xfail and will
|
||||
Whether the test function should actually be executed. If ``False``, the function will always xfail and will
|
||||
not be executed (useful if a function is segfaulting).
|
||||
:keyword bool strict:
|
||||
* If ``False`` (the default) the function will be shown in the terminal output as ``xfailed`` if it fails
|
||||
* If ``False`` the function will be shown in the terminal output as ``xfailed`` if it fails
|
||||
and as ``xpass`` if it passes. In both cases this will not cause the test suite to fail as a whole. This
|
||||
is particularly useful to mark *flaky* tests (tests that fail at random) to be tackled later.
|
||||
* If ``True``, the function will be shown in the terminal output as ``xfailed`` if it fails, but if it
|
||||
@@ -258,6 +260,8 @@ Marks a test function as *expected to fail*.
|
||||
that are always failing and there should be a clear indication if they unexpectedly start to pass (for example
|
||||
a new release of a library fixes a known bug).
|
||||
|
||||
Defaults to :confval:`xfail_strict`, which is ``False`` by default.
|
||||
|
||||
|
||||
Custom marks
|
||||
~~~~~~~~~~~~
|
||||
@@ -783,18 +787,66 @@ reporting or interaction with exceptions:
|
||||
.. autofunction:: pytest_leave_pdb
|
||||
|
||||
|
||||
Objects
|
||||
-------
|
||||
Collection tree objects
|
||||
-----------------------
|
||||
|
||||
Full reference to objects accessible from :ref:`fixtures <fixture>` or :ref:`hooks <hook-reference>`.
|
||||
These are the collector and item classes (collectively called "nodes") which
|
||||
make up the collection tree.
|
||||
|
||||
Node
|
||||
~~~~
|
||||
|
||||
CallInfo
|
||||
~~~~~~~~
|
||||
|
||||
.. autoclass:: pytest.CallInfo()
|
||||
.. autoclass:: _pytest.nodes.Node()
|
||||
:members:
|
||||
|
||||
Collector
|
||||
~~~~~~~~~
|
||||
|
||||
.. autoclass:: pytest.Collector()
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
Item
|
||||
~~~~
|
||||
|
||||
.. autoclass:: pytest.Item()
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
File
|
||||
~~~~
|
||||
|
||||
.. autoclass:: pytest.File()
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
FSCollector
|
||||
~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: _pytest.nodes.FSCollector()
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
Session
|
||||
~~~~~~~
|
||||
|
||||
.. autoclass:: pytest.Session()
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
Package
|
||||
~~~~~~~
|
||||
|
||||
.. autoclass:: pytest.Package()
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
Module
|
||||
~~~~~~
|
||||
|
||||
.. autoclass:: pytest.Module()
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
Class
|
||||
~~~~~
|
||||
@@ -803,13 +855,34 @@ Class
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
Collector
|
||||
~~~~~~~~~
|
||||
Function
|
||||
~~~~~~~~
|
||||
|
||||
.. autoclass:: pytest.Collector()
|
||||
.. autoclass:: pytest.Function()
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
FunctionDefinition
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: _pytest.python.FunctionDefinition()
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
Objects
|
||||
-------
|
||||
|
||||
Objects accessible from :ref:`fixtures <fixture>` or :ref:`hooks <hook-reference>`
|
||||
or importable from ``pytest``.
|
||||
|
||||
|
||||
CallInfo
|
||||
~~~~~~~~
|
||||
|
||||
.. autoclass:: pytest.CallInfo()
|
||||
:members:
|
||||
|
||||
CollectReport
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
@@ -837,46 +910,11 @@ ExitCode
|
||||
.. autoclass:: pytest.ExitCode
|
||||
:members:
|
||||
|
||||
File
|
||||
~~~~
|
||||
|
||||
.. autoclass:: pytest.File()
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
FixtureDef
|
||||
~~~~~~~~~~
|
||||
|
||||
.. autoclass:: _pytest.fixtures.FixtureDef()
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
FSCollector
|
||||
~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: _pytest.nodes.FSCollector()
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
Function
|
||||
~~~~~~~~
|
||||
|
||||
.. autoclass:: pytest.Function()
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
FunctionDefinition
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: _pytest.python.FunctionDefinition()
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
Item
|
||||
~~~~
|
||||
|
||||
.. autoclass:: pytest.Item()
|
||||
.. autoclass:: pytest.FixtureDef()
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -907,19 +945,6 @@ Metafunc
|
||||
.. autoclass:: pytest.Metafunc()
|
||||
:members:
|
||||
|
||||
Module
|
||||
~~~~~~
|
||||
|
||||
.. autoclass:: pytest.Module()
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
Node
|
||||
~~~~
|
||||
|
||||
.. autoclass:: _pytest.nodes.Node()
|
||||
:members:
|
||||
|
||||
Parser
|
||||
~~~~~~
|
||||
|
||||
@@ -941,13 +966,6 @@ PytestPluginManager
|
||||
:inherited-members:
|
||||
:show-inheritance:
|
||||
|
||||
Session
|
||||
~~~~~~~
|
||||
|
||||
.. autoclass:: pytest.Session()
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
TestReport
|
||||
~~~~~~~~~~
|
||||
|
||||
@@ -962,10 +980,10 @@ TestShortLogReport
|
||||
.. autoclass:: pytest.TestShortLogReport()
|
||||
:members:
|
||||
|
||||
_Result
|
||||
Result
|
||||
~~~~~~~
|
||||
|
||||
Result object used within :ref:`hook wrappers <hookwrapper>`, see :py:class:`_Result in the pluggy documentation <pluggy._callers._Result>` for more information.
|
||||
Result object used within :ref:`hook wrappers <hookwrapper>`, see :py:class:`Result in the pluggy documentation <pluggy.Result>` for more information.
|
||||
|
||||
Stash
|
||||
~~~~~
|
||||
@@ -1153,6 +1171,9 @@ Custom warnings generated in some situations such as improper usage or deprecate
|
||||
.. autoclass:: pytest.PytestRemovedIn8Warning
|
||||
:show-inheritance:
|
||||
|
||||
.. autoclass:: pytest.PytestRemovedIn9Warning
|
||||
:show-inheritance:
|
||||
|
||||
.. autoclass:: pytest.PytestUnhandledCoroutineWarning
|
||||
:show-inheritance:
|
||||
|
||||
@@ -1619,11 +1640,11 @@ passed multiple times. The expected format is ``name=value``. For example::
|
||||
Additionally, ``pytest`` will attempt to intelligently identify and ignore a
|
||||
virtualenv by the presence of an activation script. Any directory deemed to
|
||||
be the root of a virtual environment will not be considered during test
|
||||
collection unless ``‑‑collect‑in‑virtualenv`` is given. Note also that
|
||||
``norecursedirs`` takes precedence over ``‑‑collect‑in‑virtualenv``; e.g. if
|
||||
collection unless ``--collect-in-virtualenv`` is given. Note also that
|
||||
``norecursedirs`` takes precedence over ``--collect-in-virtualenv``; e.g. if
|
||||
you intend to run tests in a virtualenv with a base directory that matches
|
||||
``'.*'`` you *must* override ``norecursedirs`` in addition to using the
|
||||
``‑‑collect‑in‑virtualenv`` flag.
|
||||
``--collect-in-virtualenv`` flag.
|
||||
|
||||
|
||||
.. confval:: python_classes
|
||||
@@ -1871,8 +1892,12 @@ All the command-line flags can be obtained by running ``pytest --help``::
|
||||
tests. Optional argument: glob (default: '*').
|
||||
--cache-clear Remove all cache contents at start of test run
|
||||
--lfnf={all,none}, --last-failed-no-failures={all,none}
|
||||
Which tests to run with no previously (known)
|
||||
failures
|
||||
With ``--lf``, determines whether to execute tests
|
||||
when there are no previously (known) failures or
|
||||
when no cached ``lastfailed`` data was found.
|
||||
``all`` (the default) runs the full test suite
|
||||
again. ``none`` just emits a message about no known
|
||||
failures and exits successfully.
|
||||
--sw, --stepwise Exit on test failure and continue from last failing
|
||||
test next time
|
||||
--sw-skip, --stepwise-skip
|
||||
@@ -1923,9 +1948,9 @@ All the command-line flags can be obtained by running ``pytest --help``::
|
||||
--strict-markers Markers not registered in the `markers` section of
|
||||
the configuration file raise errors
|
||||
--strict (Deprecated) alias to --strict-markers
|
||||
-c, --config-file FILE
|
||||
-c FILE, --config-file=FILE
|
||||
Load configuration from `FILE` instead of trying to
|
||||
locate one of the implicit configuration files
|
||||
locate one of the implicit configuration files.
|
||||
--continue-on-collection-errors
|
||||
Force test execution even if collection errors occur
|
||||
--rootdir=ROOTDIR Define root directory for tests. Can be relative
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
pallets-sphinx-themes
|
||||
pluggy>=1.0
|
||||
pluggy>=1.2.0
|
||||
pygments-pytest>=2.3.0
|
||||
sphinx-removed-in>=0.2.0
|
||||
sphinx>=5,<6
|
||||
|
||||
@@ -17,7 +17,12 @@ python_classes = ["Test", "Acceptance"]
|
||||
python_functions = ["test"]
|
||||
# NOTE: "doc" is not included here, but gets tested explicitly via "doctesting".
|
||||
testpaths = ["testing"]
|
||||
norecursedirs = ["testing/example_scripts"]
|
||||
norecursedirs = [
|
||||
"testing/example_scripts",
|
||||
".*",
|
||||
"build",
|
||||
"dist",
|
||||
]
|
||||
xfail_strict = true
|
||||
filterwarnings = [
|
||||
"error",
|
||||
@@ -113,7 +118,7 @@ template = "changelog/_template.rst"
|
||||
showcontent = true
|
||||
|
||||
[tool.black]
|
||||
target-version = ['py37']
|
||||
target-version = ['py38']
|
||||
|
||||
# check-wheel-contents is executed by the build-and-inspect-python-package action.
|
||||
[tool.check-wheel-contents]
|
||||
|
||||
@@ -31,10 +31,16 @@ class InvalidFeatureRelease(Exception):
|
||||
SLUG = "pytest-dev/pytest"
|
||||
|
||||
PR_BODY = """\
|
||||
Created automatically from manual trigger.
|
||||
Created by the [prepare release pr](https://github.com/pytest-dev/pytest/actions/workflows/prepare-release-pr.yml)
|
||||
workflow.
|
||||
|
||||
Once all builds pass and it has been **approved** by one or more maintainers, the build
|
||||
can be released by pushing a tag `{version}` to this repository.
|
||||
Once all builds pass and it has been **approved** by one or more maintainers,
|
||||
start the [deploy](https://github.com/pytest-dev/pytest/actions/workflows/deploy.yml) workflow, using these parameters:
|
||||
|
||||
* `Use workflow from`: `release-{version}`.
|
||||
* `Release version`: `{version}`.
|
||||
|
||||
After the `deploy` workflow has been approved by a core maintainer, the package will be uploaded to PyPI automatically.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
@@ -5,22 +5,41 @@ from textwrap import dedent
|
||||
from textwrap import indent
|
||||
|
||||
import packaging.version
|
||||
import requests
|
||||
import platformdirs
|
||||
import tabulate
|
||||
import wcwidth
|
||||
from requests_cache import CachedResponse
|
||||
from requests_cache import CachedSession
|
||||
from requests_cache import OriginalResponse
|
||||
from requests_cache import SQLiteCache
|
||||
from tqdm import tqdm
|
||||
|
||||
|
||||
FILE_HEAD = r"""
|
||||
.. Note this file is autogenerated by scripts/update-plugin-list.py - usually weekly via github action
|
||||
|
||||
.. _plugin-list:
|
||||
|
||||
Plugin List
|
||||
===========
|
||||
Pytest Plugin List
|
||||
==================
|
||||
|
||||
PyPI projects that match "pytest-\*" are considered plugins and are listed
|
||||
automatically together with a manually-maintained list in `the source
|
||||
code <https://github.com/pytest-dev/pytest/blob/main/scripts/update-plugin-list.py>`_.
|
||||
Below is an automated compilation of ``pytest``` plugins available on `PyPI <https://pypi.org>`_.
|
||||
It includes PyPI projects whose names begin with "pytest-" and a handful of manually selected projects.
|
||||
Packages classified as inactive are excluded.
|
||||
|
||||
For detailed insights into how this list is generated,
|
||||
please refer to `the update script <https://github.com/pytest-dev/pytest/blob/main/scripts/update-plugin-list.py>`_.
|
||||
|
||||
.. warning::
|
||||
|
||||
Please be aware that this list is not a curated collection of projects
|
||||
and does not undergo a systematic review process.
|
||||
It serves purely as an informational resource to aid in the discovery of ``pytest`` plugins.
|
||||
|
||||
Do not presume any endorsement from the ``pytest`` project or its developers,
|
||||
and always conduct your own quality assessment before incorporating any of these plugins into your own projects.
|
||||
|
||||
|
||||
.. The following conditional uses a different format for this list when
|
||||
creating a PDF, because otherwise the table gets far too wide for the
|
||||
page.
|
||||
@@ -37,6 +56,8 @@ DEVELOPMENT_STATUS_CLASSIFIERS = (
|
||||
)
|
||||
ADDITIONAL_PROJECTS = { # set of additional projects to consider as plugins
|
||||
"logassert",
|
||||
"nuts",
|
||||
"flask_fixture",
|
||||
}
|
||||
|
||||
|
||||
@@ -53,19 +74,47 @@ def escape_rst(text: str) -> str:
|
||||
return text
|
||||
|
||||
|
||||
def project_response_with_refresh(
|
||||
session: CachedSession, name: str, last_serial: int
|
||||
) -> OriginalResponse | CachedResponse:
|
||||
"""Get a http cached pypi project
|
||||
|
||||
force refresh in case of last serial mismatch
|
||||
"""
|
||||
|
||||
response = session.get(f"https://pypi.org/pypi/{name}/json")
|
||||
if int(response.headers.get("X-PyPI-Last-Serial", -1)) != last_serial:
|
||||
response = session.get(f"https://pypi.org/pypi/{name}/json", refresh=True)
|
||||
return response
|
||||
|
||||
|
||||
def get_session() -> CachedSession:
|
||||
"""Configures the requests-cache session"""
|
||||
cache_path = platformdirs.user_cache_path("pytest-plugin-list")
|
||||
cache_path.mkdir(exist_ok=True, parents=True)
|
||||
cache_file = cache_path.joinpath("http_cache.sqlite3")
|
||||
return CachedSession(backend=SQLiteCache(cache_file))
|
||||
|
||||
|
||||
def pytest_plugin_projects_from_pypi(session: CachedSession) -> dict[str, int]:
|
||||
response = session.get(
|
||||
"https://pypi.org/simple",
|
||||
headers={"Accept": "application/vnd.pypi.simple.v1+json"},
|
||||
refresh=True,
|
||||
)
|
||||
return {
|
||||
name: p["_last-serial"]
|
||||
for p in response.json()["projects"]
|
||||
if (name := p["name"]).startswith("pytest-") or name in ADDITIONAL_PROJECTS
|
||||
}
|
||||
|
||||
|
||||
def iter_plugins():
|
||||
regex = r">([\d\w-]*)</a>"
|
||||
response = requests.get("https://pypi.org/simple")
|
||||
session = get_session()
|
||||
name_2_serial = pytest_plugin_projects_from_pypi(session)
|
||||
|
||||
match_names = (match.groups()[0] for match in re.finditer(regex, response.text))
|
||||
plugin_names = [
|
||||
name
|
||||
for name in match_names
|
||||
if name.startswith("pytest-") or name in ADDITIONAL_PROJECTS
|
||||
]
|
||||
|
||||
for name in tqdm(plugin_names, smoothing=0):
|
||||
response = requests.get(f"https://pypi.org/pypi/{name}/json")
|
||||
for name, last_serial in tqdm(name_2_serial.items(), smoothing=0):
|
||||
response = project_response_with_refresh(session, name, last_serial)
|
||||
if response.status_code == 404:
|
||||
# Some packages, like pytest-azurepipelines42, are included in https://pypi.org/simple
|
||||
# but return 404 on the JSON API. Skip.
|
||||
@@ -136,7 +185,7 @@ def plugin_definitions(plugins):
|
||||
|
||||
|
||||
def main():
|
||||
plugins = list(iter_plugins())
|
||||
plugins = [*iter_plugins()]
|
||||
|
||||
reference_dir = pathlib.Path("doc", "en", "reference")
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ classifiers =
|
||||
Operating System :: POSIX
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3 :: Only
|
||||
Programming Language :: Python :: 3.7
|
||||
Programming Language :: Python :: 3.8
|
||||
Programming Language :: Python :: 3.9
|
||||
Programming Language :: Python :: 3.10
|
||||
@@ -47,12 +46,11 @@ py_modules = py
|
||||
install_requires =
|
||||
iniconfig
|
||||
packaging
|
||||
pluggy>=0.12,<2.0
|
||||
pluggy>=1.3.0,<2.0
|
||||
colorama;sys_platform=="win32"
|
||||
exceptiongroup>=1.0.0rc8;python_version<"3.11"
|
||||
importlib-metadata>=0.12;python_version<"3.8"
|
||||
tomli>=1.0.0;python_version<"3.11"
|
||||
python_requires = >=3.7
|
||||
python_requires = >=3.8
|
||||
package_dir =
|
||||
=src
|
||||
setup_requires =
|
||||
|
||||
@@ -17,18 +17,21 @@ from typing import Any
|
||||
from typing import Callable
|
||||
from typing import ClassVar
|
||||
from typing import Dict
|
||||
from typing import Final
|
||||
from typing import final
|
||||
from typing import Generic
|
||||
from typing import Iterable
|
||||
from typing import List
|
||||
from typing import Literal
|
||||
from typing import Mapping
|
||||
from typing import Optional
|
||||
from typing import overload
|
||||
from typing import Pattern
|
||||
from typing import Sequence
|
||||
from typing import Set
|
||||
from typing import SupportsIndex
|
||||
from typing import Tuple
|
||||
from typing import Type
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TypeVar
|
||||
from typing import Union
|
||||
|
||||
@@ -42,22 +45,16 @@ from _pytest._code.source import Source
|
||||
from _pytest._io import TerminalWriter
|
||||
from _pytest._io.saferepr import safeformat
|
||||
from _pytest._io.saferepr import saferepr
|
||||
from _pytest.compat import final
|
||||
from _pytest.compat import get_real_func
|
||||
from _pytest.deprecated import check_ispytest
|
||||
from _pytest.pathlib import absolutepath
|
||||
from _pytest.pathlib import bestrelpath
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing_extensions import Final
|
||||
from typing_extensions import Literal
|
||||
from typing_extensions import SupportsIndex
|
||||
|
||||
_TracebackStyle = Literal["long", "short", "line", "no", "native", "value", "auto"]
|
||||
|
||||
if sys.version_info[:2] < (3, 11):
|
||||
from exceptiongroup import BaseExceptionGroup
|
||||
|
||||
_TracebackStyle = Literal["long", "short", "line", "no", "native", "value", "auto"]
|
||||
|
||||
|
||||
class Code:
|
||||
"""Wrapper around Python code objects."""
|
||||
@@ -396,11 +393,11 @@ class Traceback(List[TracebackEntry]):
|
||||
|
||||
def filter(
|
||||
self,
|
||||
# TODO(py38): change to positional only.
|
||||
_excinfo_or_fn: Union[
|
||||
excinfo_or_fn: Union[
|
||||
"ExceptionInfo[BaseException]",
|
||||
Callable[[TracebackEntry], bool],
|
||||
],
|
||||
/,
|
||||
) -> "Traceback":
|
||||
"""Return a Traceback instance with certain items removed.
|
||||
|
||||
@@ -411,10 +408,10 @@ class Traceback(List[TracebackEntry]):
|
||||
``TracebackEntry`` instance, and should return True when the item should
|
||||
be added to the ``Traceback``, False when not.
|
||||
"""
|
||||
if isinstance(_excinfo_or_fn, ExceptionInfo):
|
||||
fn = lambda x: not x.ishidden(_excinfo_or_fn) # noqa: E731
|
||||
if isinstance(excinfo_or_fn, ExceptionInfo):
|
||||
fn = lambda x: not x.ishidden(excinfo_or_fn) # noqa: E731
|
||||
else:
|
||||
fn = _excinfo_or_fn
|
||||
fn = excinfo_or_fn
|
||||
return Traceback(filter(fn, self))
|
||||
|
||||
def recursionindex(self) -> Optional[int]:
|
||||
@@ -633,7 +630,7 @@ class ExceptionInfo(Generic[E]):
|
||||
def getrepr(
|
||||
self,
|
||||
showlocals: bool = False,
|
||||
style: "_TracebackStyle" = "long",
|
||||
style: _TracebackStyle = "long",
|
||||
abspath: bool = False,
|
||||
tbfilter: Union[
|
||||
bool, Callable[["ExceptionInfo[BaseException]"], Traceback]
|
||||
@@ -700,6 +697,14 @@ class ExceptionInfo(Generic[E]):
|
||||
)
|
||||
return fmt.repr_excinfo(self)
|
||||
|
||||
def _stringify_exception(self, exc: BaseException) -> str:
|
||||
return "\n".join(
|
||||
[
|
||||
str(exc),
|
||||
*getattr(exc, "__notes__", []),
|
||||
]
|
||||
)
|
||||
|
||||
def match(self, regexp: Union[str, Pattern[str]]) -> "Literal[True]":
|
||||
"""Check whether the regular expression `regexp` matches the string
|
||||
representation of the exception using :func:`python:re.search`.
|
||||
@@ -707,7 +712,7 @@ class ExceptionInfo(Generic[E]):
|
||||
If it matches `True` is returned, otherwise an `AssertionError` is raised.
|
||||
"""
|
||||
__tracebackhide__ = True
|
||||
value = str(self.value)
|
||||
value = self._stringify_exception(self.value)
|
||||
msg = f"Regex pattern did not match.\n Regex: {regexp!r}\n Input: {value!r}"
|
||||
if regexp == value:
|
||||
msg += "\n Did you mean to `re.escape()` the regex?"
|
||||
@@ -715,6 +720,69 @@ class ExceptionInfo(Generic[E]):
|
||||
# Return True to allow for "assert excinfo.match()".
|
||||
return True
|
||||
|
||||
def _group_contains(
|
||||
self,
|
||||
exc_group: BaseExceptionGroup[BaseException],
|
||||
expected_exception: Union[Type[BaseException], Tuple[Type[BaseException], ...]],
|
||||
match: Union[str, Pattern[str], None],
|
||||
target_depth: Optional[int] = None,
|
||||
current_depth: int = 1,
|
||||
) -> bool:
|
||||
"""Return `True` if a `BaseExceptionGroup` contains a matching exception."""
|
||||
if (target_depth is not None) and (current_depth > target_depth):
|
||||
# already descended past the target depth
|
||||
return False
|
||||
for exc in exc_group.exceptions:
|
||||
if isinstance(exc, BaseExceptionGroup):
|
||||
if self._group_contains(
|
||||
exc, expected_exception, match, target_depth, current_depth + 1
|
||||
):
|
||||
return True
|
||||
if (target_depth is not None) and (current_depth != target_depth):
|
||||
# not at the target depth, no match
|
||||
continue
|
||||
if not isinstance(exc, expected_exception):
|
||||
continue
|
||||
if match is not None:
|
||||
value = self._stringify_exception(exc)
|
||||
if not re.search(match, value):
|
||||
continue
|
||||
return True
|
||||
return False
|
||||
|
||||
def group_contains(
|
||||
self,
|
||||
expected_exception: Union[Type[BaseException], Tuple[Type[BaseException], ...]],
|
||||
*,
|
||||
match: Union[str, Pattern[str], None] = None,
|
||||
depth: Optional[int] = None,
|
||||
) -> bool:
|
||||
"""Check whether a captured exception group contains a matching exception.
|
||||
|
||||
:param Type[BaseException] | Tuple[Type[BaseException]] expected_exception:
|
||||
The expected exception type, or a tuple if one of multiple possible
|
||||
exception types are expected.
|
||||
|
||||
:param str | Pattern[str] | None match:
|
||||
If specified, a string containing a regular expression,
|
||||
or a regular expression object, that is tested against the string
|
||||
representation of the exception and its `PEP-678 <https://peps.python.org/pep-0678/>` `__notes__`
|
||||
using :func:`re.search`.
|
||||
|
||||
To match a literal string that may contain :ref:`special characters
|
||||
<re-syntax>`, the pattern can first be escaped with :func:`re.escape`.
|
||||
|
||||
:param Optional[int] depth:
|
||||
If `None`, will search for a matching exception at any nesting depth.
|
||||
If >= 1, will only match an exception if it's at the specified depth (depth = 1 being
|
||||
the exceptions contained within the topmost exception group).
|
||||
"""
|
||||
msg = "Captured exception is not an instance of `BaseExceptionGroup`"
|
||||
assert isinstance(self.value, BaseExceptionGroup), msg
|
||||
msg = "`depth` must be >= 1 if specified"
|
||||
assert (depth is None) or (depth >= 1), msg
|
||||
return self._group_contains(self.value, expected_exception, match, depth)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class FormattedExcinfo:
|
||||
@@ -725,7 +793,7 @@ class FormattedExcinfo:
|
||||
fail_marker: ClassVar = "E"
|
||||
|
||||
showlocals: bool = False
|
||||
style: "_TracebackStyle" = "long"
|
||||
style: _TracebackStyle = "long"
|
||||
abspath: bool = True
|
||||
tbfilter: Union[bool, Callable[[ExceptionInfo[BaseException]], Traceback]] = True
|
||||
funcargs: bool = False
|
||||
@@ -1090,7 +1158,7 @@ class ReprExceptionInfo(ExceptionRepr):
|
||||
class ReprTraceback(TerminalRepr):
|
||||
reprentries: Sequence[Union["ReprEntry", "ReprEntryNative"]]
|
||||
extraline: Optional[str]
|
||||
style: "_TracebackStyle"
|
||||
style: _TracebackStyle
|
||||
|
||||
entrysep: ClassVar = "_ "
|
||||
|
||||
@@ -1124,7 +1192,7 @@ class ReprTracebackNative(ReprTraceback):
|
||||
class ReprEntryNative(TerminalRepr):
|
||||
lines: Sequence[str]
|
||||
|
||||
style: ClassVar["_TracebackStyle"] = "native"
|
||||
style: ClassVar[_TracebackStyle] = "native"
|
||||
|
||||
def toterminal(self, tw: TerminalWriter) -> None:
|
||||
tw.write("".join(self.lines))
|
||||
@@ -1136,7 +1204,7 @@ class ReprEntry(TerminalRepr):
|
||||
reprfuncargs: Optional["ReprFuncArgs"]
|
||||
reprlocals: Optional["ReprLocals"]
|
||||
reprfileloc: Optional["ReprFileLocation"]
|
||||
style: "_TracebackStyle"
|
||||
style: _TracebackStyle
|
||||
|
||||
def _write_entry_lines(self, tw: TerminalWriter) -> None:
|
||||
"""Write the source code portions of a list of traceback entries with syntax highlighting.
|
||||
|
||||
@@ -149,8 +149,7 @@ def get_statement_startend2(lineno: int, node: ast.AST) -> Tuple[int, Optional[i
|
||||
values: List[int] = []
|
||||
for x in ast.walk(node):
|
||||
if isinstance(x, (ast.stmt, ast.ExceptHandler)):
|
||||
# Before Python 3.8, the lineno of a decorated class or function pointed at the decorator.
|
||||
# Since Python 3.8, the lineno points to the class/def, so need to include the decorators.
|
||||
# The lineno points to the class/def, so need to include the decorators.
|
||||
if isinstance(x, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)):
|
||||
for d in x.decorator_list:
|
||||
values.append(d.lineno - 1)
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
from typing import final
|
||||
from typing import Optional
|
||||
from typing import Sequence
|
||||
from typing import TextIO
|
||||
|
||||
from .wcwidth import wcswidth
|
||||
from _pytest.compat import final
|
||||
|
||||
|
||||
# This code was initially copied from py 1.8.1, file _io/terminalwriter.py.
|
||||
|
||||
@@ -25,14 +25,12 @@ from stat import S_ISREG
|
||||
from typing import Any
|
||||
from typing import Callable
|
||||
from typing import cast
|
||||
from typing import Literal
|
||||
from typing import overload
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from . import error
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Literal
|
||||
|
||||
# Moved from local.py.
|
||||
iswin32 = sys.platform == "win32" or (getattr(os, "_name", False) == "nt")
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user