Compare commits
162 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
544b4a14d5 | ||
|
|
56dc301c50 | ||
|
|
aa05334984 | ||
|
|
4806878a7f | ||
|
|
d1d7e5d41b | ||
|
|
5b0e255e85 | ||
|
|
f0fdafeddc | ||
|
|
5049e25a6a | ||
|
|
d36c712bb0 | ||
|
|
7a0d1b387d | ||
|
|
749752d440 | ||
|
|
b91c721262 | ||
|
|
4630e2725e | ||
|
|
ea31649062 | ||
|
|
715f56dfbc | ||
|
|
99180939fe | ||
|
|
1ec5befdb7 | ||
|
|
1d3f27cef0 | ||
|
|
29703a5f51 | ||
|
|
6f7a95c32e | ||
|
|
bebfd28da3 | ||
|
|
f5844449a8 | ||
|
|
4a265ba38b | ||
|
|
910d5df6a8 | ||
|
|
00adb4e42f | ||
|
|
b2cb87fae6 | ||
|
|
189fe3ba1d | ||
|
|
d291905825 | ||
|
|
f0c7f21312 | ||
|
|
4ff7453b48 | ||
|
|
0e70acab79 | ||
|
|
8eec42f040 | ||
|
|
3adaa3d87b | ||
|
|
090e260517 | ||
|
|
117072d64c | ||
|
|
90740007a8 | ||
|
|
5e1c6ce630 | ||
|
|
61d04d3084 | ||
|
|
a3bc6df950 | ||
|
|
622995a501 | ||
|
|
36531599a4 | ||
|
|
24f8002de8 | ||
|
|
23475b6ab9 | ||
|
|
cff7843f3b | ||
|
|
356d865ad7 | ||
|
|
f46ad8d114 | ||
|
|
8dbf6a4b2d | ||
|
|
0e00069340 | ||
|
|
26a2e1aba7 | ||
|
|
7c80335c6b | ||
|
|
75f964c08d | ||
|
|
4fa819e535 | ||
|
|
61061d2147 | ||
|
|
deb4287d1c | ||
|
|
7aac48c418 | ||
|
|
3b60e36dbb | ||
|
|
162d737f68 | ||
|
|
d884164160 | ||
|
|
9811ebdc57 | ||
|
|
1c0242dec1 | ||
|
|
a5863ca760 | ||
|
|
8077168387 | ||
|
|
afa899d5e1 | ||
|
|
8de9b1be56 | ||
|
|
b532d15fe7 | ||
|
|
73702ca88b | ||
|
|
d7b0389d1a | ||
|
|
06206bcf37 | ||
|
|
9924432a2e | ||
|
|
c7ac3379e7 | ||
|
|
6cd61390c2 | ||
|
|
994909270f | ||
|
|
e1df9dbf0d | ||
|
|
ab44d3d733 | ||
|
|
d76aa8b428 | ||
|
|
4dfd2947bc | ||
|
|
536177bb56 | ||
|
|
dc7bf518b3 | ||
|
|
8be9684ab2 | ||
|
|
d4879c7afb | ||
|
|
d42f5a41a5 | ||
|
|
a176ff77bc | ||
|
|
7f24cc2feb | ||
|
|
d0c663a628 | ||
|
|
f7409f8685 | ||
|
|
10fcac7f90 | ||
|
|
8942a05cfe | ||
|
|
66c1a120ba | ||
|
|
8cdf9d43a9 | ||
|
|
2ddc330b62 | ||
|
|
1c0ab3c2a3 | ||
|
|
7ff91d8127 | ||
|
|
64c71daa21 | ||
|
|
c7f9fda42d | ||
|
|
42fb1f7ede | ||
|
|
c00a43a17d | ||
|
|
1dc612f7d4 | ||
|
|
e5bd7fb053 | ||
|
|
256a9e0027 | ||
|
|
d2d7b97a70 | ||
|
|
23f6adc760 | ||
|
|
172b82875a | ||
|
|
277857b026 | ||
|
|
d3ab56f531 | ||
|
|
209d99102d | ||
|
|
6d31684da5 | ||
|
|
4ee280ae1f | ||
|
|
8b8cba369c | ||
|
|
05008f6b55 | ||
|
|
83182b82ea | ||
|
|
31d5cedc6d | ||
|
|
91b3ff1bb7 | ||
|
|
b66dc80008 | ||
|
|
69ad2026f6 | ||
|
|
9d900930db | ||
|
|
a8230d77f4 | ||
|
|
b7d908f4a5 | ||
|
|
b9dd0e6210 | ||
|
|
57f3dc19b9 | ||
|
|
ed67312bca | ||
|
|
2d24c062b6 | ||
|
|
d940d0b657 | ||
|
|
f9f092a5e6 | ||
|
|
b7bc52f770 | ||
|
|
d1eb89d694 | ||
|
|
5e097970df | ||
|
|
0601f5cdad | ||
|
|
a7268aaa5d | ||
|
|
1f736a663d | ||
|
|
2344982d7f | ||
|
|
63c9ad02f4 | ||
|
|
8b7aeefd7d | ||
|
|
f1ac0eeef0 | ||
|
|
82424c9270 | ||
|
|
dbb8c146f0 | ||
|
|
8d686a8e46 | ||
|
|
9d1082bd30 | ||
|
|
490c7c7262 | ||
|
|
2ffbe41ae5 | ||
|
|
8f2fd8ffc0 | ||
|
|
64eb9ea670 | ||
|
|
7e10c8191d | ||
|
|
6659fe0edc | ||
|
|
4ee984ff0a | ||
|
|
b96e0a71a6 | ||
|
|
be722652f0 | ||
|
|
58ec5bea35 | ||
|
|
0be03d7fe4 | ||
|
|
2879d25812 | ||
|
|
fe69a2cfb7 | ||
|
|
af9dfc604d | ||
|
|
8c65eae5f4 | ||
|
|
2228ccbfb4 | ||
|
|
7e5ad31428 | ||
|
|
688bbefed1 | ||
|
|
5b3867fd65 | ||
|
|
36ef545b2d | ||
|
|
1d368e0ed4 | ||
|
|
faea273c93 | ||
|
|
4ad61cbcf6 | ||
|
|
ceeb7bd085 | ||
|
|
1cecdf6619 |
@@ -24,3 +24,5 @@ exclude_lines =
|
||||
\#\s*pragma: no cover
|
||||
^\s*raise NotImplementedError\b
|
||||
^\s*return NotImplemented\b
|
||||
|
||||
^\s*if TYPE_CHECKING:
|
||||
|
||||
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -11,5 +11,13 @@ Here is a quick checklist that should be present in PRs.
|
||||
Unless your change is trivial or a small documentation fix (e.g., a typo or reword of a small section) please:
|
||||
|
||||
- [ ] Create a new changelog file in the `changelog` folder, with a name like `<ISSUE NUMBER>.<TYPE>.rst`. See [changelog/README.rst](https://github.com/pytest-dev/pytest/blob/master/changelog/README.rst) for details.
|
||||
|
||||
Write sentences in the **past or present tense**, examples:
|
||||
|
||||
* *Improved verbose diff output with sequences.*
|
||||
* *Terminal summary statistics now use multiple colors.*
|
||||
|
||||
Also make sure to end the sentence with a `.`.
|
||||
|
||||
- [ ] Add yourself to `AUTHORS` in alphabetical order.
|
||||
-->
|
||||
|
||||
191
.github/workflows/main.yml
vendored
Normal file
191
.github/workflows/main.yml
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
# evaluating GitHub actions for CI, disregard failures when evaluating PRs
|
||||
#
|
||||
# this is still missing:
|
||||
# - deploy
|
||||
# - upload github notes
|
||||
#
|
||||
name: main
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- "*"
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
name: [
|
||||
"windows-py35",
|
||||
"windows-py36",
|
||||
"windows-py37",
|
||||
"windows-py37-pluggy",
|
||||
"windows-py38",
|
||||
|
||||
"ubuntu-py35",
|
||||
"ubuntu-py36",
|
||||
"ubuntu-py37",
|
||||
"ubuntu-py37-pluggy",
|
||||
"ubuntu-py37-freeze",
|
||||
"ubuntu-py38",
|
||||
"ubuntu-pypy3",
|
||||
|
||||
"macos-py37",
|
||||
"macos-py38",
|
||||
|
||||
"linting",
|
||||
]
|
||||
|
||||
include:
|
||||
- name: "windows-py35"
|
||||
python: "3.5"
|
||||
os: windows-latest
|
||||
tox_env: "py35-xdist"
|
||||
- name: "windows-py36"
|
||||
python: "3.6"
|
||||
os: windows-latest
|
||||
tox_env: "py36-xdist"
|
||||
- name: "windows-py37"
|
||||
python: "3.7"
|
||||
os: windows-latest
|
||||
tox_env: "py37-twisted-numpy"
|
||||
- name: "windows-py37-pluggy"
|
||||
python: "3.7"
|
||||
os: windows-latest
|
||||
tox_env: "py37-pluggymaster-xdist"
|
||||
- name: "windows-py38"
|
||||
python: "3.8"
|
||||
os: windows-latest
|
||||
tox_env: "py38"
|
||||
|
||||
- name: "ubuntu-py35"
|
||||
python: "3.5"
|
||||
os: ubuntu-latest
|
||||
tox_env: "py35-xdist"
|
||||
- name: "ubuntu-py36"
|
||||
python: "3.6"
|
||||
os: ubuntu-latest
|
||||
tox_env: "py36-xdist"
|
||||
- name: "ubuntu-py37"
|
||||
python: "3.7"
|
||||
os: ubuntu-latest
|
||||
tox_env: "py37-lsof-numpy-oldattrs-pexpect-twisted"
|
||||
- name: "ubuntu-py37-pluggy"
|
||||
python: "3.7"
|
||||
os: ubuntu-latest
|
||||
tox_env: "py37-pluggymaster-xdist"
|
||||
- name: "ubuntu-py37-freeze"
|
||||
python: "3.7"
|
||||
os: ubuntu-latest
|
||||
tox_env: "py37-freeze"
|
||||
# coverage does not apply for freeze test, skip it
|
||||
skip_coverage: true
|
||||
- name: "ubuntu-py38"
|
||||
python: "3.8"
|
||||
os: ubuntu-latest
|
||||
tox_env: "py38-xdist"
|
||||
- name: "ubuntu-pypy3"
|
||||
python: "pypy3"
|
||||
os: ubuntu-latest
|
||||
tox_env: "pypy3-xdist"
|
||||
# coverage too slow with pypy3, skip it
|
||||
skip_coverage: true
|
||||
|
||||
- name: "macos-py37"
|
||||
python: "3.7"
|
||||
os: macos-latest
|
||||
tox_env: "py37-xdist"
|
||||
- name: "macos-py38"
|
||||
python: "3.8"
|
||||
os: macos-latest
|
||||
tox_env: "py38-xdist"
|
||||
|
||||
- name: "linting"
|
||||
python: "3.7"
|
||||
os: ubuntu-latest
|
||||
tox_env: "linting,docs,doctesting"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Set up Python ${{ matrix.python }}
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install tox coverage
|
||||
|
||||
- name: Test without coverage
|
||||
if: "matrix.skip_coverage"
|
||||
run: "tox -e ${{ matrix.tox_env }}"
|
||||
|
||||
- name: Test with coverage
|
||||
if: "! matrix.skip_coverage"
|
||||
env:
|
||||
_PYTEST_TOX_COVERAGE_RUN: "coverage run -m"
|
||||
COVERAGE_PROCESS_START: ".coveragerc"
|
||||
_PYTEST_TOX_EXTRA_DEP: "coverage-enable-subprocess"
|
||||
run: "tox -e ${{ matrix.tox_env }}"
|
||||
|
||||
- name: Prepare coverage token
|
||||
if: success() && !matrix.skip_coverage && ( github.repository == 'pytest-dev/pytest' || github.event_name == 'pull_request' )
|
||||
run: |
|
||||
python scripts/append_codecov_token.py
|
||||
|
||||
- name: Combine coverage
|
||||
if: success() && !matrix.skip_coverage
|
||||
run: |
|
||||
python -m coverage combine
|
||||
python -m coverage xml
|
||||
|
||||
- name: Codecov upload
|
||||
if: success() && !matrix.skip_coverage
|
||||
uses: codecov/codecov-action@v1
|
||||
with:
|
||||
token: ${{ secrets.codecov }}
|
||||
file: ./coverage.xml
|
||||
flags: ${{ runner.os }}
|
||||
fail_ci_if_error: false
|
||||
name: ${{ matrix.name }}
|
||||
|
||||
deploy:
|
||||
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') && github.repository == 'pytest-dev/pytest'
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
needs: [build]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: "3.7"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install --upgrade wheel setuptools tox
|
||||
- name: Build package
|
||||
run: |
|
||||
python setup.py sdist bdist_wheel
|
||||
- name: Publish package to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@master
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.pypi_token }}
|
||||
- name: Publish GitHub release notes
|
||||
env:
|
||||
GH_RELEASE_NOTES_TOKEN: ${{ secrets.release_notes }}
|
||||
run: |
|
||||
sudo apt-get install pandoc
|
||||
tox -e publish-gh-release-notes
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -25,12 +25,14 @@ src/_pytest/_version.py
|
||||
|
||||
doc/*/_build
|
||||
doc/*/.doctrees
|
||||
doc/*/_changelog_towncrier_draft.rst
|
||||
build/
|
||||
dist/
|
||||
*.egg-info
|
||||
issue/
|
||||
env/
|
||||
.env/
|
||||
.venv/
|
||||
3rdparty/
|
||||
.tox
|
||||
.cache
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
exclude: doc/en/example/py2py3/test_py2.py
|
||||
repos:
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 19.10b0
|
||||
@@ -9,7 +8,7 @@ repos:
|
||||
rev: v1.0.0
|
||||
hooks:
|
||||
- id: blacken-docs
|
||||
additional_dependencies: [black==19.3b0]
|
||||
additional_dependencies: [black==19.10b0]
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v2.2.3
|
||||
hooks:
|
||||
@@ -37,12 +36,8 @@ repos:
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py3-plus]
|
||||
- repo: https://github.com/pre-commit/pygrep-hooks
|
||||
rev: v1.4.0
|
||||
hooks:
|
||||
- id: rst-backticks
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v0.740
|
||||
rev: v0.761 # NOTE: keep this in sync with setup.py.
|
||||
hooks:
|
||||
- id: mypy
|
||||
files: ^(src/|testing/)
|
||||
@@ -52,7 +47,7 @@ repos:
|
||||
- id: rst
|
||||
name: rst
|
||||
entry: rst-lint --encoding utf-8
|
||||
files: ^(CHANGELOG.rst|HOWTORELEASE.rst|README.rst|TIDELIFT.rst|changelog/.*)$
|
||||
files: ^(HOWTORELEASE.rst|README.rst|TIDELIFT.rst)$
|
||||
language: python
|
||||
additional_dependencies: [pygments, restructuredtext_lint]
|
||||
- id: changelogs-rst
|
||||
|
||||
36
.travis.yml
36
.travis.yml
@@ -2,10 +2,8 @@ language: python
|
||||
dist: xenial
|
||||
stages:
|
||||
- baseline
|
||||
- name: test
|
||||
if: repo = pytest-dev/pytest AND tag IS NOT present
|
||||
- name: deploy
|
||||
if: repo = pytest-dev/pytest AND tag IS present
|
||||
- test
|
||||
|
||||
python: '3.7'
|
||||
cache: false
|
||||
|
||||
@@ -52,8 +50,10 @@ jobs:
|
||||
- env: TOXENV=pypy3-xdist
|
||||
python: 'pypy3'
|
||||
|
||||
- env: TOXENV=py35-xdist
|
||||
python: '3.5'
|
||||
# Coverage for Python 3.5.{0,1} specific code, mostly typing related.
|
||||
- env: TOXENV=py35 PYTEST_COVERAGE=1 PYTEST_ADDOPTS="-k test_raises_cyclic_reference"
|
||||
python: '3.5.1'
|
||||
dist: trusty
|
||||
|
||||
# Specialized factors for py37.
|
||||
- env: TOXENV=py37-pluggymaster-xdist
|
||||
@@ -70,30 +70,6 @@ jobs:
|
||||
directories:
|
||||
- $HOME/.cache/pre-commit
|
||||
|
||||
- stage: deploy
|
||||
python: '3.6'
|
||||
install: pip install -U setuptools setuptools_scm tox
|
||||
script: skip
|
||||
# token to upload github release notes: GH_RELEASE_NOTES_TOKEN
|
||||
env:
|
||||
- secure: "OjOeL7/0JUDkV00SsTs732e8vQjHynpbG9FKTNtZZJ+1Zn4Cib+hAlwmlBnvVukML0X60YpcfjnC4quDOIGLPsh5zeXnvJmYtAIIUNQXjWz8NhcGYrhyzuP1rqV22U68RTCdmOq3lMYU/W2acwHP7T49PwJtOiUM5kF120UAQ0Zi5EmkqkIvH8oM5mO9Dlver+/U7Htpz9rhKrHBXQNCMZI6yj2aUyukqB2PN2fjAlDbCF//+FmvYw9NjT4GeFOSkTCf4ER9yfqs7yglRfwiLtOCZ2qKQhWZNsSJDB89rxIRXWavJUjJKeY2EW2/NkomYJDpqJLIF4JeFRw/HhA47CYPeo6BJqyyNV+0CovL1frpWfi9UQw2cMbgFUkUIUk3F6DD59PHNIOX2R/HX56dQsw7WKl3QuHlCOkICXYg8F7Ta684IoKjeTX03/6QNOkURfDBwfGszY0FpbxrjCSWKom6RyZdyidnESaxv9RzjcIRZVh1rp8KMrwS1OrwRSdG0zjlsPr49hWMenN/8fKgcHTV4/r1Tj6mip0dorSRCrgUNIeRBKgmui6FS8642ab5JNKOxMteVPVR2sFuhjOQ0Jy+PmvceYY9ZMWc3+/B/KVh0dZ3hwvLGZep/vxDS2PwCA5/xw31714vT5LxidKo8yECjBynMU/wUTTS695D3NY="
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
# required by publish_gh_release_notes
|
||||
- pandoc
|
||||
after_deploy: tox -e publish_gh_release_notes
|
||||
deploy:
|
||||
provider: pypi
|
||||
user: nicoddemus
|
||||
distributions: sdist bdist_wheel
|
||||
skip_upload_docs: true
|
||||
password:
|
||||
secure: xanTgTUu6XDQVqB/0bwJQXoDMnU5tkwZc5koz6mBkkqZhKdNOi2CLoC1XhiSZ+ah24l4V1E0GAqY5kBBcy9d7NVe4WNg4tD095LsHw+CRU6/HCVIFfyk2IZ+FPAlguesCcUiJSXOrlBF+Wj68wEvLoK7EoRFbJeiZ/f91Ww1sbtDlqXABWGHrmhPJL5Wva7o7+wG7JwJowqdZg1pbQExsCc7b53w4v2RBu3D6TJaTAzHiVsW+nUSI67vKI/uf+cR/OixsTfy37wlHgSwihYmrYLFls3V0bSpahCim3bCgMaFZx8S8xrdgJ++PzBCof2HeflFKvW+VCkoYzGEG4NrTWJoNz6ni4red9GdvfjGH3YCjAKS56h9x58zp2E5rpsb/kVq5/45xzV+dq6JRuhQ1nJWjBC6fSKAc/bfwnuFK3EBxNLkvBssLHvsNjj5XG++cB8DdS9wVGUqjpoK4puaXUWFqy4q3S9F86HEsKNgExtieA9qNx+pCIZVs6JCXZNjr0I5eVNzqJIyggNgJG6RyravsU35t9Zd9doL5g4Y7UKmAGTn1Sz24HQ4sMQgXdm2SyD8gEK5je4tlhUvfGtDvMSlstq71kIn9nRpFnqB6MFlbYSEAZmo8dGbCquoUc++6Rum208wcVbrzzVtGlXB/Ow9AbFMYeAGA0+N/K1e59c=
|
||||
on:
|
||||
tags: true
|
||||
repo: pytest-dev/pytest
|
||||
|
||||
before_script:
|
||||
- |
|
||||
# Do not (re-)upload coverage with cron runs.
|
||||
|
||||
4
AUTHORS
4
AUTHORS
@@ -54,6 +54,7 @@ Ceridwen
|
||||
Charles Cloud
|
||||
Charnjit SiNGH (CCSJ)
|
||||
Chris Lamb
|
||||
Chris NeJame
|
||||
Christian Boelsen
|
||||
Christian Fetzer
|
||||
Christian Neumüller
|
||||
@@ -61,6 +62,7 @@ Christian Theunert
|
||||
Christian Tismer
|
||||
Christopher Gilling
|
||||
Christopher Dignam
|
||||
Claudio Madotto
|
||||
CrazyMerlyn
|
||||
Cyrus Maden
|
||||
Damian Skrzypczak
|
||||
@@ -165,6 +167,7 @@ Marcelo Duarte Trevisani
|
||||
Marcin Bachry
|
||||
Marco Gorelli
|
||||
Mark Abramowitz
|
||||
Mark Dickinson
|
||||
Markus Unterwaditzer
|
||||
Martijn Faassen
|
||||
Martin Altmayer
|
||||
@@ -233,6 +236,7 @@ Samuele Pedroni
|
||||
Sankt Petersbug
|
||||
Segev Finer
|
||||
Serhii Mozghovyi
|
||||
Seth Junot
|
||||
Simon Gomizelj
|
||||
Skylar Downes
|
||||
Srinivas Reddy Thatiparthy
|
||||
|
||||
7513
CHANGELOG.rst
7513
CHANGELOG.rst
File diff suppressed because it is too large
Load Diff
@@ -51,7 +51,8 @@ Fix bugs
|
||||
|
||||
Look through the `GitHub issues for bugs <https://github.com/pytest-dev/pytest/labels/type:%20bug>`_.
|
||||
|
||||
:ref:`Talk <contact>` to developers to find out how you can fix specific bugs.
|
||||
:ref:`Talk <contact>` to developers to find out how you can fix specific bugs. To indicate that you are going
|
||||
to work on a particular issue, add a comment to that effect on the specific issue.
|
||||
|
||||
Don't forget to check the issue trackers of your favourite plugins, too!
|
||||
|
||||
@@ -262,6 +263,19 @@ Here is a simple overview, with pytest-specific bits:
|
||||
|
||||
When committing, ``pre-commit`` will re-format the files if necessary.
|
||||
|
||||
#. If instead of using ``tox`` you prefer to run the tests directly, then we suggest to create a virtual environment and use
|
||||
an editable install with the ``testing`` extra::
|
||||
|
||||
$ python3 -m venv .venv
|
||||
$ source .venv/bin/activate # Linux
|
||||
$ .venv/Scripts/activate.bat # Windows
|
||||
$ pip install -e ".[testing]"
|
||||
|
||||
Afterwards, you can edit the files and run pytest normally::
|
||||
|
||||
$ pytest testing/test_config.py
|
||||
|
||||
|
||||
#. Commit and push once your tests pass and you are happy with your change(s)::
|
||||
|
||||
$ git commit -a -m "<commit message>"
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2004-2019 Holger Krekel and others
|
||||
Copyright (c) 2004-2020 Holger Krekel and others
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
|
||||
@@ -112,12 +112,12 @@ Support pytest
|
||||
--------------
|
||||
|
||||
`Open Collective`_ is an online funding platform for open and transparent communities.
|
||||
It provide tools to raise money and share your finances in full transparency.
|
||||
It provides tools to raise money and share your finances in full transparency.
|
||||
|
||||
It is the platform of choice for individuals and companies that want to make one-time or
|
||||
monthly donations directly to the project.
|
||||
|
||||
See more datails in the `pytest collective`_.
|
||||
See more details in the `pytest collective`_.
|
||||
|
||||
.. _Open Collective: https://opencollective.com
|
||||
.. _pytest collective: https://opencollective.com/pytest
|
||||
@@ -137,7 +137,7 @@ Save time, reduce risk, and improve code health, while paying the maintainers of
|
||||
Security
|
||||
^^^^^^^^
|
||||
|
||||
pytest has never been associated with a security vunerability, but in any case, to report a
|
||||
pytest has never been associated with a security vulnerability, but in any case, to report a
|
||||
security vulnerability please use the `Tidelift security contact <https://tidelift.com/security>`_.
|
||||
Tidelift will coordinate the fix and disclosure.
|
||||
|
||||
@@ -145,7 +145,7 @@ Tidelift will coordinate the fix and disclosure.
|
||||
License
|
||||
-------
|
||||
|
||||
Copyright Holger Krekel and others, 2004-2019.
|
||||
Copyright Holger Krekel and others, 2004-2020.
|
||||
|
||||
Distributed under the terms of the `MIT`_ license, pytest is free and open source software.
|
||||
|
||||
|
||||
@@ -39,6 +39,9 @@ jobs:
|
||||
py37-pluggymaster-xdist:
|
||||
python.version: '3.7'
|
||||
tox.env: 'py37-pluggymaster-xdist'
|
||||
py38-xdist:
|
||||
python.version: '3.8'
|
||||
tox.env: 'py38-xdist'
|
||||
maxParallel: 10
|
||||
|
||||
steps:
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
This directory contains "newsfragments" which are short files that contain a small **ReST**-formatted
|
||||
text that will be added to the next ``CHANGELOG``.
|
||||
|
||||
The ``CHANGELOG`` will be read by users, so this description should be aimed to pytest users
|
||||
The ``CHANGELOG`` will be read by **users**, so this description should be aimed to pytest users
|
||||
instead of describing internal changes which are only relevant to the developers.
|
||||
|
||||
Make sure to use full sentences with correct case and punctuation, for example::
|
||||
Make sure to use full sentences in the **past or present tense** and use punctuation, examples::
|
||||
|
||||
Fix issue with non-ascii messages from the ``warnings`` module.
|
||||
Improved verbose diff output with sequences.
|
||||
|
||||
Terminal summary statistics now use multiple colors.
|
||||
|
||||
Each file should be named like ``<ISSUE>.<TYPE>.rst``, where
|
||||
``<ISSUE>`` is an issue number, and ``<TYPE>`` is one of:
|
||||
@@ -29,6 +31,7 @@ changelog using that instead.
|
||||
If you are not sure what issue type to use, don't hesitate to ask in your PR.
|
||||
|
||||
``towncrier`` preserves multiple paragraphs and formatting (code blocks, lists, and so on), but for entries
|
||||
other than ``features`` it is usually better to stick to a single paragraph to keep it concise. You can install
|
||||
``towncrier`` and then run ``towncrier --draft``
|
||||
if you want to get a preview of how your change will look in the final release notes.
|
||||
other than ``features`` it is usually better to stick to a single paragraph to keep it concise.
|
||||
|
||||
You can also run ``tox -e docs`` to build the documentation
|
||||
with the draft changelog (``doc/en/_build/changelog.html``) if you want to get a preview of how your change will look in the final release notes.
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
``repr`` of ``ExceptionInfo`` objects has been improved to honor the ``__repr__`` method of the underlying exception.
|
||||
@@ -6,6 +6,9 @@ Release announcements
|
||||
:maxdepth: 2
|
||||
|
||||
|
||||
release-5.3.3
|
||||
release-5.3.2
|
||||
release-5.3.1
|
||||
release-5.3.0
|
||||
release-5.2.4
|
||||
release-5.2.3
|
||||
@@ -18,6 +21,10 @@ Release announcements
|
||||
release-5.1.0
|
||||
release-5.0.1
|
||||
release-5.0.0
|
||||
release-4.6.9
|
||||
release-4.6.8
|
||||
release-4.6.7
|
||||
release-4.6.6
|
||||
release-4.6.5
|
||||
release-4.6.4
|
||||
release-4.6.3
|
||||
|
||||
@@ -7,7 +7,7 @@ see below for summary and detailed lists. A lot of long-deprecated code
|
||||
has been removed, resulting in a much smaller and cleaner
|
||||
implementation. See the new docs with examples here:
|
||||
|
||||
http://pytest.org/2.0.0/index.html
|
||||
http://pytest.org/en/latest/index.html
|
||||
|
||||
A note on packaging: pytest used to part of the "py" distribution up
|
||||
until version py-1.3.4 but this has changed now: pytest-2.0.0 only
|
||||
@@ -36,12 +36,12 @@ New Features
|
||||
|
||||
import pytest ; pytest.main(arglist, pluginlist)
|
||||
|
||||
see http://pytest.org/2.0.0/usage.html for details.
|
||||
see http://pytest.org/en/latest/usage.html for details.
|
||||
|
||||
- new and better reporting information in assert expressions
|
||||
if comparing lists, sequences or strings.
|
||||
|
||||
see http://pytest.org/2.0.0/assert.html#newreport
|
||||
see http://pytest.org/en/latest/assert.html#newreport
|
||||
|
||||
- new configuration through ini-files (setup.cfg or tox.ini recognized),
|
||||
for example::
|
||||
@@ -50,7 +50,7 @@ New Features
|
||||
norecursedirs = .hg data* # don't ever recurse in such dirs
|
||||
addopts = -x --pyargs # add these command line options by default
|
||||
|
||||
see http://pytest.org/2.0.0/customize.html
|
||||
see http://pytest.org/en/latest/customize.html
|
||||
|
||||
- improved standard unittest support. In general py.test should now
|
||||
better be able to run custom unittest.TestCases like twisted trial
|
||||
|
||||
@@ -57,7 +57,7 @@ Changes between 2.0.0 and 2.0.1
|
||||
- refinements to "collecting" output on non-ttys
|
||||
- refine internal plugin registration and --traceconfig output
|
||||
- introduce a mechanism to prevent/unregister plugins from the
|
||||
command line, see http://pytest.org/latest/plugins.html#cmdunregister
|
||||
command line, see http://pytest.org/en/latest/plugins.html#cmdunregister
|
||||
- activate resultlog plugin by default
|
||||
- fix regression wrt yielded tests which due to the
|
||||
collection-before-running semantics were not
|
||||
|
||||
@@ -9,7 +9,7 @@ with these improvements:
|
||||
|
||||
- new @pytest.mark.parametrize decorator to run tests with different arguments
|
||||
- new metafunc.parametrize() API for parametrizing arguments independently
|
||||
- see examples at http://pytest.org/latest/example/parametrize.html
|
||||
- see examples at http://pytest.org/en/latest/example/parametrize.html
|
||||
- NOTE that parametrize() related APIs are still a bit experimental
|
||||
and might change in future releases.
|
||||
|
||||
@@ -18,7 +18,7 @@ with these improvements:
|
||||
- "-m markexpr" option for selecting tests according to their mark
|
||||
- a new "markers" ini-variable for registering test markers for your project
|
||||
- the new "--strict" bails out with an error if using unregistered markers.
|
||||
- see examples at http://pytest.org/latest/example/markers.html
|
||||
- see examples at http://pytest.org/en/latest/example/markers.html
|
||||
|
||||
* duration profiling: new "--duration=N" option showing the N slowest test
|
||||
execution or setup/teardown calls. This is most useful if you want to
|
||||
@@ -78,7 +78,7 @@ Changes between 2.1.3 and 2.2.0
|
||||
or through plugin hooks. Also introduce a "--strict" option which
|
||||
will treat unregistered markers as errors
|
||||
allowing to avoid typos and maintain a well described set of markers
|
||||
for your test suite. See examples at http://pytest.org/latest/mark.html
|
||||
for your test suite. See examples at http://pytest.org/en/latest/mark.html
|
||||
and its links.
|
||||
- issue50: introduce "-m marker" option to select tests based on markers
|
||||
(this is a stricter and more predictable version of "-k" in that "-m"
|
||||
|
||||
@@ -3,22 +3,22 @@ pytest-2.3: improved fixtures / better unittest integration
|
||||
|
||||
pytest-2.3 comes with many major improvements for fixture/funcarg management
|
||||
and parametrized testing in Python. It is now easier, more efficient and
|
||||
more predicatable to re-run the same tests with different fixture
|
||||
more predictable to re-run the same tests with different fixture
|
||||
instances. Also, you can directly declare the caching "scope" of
|
||||
fixtures so that dependent tests throughout your whole test suite can
|
||||
re-use database or other expensive fixture objects with ease. Lastly,
|
||||
it's possible for fixture functions (formerly known as funcarg
|
||||
factories) to use other fixtures, allowing for a completely modular and
|
||||
re-useable fixture design.
|
||||
re-usable fixture design.
|
||||
|
||||
For detailed info and tutorial-style examples, see:
|
||||
|
||||
http://pytest.org/latest/fixture.html
|
||||
http://pytest.org/en/latest/fixture.html
|
||||
|
||||
Moreover, there is now support for using pytest fixtures/funcargs with
|
||||
unittest-style suites, see here for examples:
|
||||
|
||||
http://pytest.org/latest/unittest.html
|
||||
http://pytest.org/en/latest/unittest.html
|
||||
|
||||
Besides, more unittest-test suites are now expected to "simply work"
|
||||
with pytest.
|
||||
@@ -29,11 +29,11 @@ pytest-2.2.4.
|
||||
|
||||
If you are interested in the precise reasoning (including examples) of the
|
||||
pytest-2.3 fixture evolution, please consult
|
||||
http://pytest.org/latest/funcarg_compare.html
|
||||
http://pytest.org/en/latest/funcarg_compare.html
|
||||
|
||||
For general info on installation and getting started:
|
||||
|
||||
http://pytest.org/latest/getting-started.html
|
||||
http://pytest.org/en/latest/getting-started.html
|
||||
|
||||
Docs and PDF access as usual at:
|
||||
|
||||
@@ -94,7 +94,7 @@ Changes between 2.2.4 and 2.3.0
|
||||
- pluginmanager.register(...) now raises ValueError if the
|
||||
plugin has been already registered or the name is taken
|
||||
|
||||
- fix issue159: improve http://pytest.org/latest/faq.html
|
||||
- fix issue159: improve http://pytest.org/en/latest/faq.html
|
||||
especially with respect to the "magic" history, also mention
|
||||
pytest-django, trial and unittest integration.
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ comes with the following fixes and features:
|
||||
- yielded test functions will now have autouse-fixtures active but
|
||||
cannot accept fixtures as funcargs - it's anyway recommended to
|
||||
rather use the post-2.0 parametrize features instead of yield, see:
|
||||
http://pytest.org/latest/example/parametrize.html
|
||||
http://pytest.org/en/latest/example/parametrize.html
|
||||
- fix autouse-issue where autouse-fixtures would not be discovered
|
||||
if defined in an a/conftest.py file and tests in a/tests/test_some.py
|
||||
- fix issue226 - LIFO ordering for fixture teardowns
|
||||
|
||||
@@ -7,7 +7,7 @@ from a few supposedly very minor incompatibilities. See below for
|
||||
a full list of details. A few feature highlights:
|
||||
|
||||
- new yield-style fixtures `pytest.yield_fixture
|
||||
<http://pytest.org/latest/yieldfixture.html>`_, allowing to use
|
||||
<http://pytest.org/en/latest/yieldfixture.html>`_, allowing to use
|
||||
existing with-style context managers in fixture functions.
|
||||
|
||||
- improved pdb support: ``import pdb ; pdb.set_trace()`` now works
|
||||
|
||||
@@ -91,7 +91,7 @@ holger krekel
|
||||
it might be the cause for other finalizers to fail.
|
||||
|
||||
- fix ordering when mock.patch or other standard decorator-wrappings
|
||||
are used with test methods. This fixues issue346 and should
|
||||
are used with test methods. This fixes issue346 and should
|
||||
help with random "xdist" collection failures. Thanks to
|
||||
Ronny Pfannschmidt and Donald Stufft for helping to isolate it.
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ holger krekel
|
||||
- fix issue435: make reload() work when assert rewriting is active.
|
||||
Thanks Daniel Hahler.
|
||||
|
||||
- fix issue616: conftest.py files and their contained fixutres are now
|
||||
- fix issue616: conftest.py files and their contained fixtures are now
|
||||
properly considered for visibility, independently from the exact
|
||||
current working directory and test arguments that are used.
|
||||
Many thanks to Eric Siegerman and his PR235 which contains
|
||||
@@ -52,7 +52,7 @@ holger krekel
|
||||
- add ability to set command line options by environment variable PYTEST_ADDOPTS.
|
||||
|
||||
- added documentation on the new pytest-dev teams on bitbucket and
|
||||
github. See https://pytest.org/latest/contributing.html .
|
||||
github. See https://pytest.org/en/latest/contributing.html .
|
||||
Thanks to Anatoly for pushing and initial work on this.
|
||||
|
||||
- fix issue650: new option ``--docttest-ignore-import-errors`` which
|
||||
|
||||
@@ -131,7 +131,7 @@ The py.test Development Team
|
||||
with same name.
|
||||
|
||||
|
||||
.. _`traceback style docs`: https://pytest.org/latest/usage.html#modifying-python-traceback-printing
|
||||
.. _`traceback style docs`: https://pytest.org/en/latest/usage.html#modifying-python-traceback-printing
|
||||
|
||||
.. _#1422: https://github.com/pytest-dev/pytest/issues/1422
|
||||
.. _#1379: https://github.com/pytest-dev/pytest/issues/1379
|
||||
|
||||
20
doc/en/announce/release-4.6.6.rst
Normal file
20
doc/en/announce/release-4.6.6.rst
Normal file
@@ -0,0 +1,20 @@
|
||||
pytest-4.6.6
|
||||
=======================================
|
||||
|
||||
pytest 4.6.6 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/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Michael Goerz
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
19
doc/en/announce/release-4.6.7.rst
Normal file
19
doc/en/announce/release-4.6.7.rst
Normal file
@@ -0,0 +1,19 @@
|
||||
pytest-4.6.7
|
||||
=======================================
|
||||
|
||||
pytest 4.6.7 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/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Bruno Oliveira
|
||||
* Daniel Hahler
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
20
doc/en/announce/release-4.6.8.rst
Normal file
20
doc/en/announce/release-4.6.8.rst
Normal file
@@ -0,0 +1,20 @@
|
||||
pytest-4.6.8
|
||||
=======================================
|
||||
|
||||
pytest 4.6.8 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/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Ryan Mast
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
21
doc/en/announce/release-4.6.9.rst
Normal file
21
doc/en/announce/release-4.6.9.rst
Normal file
@@ -0,0 +1,21 @@
|
||||
pytest-4.6.9
|
||||
=======================================
|
||||
|
||||
pytest 4.6.9 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/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Felix Yan
|
||||
* Hugo
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
26
doc/en/announce/release-5.3.1.rst
Normal file
26
doc/en/announce/release-5.3.1.rst
Normal file
@@ -0,0 +1,26 @@
|
||||
pytest-5.3.1
|
||||
=======================================
|
||||
|
||||
pytest 5.3.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/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Daniel Hahler
|
||||
* Felix Yan
|
||||
* Florian Bruhin
|
||||
* Mark Dickinson
|
||||
* Nikolay Kondratyev
|
||||
* Steffen Schroeder
|
||||
* Zac Hatfield-Dodds
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
26
doc/en/announce/release-5.3.2.rst
Normal file
26
doc/en/announce/release-5.3.2.rst
Normal file
@@ -0,0 +1,26 @@
|
||||
pytest-5.3.2
|
||||
=======================================
|
||||
|
||||
pytest 5.3.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/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Claudio Madotto
|
||||
* Daniel Hahler
|
||||
* Jared Vasquez
|
||||
* Michael Rose
|
||||
* Ran Benita
|
||||
* Ronny Pfannschmidt
|
||||
* Zac Hatfield-Dodds
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
30
doc/en/announce/release-5.3.3.rst
Normal file
30
doc/en/announce/release-5.3.3.rst
Normal file
@@ -0,0 +1,30 @@
|
||||
pytest-5.3.3
|
||||
=======================================
|
||||
|
||||
pytest 5.3.3 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Adam Johnson
|
||||
* Alexandre Mulatinho
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Chris NeJame
|
||||
* Daniel Hahler
|
||||
* Hugo van Kemenade
|
||||
* Marcelo Duarte Trevisani
|
||||
* PaulC
|
||||
* Ran Benita
|
||||
* Ryan Barner
|
||||
* Seth Junot
|
||||
* marc
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
7659
doc/en/changelog.rst
7659
doc/en/changelog.rst
File diff suppressed because it is too large
Load Diff
@@ -19,6 +19,11 @@ import os
|
||||
import sys
|
||||
|
||||
from _pytest import __version__ as version
|
||||
from _pytest.compat import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import sphinx.application
|
||||
|
||||
|
||||
release = ".".join(version.split(".")[:2])
|
||||
|
||||
@@ -62,7 +67,7 @@ master_doc = "contents"
|
||||
|
||||
# General information about the project.
|
||||
project = "pytest"
|
||||
copyright = "2015–2019, holger krekel and pytest-dev team"
|
||||
copyright = "2015–2020, holger krekel and pytest-dev team"
|
||||
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
@@ -92,7 +97,7 @@ exclude_patterns = [
|
||||
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
# default_role = None
|
||||
default_role = "literal"
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
# add_function_parentheses = True
|
||||
@@ -112,6 +117,19 @@ pygments_style = "sphinx"
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
# modindex_common_prefix = []
|
||||
|
||||
# A list of regular expressions that match URIs that should not be checked when
|
||||
# doing a linkcheck.
|
||||
linkcheck_ignore = [
|
||||
"https://github.com/numpy/numpy/blob/master/doc/release/1.16.0-notes.rst#new-deprecations",
|
||||
"https://blogs.msdn.microsoft.com/bharry/2017/06/28/testing-in-a-cloud-delivery-cadence/",
|
||||
"http://pythontesting.net/framework/pytest-introduction/",
|
||||
r"https://github.com/pytest-dev/pytest/issues/\d+",
|
||||
r"https://github.com/pytest-dev/pytest/pull/\d+",
|
||||
]
|
||||
|
||||
# The number of worker threads to use when checking links (default=5).
|
||||
linkcheck_workers = 5
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
||||
@@ -272,7 +290,7 @@ man_pages = [("usage", "pytest", "pytest usage", ["holger krekel at merlinux eu"
|
||||
epub_title = "pytest"
|
||||
epub_author = "holger krekel at merlinux eu"
|
||||
epub_publisher = "holger krekel at merlinux eu"
|
||||
epub_copyright = "2013, holger krekel et alii"
|
||||
epub_copyright = "2013-2020, holger krekel et alii"
|
||||
|
||||
# The language of the text. It defaults to the language option
|
||||
# or en if the language is not set.
|
||||
@@ -329,7 +347,30 @@ texinfo_documents = [
|
||||
intersphinx_mapping = {"python": ("https://docs.python.org/3", None)}
|
||||
|
||||
|
||||
def setup(app):
|
||||
def configure_logging(app: "sphinx.application.Sphinx") -> None:
|
||||
"""Configure Sphinx's WarningHandler to handle (expected) missing include."""
|
||||
import sphinx.util.logging
|
||||
import logging
|
||||
|
||||
class WarnLogFilter(logging.Filter):
|
||||
def filter(self, record: logging.LogRecord) -> bool:
|
||||
"""Ignore warnings about missing include with "only" directive.
|
||||
|
||||
Ref: https://github.com/sphinx-doc/sphinx/issues/2150."""
|
||||
if (
|
||||
record.msg.startswith('Problems with "include" directive path:')
|
||||
and "_changelog_towncrier_draft.rst" in record.msg
|
||||
):
|
||||
return False
|
||||
return True
|
||||
|
||||
logger = logging.getLogger(sphinx.util.logging.NAMESPACE)
|
||||
warn_handler = [x for x in logger.handlers if x.level == logging.WARNING]
|
||||
assert len(warn_handler) == 1, warn_handler
|
||||
warn_handler[0].filters.insert(0, WarnLogFilter())
|
||||
|
||||
|
||||
def setup(app: "sphinx.application.Sphinx") -> None:
|
||||
# from sphinx.ext.autodoc import cut_lines
|
||||
# app.connect('autodoc-process-docstring', cut_lines(4, what=['module']))
|
||||
app.add_object_type(
|
||||
@@ -338,3 +379,4 @@ def setup(app):
|
||||
objname="configuration value",
|
||||
indextemplate="pair: %s; configuration value",
|
||||
)
|
||||
configure_logging(app)
|
||||
|
||||
@@ -20,8 +20,6 @@ which were registered by installed plugins.
|
||||
Initialization: determining rootdir and inifile
|
||||
-----------------------------------------------
|
||||
|
||||
|
||||
|
||||
pytest determines a ``rootdir`` for each test run which depends on
|
||||
the command line arguments (specified test files, paths) and on
|
||||
the existence of *ini-files*. The determined ``rootdir`` and *ini-file* are
|
||||
@@ -30,17 +28,17 @@ printed as part of the pytest header during startup.
|
||||
Here's a summary what ``pytest`` uses ``rootdir`` for:
|
||||
|
||||
* Construct *nodeids* during collection; each test is assigned
|
||||
a unique *nodeid* which is rooted at the ``rootdir`` and takes in account full path,
|
||||
class name, function name and parametrization (if any).
|
||||
a unique *nodeid* which is rooted at the ``rootdir`` and takes into account
|
||||
the full path, class name, function name and parametrization (if any).
|
||||
|
||||
* Is used by plugins as a stable location to store project/test run specific information;
|
||||
for example, the internal :ref:`cache <cache>` plugin creates a ``.pytest_cache`` subdirectory
|
||||
in ``rootdir`` to store its cross-test run state.
|
||||
|
||||
Important to emphasize that ``rootdir`` is **NOT** used to modify ``sys.path``/``PYTHONPATH`` or
|
||||
``rootdir`` is **NOT** used to modify ``sys.path``/``PYTHONPATH`` or
|
||||
influence how modules are imported. See :ref:`pythonpath` for more details.
|
||||
|
||||
``--rootdir=path`` command-line option can be used to force a specific directory.
|
||||
The ``--rootdir=path`` command-line option can be used to force a specific directory.
|
||||
The directory passed may contain environment variables when it is used in conjunction
|
||||
with ``addopts`` in a ``pytest.ini`` file.
|
||||
|
||||
|
||||
@@ -475,10 +475,10 @@ Running it results in some skips if we don't have all the python interpreters in
|
||||
.. code-block:: pytest
|
||||
|
||||
. $ pytest -rs -q multipython.py
|
||||
ssssssssssss...ssssssssssss [100%]
|
||||
ssssssssssssssssssssssss... [100%]
|
||||
========================= short test summary info ==========================
|
||||
SKIPPED [12] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.5' not found
|
||||
SKIPPED [12] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.7' not found
|
||||
SKIPPED [12] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.6' not found
|
||||
3 passed, 24 skipped in 0.12s
|
||||
|
||||
Indirect parametrization of optional implementations/imports
|
||||
@@ -604,13 +604,13 @@ Then run ``pytest`` with verbose mode and with only the ``basic`` marker:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collecting ... collected 18 items / 15 deselected / 3 selected
|
||||
collecting ... collected 17 items / 14 deselected / 3 selected
|
||||
|
||||
test_pytest_param_example.py::test_eval[1+7-8] PASSED [ 33%]
|
||||
test_pytest_param_example.py::test_eval[basic_2+4] PASSED [ 66%]
|
||||
test_pytest_param_example.py::test_eval[basic_6*9] XFAIL [100%]
|
||||
|
||||
=============== 2 passed, 15 deselected, 1 xfailed in 0.12s ================
|
||||
=============== 2 passed, 14 deselected, 1 xfailed in 0.12s ================
|
||||
|
||||
As the result:
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
py3 = sys.version_info[0] >= 3
|
||||
|
||||
|
||||
class DummyCollector(pytest.collect.File):
|
||||
def collect(self):
|
||||
return []
|
||||
|
||||
|
||||
def pytest_pycollect_makemodule(path, parent):
|
||||
bn = path.basename
|
||||
if "py3" in bn and not py3 or ("py2" in bn and py3):
|
||||
return DummyCollector(path, parent=parent)
|
||||
@@ -1,5 +0,0 @@
|
||||
def test_exception_syntax():
|
||||
try:
|
||||
0 / 0
|
||||
except ZeroDivisionError, e:
|
||||
assert e
|
||||
@@ -1,5 +0,0 @@
|
||||
def test_exception_syntax():
|
||||
try:
|
||||
0 / 0
|
||||
except ZeroDivisionError as e:
|
||||
assert e
|
||||
@@ -436,7 +436,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
|
||||
items = [1, 2, 3]
|
||||
print("items is {!r}".format(items))
|
||||
> a, b = items.pop()
|
||||
E TypeError: 'int' object is not iterable
|
||||
E TypeError: cannot unpack non-iterable int object
|
||||
|
||||
failure_demo.py:181: TypeError
|
||||
--------------------------- Captured stdout call ---------------------------
|
||||
@@ -516,7 +516,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
|
||||
def test_z2_type_error(self):
|
||||
items = 3
|
||||
> a, b = items
|
||||
E TypeError: 'int' object is not iterable
|
||||
E TypeError: cannot unpack non-iterable int object
|
||||
|
||||
failure_demo.py:222: TypeError
|
||||
______________________ TestMoreErrors.test_startswith ______________________
|
||||
|
||||
@@ -442,7 +442,7 @@ Now we can profile which test functions execute the slowest:
|
||||
|
||||
========================= slowest 3 test durations =========================
|
||||
0.30s call test_some_are_slow.py::test_funcslow2
|
||||
0.20s call test_some_are_slow.py::test_funcslow1
|
||||
0.21s call test_some_are_slow.py::test_funcslow1
|
||||
0.11s call test_some_are_slow.py::test_funcfast
|
||||
============================ 3 passed in 0.12s =============================
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@ pytest fixtures: explicit, modular, scalable
|
||||
|
||||
|
||||
|
||||
.. _`xUnit`: http://en.wikipedia.org/wiki/XUnit
|
||||
.. _`purpose of test fixtures`: http://en.wikipedia.org/wiki/Test_fixture#Software
|
||||
.. _`Dependency injection`: http://en.wikipedia.org/wiki/Dependency_injection
|
||||
.. _`xUnit`: https://en.wikipedia.org/wiki/XUnit
|
||||
.. _`purpose of test fixtures`: https://en.wikipedia.org/wiki/Test_fixture#Software
|
||||
.. _`Dependency injection`: https://en.wikipedia.org/wiki/Dependency_injection
|
||||
|
||||
The `purpose of test fixtures`_ is to provide a fixed baseline
|
||||
upon which tests can reliably and repeatedly execute. pytest fixtures
|
||||
@@ -1035,15 +1035,19 @@ file:
|
||||
|
||||
# content of conftest.py
|
||||
|
||||
import pytest
|
||||
import tempfile
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def cleandir():
|
||||
newpath = tempfile.mkdtemp()
|
||||
os.chdir(newpath)
|
||||
yield
|
||||
shutil.rmtree(newpath)
|
||||
|
||||
and declare its use in a test module via a ``usefixtures`` marker:
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ Install ``pytest``
|
||||
.. code-block:: bash
|
||||
|
||||
$ pytest --version
|
||||
This is pytest version 5.x.y, imported from $PYTHON_PREFIX/lib/python3.6/site-packages/pytest.py
|
||||
This is pytest version 5.x.y, imported from $PYTHON_PREFIX/lib/python3.7/site-packages/pytest/__init__.py
|
||||
|
||||
.. _`simpletest`:
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ More details can be found in the `original PR <https://github.com/pytest-dev/pyt
|
||||
|
||||
.. note::
|
||||
|
||||
in a future major relase of pytest we will introduce class based markers,
|
||||
in a future major release of pytest we will introduce class based markers,
|
||||
at which point markers will no longer be limited to instances of :py:class:`Mark`.
|
||||
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ It provide tools to raise money and share your finances in full transparency.
|
||||
It is the platform of choice for individuals and companies that want to make one-time or
|
||||
monthly donations directly to the project.
|
||||
|
||||
See more datails in the `pytest collective`_.
|
||||
See more details in the `pytest collective`_.
|
||||
|
||||
.. _Open Collective: https://opencollective.com
|
||||
.. _pytest collective: https://opencollective.com/pytest
|
||||
@@ -112,7 +112,7 @@ Save time, reduce risk, and improve code health, while paying the maintainers of
|
||||
Security
|
||||
^^^^^^^^
|
||||
|
||||
pytest has never been associated with a security vunerability, but in any case, to report a
|
||||
pytest has never been associated with a security vulnerability, but in any case, to report a
|
||||
security vulnerability please use the `Tidelift security contact <https://tidelift.com/security>`_.
|
||||
Tidelift will coordinate the fix and disclosure.
|
||||
|
||||
@@ -120,7 +120,7 @@ Tidelift will coordinate the fix and disclosure.
|
||||
License
|
||||
-------
|
||||
|
||||
Copyright Holger Krekel and others, 2004-2017.
|
||||
Copyright Holger Krekel and others, 2004-2020.
|
||||
|
||||
Distributed under the terms of the `MIT`_ license, pytest is free and open source software.
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ Distributed under the terms of the `MIT`_ license, pytest is free and open sourc
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2004-2019 Holger Krekel and others
|
||||
Copyright (c) 2004-2020 Holger Krekel and others
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
|
||||
@@ -73,7 +73,6 @@ Some organisations using pytest
|
||||
|
||||
* `Square Kilometre Array, Cape Town <http://ska.ac.za/>`_
|
||||
* `Some Mozilla QA people <http://www.theautomatedtester.co.uk/blog/2011/pytest_and_xdist_plugin.html>`_ use pytest to distribute their Selenium tests
|
||||
* `Tandberg <http://www.tandberg.com/>`_
|
||||
* `Shootq <http://web.shootq.com/>`_
|
||||
* `Stups department of Heinrich Heine University Duesseldorf <http://www.stups.uni-duesseldorf.de/projects.php>`_
|
||||
* cellzome
|
||||
|
||||
@@ -1,31 +1,97 @@
|
||||
Python 2.7 and 3.4 support plan
|
||||
===============================
|
||||
Python 2.7 and 3.4 support
|
||||
==========================
|
||||
|
||||
Python 2.7 EOL is fast approaching, with
|
||||
upstream support `ending in 2020 <https://legacy.python.org/dev/peps/pep-0373/#id4>`__.
|
||||
Python 3.4's last release is scheduled for
|
||||
`March 2019 <https://www.python.org/dev/peps/pep-0429/#release-schedule>`__. pytest is one of
|
||||
the participating projects of the https://python3statement.org.
|
||||
It is demanding on the maintainers of an open source project to support many Python versions, as
|
||||
there's extra cost of keeping code compatible between all versions, while holding back on
|
||||
features only made possible on newer Python versions.
|
||||
|
||||
The **pytest 4.6** series is the last to support Python 2.7 and 3.4, and was released in
|
||||
**June 2019**. **pytest 5.0** and onwards will support only Python 3.5+.
|
||||
In case of Python 2 and 3, the difference between the languages makes it even more prominent,
|
||||
because many new Python 3 features cannot be used in a Python 2/3 compatible code base.
|
||||
|
||||
Thanks to the `python_requires`_ ``setuptools`` option,
|
||||
Python 2.7 and Python 3.4 users using a modern ``pip`` version
|
||||
will install the last pytest ``4.6`` version automatically even if ``5.0`` or later
|
||||
Python 2.7 EOL has been reached `in 2020 <https://legacy.python.org/dev/peps/pep-0373/#id4>`__, with
|
||||
the last release planned for mid-April, 2020.
|
||||
|
||||
Python 3.4 EOL has been reached `in 2019 <https://www.python.org/dev/peps/pep-0429/#release-schedule>`__, with the last release made in March, 2019.
|
||||
|
||||
For those reasons, in Jun 2019 it was decided that **pytest 4.6** series will be the last to support Python 2.7 and 3.4.
|
||||
|
||||
What this means for general users
|
||||
---------------------------------
|
||||
|
||||
Thanks to the `python_requires`_ setuptools option,
|
||||
Python 2.7 and Python 3.4 users using a modern pip version
|
||||
will install the last pytest 4.6.X version automatically even if 5.0 or later versions
|
||||
are available on PyPI.
|
||||
|
||||
While pytest ``5.0`` will be the new mainstream and development version, until **January 2020**
|
||||
the pytest core team plans to make bug-fix releases of the pytest ``4.6`` series by
|
||||
back-porting patches to the ``4.6-maintenance`` branch that affect Python 2 users.
|
||||
Users should ensure they are using the latest pip and setuptools versions for this to work.
|
||||
|
||||
**After 2020**, the core team will no longer actively backport patches, but the ``4.6-maintenance``
|
||||
branch will continue to exist so the community itself can contribute patches. The core team will
|
||||
be happy to accept those patches and make new ``4.6`` releases **until mid-2020**.
|
||||
Maintenance of 4.6.X versions
|
||||
-----------------------------
|
||||
|
||||
Until January 2020, the pytest core team ported many bug-fixes from the main release into the
|
||||
``4.6-maintenance`` branch, with several 4.6.X releases being made along the year.
|
||||
|
||||
From now on, the core team will **no longer actively backport patches**, but the ``4.6-maintenance``
|
||||
branch will continue to exist so the community itself can contribute patches.
|
||||
|
||||
The core team will be happy to accept those patches, and make new 4.6.X releases **until mid-2020**
|
||||
(but consider that date as a ballpark, after that date the team might still decide to make new releases
|
||||
for critical bugs).
|
||||
|
||||
.. _`python_requires`: https://packaging.python.org/guides/distributing-packages-using-setuptools/#python-requires
|
||||
|
||||
Technical Aspects
|
||||
-----------------
|
||||
Technical aspects
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
The technical aspects of the Python 2.7 and 3.4 support plan (such as when releases will occurr, how to backport fixes, etc) is described in issue `#5275 <https://github.com/pytest-dev/pytest/issues/5275>`__.
|
||||
(This section is a transcript from `#5275 <https://github.com/pytest-dev/pytest/issues/5275>`__).
|
||||
|
||||
In this section we describe the technical aspects of the Python 2.7 and 3.4 support plan.
|
||||
|
||||
What goes into 4.6.X releases
|
||||
+++++++++++++++++++++++++++++
|
||||
|
||||
New 4.6.X releases will contain bug fixes only.
|
||||
|
||||
When will 4.6.X releases happen
|
||||
+++++++++++++++++++++++++++++++
|
||||
|
||||
New 4.6.X releases will happen after we have a few bugs in place to release, or if a few weeks have
|
||||
passed (say a single bug has been fixed a month after the latest 4.6.X release).
|
||||
|
||||
No hard rules here, just ballpark.
|
||||
|
||||
Who will handle applying bug fixes
|
||||
++++++++++++++++++++++++++++++++++
|
||||
|
||||
We core maintainers expect that people still using Python 2.7/3.4 and being affected by
|
||||
bugs to step up and provide patches and/or port bug fixes from the active branches.
|
||||
|
||||
We will be happy to guide users interested in doing so, so please don't hesitate to ask.
|
||||
|
||||
**Backporting changes into 4.6**
|
||||
|
||||
Please follow these instructions:
|
||||
|
||||
#. ``git fetch --all --prune``
|
||||
|
||||
#. ``git checkout origin/4.6-maintenance -b backport-XXXX`` # use the PR number here
|
||||
|
||||
#. Locate the merge commit on the PR, in the *merged* message, for example:
|
||||
|
||||
nicoddemus merged commit 0f8b462 into pytest-dev:features
|
||||
|
||||
#. ``git cherry-pick -m1 REVISION`` # use the revision you found above (``0f8b462``).
|
||||
|
||||
#. Open a PR targeting ``4.6-maintenance``:
|
||||
|
||||
* Prefix the message with ``[4.6]`` so it is an obvious backport
|
||||
* Delete the PR body, it usually contains a duplicate commit message.
|
||||
|
||||
**Providing new PRs to 4.6**
|
||||
|
||||
Fresh pull requests to ``4.6-maintenance`` will be accepted provided that
|
||||
the equivalent code in the active branches does not contain that bug (for example, a bug is specific
|
||||
to Python 2 only).
|
||||
|
||||
Bug fixes that also happen in the mainstream version should be first fixed
|
||||
there, and then backported as per instructions above.
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
.. _`reference`:
|
||||
|
||||
API Reference
|
||||
=============
|
||||
|
||||
@@ -360,7 +362,7 @@ capfd
|
||||
|
||||
def test_system_echo(capfd):
|
||||
os.system('echo "hello"')
|
||||
captured = capsys.readouterr()
|
||||
captured = capfd.readouterr()
|
||||
assert captured.out == "hello\n"
|
||||
|
||||
|
||||
@@ -463,6 +465,8 @@ monkeypatch
|
||||
.. autoclass:: _pytest.monkeypatch.MonkeyPatch
|
||||
:members:
|
||||
|
||||
.. _testdir:
|
||||
|
||||
testdir
|
||||
~~~~~~~
|
||||
|
||||
@@ -1215,6 +1219,15 @@ passed multiple times. The expected format is ``name=value``. For example::
|
||||
a specific entry in the log. ``extra`` kwarg overrides the value specified
|
||||
on the command line or in the config.
|
||||
|
||||
.. confval:: log_cli
|
||||
|
||||
Enable log display during test run (also known as :ref:`"live logging" <live_logs>`).
|
||||
The default is ``False``.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[pytest]
|
||||
log_cli = True
|
||||
|
||||
.. confval:: log_cli_date_format
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ It provide tools to raise money and share your finances in full transparency.
|
||||
It is the platform of choice for individuals and companies that want to make one-time or
|
||||
monthly donations directly to the project.
|
||||
|
||||
See more datails in the `pytest collective`_.
|
||||
See more details in the `pytest collective`_.
|
||||
|
||||
|
||||
.. _Tidelift: https://tidelift.com
|
||||
|
||||
@@ -64,7 +64,7 @@ Talks and blog postings
|
||||
- `pytest introduction from Brian Okken (January 2013)
|
||||
<http://pythontesting.net/framework/pytest-introduction/>`_
|
||||
|
||||
- pycon australia 2012 pytest talk from Brianna Laugher (`video <http://www.youtube.com/watch?v=DTNejE9EraI>`_, `slides <http://www.slideshare.net/pfctdayelise/funcargs-other-fun-with-pytest>`_, `code <https://gist.github.com/3386951>`_)
|
||||
- pycon australia 2012 pytest talk from Brianna Laugher (`video <http://www.youtube.com/watch?v=DTNejE9EraI>`_, `slides <https://www.slideshare.net/pfctdayelise/funcargs-other-fun-with-pytest>`_, `code <https://gist.github.com/3386951>`_)
|
||||
- `pycon 2012 US talk video from Holger Krekel <http://www.youtube.com/watch?v=9LVqBQcFmyw>`_
|
||||
|
||||
- `monkey patching done right`_ (blog post, consult `monkeypatch plugin`_ for up-to-date API)
|
||||
|
||||
@@ -198,7 +198,7 @@ the regular expression ``".*U.*mode is deprecated"``.
|
||||
Ensuring code triggers a deprecation warning
|
||||
--------------------------------------------
|
||||
|
||||
You can also call a global helper for checking
|
||||
You can also use :func:`pytest.deprecated_call` for checking
|
||||
that a certain function call triggers a ``DeprecationWarning`` or
|
||||
``PendingDeprecationWarning``:
|
||||
|
||||
@@ -207,13 +207,18 @@ that a certain function call triggers a ``DeprecationWarning`` or
|
||||
import pytest
|
||||
|
||||
|
||||
def test_global():
|
||||
pytest.deprecated_call(myfunction, 17)
|
||||
def test_myfunction_deprecated():
|
||||
with pytest.deprecated_call():
|
||||
myfunction(17)
|
||||
|
||||
This test will fail if ``myfunction`` does not issue a deprecation warning
|
||||
when called with a ``17`` argument.
|
||||
|
||||
By default, ``DeprecationWarning`` and ``PendingDeprecationWarning`` will not be
|
||||
caught when using ``pytest.warns`` or ``recwarn`` because default Python warnings filters hide
|
||||
them. If you wish to record them in your own code, use the
|
||||
command ``warnings.simplefilter('always')``:
|
||||
caught when using :func:`pytest.warns` or :ref:`recwarn <recwarn>` because
|
||||
the default Python warnings filters hide
|
||||
them. If you wish to record them in your own code, use
|
||||
``warnings.simplefilter('always')``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@@ -223,19 +228,13 @@ command ``warnings.simplefilter('always')``:
|
||||
|
||||
def test_deprecation(recwarn):
|
||||
warnings.simplefilter("always")
|
||||
warnings.warn("deprecated", DeprecationWarning)
|
||||
myfunction(17)
|
||||
assert len(recwarn) == 1
|
||||
assert recwarn.pop(DeprecationWarning)
|
||||
|
||||
You can also use it as a contextmanager:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def test_global():
|
||||
with pytest.deprecated_call():
|
||||
myobject.deprecated_method()
|
||||
|
||||
|
||||
The :ref:`recwarn <recwarn>` fixture automatically ensures to reset the warnings
|
||||
filter at the end of the test, so no global state is leaked.
|
||||
|
||||
.. _`asserting warnings`:
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ build-backend = "setuptools.build_meta"
|
||||
[tool.towncrier]
|
||||
package = "pytest"
|
||||
package_dir = "src"
|
||||
filename = "CHANGELOG.rst"
|
||||
filename = "doc/en/changelog.rst"
|
||||
directory = "changelog/"
|
||||
title_format = "pytest {version} ({project_date})"
|
||||
template = "changelog/_template.rst"
|
||||
|
||||
36
scripts/append_codecov_token.py
Normal file
36
scripts/append_codecov_token.py
Normal file
@@ -0,0 +1,36 @@
|
||||
"""
|
||||
Appends the codecov token to the 'codecov.yml' file at the root of the repository.
|
||||
|
||||
This is done by CI during PRs and builds on the pytest-dev repository so we can upload coverage, at least
|
||||
until codecov grows some native integration like it has with Travis and AppVeyor.
|
||||
|
||||
See discussion in https://github.com/pytest-dev/pytest/pull/6441 for more information.
|
||||
"""
|
||||
import os.path
|
||||
from textwrap import dedent
|
||||
|
||||
|
||||
def main():
|
||||
this_dir = os.path.dirname(__file__)
|
||||
cov_file = os.path.join(this_dir, "..", "codecov.yml")
|
||||
|
||||
assert os.path.isfile(cov_file), "{cov_file} does not exist".format(
|
||||
cov_file=cov_file
|
||||
)
|
||||
|
||||
with open(cov_file, "a") as f:
|
||||
# token from: https://codecov.io/gh/pytest-dev/pytest/settings
|
||||
# use same URL to regenerate it if needed
|
||||
text = dedent(
|
||||
"""
|
||||
codecov:
|
||||
token: "1eca3b1f-31a2-4fb8-a8c3-138b441b50a7"
|
||||
"""
|
||||
)
|
||||
f.write(text)
|
||||
|
||||
print("Token updated:", cov_file)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -6,7 +6,13 @@ This script is meant to be executed after a successful deployment in Travis.
|
||||
Uses the following environment variables:
|
||||
|
||||
* GIT_TAG: the name of the tag of the current commit.
|
||||
* GH_RELEASE_NOTES_TOKEN: a personal access token with 'repo' permissions. It should be encrypted using:
|
||||
* GH_RELEASE_NOTES_TOKEN: a personal access token with 'repo' permissions.
|
||||
|
||||
Create one at:
|
||||
|
||||
https://github.com/settings/tokens
|
||||
|
||||
It should be encrypted using:
|
||||
|
||||
$travis encrypt GH_RELEASE_NOTES_TOKEN=<token> -r pytest-dev/pytest
|
||||
|
||||
@@ -33,7 +39,7 @@ def publish_github_release(slug, token, tag_name, body):
|
||||
|
||||
|
||||
def parse_changelog(tag_name):
|
||||
p = Path(__file__).parent.parent / "CHANGELOG.rst"
|
||||
p = Path(__file__).parent.parent / "doc/en/changelog.rst"
|
||||
changelog_lines = p.read_text(encoding="UTF-8").splitlines()
|
||||
|
||||
title_regex = re.compile(r"pytest (\d\.\d+\.\d+) \(\d{4}-\d{2}-\d{2}\)")
|
||||
@@ -62,19 +68,21 @@ def main(argv):
|
||||
if len(argv) > 1:
|
||||
tag_name = argv[1]
|
||||
else:
|
||||
tag_name = os.environ.get("TRAVIS_TAG")
|
||||
tag_name = os.environ.get("GITHUB_REF")
|
||||
if not tag_name:
|
||||
print("tag_name not given and $TRAVIS_TAG not set", file=sys.stderr)
|
||||
print("tag_name not given and $GITHUB_REF not set", file=sys.stderr)
|
||||
return 1
|
||||
if tag_name.startswith("refs/tags/"):
|
||||
tag_name = tag_name[len("refs/tags/") :]
|
||||
|
||||
token = os.environ.get("GH_RELEASE_NOTES_TOKEN")
|
||||
if not token:
|
||||
print("GH_RELEASE_NOTES_TOKEN not set", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
slug = os.environ.get("TRAVIS_REPO_SLUG")
|
||||
slug = os.environ.get("GITHUB_REPOSITORY")
|
||||
if not slug:
|
||||
print("TRAVIS_REPO_SLUG not set", file=sys.stderr)
|
||||
print("GITHUB_REPOSITORY not set", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
rst_body = parse_changelog(tag_name)
|
||||
@@ -79,12 +79,20 @@ def fix_formatting():
|
||||
call(["pre-commit", "run", "--all-files"])
|
||||
|
||||
|
||||
def pre_release(version):
|
||||
def check_links():
|
||||
"""Runs sphinx-build to check links"""
|
||||
print(f"{Fore.CYAN}[generate.check_links] {Fore.RESET}Checking links")
|
||||
check_call(["tox", "-e", "docs-checklinks"])
|
||||
|
||||
|
||||
def pre_release(version, *, skip_check_links):
|
||||
"""Generates new docs, release announcements and creates a local tag."""
|
||||
announce(version)
|
||||
regen()
|
||||
changelog(version, write_out=True)
|
||||
fix_formatting()
|
||||
if not skip_check_links:
|
||||
check_links()
|
||||
|
||||
msg = "Preparing release version {}".format(version)
|
||||
check_call(["git", "commit", "-a", "-m", msg])
|
||||
@@ -107,8 +115,9 @@ def main():
|
||||
init(autoreset=True)
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("version", help="Release version")
|
||||
parser.add_argument("--skip-check-links", action="store_true", default=False)
|
||||
options = parser.parse_args()
|
||||
pre_release(options.version)
|
||||
pre_release(options.version, skip_check_links=options.skip_check_links)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -38,8 +38,8 @@ packages =
|
||||
_pytest.assertion
|
||||
_pytest.config
|
||||
_pytest.mark
|
||||
pytest
|
||||
|
||||
py_modules = pytest
|
||||
python_requires = >=3.5
|
||||
|
||||
[options.entry_points]
|
||||
|
||||
5
setup.py
5
setup.py
@@ -29,7 +29,10 @@ def main():
|
||||
"nose",
|
||||
"requests",
|
||||
"xmlschema",
|
||||
]
|
||||
],
|
||||
"checkqa-mypy": [
|
||||
"mypy==v0.761", # keep this in sync with .pre-commit-config.yaml.
|
||||
],
|
||||
},
|
||||
install_requires=INSTALL_REQUIRES,
|
||||
)
|
||||
|
||||
@@ -32,8 +32,9 @@ import _pytest
|
||||
from _pytest._io.saferepr import safeformat
|
||||
from _pytest._io.saferepr import saferepr
|
||||
from _pytest.compat import overload
|
||||
from _pytest.compat import TYPE_CHECKING
|
||||
|
||||
if False: # TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from typing import Type
|
||||
from typing_extensions import Literal
|
||||
from weakref import ReferenceType # noqa: F401
|
||||
@@ -479,7 +480,7 @@ class ExceptionInfo(Generic[_E]):
|
||||
assert tup[1] is not None, "no current exception"
|
||||
assert tup[2] is not None, "no current exception"
|
||||
exc_info = (tup[0], tup[1], tup[2])
|
||||
return cls.from_exc_info(exc_info, exprinfo)
|
||||
return ExceptionInfo.from_exc_info(exc_info, exprinfo)
|
||||
|
||||
@classmethod
|
||||
def for_later(cls) -> "ExceptionInfo[_E]":
|
||||
|
||||
@@ -33,7 +33,7 @@ def pytest_addoption(parser):
|
||||
)
|
||||
|
||||
|
||||
def register_assert_rewrite(*names):
|
||||
def register_assert_rewrite(*names) -> None:
|
||||
"""Register one or more module names to be rewritten on import.
|
||||
|
||||
This function will make sure that this module or all modules inside
|
||||
|
||||
@@ -143,10 +143,12 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder):
|
||||
exec(co, module.__dict__)
|
||||
|
||||
def _early_rewrite_bailout(self, name, state):
|
||||
"""This is a fast way to get out of rewriting modules. Profiling has
|
||||
shown that the call to PathFinder.find_spec (inside of the find_spec
|
||||
from this class) is a major slowdown, so, this method tries to
|
||||
filter what we're sure won't be rewritten before getting to it.
|
||||
"""This is a fast way to get out of rewriting modules.
|
||||
|
||||
Profiling has shown that the call to PathFinder.find_spec (inside of
|
||||
the find_spec from this class) is a major slowdown, so, this method
|
||||
tries to filter what we're sure won't be rewritten before getting to
|
||||
it.
|
||||
"""
|
||||
if self.session is not None and not self._session_paths_checked:
|
||||
self._session_paths_checked = True
|
||||
@@ -799,13 +801,6 @@ class AssertionRewriter(ast.NodeVisitor):
|
||||
self.push_format_context()
|
||||
# Rewrite assert into a bunch of statements.
|
||||
top_condition, explanation = self.visit(assert_.test)
|
||||
# If in a test module, check if directly asserting None, in order to warn [Issue #3191]
|
||||
if self.module_path is not None:
|
||||
self.statements.append(
|
||||
self.warn_about_none_ast(
|
||||
top_condition, module_path=self.module_path, lineno=assert_.lineno
|
||||
)
|
||||
)
|
||||
|
||||
negation = ast.UnaryOp(ast.Not(), top_condition)
|
||||
|
||||
@@ -887,30 +882,6 @@ class AssertionRewriter(ast.NodeVisitor):
|
||||
set_location(stmt, assert_.lineno, assert_.col_offset)
|
||||
return self.statements
|
||||
|
||||
def warn_about_none_ast(self, node, module_path, lineno):
|
||||
"""
|
||||
Returns an AST issuing a warning if the value of node is `None`.
|
||||
This is used to warn the user when asserting a function that asserts
|
||||
internally already.
|
||||
See issue #3191 for more details.
|
||||
"""
|
||||
val_is_none = ast.Compare(node, [ast.Is()], [ast.NameConstant(None)])
|
||||
send_warning = ast.parse(
|
||||
"""\
|
||||
from _pytest.warning_types import PytestAssertRewriteWarning
|
||||
from warnings import warn_explicit
|
||||
warn_explicit(
|
||||
PytestAssertRewriteWarning('asserting the value None, please use "assert is None"'),
|
||||
category=None,
|
||||
filename={filename!r},
|
||||
lineno={lineno},
|
||||
)
|
||||
""".format(
|
||||
filename=fspath(module_path), lineno=lineno
|
||||
)
|
||||
).body
|
||||
return ast.If(val_is_none, send_warning, [])
|
||||
|
||||
def visit_Name(self, name):
|
||||
# Display the repr of the name if it's a local variable or
|
||||
# _should_repr_global_name() thinks it's acceptable.
|
||||
@@ -1074,14 +1045,13 @@ def try_makedirs(cache_dir) -> bool:
|
||||
|
||||
def get_cache_dir(file_path: Path) -> Path:
|
||||
"""Returns the cache directory to write .pyc files for the given .py file path"""
|
||||
# Type ignored until added in next mypy release.
|
||||
if sys.version_info >= (3, 8) and sys.pycache_prefix: # type: ignore
|
||||
if sys.version_info >= (3, 8) and sys.pycache_prefix:
|
||||
# given:
|
||||
# prefix = '/tmp/pycs'
|
||||
# path = '/home/user/proj/test_app.py'
|
||||
# we want:
|
||||
# '/tmp/pycs/home/user/proj'
|
||||
return Path(sys.pycache_prefix) / Path(*file_path.parts[1:-1]) # type: ignore
|
||||
return Path(sys.pycache_prefix) / Path(*file_path.parts[1:-1])
|
||||
else:
|
||||
# classic pycache directory
|
||||
return file_path.parent / "__pycache__"
|
||||
|
||||
@@ -44,14 +44,27 @@ class Cache:
|
||||
_cachedir = attr.ib(repr=False)
|
||||
_config = attr.ib(repr=False)
|
||||
|
||||
# sub-directory under cache-dir for directories created by "makedir"
|
||||
_CACHE_PREFIX_DIRS = "d"
|
||||
|
||||
# sub-directory under cache-dir for values created by "set"
|
||||
_CACHE_PREFIX_VALUES = "v"
|
||||
|
||||
@classmethod
|
||||
def for_config(cls, config):
|
||||
cachedir = cls.cache_dir_from_config(config)
|
||||
if config.getoption("cacheclear") and cachedir.exists():
|
||||
rm_rf(cachedir)
|
||||
cachedir.mkdir()
|
||||
if config.getoption("cacheclear") and cachedir.is_dir():
|
||||
cls.clear_cache(cachedir)
|
||||
return cls(cachedir, config)
|
||||
|
||||
@classmethod
|
||||
def clear_cache(cls, cachedir: Path):
|
||||
"""Clears the sub-directories used to hold cached directories and values."""
|
||||
for prefix in (cls._CACHE_PREFIX_DIRS, cls._CACHE_PREFIX_VALUES):
|
||||
d = cachedir / prefix
|
||||
if d.is_dir():
|
||||
rm_rf(d)
|
||||
|
||||
@staticmethod
|
||||
def cache_dir_from_config(config):
|
||||
return resolve_from_str(config.getini("cache_dir"), config.rootdir)
|
||||
@@ -79,12 +92,12 @@ class Cache:
|
||||
name = Path(name)
|
||||
if len(name.parts) > 1:
|
||||
raise ValueError("name is not allowed to contain path separators")
|
||||
res = self._cachedir.joinpath("d", name)
|
||||
res = self._cachedir.joinpath(self._CACHE_PREFIX_DIRS, name)
|
||||
res.mkdir(exist_ok=True, parents=True)
|
||||
return py.path.local(res)
|
||||
|
||||
def _getvaluepath(self, key):
|
||||
return self._cachedir.joinpath("v", Path(key))
|
||||
return self._cachedir.joinpath(self._CACHE_PREFIX_VALUES, Path(key))
|
||||
|
||||
def get(self, key, default):
|
||||
""" return cached value for the given key. If no value
|
||||
@@ -417,7 +430,7 @@ def cacheshow(config, session):
|
||||
|
||||
dummy = object()
|
||||
basedir = config.cache._cachedir
|
||||
vdir = basedir / "v"
|
||||
vdir = basedir / Cache._CACHE_PREFIX_VALUES
|
||||
tw.sep("-", "cache values for %r" % glob)
|
||||
for valpath in sorted(x for x in vdir.rglob(glob) if x.is_file()):
|
||||
key = valpath.relative_to(vdir)
|
||||
@@ -429,7 +442,7 @@ def cacheshow(config, session):
|
||||
for line in pformat(val).splitlines():
|
||||
tw.line(" " + line)
|
||||
|
||||
ddir = basedir / "d"
|
||||
ddir = basedir / Cache._CACHE_PREFIX_DIRS
|
||||
if ddir.is_dir():
|
||||
contents = sorted(ddir.rglob(glob))
|
||||
tw.sep("-", "cache directories for %r" % glob)
|
||||
|
||||
@@ -27,7 +27,13 @@ from _pytest._io.saferepr import saferepr
|
||||
from _pytest.outcomes import fail
|
||||
from _pytest.outcomes import TEST_OUTCOME
|
||||
|
||||
if False: # TYPE_CHECKING
|
||||
if sys.version_info < (3, 5, 2):
|
||||
TYPE_CHECKING = False # type: bool
|
||||
else:
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Type # noqa: F401 (used in type string)
|
||||
|
||||
|
||||
@@ -43,8 +49,7 @@ MODULE_NOT_FOUND_ERROR = (
|
||||
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
# Type ignored until next mypy release.
|
||||
from importlib import metadata as importlib_metadata # type: ignore
|
||||
from importlib import metadata as importlib_metadata
|
||||
else:
|
||||
import importlib_metadata # noqa: F401
|
||||
|
||||
@@ -372,7 +377,7 @@ class CaptureIO(io.TextIOWrapper):
|
||||
return self.buffer.getvalue().decode("UTF-8")
|
||||
|
||||
|
||||
if sys.version_info < (3, 5, 2): # pragma: no cover
|
||||
if sys.version_info < (3, 5, 2):
|
||||
|
||||
def overload(f): # noqa: F811
|
||||
return f
|
||||
@@ -385,9 +390,7 @@ else:
|
||||
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
# TODO: Remove type ignore on next mypy update.
|
||||
# https://github.com/python/typeshed/commit/add0b5e930a1db16560fde45a3b710eefc625709
|
||||
from functools import cached_property # type: ignore
|
||||
from functools import cached_property
|
||||
else:
|
||||
|
||||
class cached_property(Generic[_S, _T]):
|
||||
|
||||
@@ -37,12 +37,13 @@ from .findpaths import exists
|
||||
from _pytest._code import ExceptionInfo
|
||||
from _pytest._code import filter_traceback
|
||||
from _pytest.compat import importlib_metadata
|
||||
from _pytest.compat import TYPE_CHECKING
|
||||
from _pytest.outcomes import fail
|
||||
from _pytest.outcomes import Skipped
|
||||
from _pytest.pathlib import Path
|
||||
from _pytest.warning_types import PytestConfigWarning
|
||||
|
||||
if False: # TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from typing import Type
|
||||
|
||||
|
||||
@@ -630,16 +631,67 @@ notset = Notset()
|
||||
|
||||
|
||||
def _iter_rewritable_modules(package_files):
|
||||
"""
|
||||
Given an iterable of file names in a source distribution, return the "names" that should
|
||||
be marked for assertion rewrite (for example the package "pytest_mock/__init__.py" should
|
||||
be added as "pytest_mock" in the assertion rewrite mechanism.
|
||||
|
||||
This function has to deal with dist-info based distributions and egg based distributions
|
||||
(which are still very much in use for "editable" installs).
|
||||
|
||||
Here are the file names as seen in a dist-info based distribution:
|
||||
|
||||
pytest_mock/__init__.py
|
||||
pytest_mock/_version.py
|
||||
pytest_mock/plugin.py
|
||||
pytest_mock.egg-info/PKG-INFO
|
||||
|
||||
Here are the file names as seen in an egg based distribution:
|
||||
|
||||
src/pytest_mock/__init__.py
|
||||
src/pytest_mock/_version.py
|
||||
src/pytest_mock/plugin.py
|
||||
src/pytest_mock.egg-info/PKG-INFO
|
||||
LICENSE
|
||||
setup.py
|
||||
|
||||
We have to take in account those two distribution flavors in order to determine which
|
||||
names should be considered for assertion rewriting.
|
||||
|
||||
More information:
|
||||
https://github.com/pytest-dev/pytest-mock/issues/167
|
||||
"""
|
||||
package_files = list(package_files)
|
||||
seen_some = False
|
||||
for fn in package_files:
|
||||
is_simple_module = "/" not in fn and fn.endswith(".py")
|
||||
is_package = fn.count("/") == 1 and fn.endswith("__init__.py")
|
||||
if is_simple_module:
|
||||
module_name, _ = os.path.splitext(fn)
|
||||
yield module_name
|
||||
# we ignore "setup.py" at the root of the distribution
|
||||
if module_name != "setup":
|
||||
seen_some = True
|
||||
yield module_name
|
||||
elif is_package:
|
||||
package_name = os.path.dirname(fn)
|
||||
seen_some = True
|
||||
yield package_name
|
||||
|
||||
if not seen_some:
|
||||
# at this point we did not find any packages or modules suitable for assertion
|
||||
# rewriting, so we try again by stripping the first path component (to account for
|
||||
# "src" based source trees for example)
|
||||
# this approach lets us have the common case continue to be fast, as egg-distributions
|
||||
# are rarer
|
||||
new_package_files = []
|
||||
for fn in package_files:
|
||||
parts = fn.split("/")
|
||||
new_fn = "/".join(parts[1:])
|
||||
if new_fn:
|
||||
new_package_files.append(new_fn)
|
||||
if new_package_files:
|
||||
yield from _iter_rewritable_modules(new_package_files)
|
||||
|
||||
|
||||
class Config:
|
||||
"""
|
||||
@@ -784,7 +836,7 @@ class Config:
|
||||
|
||||
@classmethod
|
||||
def fromdictargs(cls, option_dict, args):
|
||||
""" constructor useable for subprocesses. """
|
||||
""" constructor usable for subprocesses. """
|
||||
config = get_config(args)
|
||||
config.option.__dict__.update(option_dict)
|
||||
config.parse(args, addopts=False)
|
||||
|
||||
@@ -5,9 +5,10 @@ from typing import Optional
|
||||
import py
|
||||
|
||||
from .exceptions import UsageError
|
||||
from _pytest.compat import TYPE_CHECKING
|
||||
from _pytest.outcomes import fail
|
||||
|
||||
if False:
|
||||
if TYPE_CHECKING:
|
||||
from . import Config # noqa: F401
|
||||
|
||||
|
||||
@@ -108,7 +109,7 @@ CFG_PYTEST_SECTION = "[pytest] section in {filename} files is no longer supporte
|
||||
|
||||
|
||||
def determine_setup(
|
||||
inifile: str,
|
||||
inifile: Optional[str],
|
||||
args: List[str],
|
||||
rootdir_cmd_arg: Optional[str] = None,
|
||||
config: Optional["Config"] = None,
|
||||
|
||||
@@ -37,5 +37,6 @@ FIXTURE_POSITIONAL_ARGUMENTS = PytestDeprecationWarning(
|
||||
|
||||
JUNIT_XML_DEFAULT_FAMILY = PytestDeprecationWarning(
|
||||
"The 'junit_family' default value will change to 'xunit2' in pytest 6.0.\n"
|
||||
"Add 'junit_family=legacy' to your pytest.ini file to silence this warning and make your suite compatible."
|
||||
"Add 'junit_family=xunit1' to your pytest.ini file to keep the current format "
|
||||
"in future versions of pytest and silence this warning."
|
||||
)
|
||||
|
||||
@@ -19,12 +19,13 @@ from _pytest._code.code import ExceptionInfo
|
||||
from _pytest._code.code import ReprFileLocation
|
||||
from _pytest._code.code import TerminalRepr
|
||||
from _pytest.compat import safe_getattr
|
||||
from _pytest.compat import TYPE_CHECKING
|
||||
from _pytest.fixtures import FixtureRequest
|
||||
from _pytest.outcomes import Skipped
|
||||
from _pytest.python_api import approx
|
||||
from _pytest.warning_types import PytestWarning
|
||||
|
||||
if False: # TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
import doctest
|
||||
from typing import Type
|
||||
|
||||
@@ -435,7 +436,19 @@ class DoctestModule(pytest.Module):
|
||||
https://bugs.python.org/issue25532
|
||||
"""
|
||||
|
||||
def _find(self, tests, obj, name, module, source_lines, globs, seen):
|
||||
def _find_lineno(self, obj, source_lines):
|
||||
"""
|
||||
Doctest code does not take into account `@property`, this is a hackish way to fix it.
|
||||
|
||||
https://bugs.python.org/issue17446
|
||||
"""
|
||||
if isinstance(obj, property):
|
||||
obj = getattr(obj, "fget", obj)
|
||||
return doctest.DocTestFinder._find_lineno(self, obj, source_lines)
|
||||
|
||||
def _find(
|
||||
self, tests, obj, name, module, source_lines, globs, seen
|
||||
) -> None:
|
||||
if _is_mocked(obj):
|
||||
return
|
||||
with _patch_unwrap_mock_aware():
|
||||
|
||||
@@ -27,12 +27,13 @@ from _pytest.compat import getlocation
|
||||
from _pytest.compat import is_generator
|
||||
from _pytest.compat import NOTSET
|
||||
from _pytest.compat import safe_getattr
|
||||
from _pytest.compat import TYPE_CHECKING
|
||||
from _pytest.deprecated import FIXTURE_POSITIONAL_ARGUMENTS
|
||||
from _pytest.deprecated import FUNCARGNAMES
|
||||
from _pytest.outcomes import fail
|
||||
from _pytest.outcomes import TEST_OUTCOME
|
||||
|
||||
if False: # TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from typing import Type
|
||||
|
||||
from _pytest import nodes
|
||||
@@ -882,9 +883,7 @@ class FixtureDef:
|
||||
self._finalizers = []
|
||||
|
||||
def execute(self, request):
|
||||
# get required arguments and register our own finish()
|
||||
# with their finalization
|
||||
for argname in self.argnames:
|
||||
for argname in self._dependee_fixture_argnames(request):
|
||||
fixturedef = request._get_active_fixturedef(argname)
|
||||
if argname != "request":
|
||||
fixturedef.addfinalizer(functools.partial(self.finish, request=request))
|
||||
@@ -907,6 +906,61 @@ class FixtureDef:
|
||||
hook = self._fixturemanager.session.gethookproxy(request.node.fspath)
|
||||
return hook.pytest_fixture_setup(fixturedef=self, request=request)
|
||||
|
||||
def _dependee_fixture_argnames(self, request):
|
||||
"""A list of argnames for fixtures that this fixture depends on.
|
||||
|
||||
Given a request, this looks at the currently known list of fixture argnames, and
|
||||
attempts to determine what slice of the list contains fixtures that it can know
|
||||
should execute before it. This information is necessary so that this fixture can
|
||||
know what fixtures to register its finalizer with to make sure that if they
|
||||
would be torn down, they would tear down this fixture before themselves. It's
|
||||
crucial for fixtures to be torn down in the inverse order that they were set up
|
||||
in so that they don't try to clean up something that another fixture is still
|
||||
depending on.
|
||||
|
||||
When autouse fixtures are involved, it can be tricky to figure out when fixtures
|
||||
should be torn down. To solve this, this method leverages the ``fixturenames``
|
||||
list provided by the ``request`` object, as this list is at least somewhat
|
||||
sorted (in terms of the order fixtures are set up in) by the time this method is
|
||||
reached. It's sorted enough that the starting point of fixtures that depend on
|
||||
this one can be found using the ``self._parent_request`` stack.
|
||||
|
||||
If a request in the ``self._parent_request`` stack has a ``:class:FixtureDef``
|
||||
associated with it, then that fixture is dependent on this one, so any fixture
|
||||
names that appear in the list of fixture argnames that come after it can also be
|
||||
ruled out. The argnames of all fixtures associated with a request in the
|
||||
``self._parent_request`` stack are found, and the lowest index argname is
|
||||
considered the earliest point in the list of fixture argnames where everything
|
||||
from that point onward can be considered to execute after this fixture.
|
||||
Everything before this point can be considered fixtures that this fixture
|
||||
depends on, and so this fixture should register its finalizer with all of them
|
||||
to ensure that if any of them are to be torn down, they will tear this fixture
|
||||
down first.
|
||||
|
||||
This is the first part of the list of fixture argnames that is returned. The last
|
||||
part of the list is everything in ``self.argnames`` as those are explicit
|
||||
dependees of this fixture, so this fixture should definitely register its
|
||||
finalizer with them.
|
||||
"""
|
||||
all_fix_names = request.fixturenames
|
||||
try:
|
||||
current_fix_index = all_fix_names.index(self.argname)
|
||||
except ValueError:
|
||||
current_fix_index = len(request.fixturenames)
|
||||
parent_fixture_indexes = set()
|
||||
|
||||
parent_request = request._parent_request
|
||||
while hasattr(parent_request, "_parent_request"):
|
||||
if hasattr(parent_request, "_fixturedef"):
|
||||
parent_fix_name = parent_request._fixturedef.argname
|
||||
if parent_fix_name in all_fix_names:
|
||||
parent_fixture_indexes.add(all_fix_names.index(parent_fix_name))
|
||||
parent_request = parent_request._parent_request
|
||||
|
||||
stack_slice_index = min([current_fix_index, *parent_fixture_indexes])
|
||||
active_fixture_argnames = all_fix_names[:stack_slice_index]
|
||||
return {*active_fixture_argnames, *self.argnames}
|
||||
|
||||
def cache_key(self, request):
|
||||
return request.param_index if not hasattr(request, "param") else request.param
|
||||
|
||||
|
||||
@@ -591,6 +591,8 @@ class LogXML:
|
||||
if report.when == "call":
|
||||
reporter.append_failure(report)
|
||||
self.open_reports.append(report)
|
||||
if not self.log_passing_tests:
|
||||
reporter.write_captured_output(report)
|
||||
else:
|
||||
reporter.append_error(report)
|
||||
elif report.skipped:
|
||||
|
||||
@@ -212,11 +212,17 @@ def wrap_session(config, doit):
|
||||
config.hook.pytest_keyboard_interrupt(excinfo=excinfo)
|
||||
session.exitstatus = exitstatus
|
||||
except: # noqa
|
||||
excinfo = _pytest._code.ExceptionInfo.from_current()
|
||||
config.notify_exception(excinfo, config.option)
|
||||
session.exitstatus = ExitCode.INTERNAL_ERROR
|
||||
if excinfo.errisinstance(SystemExit):
|
||||
sys.stderr.write("mainloop: caught unexpected SystemExit!\n")
|
||||
excinfo = _pytest._code.ExceptionInfo.from_current()
|
||||
try:
|
||||
config.notify_exception(excinfo, config.option)
|
||||
except exit.Exception as exc:
|
||||
if exc.returncode is not None:
|
||||
session.exitstatus = exc.returncode
|
||||
sys.stderr.write("{}: {}\n".format(type(exc).__name__, exc))
|
||||
else:
|
||||
if excinfo.errisinstance(SystemExit):
|
||||
sys.stderr.write("mainloop: caught unexpected SystemExit!\n")
|
||||
|
||||
finally:
|
||||
excinfo = None # Explicitly break reference cycle.
|
||||
|
||||
@@ -314,13 +314,18 @@ class MarkGenerator:
|
||||
"{!r} not found in `markers` configuration option".format(name),
|
||||
pytrace=False,
|
||||
)
|
||||
else:
|
||||
warnings.warn(
|
||||
"Unknown pytest.mark.%s - is this a typo? You can register "
|
||||
"custom marks to avoid this warning - for details, see "
|
||||
"https://docs.pytest.org/en/latest/mark.html" % name,
|
||||
PytestUnknownMarkWarning,
|
||||
)
|
||||
|
||||
# Raise a specific error for common misspellings of "parametrize".
|
||||
if name in ["parameterize", "parametrise", "parameterise"]:
|
||||
__tracebackhide__ = True
|
||||
fail("Unknown '{}' mark, did you mean 'parametrize'?".format(name))
|
||||
|
||||
warnings.warn(
|
||||
"Unknown pytest.mark.%s - is this a typo? You can register "
|
||||
"custom marks to avoid this warning - for details, see "
|
||||
"https://docs.pytest.org/en/latest/mark.html" % name,
|
||||
PytestUnknownMarkWarning,
|
||||
)
|
||||
|
||||
return MarkDecorator(Mark(name, (), {}))
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ from _pytest._code.code import ExceptionInfo
|
||||
from _pytest._code.code import ReprExceptionInfo
|
||||
from _pytest.compat import cached_property
|
||||
from _pytest.compat import getfslineno
|
||||
from _pytest.compat import TYPE_CHECKING
|
||||
from _pytest.config import Config
|
||||
from _pytest.fixtures import FixtureDef
|
||||
from _pytest.fixtures import FixtureLookupError
|
||||
@@ -26,7 +27,7 @@ from _pytest.mark.structures import MarkDecorator
|
||||
from _pytest.mark.structures import NodeKeywords
|
||||
from _pytest.outcomes import Failed
|
||||
|
||||
if False: # TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
# Imported here due to circular import.
|
||||
from _pytest.main import Session # noqa: F401
|
||||
|
||||
@@ -49,7 +50,7 @@ def _splitnode(nodeid):
|
||||
[]
|
||||
['testing', 'code']
|
||||
['testing', 'code', 'test_excinfo.py']
|
||||
['testing', 'code', 'test_excinfo.py', 'TestFormattedExcinfo', '()']
|
||||
['testing', 'code', 'test_excinfo.py', 'TestFormattedExcinfo']
|
||||
"""
|
||||
if nodeid == "":
|
||||
# If there is no root node at all, return an empty list so the caller's logic can remain sane
|
||||
|
||||
@@ -8,7 +8,9 @@ from typing import Optional
|
||||
|
||||
from packaging.version import Version
|
||||
|
||||
if False: # TYPE_CHECKING
|
||||
TYPE_CHECKING = False # avoid circular import through compat
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import NoReturn
|
||||
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ from _pytest._code import Source
|
||||
from _pytest._io.saferepr import saferepr
|
||||
from _pytest.capture import MultiCapture
|
||||
from _pytest.capture import SysCapture
|
||||
from _pytest.compat import TYPE_CHECKING
|
||||
from _pytest.fixtures import FixtureRequest
|
||||
from _pytest.main import ExitCode
|
||||
from _pytest.main import Session
|
||||
@@ -35,7 +36,7 @@ from _pytest.monkeypatch import MonkeyPatch
|
||||
from _pytest.pathlib import Path
|
||||
from _pytest.reports import TestReport
|
||||
|
||||
if False: # TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from typing import Type
|
||||
|
||||
|
||||
@@ -189,7 +190,7 @@ class ParsedCall:
|
||||
del d["_name"]
|
||||
return "<ParsedCall {!r}(**{!r})>".format(self._name, d)
|
||||
|
||||
if False: # TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
# The class has undetermined attributes, this tells mypy about it.
|
||||
def __getattr__(self, key):
|
||||
raise NotImplementedError()
|
||||
@@ -433,9 +434,14 @@ class RunResult:
|
||||
for line in reversed(self.outlines):
|
||||
if rex_session_duration.search(line):
|
||||
outcomes = rex_outcome.findall(line)
|
||||
return {noun: int(count) for (count, noun) in outcomes}
|
||||
|
||||
raise ValueError("Pytest terminal summary report not found")
|
||||
ret = {noun: int(count) for (count, noun) in outcomes}
|
||||
break
|
||||
else:
|
||||
raise ValueError("Pytest terminal summary report not found")
|
||||
if "errors" in ret:
|
||||
assert "error" not in ret
|
||||
ret["error"] = ret.pop("errors")
|
||||
return ret
|
||||
|
||||
def assert_outcomes(
|
||||
self,
|
||||
@@ -827,7 +833,7 @@ class Testdir:
|
||||
items = [x.item for x in rec.getcalls("pytest_itemcollected")]
|
||||
return items, rec
|
||||
|
||||
def inline_run(self, *args, plugins=(), no_reraise_ctrlc=False):
|
||||
def inline_run(self, *args, plugins=(), no_reraise_ctrlc: bool = False):
|
||||
"""Run ``pytest.main()`` in-process, returning a HookRecorder.
|
||||
|
||||
Runs the :py:func:`pytest.main` function to run all of pytest inside
|
||||
@@ -1438,8 +1444,10 @@ class LineMatcher:
|
||||
self._log("{:>{width}}".format("and:", width=wnick), repr(nextline))
|
||||
extralines.append(nextline)
|
||||
else:
|
||||
self._log("remains unmatched: {!r}".format(line))
|
||||
pytest.fail(self._log_text.lstrip())
|
||||
msg = "remains unmatched: {!r}".format(line)
|
||||
self._log(msg)
|
||||
self._fail(msg)
|
||||
self._log_output = []
|
||||
|
||||
def no_fnmatch_line(self, pat):
|
||||
"""Ensure captured lines do not match the given pattern, using ``fnmatch.fnmatch``.
|
||||
@@ -1465,18 +1473,21 @@ class LineMatcher:
|
||||
__tracebackhide__ = True
|
||||
nomatch_printed = False
|
||||
wnick = len(match_nickname) + 1
|
||||
try:
|
||||
for line in self.lines:
|
||||
if match_func(line, pat):
|
||||
self._log("%s:" % match_nickname, repr(pat))
|
||||
self._log("{:>{width}}".format("with:", width=wnick), repr(line))
|
||||
pytest.fail(self._log_text.lstrip())
|
||||
else:
|
||||
if not nomatch_printed:
|
||||
self._log(
|
||||
"{:>{width}}".format("nomatch:", width=wnick), repr(pat)
|
||||
)
|
||||
nomatch_printed = True
|
||||
self._log("{:>{width}}".format("and:", width=wnick), repr(line))
|
||||
finally:
|
||||
self._log_output = []
|
||||
for line in self.lines:
|
||||
if match_func(line, pat):
|
||||
msg = "{}: {!r}".format(match_nickname, pat)
|
||||
self._log(msg)
|
||||
self._log("{:>{width}}".format("with:", width=wnick), repr(line))
|
||||
self._fail(msg)
|
||||
else:
|
||||
if not nomatch_printed:
|
||||
self._log("{:>{width}}".format("nomatch:", width=wnick), repr(pat))
|
||||
nomatch_printed = True
|
||||
self._log("{:>{width}}".format("and:", width=wnick), repr(line))
|
||||
self._log_output = []
|
||||
|
||||
def _fail(self, msg):
|
||||
__tracebackhide__ = True
|
||||
log_text = self._log_text
|
||||
self._log_output = []
|
||||
pytest.fail(log_text)
|
||||
|
||||
@@ -6,6 +6,7 @@ import os
|
||||
import sys
|
||||
import warnings
|
||||
from collections import Counter
|
||||
from collections import defaultdict
|
||||
from collections.abc import Sequence
|
||||
from functools import partial
|
||||
from textwrap import dedent
|
||||
@@ -120,17 +121,7 @@ def pytest_cmdline_main(config):
|
||||
return 0
|
||||
|
||||
|
||||
def _validate_parametrize_spelling(metafunc):
|
||||
"""Raise a specific error for common misspellings of "parametrize"."""
|
||||
for mark_name in ["parameterize", "parametrise", "parameterise"]:
|
||||
if metafunc.definition.get_closest_marker(mark_name):
|
||||
msg = "{0} has '{1}' mark, spelling should be 'parametrize'"
|
||||
fail(msg.format(metafunc.function.__name__, mark_name), pytrace=False)
|
||||
|
||||
|
||||
def pytest_generate_tests(metafunc):
|
||||
_validate_parametrize_spelling(metafunc)
|
||||
|
||||
for marker in metafunc.definition.iter_markers(name="parametrize"):
|
||||
metafunc.parametrize(*marker.args, **marker.kwargs)
|
||||
|
||||
@@ -1200,14 +1191,23 @@ def idmaker(argnames, parametersets, idfn=None, ids=None, config=None, item=None
|
||||
_idvalset(valindex, parameterset, argnames, idfn, ids, config=config, item=item)
|
||||
for valindex, parameterset in enumerate(parametersets)
|
||||
]
|
||||
if len(set(ids)) != len(ids):
|
||||
# The ids are not unique
|
||||
duplicates = [testid for testid in ids if ids.count(testid) > 1]
|
||||
counters = Counter()
|
||||
for index, testid in enumerate(ids):
|
||||
if testid in duplicates:
|
||||
ids[index] = testid + str(counters[testid])
|
||||
counters[testid] += 1
|
||||
|
||||
# All IDs must be unique!
|
||||
unique_ids = set(ids)
|
||||
if len(unique_ids) != len(ids):
|
||||
|
||||
# Record the number of occurrences of each test ID
|
||||
test_id_counts = Counter(ids)
|
||||
|
||||
# Map the test ID to its next suffix
|
||||
test_id_suffixes = defaultdict(int)
|
||||
|
||||
# Suffix non-unique IDs to make them unique
|
||||
for index, test_id in enumerate(ids):
|
||||
if test_id_counts[test_id] > 1:
|
||||
ids[index] = "{}{}".format(test_id, test_id_suffixes[test_id])
|
||||
test_id_suffixes[test_id] += 1
|
||||
|
||||
return ids
|
||||
|
||||
|
||||
|
||||
@@ -23,9 +23,10 @@ from more_itertools.more import always_iterable
|
||||
import _pytest._code
|
||||
from _pytest.compat import overload
|
||||
from _pytest.compat import STRING_TYPES
|
||||
from _pytest.compat import TYPE_CHECKING
|
||||
from _pytest.outcomes import fail
|
||||
|
||||
if False: # TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from typing import Type # noqa: F401 (used in type string)
|
||||
|
||||
|
||||
|
||||
@@ -12,10 +12,11 @@ from typing import Tuple
|
||||
from typing import Union
|
||||
|
||||
from _pytest.compat import overload
|
||||
from _pytest.compat import TYPE_CHECKING
|
||||
from _pytest.fixtures import yield_fixture
|
||||
from _pytest.outcomes import fail
|
||||
|
||||
if False: # TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from typing import Type
|
||||
|
||||
|
||||
|
||||
@@ -259,7 +259,7 @@ class TestReport(BaseReport):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_item_and_call(cls, item, call):
|
||||
def from_item_and_call(cls, item, call) -> "TestReport":
|
||||
"""
|
||||
Factory method to create and fill a TestReport with standard item and call info.
|
||||
"""
|
||||
@@ -374,8 +374,11 @@ def _report_to_json(report):
|
||||
]
|
||||
return result
|
||||
|
||||
def serialize_repr_crash(reprcrash):
|
||||
return reprcrash.__dict__.copy()
|
||||
def serialize_repr_crash(reprcrash: Optional[ReprFileLocation]):
|
||||
if reprcrash is not None:
|
||||
return reprcrash.__dict__.copy()
|
||||
else:
|
||||
return None
|
||||
|
||||
def serialize_longrepr(rep):
|
||||
result = {
|
||||
@@ -455,8 +458,11 @@ def _report_kwargs_from_json(reportdict):
|
||||
]
|
||||
return ReprTraceback(**repr_traceback_dict)
|
||||
|
||||
def deserialize_repr_crash(repr_crash_dict):
|
||||
return ReprFileLocation(**repr_crash_dict)
|
||||
def deserialize_repr_crash(repr_crash_dict: Optional[dict]):
|
||||
if repr_crash_dict is not None:
|
||||
return ReprFileLocation(**repr_crash_dict)
|
||||
else:
|
||||
return None
|
||||
|
||||
if (
|
||||
reportdict["longrepr"]
|
||||
|
||||
@@ -15,12 +15,13 @@ from .reports import CollectErrorRepr
|
||||
from .reports import CollectReport
|
||||
from .reports import TestReport
|
||||
from _pytest._code.code import ExceptionInfo
|
||||
from _pytest.compat import TYPE_CHECKING
|
||||
from _pytest.nodes import Node
|
||||
from _pytest.outcomes import Exit
|
||||
from _pytest.outcomes import Skipped
|
||||
from _pytest.outcomes import TEST_OUTCOME
|
||||
|
||||
if False: # TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from typing import Type
|
||||
|
||||
#
|
||||
@@ -121,7 +122,12 @@ def pytest_runtest_setup(item):
|
||||
|
||||
def pytest_runtest_call(item):
|
||||
_update_current_test_var(item, "call")
|
||||
sys.last_type, sys.last_value, sys.last_traceback = (None, None, None)
|
||||
try:
|
||||
del sys.last_type
|
||||
del sys.last_value
|
||||
del sys.last_traceback
|
||||
except AttributeError:
|
||||
pass
|
||||
try:
|
||||
item.runtest()
|
||||
except Exception:
|
||||
@@ -245,7 +251,7 @@ def pytest_runtest_makereport(item, call):
|
||||
return TestReport.from_item_and_call(item, call)
|
||||
|
||||
|
||||
def pytest_make_collect_report(collector):
|
||||
def pytest_make_collect_report(collector) -> CollectReport:
|
||||
call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
|
||||
longrepr = None
|
||||
if not call.excinfo:
|
||||
|
||||
@@ -833,8 +833,20 @@ class TerminalReporter:
|
||||
msg = self._getfailureheadline(rep)
|
||||
self.write_sep("_", msg, green=True, bold=True)
|
||||
self._outrep_summary(rep)
|
||||
self._handle_teardown_sections(rep.nodeid)
|
||||
|
||||
def print_teardown_sections(self, rep):
|
||||
def _get_teardown_reports(self, nodeid: str) -> List[TestReport]:
|
||||
return [
|
||||
report
|
||||
for report in self.getreports("")
|
||||
if report.when == "teardown" and report.nodeid == nodeid
|
||||
]
|
||||
|
||||
def _handle_teardown_sections(self, nodeid: str) -> None:
|
||||
for report in self._get_teardown_reports(nodeid):
|
||||
self.print_teardown_sections(report)
|
||||
|
||||
def print_teardown_sections(self, rep: TestReport) -> None:
|
||||
showcapture = self.config.option.showcapture
|
||||
if showcapture == "no":
|
||||
return
|
||||
@@ -858,17 +870,11 @@ class TerminalReporter:
|
||||
line = self._getcrashline(rep)
|
||||
self.write_line(line)
|
||||
else:
|
||||
teardown_sections = {}
|
||||
for report in self.getreports(""):
|
||||
if report.when == "teardown":
|
||||
teardown_sections.setdefault(report.nodeid, []).append(report)
|
||||
|
||||
for rep in reports:
|
||||
msg = self._getfailureheadline(rep)
|
||||
self.write_sep("_", msg, red=True, bold=True)
|
||||
self._outrep_summary(rep)
|
||||
for report in teardown_sections.get(rep.nodeid, []):
|
||||
self.print_teardown_sections(report)
|
||||
self._handle_teardown_sections(rep.nodeid)
|
||||
|
||||
def summary_errors(self):
|
||||
if self.config.option.tbstyle != "no":
|
||||
@@ -1099,7 +1105,7 @@ def _get_main_color(stats) -> Tuple[str, List[str]]:
|
||||
"failed passed skipped deselected xfailed xpassed warnings error".split()
|
||||
)
|
||||
unknown_type_seen = False
|
||||
for found_type in stats:
|
||||
for found_type in stats.keys():
|
||||
if found_type not in known_types:
|
||||
if found_type: # setup/teardown reports have an empty key, ignore them
|
||||
known_types.append(found_type)
|
||||
|
||||
@@ -4,8 +4,9 @@ from typing import TypeVar
|
||||
|
||||
import attr
|
||||
|
||||
from _pytest.compat import TYPE_CHECKING
|
||||
|
||||
if False: # TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from typing import Type # noqa: F401 (used in type string)
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ pytest: unit and functional testing with Python.
|
||||
"""
|
||||
from _pytest import __version__
|
||||
from _pytest.assertion import register_assert_rewrite
|
||||
from _pytest.compat import _setup_collect_fakemodule
|
||||
from _pytest.config import cmdline
|
||||
from _pytest.config import hookimpl
|
||||
from _pytest.config import hookspec
|
||||
@@ -93,14 +94,6 @@ __all__ = [
|
||||
"yield_fixture",
|
||||
]
|
||||
|
||||
if __name__ == "__main__":
|
||||
# if run as a script or by 'python -m pytest'
|
||||
# we trigger the below "else" condition by the following import
|
||||
import pytest
|
||||
|
||||
raise SystemExit(pytest.main())
|
||||
else:
|
||||
|
||||
from _pytest.compat import _setup_collect_fakemodule
|
||||
|
||||
_setup_collect_fakemodule()
|
||||
_setup_collect_fakemodule()
|
||||
del _setup_collect_fakemodule
|
||||
7
src/pytest/__main__.py
Normal file
7
src/pytest/__main__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
"""
|
||||
pytest entry point
|
||||
"""
|
||||
import pytest
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(pytest.main())
|
||||
@@ -606,7 +606,7 @@ class TestInvocationVariants:
|
||||
def test_equivalence_pytest_pytest(self):
|
||||
assert pytest.main == py.test.cmdline.main
|
||||
|
||||
def test_invoke_with_invalid_type(self, capsys):
|
||||
def test_invoke_with_invalid_type(self):
|
||||
with pytest.raises(
|
||||
TypeError, match="expected to be a list or tuple of strings, got: '-h'"
|
||||
):
|
||||
@@ -617,7 +617,7 @@ class TestInvocationVariants:
|
||||
assert retcode == ExitCode.NO_TESTS_COLLECTED
|
||||
out, err = capsys.readouterr()
|
||||
|
||||
def test_invoke_plugin_api(self, testdir, capsys):
|
||||
def test_invoke_plugin_api(self, capsys):
|
||||
class MyPlugin:
|
||||
def pytest_addoption(self, parser):
|
||||
parser.addoption("--myopt")
|
||||
|
||||
@@ -318,16 +318,12 @@ class TestSourceParsingAndCompiling:
|
||||
|
||||
@pytest.mark.parametrize("name", ["", None, "my"])
|
||||
def test_compilefuncs_and_path_sanity(self, name: Optional[str]) -> None:
|
||||
def check(comp, name):
|
||||
def check(comp, name) -> None:
|
||||
co = comp(self.source, name)
|
||||
if not name:
|
||||
expected = "codegen %s:%d>" % (mypath, mylineno + 2 + 2) # type: ignore
|
||||
else:
|
||||
expected = "codegen %r %s:%d>" % (
|
||||
name,
|
||||
mypath, # type: ignore
|
||||
mylineno + 2 + 2, # type: ignore
|
||||
) # type: ignore
|
||||
expected = "codegen %r %s:%d>" % (name, mypath, mylineno + 2 + 2) # type: ignore
|
||||
fn = co.co_filename
|
||||
assert fn.endswith(expected)
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ if sys.gettrace():
|
||||
|
||||
|
||||
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
|
||||
def pytest_collection_modifyitems(config, items):
|
||||
def pytest_collection_modifyitems(items):
|
||||
"""Prefer faster tests.
|
||||
|
||||
Use a hookwrapper to do this in the beginning, so e.g. --ff still works
|
||||
|
||||
@@ -626,7 +626,7 @@ def test_log_cli_ini_level(testdir):
|
||||
"cli_args",
|
||||
["", "--log-level=WARNING", "--log-file-level=WARNING", "--log-cli-level=WARNING"],
|
||||
)
|
||||
def test_log_cli_auto_enable(testdir, request, cli_args):
|
||||
def test_log_cli_auto_enable(testdir, cli_args):
|
||||
"""Check that live logs are enabled if --log-level or --log-cli-level is passed on the CLI.
|
||||
It should not be auto enabled if the same configs are set on the INI file.
|
||||
"""
|
||||
|
||||
@@ -286,7 +286,7 @@ class TestFunction:
|
||||
|
||||
return pytest.Function(config=config, parent=session, **kwargs)
|
||||
|
||||
def test_function_equality(self, testdir, tmpdir):
|
||||
def test_function_equality(self, testdir):
|
||||
def func1():
|
||||
pass
|
||||
|
||||
@@ -492,7 +492,7 @@ class TestFunction:
|
||||
)
|
||||
assert "foo" in keywords[1] and "bar" in keywords[1] and "baz" in keywords[1]
|
||||
|
||||
def test_function_equality_with_callspec(self, testdir, tmpdir):
|
||||
def test_function_equality_with_callspec(self, testdir):
|
||||
items = testdir.getitems(
|
||||
"""
|
||||
import pytest
|
||||
@@ -509,11 +509,11 @@ class TestFunction:
|
||||
config = item.config
|
||||
|
||||
class MyPlugin1:
|
||||
def pytest_pyfunc_call(self, pyfuncitem):
|
||||
def pytest_pyfunc_call(self):
|
||||
raise ValueError
|
||||
|
||||
class MyPlugin2:
|
||||
def pytest_pyfunc_call(self, pyfuncitem):
|
||||
def pytest_pyfunc_call(self):
|
||||
return True
|
||||
|
||||
config.pluginmanager.register(MyPlugin1())
|
||||
@@ -1015,7 +1015,7 @@ class TestTracebackCutting:
|
||||
|
||||
|
||||
class TestReportInfo:
|
||||
def test_itemreport_reportinfo(self, testdir, linecomp):
|
||||
def test_itemreport_reportinfo(self, testdir):
|
||||
testdir.makeconftest(
|
||||
"""
|
||||
import pytest
|
||||
|
||||
@@ -1716,6 +1716,138 @@ class TestAutouseDiscovery:
|
||||
reprec.assertoutcome(passed=3)
|
||||
|
||||
|
||||
class TestMultiLevelAutouseAndParameterization:
|
||||
def test_setup_and_teardown_order(self, testdir):
|
||||
"""Tests that parameterized fixtures effect subsequent fixtures. (#6436)
|
||||
|
||||
If a fixture uses a parameterized fixture, or, for any other reason, is executed
|
||||
after the parameterized fixture in the fixture stack, then it should be affected
|
||||
by the parameterization, and as a result, should be torn down before the
|
||||
parameterized fixture, every time the parameterized fixture is torn down. This
|
||||
should be the case even if autouse is involved and/or the linear order of
|
||||
fixture execution isn't deterministic. In other words, before any fixture can be
|
||||
torn down, every fixture that was executed after it must also be torn down.
|
||||
"""
|
||||
testdir.makepyfile(
|
||||
test_auto="""
|
||||
import pytest
|
||||
def f(param):
|
||||
return param
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def s_fix(request):
|
||||
yield
|
||||
@pytest.fixture(scope="package", params=["p1", "p2"], ids=f, autouse=True)
|
||||
def p_fix(request):
|
||||
yield
|
||||
@pytest.fixture(scope="module", params=["m1", "m2"], ids=f, autouse=True)
|
||||
def m_fix(request):
|
||||
yield
|
||||
@pytest.fixture(scope="class", autouse=True)
|
||||
def another_c_fix(m_fix):
|
||||
yield
|
||||
@pytest.fixture(scope="class")
|
||||
def c_fix():
|
||||
yield
|
||||
@pytest.fixture(scope="function", params=["f1", "f2"], ids=f, autouse=True)
|
||||
def f_fix(request):
|
||||
yield
|
||||
class TestFixtures:
|
||||
def test_a(self, c_fix):
|
||||
pass
|
||||
def test_b(self, c_fix):
|
||||
pass
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest("--setup-plan")
|
||||
test_fixtures_used = (
|
||||
"(fixtures used: another_c_fix, c_fix, f_fix, m_fix, p_fix, request, s_fix)"
|
||||
)
|
||||
result.stdout.fnmatch_lines(
|
||||
"""
|
||||
SETUP S s_fix
|
||||
SETUP P p_fix[p1]
|
||||
SETUP M m_fix[m1]
|
||||
SETUP C another_c_fix (fixtures used: m_fix)
|
||||
SETUP C c_fix
|
||||
SETUP F f_fix[f1]
|
||||
test_auto.py::TestFixtures::test_a[p1-m1-f1] {0}
|
||||
TEARDOWN F f_fix[f1]
|
||||
SETUP F f_fix[f2]
|
||||
test_auto.py::TestFixtures::test_a[p1-m1-f2] {0}
|
||||
TEARDOWN F f_fix[f2]
|
||||
SETUP F f_fix[f1]
|
||||
test_auto.py::TestFixtures::test_b[p1-m1-f1] {0}
|
||||
TEARDOWN F f_fix[f1]
|
||||
SETUP F f_fix[f2]
|
||||
test_auto.py::TestFixtures::test_b[p1-m1-f2] {0}
|
||||
TEARDOWN F f_fix[f2]
|
||||
TEARDOWN C c_fix
|
||||
TEARDOWN C another_c_fix
|
||||
TEARDOWN M m_fix[m1]
|
||||
SETUP M m_fix[m2]
|
||||
SETUP C another_c_fix (fixtures used: m_fix)
|
||||
SETUP C c_fix
|
||||
SETUP F f_fix[f1]
|
||||
test_auto.py::TestFixtures::test_a[p1-m2-f1] {0}
|
||||
TEARDOWN F f_fix[f1]
|
||||
SETUP F f_fix[f2]
|
||||
test_auto.py::TestFixtures::test_a[p1-m2-f2] {0}
|
||||
TEARDOWN F f_fix[f2]
|
||||
SETUP F f_fix[f1]
|
||||
test_auto.py::TestFixtures::test_b[p1-m2-f1] {0}
|
||||
TEARDOWN F f_fix[f1]
|
||||
SETUP F f_fix[f2]
|
||||
test_auto.py::TestFixtures::test_b[p1-m2-f2] {0}
|
||||
TEARDOWN F f_fix[f2]
|
||||
TEARDOWN C c_fix
|
||||
TEARDOWN C another_c_fix
|
||||
TEARDOWN M m_fix[m2]
|
||||
TEARDOWN P p_fix[p1]
|
||||
SETUP P p_fix[p2]
|
||||
SETUP M m_fix[m1]
|
||||
SETUP C another_c_fix (fixtures used: m_fix)
|
||||
SETUP C c_fix
|
||||
SETUP F f_fix[f1]
|
||||
test_auto.py::TestFixtures::test_a[p2-m1-f1] {0}
|
||||
TEARDOWN F f_fix[f1]
|
||||
SETUP F f_fix[f2]
|
||||
test_auto.py::TestFixtures::test_a[p2-m1-f2] {0}
|
||||
TEARDOWN F f_fix[f2]
|
||||
SETUP F f_fix[f1]
|
||||
test_auto.py::TestFixtures::test_b[p2-m1-f1] {0}
|
||||
TEARDOWN F f_fix[f1]
|
||||
SETUP F f_fix[f2]
|
||||
test_auto.py::TestFixtures::test_b[p2-m1-f2] {0}
|
||||
TEARDOWN F f_fix[f2]
|
||||
TEARDOWN C c_fix
|
||||
TEARDOWN C another_c_fix
|
||||
TEARDOWN M m_fix[m1]
|
||||
SETUP M m_fix[m2]
|
||||
SETUP C another_c_fix (fixtures used: m_fix)
|
||||
SETUP C c_fix
|
||||
SETUP F f_fix[f1]
|
||||
test_auto.py::TestFixtures::test_a[p2-m2-f1] {0}
|
||||
TEARDOWN F f_fix[f1]
|
||||
SETUP F f_fix[f2]
|
||||
test_auto.py::TestFixtures::test_a[p2-m2-f2] {0}
|
||||
TEARDOWN F f_fix[f2]
|
||||
SETUP F f_fix[f1]
|
||||
test_auto.py::TestFixtures::test_b[p2-m2-f1] {0}
|
||||
TEARDOWN F f_fix[f1]
|
||||
SETUP F f_fix[f2]
|
||||
test_auto.py::TestFixtures::test_b[p2-m2-f2] {0}
|
||||
TEARDOWN F f_fix[f2]
|
||||
TEARDOWN C c_fix
|
||||
TEARDOWN C another_c_fix
|
||||
TEARDOWN M m_fix[m2]
|
||||
TEARDOWN P p_fix[p2]
|
||||
TEARDOWN S s_fix
|
||||
""".format(
|
||||
test_fixtures_used
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class TestAutouseManagement:
|
||||
def test_autouse_conftest_mid_directory(self, testdir):
|
||||
pkgdir = testdir.mkpydir("xyz123")
|
||||
@@ -4106,7 +4238,7 @@ def test_fixture_named_request(testdir):
|
||||
)
|
||||
|
||||
|
||||
def test_fixture_duplicated_arguments(testdir):
|
||||
def test_fixture_duplicated_arguments():
|
||||
"""Raise error if there are positional and keyword arguments for the same parameter (#1682)."""
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
|
||||
@@ -4121,7 +4253,7 @@ def test_fixture_duplicated_arguments(testdir):
|
||||
)
|
||||
|
||||
|
||||
def test_fixture_with_positionals(testdir):
|
||||
def test_fixture_with_positionals():
|
||||
"""Raise warning, but the positionals should still works (#1682)."""
|
||||
from _pytest.deprecated import FIXTURE_POSITIONAL_ARGUMENTS
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ class TestMetafunc:
|
||||
definition = DefinitionMock(func)
|
||||
return python.Metafunc(definition, fixtureinfo, config)
|
||||
|
||||
def test_no_funcargs(self, testdir):
|
||||
def test_no_funcargs(self):
|
||||
def function():
|
||||
pass
|
||||
|
||||
@@ -61,7 +61,7 @@ class TestMetafunc:
|
||||
pytest.raises(ValueError, lambda: metafunc.parametrize("y", [5, 6]))
|
||||
pytest.raises(ValueError, lambda: metafunc.parametrize("y", [5, 6]))
|
||||
|
||||
def test_parametrize_bad_scope(self, testdir):
|
||||
def test_parametrize_bad_scope(self):
|
||||
def func(x):
|
||||
pass
|
||||
|
||||
@@ -153,7 +153,7 @@ class TestMetafunc:
|
||||
ids = [x.id for x in metafunc._calls]
|
||||
assert ids == ["basic", "advanced"]
|
||||
|
||||
def test_parametrize_with_wrong_number_of_ids(self, testdir):
|
||||
def test_parametrize_with_wrong_number_of_ids(self):
|
||||
def func(x, y):
|
||||
pass
|
||||
|
||||
@@ -652,7 +652,7 @@ class TestMetafunc:
|
||||
result = testdir.runpytest("-v")
|
||||
result.stdout.fnmatch_lines(["*test_simple*a-b*", "*1 passed*"])
|
||||
|
||||
def test_parametrize_indirect_list_error(self, testdir):
|
||||
def test_parametrize_indirect_list_error(self):
|
||||
"""#714"""
|
||||
|
||||
def func(x, y):
|
||||
@@ -1323,25 +1323,29 @@ class TestMetafuncFunctional:
|
||||
reprec = testdir.runpytest()
|
||||
reprec.assert_outcomes(passed=4)
|
||||
|
||||
@pytest.mark.parametrize("attr", ["parametrise", "parameterize", "parameterise"])
|
||||
def test_parametrize_misspelling(self, testdir, attr):
|
||||
def test_parametrize_misspelling(self, testdir):
|
||||
"""#463"""
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
|
||||
@pytest.mark.{}("x", range(2))
|
||||
@pytest.mark.parametrise("x", range(2))
|
||||
def test_foo(x):
|
||||
pass
|
||||
""".format(
|
||||
attr
|
||||
)
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest("--collectonly")
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"test_foo has '{}' mark, spelling should be 'parametrize'".format(attr),
|
||||
"*1 error in*",
|
||||
"collected 0 items / 1 error",
|
||||
"",
|
||||
"*= ERRORS =*",
|
||||
"*_ ERROR collecting test_parametrize_misspelling.py _*",
|
||||
"test_parametrize_misspelling.py:3: in <module>",
|
||||
' @pytest.mark.parametrise("x", range(2))',
|
||||
"E Failed: Unknown 'parametrise' mark, did you mean 'parametrize'?",
|
||||
"*! Interrupted: 1 error during collection !*",
|
||||
"*= 1 error in *",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@ class TestRaises:
|
||||
# Early versions of Python 3.5 have some bug causing the
|
||||
# __call__ frame to still refer to t even after everything
|
||||
# is done. This makes the test pass for them.
|
||||
if sys.version_info < (3, 5, 2): # pragma: no cover
|
||||
if sys.version_info < (3, 5, 2):
|
||||
del self
|
||||
raise ValueError
|
||||
|
||||
|
||||
@@ -1299,7 +1299,7 @@ def test_AssertionError_message(testdir):
|
||||
)
|
||||
|
||||
|
||||
def test_diff_newline_at_end(monkeypatch, testdir):
|
||||
def test_diff_newline_at_end(testdir):
|
||||
testdir.makepyfile(
|
||||
r"""
|
||||
def test_diff():
|
||||
@@ -1354,7 +1354,7 @@ def test_assert_indirect_tuple_no_warning(testdir):
|
||||
assert "WR1" not in output
|
||||
|
||||
|
||||
def test_assert_with_unicode(monkeypatch, testdir):
|
||||
def test_assert_with_unicode(testdir):
|
||||
testdir.makepyfile(
|
||||
"""\
|
||||
def test_unicode():
|
||||
|
||||
@@ -270,6 +270,7 @@ class TestLastFailed:
|
||||
)
|
||||
result = testdir.runpytest(str(p), "--lf", "--cache-clear")
|
||||
result.stdout.fnmatch_lines(["*1 failed*2 passed*"])
|
||||
assert testdir.tmpdir.join(".pytest_cache", "README.md").isfile()
|
||||
|
||||
# Run this again to make sure clear-cache is robust
|
||||
if os.path.isdir(".pytest_cache"):
|
||||
|
||||
@@ -937,7 +937,7 @@ class TestFDCapture:
|
||||
cap.done()
|
||||
assert s == "hello\n"
|
||||
|
||||
def test_stdin(self, tmpfile):
|
||||
def test_stdin(self):
|
||||
cap = capture.FDCapture(0)
|
||||
cap.start()
|
||||
x = os.read(0, 100).strip()
|
||||
@@ -958,7 +958,7 @@ class TestFDCapture:
|
||||
stmp = stmp_file.read()
|
||||
assert stmp == data2
|
||||
|
||||
def test_simple_resume_suspend(self, tmpfile):
|
||||
def test_simple_resume_suspend(self):
|
||||
with saved_fd(1):
|
||||
cap = capture.FDCapture(1)
|
||||
cap.start()
|
||||
|
||||
@@ -243,7 +243,7 @@ class TestCollectPluginHookRelay:
|
||||
wascalled = []
|
||||
|
||||
class Plugin:
|
||||
def pytest_collect_file(self, path, parent):
|
||||
def pytest_collect_file(self, path):
|
||||
if not path.basename.startswith("."):
|
||||
# Ignore hidden files, e.g. .testmondata.
|
||||
wascalled.append(path)
|
||||
@@ -257,7 +257,7 @@ class TestCollectPluginHookRelay:
|
||||
wascalled = []
|
||||
|
||||
class Plugin:
|
||||
def pytest_collect_directory(self, path, parent):
|
||||
def pytest_collect_directory(self, path):
|
||||
wascalled.append(path.basename)
|
||||
|
||||
testdir.mkdir("hello")
|
||||
@@ -1173,7 +1173,7 @@ def test_collect_symlink_out_of_tree(testdir):
|
||||
assert result.ret == 0
|
||||
|
||||
|
||||
def test_collectignore_via_conftest(testdir, monkeypatch):
|
||||
def test_collectignore_via_conftest(testdir):
|
||||
"""collect_ignore in parent conftest skips importing child (issue #4592)."""
|
||||
tests = testdir.mkpydir("tests")
|
||||
tests.ensure("conftest.py").write("collect_ignore = ['ignore_me']")
|
||||
|
||||
@@ -146,12 +146,16 @@ def test_is_generator_async_gen_syntax(testdir):
|
||||
|
||||
|
||||
class ErrorsHelper:
|
||||
@property
|
||||
def raise_baseexception(self):
|
||||
raise BaseException("base exception should be raised")
|
||||
|
||||
@property
|
||||
def raise_exception(self):
|
||||
raise Exception("exception should be catched")
|
||||
|
||||
@property
|
||||
def raise_fail(self):
|
||||
def raise_fail_outcome(self):
|
||||
pytest.fail("fail should be catched")
|
||||
|
||||
|
||||
@@ -160,13 +164,15 @@ def test_helper_failures():
|
||||
with pytest.raises(Exception):
|
||||
helper.raise_exception
|
||||
with pytest.raises(OutcomeException):
|
||||
helper.raise_fail
|
||||
helper.raise_fail_outcome
|
||||
|
||||
|
||||
def test_safe_getattr():
|
||||
helper = ErrorsHelper()
|
||||
assert safe_getattr(helper, "raise_exception", "default") == "default"
|
||||
assert safe_getattr(helper, "raise_fail", "default") == "default"
|
||||
assert safe_getattr(helper, "raise_fail_outcome", "default") == "default"
|
||||
with pytest.raises(BaseException):
|
||||
assert safe_getattr(helper, "raise_baseexception", "default")
|
||||
|
||||
|
||||
def test_safe_isclass():
|
||||
|
||||
@@ -422,15 +422,21 @@ class TestConfigAPI:
|
||||
@pytest.mark.parametrize(
|
||||
"names, expected",
|
||||
[
|
||||
# dist-info based distributions root are files as will be put in PYTHONPATH
|
||||
(["bar.py"], ["bar"]),
|
||||
(["foo", "bar.py"], []),
|
||||
(["foo", "bar.pyc"], []),
|
||||
(["foo", "__init__.py"], ["foo"]),
|
||||
(["foo", "bar", "__init__.py"], []),
|
||||
(["foo/bar.py"], ["bar"]),
|
||||
(["foo/bar.pyc"], []),
|
||||
(["foo/__init__.py"], ["foo"]),
|
||||
(["bar/__init__.py", "xz.py"], ["bar", "xz"]),
|
||||
(["setup.py"], []),
|
||||
# egg based distributions root contain the files from the dist root
|
||||
(["src/bar/__init__.py"], ["bar"]),
|
||||
(["src/bar/__init__.py", "setup.py"], ["bar"]),
|
||||
(["source/python/bar/__init__.py", "setup.py"], ["bar"]),
|
||||
],
|
||||
)
|
||||
def test_iter_rewritable_modules(self, names, expected):
|
||||
assert list(_iter_rewritable_modules(["/".join(names)])) == expected
|
||||
assert list(_iter_rewritable_modules(names)) == expected
|
||||
|
||||
|
||||
class TestConfigFromdictargs:
|
||||
@@ -853,7 +859,7 @@ class TestRootdir:
|
||||
assert get_common_ancestor([no_path.join("a")]) == tmpdir
|
||||
|
||||
@pytest.mark.parametrize("name", "setup.cfg tox.ini pytest.ini".split())
|
||||
def test_with_ini(self, tmpdir, name):
|
||||
def test_with_ini(self, tmpdir, name) -> None:
|
||||
inifile = tmpdir.join(name)
|
||||
inifile.write("[pytest]\n" if name != "setup.cfg" else "[tool:pytest]\n")
|
||||
|
||||
@@ -868,7 +874,7 @@ class TestRootdir:
|
||||
assert inifile == inifile
|
||||
|
||||
@pytest.mark.parametrize("name", "setup.cfg tox.ini".split())
|
||||
def test_pytestini_overrides_empty_other(self, tmpdir, name):
|
||||
def test_pytestini_overrides_empty_other(self, tmpdir, name) -> None:
|
||||
inifile = tmpdir.ensure("pytest.ini")
|
||||
a = tmpdir.mkdir("a")
|
||||
a.ensure(name)
|
||||
@@ -876,7 +882,7 @@ class TestRootdir:
|
||||
assert rootdir == tmpdir
|
||||
assert inifile == inifile
|
||||
|
||||
def test_setuppy_fallback(self, tmpdir):
|
||||
def test_setuppy_fallback(self, tmpdir) -> None:
|
||||
a = tmpdir.mkdir("a")
|
||||
a.ensure("setup.cfg")
|
||||
tmpdir.ensure("setup.py")
|
||||
@@ -885,14 +891,14 @@ class TestRootdir:
|
||||
assert inifile is None
|
||||
assert inicfg == {}
|
||||
|
||||
def test_nothing(self, tmpdir, monkeypatch):
|
||||
def test_nothing(self, tmpdir, monkeypatch) -> None:
|
||||
monkeypatch.chdir(str(tmpdir))
|
||||
rootdir, inifile, inicfg = determine_setup(None, [tmpdir])
|
||||
assert rootdir == tmpdir
|
||||
assert inifile is None
|
||||
assert inicfg == {}
|
||||
|
||||
def test_with_specific_inifile(self, tmpdir):
|
||||
def test_with_specific_inifile(self, tmpdir) -> None:
|
||||
inifile = tmpdir.ensure("pytest.ini")
|
||||
rootdir, inifile, inicfg = determine_setup(inifile, [tmpdir])
|
||||
assert rootdir == tmpdir
|
||||
@@ -1033,7 +1039,7 @@ class TestOverrideIniArgs:
|
||||
result = testdir.runpytest("--override-ini", "python_files=unittest_*.py")
|
||||
result.stdout.fnmatch_lines(["*1 passed in*"])
|
||||
|
||||
def test_with_arg_outside_cwd_without_inifile(self, tmpdir, monkeypatch):
|
||||
def test_with_arg_outside_cwd_without_inifile(self, tmpdir, monkeypatch) -> None:
|
||||
monkeypatch.chdir(str(tmpdir))
|
||||
a = tmpdir.mkdir("a")
|
||||
b = tmpdir.mkdir("b")
|
||||
@@ -1041,7 +1047,7 @@ class TestOverrideIniArgs:
|
||||
assert rootdir == tmpdir
|
||||
assert inifile is None
|
||||
|
||||
def test_with_arg_outside_cwd_with_inifile(self, tmpdir):
|
||||
def test_with_arg_outside_cwd_with_inifile(self, tmpdir) -> None:
|
||||
a = tmpdir.mkdir("a")
|
||||
b = tmpdir.mkdir("b")
|
||||
inifile = a.ensure("pytest.ini")
|
||||
@@ -1050,13 +1056,13 @@ class TestOverrideIniArgs:
|
||||
assert inifile == parsed_inifile
|
||||
|
||||
@pytest.mark.parametrize("dirs", ([], ["does-not-exist"], ["a/does-not-exist"]))
|
||||
def test_with_non_dir_arg(self, dirs, tmpdir):
|
||||
def test_with_non_dir_arg(self, dirs, tmpdir) -> None:
|
||||
with tmpdir.ensure(dir=True).as_cwd():
|
||||
rootdir, inifile, inicfg = determine_setup(None, dirs)
|
||||
assert rootdir == tmpdir
|
||||
assert inifile is None
|
||||
|
||||
def test_with_existing_file_in_subdir(self, tmpdir):
|
||||
def test_with_existing_file_in_subdir(self, tmpdir) -> None:
|
||||
a = tmpdir.mkdir("a")
|
||||
a.ensure("exist")
|
||||
with tmpdir.as_cwd():
|
||||
|
||||
@@ -357,7 +357,7 @@ def test_conftest_import_order(testdir, monkeypatch):
|
||||
assert conftest._getconftestmodules(sub) == [ct1, ct2]
|
||||
|
||||
|
||||
def test_fixture_dependency(testdir, monkeypatch):
|
||||
def test_fixture_dependency(testdir):
|
||||
ct1 = testdir.makeconftest("")
|
||||
ct1 = testdir.makepyfile("__init__.py")
|
||||
ct1.write("")
|
||||
|
||||
@@ -287,13 +287,68 @@ class TestDoctests:
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest("--doctest-modules")
|
||||
result.stdout.fnmatch_lines(
|
||||
["*hello*", "006*>>> 1/0*", "*UNEXPECTED*ZeroDivision*", "*1 failed*"]
|
||||
)
|
||||
|
||||
def test_doctest_linedata_on_property(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
class Sample(object):
|
||||
@property
|
||||
def some_property(self):
|
||||
'''
|
||||
>>> Sample().some_property
|
||||
'another thing'
|
||||
'''
|
||||
return 'something'
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest("--doctest-modules")
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"*hello*",
|
||||
"*EXAMPLE LOCATION UNKNOWN, not showing all tests of that example*",
|
||||
"*1/0*",
|
||||
"*UNEXPECTED*ZeroDivision*",
|
||||
"*1 failed*",
|
||||
"*= FAILURES =*",
|
||||
"*_ [[]doctest[]] test_doctest_linedata_on_property.Sample.some_property _*",
|
||||
"004 ",
|
||||
"005 >>> Sample().some_property",
|
||||
"Expected:",
|
||||
" 'another thing'",
|
||||
"Got:",
|
||||
" 'something'",
|
||||
"",
|
||||
"*/test_doctest_linedata_on_property.py:5: DocTestFailure",
|
||||
"*= 1 failed in *",
|
||||
]
|
||||
)
|
||||
|
||||
def test_doctest_no_linedata_on_overriden_property(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
class Sample(object):
|
||||
@property
|
||||
def some_property(self):
|
||||
'''
|
||||
>>> Sample().some_property
|
||||
'another thing'
|
||||
'''
|
||||
return 'something'
|
||||
some_property = property(some_property.__get__, None, None, some_property.__doc__)
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest("--doctest-modules")
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"*= FAILURES =*",
|
||||
"*_ [[]doctest[]] test_doctest_no_linedata_on_overriden_property.Sample.some_property _*",
|
||||
"EXAMPLE LOCATION UNKNOWN, not showing all tests of that example",
|
||||
"[?][?][?] >>> Sample().some_property",
|
||||
"Expected:",
|
||||
" 'another thing'",
|
||||
"Got:",
|
||||
" 'something'",
|
||||
"",
|
||||
"*/test_doctest_no_linedata_on_overriden_property.py:None: DocTestFailure",
|
||||
"*= 1 failed in *",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ def test_timeout(testdir, enabled):
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hook_name", ["pytest_enter_pdb", "pytest_exception_interact"])
|
||||
def test_cancel_timeout_on_hook(monkeypatch, pytestconfig, hook_name):
|
||||
def test_cancel_timeout_on_hook(monkeypatch, hook_name):
|
||||
"""Make sure that we are cancelling any scheduled traceback dumping due
|
||||
to timeout before entering pdb (pytest-dev/pytest-faulthandler#12) or any other interactive
|
||||
exception (pytest-dev/pytest-faulthandler#14).
|
||||
|
||||
@@ -57,7 +57,7 @@ def test_traceconfig(testdir):
|
||||
result.stdout.fnmatch_lines(["*using*pytest*py*", "*active plugins*"])
|
||||
|
||||
|
||||
def test_debug(testdir, monkeypatch):
|
||||
def test_debug(testdir):
|
||||
result = testdir.runpytest_subprocess("--debug")
|
||||
assert result.ret == ExitCode.NO_TESTS_COLLECTED
|
||||
p = testdir.tmpdir.join("pytestdebug.log")
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user