Compare commits

...

356 Commits
7.2.x ... 7.4.2

Author SHA1 Message Date
pytest bot
45f34dfb8d Prepare release version 7.4.2 2023-09-07 17:21:49 +00:00
Bruno Oliveira
e4f022f0d8 Merge pull request #11406 from nicoddemus/backport-11404-to-7.4.x
[7.4.x] Fix crash when passing a very long cmdline argument (#11404)
2023-09-07 14:14:40 -03:00
Bruno Oliveira
63b0c6f75f Use _pytest.pathlib.safe_exists in get_dirs_from_args
Related to #11394
2023-09-07 13:50:02 -03:00
Bruno Oliveira
884b911a9c Fix crash when passing a very long cmdline argument (#11404)
Fixes #11394

(cherry picked from commit 28ccf476b9)
2023-09-07 12:54:41 -03:00
github-actions[bot]
6e49a74089 [7.4.x] Fix doctest collection of functools.cached_property objects. (#11403)
Co-authored-by: Ronny Pfannschmidt <opensource@ronnypfannschmidt.de>
2023-09-07 13:33:12 +00:00
github-actions[bot]
79c2012d40 [7.4.x] doc: Remove done training (#11400)
Co-authored-by: Florian Bruhin <me@the-compiler.org>
2023-09-06 13:50:00 +00:00
github-actions[bot]
de69883e3a [7.4.x] improve plugin list disclaimer (#11398)
Co-authored-by: Stefaan Lippens <soxofaan@users.noreply.github.com>
2023-09-06 11:02:29 +00:00
github-actions[bot]
1de00e9830 [7.4.x] Fix import_path for packages (#11395)
Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
2023-09-05 23:07:48 +00:00
Bruno Oliveira
7f5d9b9df4 Fix user_properties not saved to XML if fixture errors during teardown (#11382)
Move handling of user_properties to `finalize()`.

Previously if a fixture failed during teardown, `pytest_runtest_logreport` would not be called with "teardown", resulting in the user properties not being saved on the JUnit XML file.

Fixes: #11367
(cherry picked from commit 917ce9aa01)

Co-authored-by: Israel Fruchter <israel.fruchter@gmail.com>
2023-09-03 15:01:56 -03:00
Bruno Oliveira
82eb86f707 Merge pull request #11377 from pytest-dev/release-7.4.1
Prepare release 7.4.1
2023-09-02 12:41:32 -03:00
Bruno Oliveira
0319a0d4fd Checkout source code during deploy
We need the checked out repository in order to push the tag.
2023-09-02 12:38:39 -03:00
Bruno Oliveira
7855a72d2c Improve CI workflow
* Build the package only once, and test on all platforms.
* Deploy is now triggered manually via an Action, which is then responsible for tagging the repository after the package has been uploaded successfully.
* Drop 'docs': we nowadays rely on readthedocs preview PR builds.
2023-09-02 08:46:22 -03:00
pytest bot
7a0a0e8b08 Prepare release version 7.4.1 2023-09-02 11:03:06 +00:00
github-actions[bot]
fbcfd3a52e [7.4.x] Update CONTRIBUTING.rst (#11371)
Co-authored-by: Sourabh Beniwal <sourabhbeniwal@outlook.com>
2023-08-30 08:57:49 -03:00
github-actions[bot]
b170081788 [7.4.x] Issue 11354 fixing docs for lfnf (#11364)
Co-authored-by: Sean Patrick Malloy <spmalloy@ucdavis.edu>
2023-08-29 00:40:49 +00:00
Ran Benita
7a5f2feefb [7.4.x] Fixes for typed pluggy (#11355)
Since version 1.3 pluggy added typing, which requires some fixes to
please mypy.
2023-08-26 22:15:32 +00:00
github-actions[bot]
69140717d4 [7.4.x] Improve duplicate values documentation (#11296)
Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
2023-08-08 14:44:03 +03:00
Bruno Oliveira
5c7c3f6329 Merge pull request #11294 from The-Compiler/pluggy-py38
ci: Use Python 3.8 to test latest pluggy
2023-08-07 08:13:03 -03:00
Florian Bruhin
ba40975bb7 ci: Use Python 3.8 to test latest pluggy
Pluggy dropped Python 3.7 support.
Also see 165fbbd12a

Fixes #11293
2023-08-07 12:08:56 +02:00
github-actions[bot]
e3fe7286f8 [7.4.x] doc: Link pytest.main to how-to guide (#11290)
Co-authored-by: Florian Bruhin <me@the-compiler.org>
2023-08-07 10:47:37 +02:00
github-actions[bot]
34c73944e1 [7.4.x] doc: update information about assertion messages (#11286)
Co-authored-by: Christoph Anton Mitterer <mail@christoph.anton.mitterer.name>
2023-08-07 10:47:12 +02:00
github-actions[bot]
350122abb2 [7.4.x] Remove ep2023 training (#11242)
Co-authored-by: Florian Bruhin <me@the-compiler.org>
2023-07-22 18:40:56 +00:00
github-actions[bot]
06ff7ca13b [7.4.x] Clarify docs for pytest.main default behavior (#11188)
Co-authored-by: antosikv <79337398+antosikv@users.noreply.github.com>
2023-07-09 15:54:59 +00:00
github-actions[bot]
6dfe498c77 [7.4.x] doc: fix EncodingWarnings in examples (#11182)
Co-authored-by: Ran Benita <ran@unusedvar.com>
2023-07-08 19:17:21 +00:00
github-actions[bot]
a566b78730 [7.4.x] reference: improve the node types docs a bit (#11181)
Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
2023-07-08 19:07:55 +00:00
github-actions[bot]
511adf85be [7.4.x] Fix error assertion handling in approx when None in dict comparison (#11180)
Co-authored-by: Zac Hatfield-Dodds <zac.hatfield.dodds@gmail.com>
2023-07-08 18:37:35 +00:00
github-actions[bot]
c71b5df734 [7.4.x] Add child modules as attributes of parent modules. (#11163)
* [7.4.x] Add child modules as attributes of parent modules.

* Update 10337.bugfix.rst

---------

Co-authored-by: akhilramkee <31619526+akhilramkee@users.noreply.github.com>
Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
2023-07-08 15:11:26 -03:00
github-actions[bot]
d53951836d [7.4.x] Update open trainings (#11172)
Co-authored-by: Florian Bruhin <me@the-compiler.org>
2023-07-04 21:49:24 +00:00
github-actions[bot]
a4d7254d18 [7.4.x] Fix duplicated imports with importlib mode and doctest-modules (#11164)
Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
2023-07-03 16:33:47 +00:00
Bruno Oliveira
b6c55787fe Switch to deploy environment and configure for pypi oidc (#10925) (#11162)
Closes #10871
Closes #10870

Co-authored-by: Ronny Pfannschmidt <opensource@ronnypfannschmidt.de>
2023-07-03 16:17:02 +00:00
Ran Benita
fb03d1388b Merge pull request #11131 from pytest-dev/release-7.4.0
Prepare release 7.4.0
2023-06-23 14:18:43 +03:00
pytest bot
d9bf9dbec1 Prepare release version 7.4.0
[ran: made some fixups]
2023-06-23 14:03:58 +03:00
Ran Benita
64319dbc01 Merge pull request #11128 from bluetech/pythonpath-note
reference: add note that `pythonpath` does not affect `-p`
2023-06-22 18:12:37 +03:00
Ran Benita
1e8135df16 reference: add note that pythonpath does not affect -p
Fix #11118.
2023-06-22 15:45:20 +03:00
Zac Hatfield-Dodds
1e32a4b570 Merge pull request #10935 from nondescryptid/10328 2023-06-21 11:04:10 -07:00
Ran Benita
faa1f9d2ad Merge pull request #11125 from bluetech/initial-conftests-testpaths
config: fix the paths considered for initial conftest discovery
2023-06-21 09:21:27 +03:00
Ran Benita
14890329dc config: fix the paths considered for initial conftest discovery
Fixes #11104.

See the issue for a description of the problem.

Now, we use the same logic for initial conftest paths as we do for
deciding the initial args, which was the idea behind checking
`namespace.file_or_dir` and `testpaths` previously.

This fixes the issue of `testpaths` being considered for initial
conftests even when it's not used for the args.

(Another issue in faeb16146b was that the
`testpaths` were not glob-expanded, this is also fixed.)
2023-06-21 09:01:42 +03:00
Ran Benita
d97d44a97a config: extract initial paths/nodeids args logic to a function
Will be reused in the next commit.
2023-06-20 21:28:48 +03:00
Zac Hatfield-Dodds
f6b995e9d5 Use utf-8 debug file 2023-06-20 04:55:40 -07:00
Zac Hatfield-Dodds
661b938fca Add encoding in more tests 2023-06-20 04:55:40 -07:00
Zac Hatfield-Dodds
7e510769b4 Encoding for subprocess.run 2023-06-20 04:55:39 -07:00
nondescryptid
a704605cf1 Fix encoding warnings 2023-06-20 04:55:39 -07:00
pre-commit-ci[bot]
797b924fc4 [pre-commit.ci] pre-commit autoupdate (#11124)
updates:
- [github.com/asottile/blacken-docs: 1.13.0 → 1.14.0](https://github.com/asottile/blacken-docs/compare/1.13.0...1.14.0)
- [github.com/asottile/reorder-python-imports: v3.9.0 → v3.10.0](https://github.com/asottile/reorder-python-imports/compare/v3.9.0...v3.10.0)
- [github.com/asottile/pyupgrade: v3.6.0 → v3.7.0](https://github.com/asottile/pyupgrade/compare/v3.6.0...v3.7.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-06-20 06:38:36 +00:00
Zac Hatfield-Dodds
b5ec092525 Merge pull request #9149 from Vijay-Arora/main 2023-06-19 20:35:01 -07:00
Zac Hatfield-Dodds
5b35518389 Apply suggestions from code review
Co-authored-by: Ronnie Dutta <61982285+MetRonnie@users.noreply.github.com>
2023-06-19 20:06:21 -07:00
dependabot[bot]
8528052a95 build(deps): Bump peter-evans/create-pull-request from 5.0.0 to 5.0.2 (#11120)
Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 5.0.0 to 5.0.2.
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](5b4a9f6a9e...153407881e)

---
updated-dependencies:
- dependency-name: peter-evans/create-pull-request
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-19 19:22:49 -03:00
dependabot[bot]
1eb83706b6 build(deps): Bump pytest-mock in /testing/plugins_integration (#11119)
Bumps [pytest-mock](https://github.com/pytest-dev/pytest-mock) from 3.10.0 to 3.11.1.
- [Release notes](https://github.com/pytest-dev/pytest-mock/releases)
- [Changelog](https://github.com/pytest-dev/pytest-mock/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-mock/compare/v3.10.0...v3.11.1)

---
updated-dependencies:
- dependency-name: pytest-mock
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-19 04:15:50 +00:00
github-actions[bot]
fa09f6dcc6 [automated] Update plugin list (#11117)
Co-authored-by: pytest bot <pytestbot@users.noreply.github.com>
2023-06-18 09:40:52 -03:00
Florian Bruhin
b55c02c3c9 doc: Add ep2023 training (#11113) 2023-06-15 14:17:14 +02:00
pre-commit-ci[bot]
b7a142f4ba [pre-commit.ci] pre-commit autoupdate (#11103)
updates:
- [github.com/asottile/pyupgrade: v3.4.0 → v3.6.0](https://github.com/asottile/pyupgrade/compare/v3.4.0...v3.6.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-06-13 05:36:28 +00:00
dependabot[bot]
2d824329eb build(deps): Bump django in /testing/plugins_integration (#11102)
Bumps [django](https://github.com/django/django) from 4.2.1 to 4.2.2.
- [Commits](https://github.com/django/django/compare/4.2.1...4.2.2)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-12 04:19:01 +00:00
Ran Benita
ecb23106d8 Merge pull request #11097 from bluetech/cherry-pick-release
Cherry-pick 7.3.2 release notes
2023-06-10 22:57:31 +03:00
Ran Benita
8174a30164 changelog: add note to norecursedir/pytest_ignore_collect change (#11095) 2023-06-10 19:34:59 +00:00
Ran Benita
0142bb6687 Merge pull request #11096 from pytest-dev/release-7.3.2
Prepare release 7.3.2

(cherry picked from commit 5dcd2be466)
2023-06-10 22:31:49 +03:00
Ran Benita
52cf700f1b Merge pull request #10894 from pytest-dev/py312-ci
Python 3.12 support
2023-06-10 20:46:02 +03:00
Ran Benita
6b3ece5bdb ci; use check-latest: true for -dev pythons
When testing -dev python versions, we want to always use the latest.
2023-06-09 11:29:15 +03:00
Ran Benita
4059000834 testing/python/collect: replace use of deprecated/removed imp module 2023-06-07 17:05:52 +03:00
Ran Benita
7d5207a736 Declare support for Python 3.12 2023-06-07 17:05:52 +03:00
Ran Benita
3e65a461c7 ci: start testing Python 3.12-dev 2023-06-07 17:05:52 +03:00
Ran Benita
fc3b5b2610 testing: install setuptools to fix pkg_resources import in a test
Since Python 3.12, setuptools is no longer installed by default in
venvs. We have a test which uses it, so add it to the testing extra.
2023-06-07 17:05:52 +03:00
Ran Benita
9335a0b445 Avoid ast deprecation warnings on Python 3.12
Fix #10977.
2023-06-07 17:05:52 +03:00
Ran Benita
0ded3297a9 Merge pull request #11086 from pytest-dev/update-plugin-list/patch-32de8e289
[automated] Update plugin list
2023-06-07 09:02:06 +03:00
pytest bot
fc9cbbd4c4 [automated] Update plugin list 2023-06-06 14:05:44 +00:00
Ran Benita
1a17539065 Merge pull request #10853 from stefmolin/patch-1
Update fixture scope in package/directory fixture example.
2023-06-06 17:04:45 +03:00
Ran Benita
32de8e2893 Merge pull request #11082 from bluetech/norecursedir-in-hook
main: move norecursedir check to main's pytest_ignore_collect
2023-06-06 12:44:03 +03:00
Ran Benita
5d4a342a7a Merge pull request #11076 from pytest-dev/update-plugin-list/patch-b5ff089d2
[automated] Update plugin list
2023-06-06 12:37:46 +03:00
Facundo Batista
1790f17228 Introduced a hardcoded list of project to include as plugins beyond those found by their names. (#11077) 2023-06-06 12:15:57 +03:00
pre-commit-ci[bot]
ee8baa2676 [pre-commit.ci] pre-commit autoupdate (#11084)
updates:
- [github.com/asottile/setup-cfg-fmt: v2.2.0 → v2.3.0](https://github.com/asottile/setup-cfg-fmt/compare/v2.2.0...v2.3.0)
2023-06-06 12:13:38 +03:00
Ran Benita
ae38b076da main: move norecursedir check to main's pytest_ignore_collect
Fix #11081
2023-06-05 17:21:45 +03:00
dependabot[bot]
85c5bd26b6 build(deps): Bump pytest-xvfb in /testing/plugins_integration (#11079)
Bumps [pytest-xvfb](https://github.com/The-Compiler/pytest-xvfb) from 2.0.0 to 3.0.0.
- [Changelog](https://github.com/The-Compiler/pytest-xvfb/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/The-Compiler/pytest-xvfb/compare/v2.0.0...v3.0.0)

---
updated-dependencies:
- dependency-name: pytest-xvfb
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-05 12:06:55 +02:00
Ran Benita
e1204e1e23 Merge pull request #11078 from bluetech/pkg-roots-path
main: change pkg_roots to work with `Path`s instead of string paths
2023-06-04 21:45:45 +03:00
Ran Benita
313b61471f main: change pkg_roots to work with Paths instead of string paths
- Works better on Windows (case sensitivity)
- Simpler code
2023-06-04 20:43:34 +03:00
pytest bot
1daa8129c6 [automated] Update plugin list 2023-06-04 00:26:58 +00:00
Ran Benita
b5ff089d2c Merge pull request #11069 from bluetech/lf-file
cacheprovider: make file-skipping work with any File, not just Modules
2023-06-03 18:50:50 +03:00
Ran Benita
fda8024622 cacheprovider: make file-skipping work with any File, not just Modules
No reason for `--lf`'s whole-file-skipping feature to not for for
non-Python files.

Fix #11068.
2023-06-03 09:32:26 +03:00
Ran Benita
4b823a42ce Merge pull request #11059 from bluetech/lf-skip-across
cacheprovider: fix file-skipping functionality across packages
2023-06-03 09:31:04 +03:00
Ran Benita
6c9b277ce4 Merge pull request #11070 from bluetech/pkg-redundant-methods
python: remove redundant methods from Package
2023-06-02 18:58:21 +03:00
Ran Benita
3de43e5102 python: remove redundant methods from Package
They are already inherited exactly the same from FSCollector.
2023-06-02 16:08:04 +03:00
Ran Benita
c76ae74bd7 cacheprovider: fix file-skipping functionality across packages
Continuation of fc538c5766.
Fixes #11054 again.
2023-05-30 23:16:43 +03:00
Ran Benita
24534cdd29 Merge pull request #11043 from bluetech/confcutdir-rootpath
config: fallback confcutdir to rootpath if inipath is not set
2023-05-30 20:21:20 +03:00
Ran Benita
99c78aa93a Merge pull request #10921 from bluetech/tb-simplify-2
Fix hidden traceback entries of chained exceptions getting shown
2023-05-30 20:09:13 +03:00
Ran Benita
3a6bdcd76b Merge pull request #11055 from bluetech/lf-skipped-package
cacheprovider: fix file-skipping feature for files in packages
2023-05-30 20:04:06 +03:00
Ran Benita
4a1bba25b9 config: fallback confcutdir to rootpath if inipath is not set
Currently, if `--confcutdir` is not set, `inipath.parent` is used, and
if `initpath` is not set, then `confcutdir` is None, which means there
is no cutoff.

Having no cutoff is not great, it means we potentially start probing
stuff all the way up to the filesystem root directory. So let's add
another fallback, to `rootpath`, which is always something reasonable.
2023-05-30 19:52:59 +03:00
Alessio Izzo
9e1add75f7 Fix warlus operator behavior when called by a function (#11041)
In #10758 we introduced the support for the use of the walrus operator in the test cases. There was a case which was not handled that caused a bug report #11028. This PR aims to fix the issue and also to improve how the walrus operator is handled in the AssertionRewriter class.

Closes #11028
2023-05-30 11:59:24 -03:00
theirix
4da9026766 Handle microseconds with custom logging.Formatter (#11047)
Added handling of %f directive to print microseconds in log format options, such as log-date-format. It is impossible to do with a standard logging.Formatter because it uses time.strftime which doesn't know about milliseconds and %f. In this PR I added a custom Formatter which converts LogRecord to a datetime.datetime object and formats it with %f flag. This behaviour is enabled only if a microsecond flag is specified in a format string.

Also added a few tests to check the standard and changed behavior.

Closes #10991
2023-05-30 09:35:33 -03:00
Kenny Y
7c231baa64 Add warning when testpaths is set but paths are not found by glob (#11044)
Closes #11013

---------

Co-authored-by: Ran Benita <ran@unusedvar.com>
2023-05-30 07:06:13 -03:00
Ran Benita
fc538c5766 cacheprovider: fix file-skipping feature for files in packages
`--lf` has a feature where if a certain `Module` (python file) does not
contain any failed tests, it is skipped entirely at the collector level
instead of being collected and each item skipped individually. When this
happens the collection summary looks like this:

    run-last-failure: rerun previous 1 failure (skipped 1 file)

However, this feature didn't work for `Module`s inside of `Package`s,
only for those directly beneath the `Session`.

Fix #11054.
2023-05-29 22:55:44 +03:00
dependabot[bot]
fbfd4b5005 build(deps): Bump anyio[curio,trio] in /testing/plugins_integration (#11050)
Bumps [anyio[curio,trio]](https://github.com/agronholm/anyio) from 3.6.2 to 3.7.0.
- [Changelog](https://github.com/agronholm/anyio/blob/3.7.0/docs/versionhistory.rst)
- [Commits](https://github.com/agronholm/anyio/compare/3.6.2...3.7.0)

---
updated-dependencies:
- dependency-name: anyio[curio,trio]
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-29 07:55:42 +02:00
dependabot[bot]
ec752537ea build(deps): Bump pytest-cov in /testing/plugins_integration (#11051)
Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 4.0.0 to 4.1.0.
- [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-cov/compare/v4.0.0...v4.1.0)

---
updated-dependencies:
- dependency-name: pytest-cov
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-29 07:55:13 +02:00
Ran Benita
dd667336ce nodes: apply same traceback filtering for chained exceptions as main exception
Fix #1904.
2023-05-28 17:20:50 +03:00
Ran Benita
29d16d2939 Merge pull request #11046 from pytest-dev/update-plugin-list/patch-4f3f36c39
[automated] Update plugin list
2023-05-28 16:02:07 +03:00
pytest bot
5313d50e18 [automated] Update plugin list 2023-05-28 00:22:05 +00:00
Chris Mahoney
4f3f36c396 Add alias --config-file to -c (#11036)
Fixes #11031

Signed-off-by: Chris Mahoney <chrismahoey@hotmail.com>
Co-authored-by: Chris Mahoney <chrismahoey@hotmail.com>
2023-05-26 07:56:18 -03:00
Ran Benita
af124c7f21 Merge pull request #11026 from bluetech/small-fixes
Small fixes and improvements
2023-05-24 21:44:03 +03:00
Bruno Oliveira
4e6d53fef5 Merge pull request #11033 from jkeifer/patch-1
nonpython example now repr all exceptions
2023-05-24 08:10:42 -03:00
Jarrett Keifer
751d726d21 nonpython example now repr all exceptions
The definition of `repr_failure` on the `YamlItem` subclass only handled the custom `YamlException` class, which hides all other errors from the user. By adding in the `super` call we ensure all other exception types also appropriately handled by `repr_failure`.
2023-05-23 20:06:05 -07:00
github-actions[bot]
9e491f430e [automated] Update plugin list (#11027)
Co-authored-by: pytest bot <pytestbot@users.noreply.github.com>
2023-05-21 10:47:02 -03:00
Ran Benita
63f258f432 python: fix syntax typo 2023-05-20 21:14:29 +03:00
Ran Benita
baaa67dfb9 python: simplify code in Package.collect()
The path of Package is already the `__init__.py` file, and we're already
assured it's a file.
2023-05-20 21:14:29 +03:00
Ran Benita
519f351b4f pathlib: extract scandir utility from visit
Will be used on its in some upcoming changes, but good on its own.
2023-05-20 21:14:29 +03:00
Ran Benita
5d53447a73 fixtures: use isinstance in get_scope_package
No reason for the exact type equality.
2023-05-20 21:13:48 +03:00
Ran Benita
1716d3c9bf fixtures: type annotate get_scope_package 2023-05-20 21:06:24 +03:00
Ran Benita
ac699e7b25 fixtures: add type annotations and docstring to parsefactories 2023-05-20 21:06:13 +03:00
Ran Benita
a5f37199a9 fixtures: inline FixtureRequest._addfinalizer
There are no longer any calls to `_addfinalizer` other than this one
place, so inline it.
2023-05-20 21:06:07 +03:00
Roberto Aldera
9fa82598a9 Use NamedTuple for pytest_report_teststatus return value (#10972)
Closes #10872

---------

Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
2023-05-19 08:24:28 -03:00
Alex Lambson
ba32a3bd87 Handle disabled logging in 'caplog.set_level' and 'caplog.at_level' (#8758)
Forces requested `caplog` logging levels to be enabled if they were disabled via `logging.disable()`

`[attr-defined]` mypy error ignored in `logging.py` because there were existing errors with the imports
and `loggin.Logger.manager` is an attr set at runtime. Since it's in the standard lib I can't really fix that.

Ignored an attr-defined error in `src/_pytest/config/__init__.py` because the re-export is necessary.

Fixes #8711
2023-05-18 10:18:59 -03:00
Ville Skyttä
c8641f879f Include reason in cache path warnings to aid debugging (#11005)
Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
2023-05-18 10:11:47 -03:00
Ville Skyttä
6041511fb4 Spelling and grammar fixes (#11014) 2023-05-18 10:10:44 -03:00
Ronny Pfannschmidt
739408b958 Merge pull request #11009 from nicoddemus/reference-status-of-python-versions
Reference "Status of Python Versions" in backwards-compatibility policy
2023-05-17 09:51:30 +02:00
Bruno Oliveira
1636322995 Reference "Status of Python Versions" in backwards-compatibility policy
As suggested in #10981.
2023-05-16 20:24:06 -03:00
pre-commit-ci[bot]
b8edacb8f1 [pre-commit.ci] pre-commit autoupdate (#11007)
updates:
- [github.com/pre-commit/mirrors-mypy: v1.2.0 → v1.3.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.2.0...v1.3.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-05-16 09:19:38 +02:00
github-actions[bot]
612489e2bd [automated] Update plugin list (#11001)
Co-authored-by: pytest bot <pytestbot@users.noreply.github.com>
2023-05-15 08:26:41 -03:00
dependabot[bot]
383774db10 build(deps): Bump actions/stale from 5 to 8 (#11003)
Bumps [actions/stale](https://github.com/actions/stale) from 5 to 8.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v5...v8)

---
updated-dependencies:
- dependency-name: actions/stale
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-15 07:29:18 +02:00
Adam J. Stewart
3b5b3cf50e monkeypatch: add support for TypedDict (#11000) 2023-05-14 22:17:00 +03:00
Bruno Oliveira
23e343af60 Fix close stale issues workflow (#10990)
Fixed how the label is configured.
2023-05-14 08:39:45 -03:00
Ronny Pfannschmidt
f9a995b56d Merge pull request #10998 from bluetech/pre-commit-rm-default
pre-commit: remove `default_language_version` setting
2023-05-13 23:27:48 +02:00
Ran Benita
d9d78a8aef pre-commit: remove default_language_version setting
This makes it difficult to run on newer python versions than the one
specified.
2023-05-13 22:24:30 +03:00
Bruno Oliveira
76d15231f5 Merge pull request #10988 from nicoddemus/initial-testpaths-10987
Consider testpaths for initial conftests
2023-05-12 09:58:59 -03:00
Bruno Oliveira
4cc05e7bee Fix trailing whitespace in .github/workflows/stale.yml 2023-05-12 09:34:44 -03:00
Bruno Oliveira
2d57d5c32f Do not break on very long command-line options
`_set_initial_conftests` could break on some systems if a very long
option was passed, because the `Path.exists()` call raises an
`OSError` instead of returning `False`.

Fix #10169
2023-05-12 09:34:15 -03:00
Bruno Oliveira
faeb16146b Consider testpaths for initial conftests
The 'testpaths' option is meant to be identical to execute
pytest passing the 'testpaths' directories explicitly.

Fix #10987
2023-05-12 09:34:15 -03:00
Bruno Oliveira
b241c0b479 Fix defaults for tmp_path_retention_count and tmp_path_retention_policy in docs 2023-05-12 09:34:15 -03:00
Bruno Oliveira
78403237cf Add workflow to close "needs information" labeled issues (#10986)
This introduces a workflow to automatically close issues with the label
`status: needs information` after a number of days of inactivity.

This work has been done manually for a number of years, but I think it is safe
to close issues with this label automatically.

Not tested yet, but it is in `debug-only` mode so we can watch what it does before
deciding to turn it on (however it needs to be in `main` for it to run).
2023-05-11 15:14:50 -03:00
leeyueh
f84fea0888 Update usage.rst (#10974)
Added a note for single quotation used in Windows.
2023-05-10 22:28:52 -03:00
Ran Benita
271bdf6c23 Merge pull request #10979 from bluetech/faulthandler-no-encoding
faulthandler: avoid accessing sys.stderr.encoding
2023-05-10 14:17:54 +03:00
Ran Benita
fd56968f2b Merge pull request #10970 from pytest-dev/dependabot/pip/testing/plugins_integration/django-4.2.1
build(deps): Bump django from 4.2 to 4.2.1 in /testing/plugins_integration
2023-05-10 10:18:17 +03:00
Ran Benita
5b75b0d03f Merge pull request #10978 from bzoracler/fix-pytest-code-import
fix reference to non-existent module
2023-05-10 10:15:20 +03:00
Ran Benita
aac5d5d08b faulthandler: avoid accessing sys.stderr.encoding
Fixes a pytest-xdist regression after
762bb61562 (not yet released).

pytest-xdist patches sys.stderr with an object which doesn't have
`encoding`. Strictly speaking, this should be fixed there (or more
precisely, in execnet), but it will drop support for older versions
which don't want.

But in any case, the fix turns out to simplify the code, using FD
support added in Python 3.5, so it's good anyway!

Refs: https://github.com/pytest-dev/pytest-xdist/pull/900
2023-05-10 09:59:57 +03:00
bzoracler
b1460f3261 fix reference to non-existent module 2023-05-10 10:48:20 +12:00
github-actions[bot]
a88ae8289c [automated] Update plugin list (#10968)
Co-authored-by: pytest bot <pytestbot@users.noreply.github.com>
2023-05-09 19:05:13 -03:00
pre-commit-ci[bot]
62320e4ff7 [pre-commit.ci] pre-commit autoupdate (#10975)
updates:
- https://github.com/asottile/reorder_python_importshttps://github.com/asottile/reorder-python-imports
- [github.com/asottile/pyupgrade: v3.3.2 → v3.4.0](https://github.com/asottile/pyupgrade/compare/v3.3.2...v3.4.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-05-09 11:22:50 +02:00
dependabot[bot]
6514041a35 build(deps): Bump django in /testing/plugins_integration
Bumps [django](https://github.com/django/django) from 4.2 to 4.2.1.
- [Commits](https://github.com/django/django/compare/4.2...4.2.1)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-08 03:56:49 +00:00
Brian Larsen
7d548c38e2 Improve verbose output by wrapping skip/xfail reasons with margin (#10958)
Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
2023-05-06 12:15:11 -03:00
Bruno Oliveira
07eeeb8dfc Merge pull request #10957 from pytest-dev/update-plugin-list/patch-762bb6156
[automated] Update plugin list
2023-05-02 10:02:58 -04:00
pytest bot
be774667c2 [automated] Update plugin list 2023-04-30 00:23:40 +00:00
Ran Benita
762bb61562 Fix couple of EncodingWarnings (#10954)
* faulthandler: fix an EncodingWarning

* _py/path: tiny change to `ensure` to silence EncodingWarning

We're not supposed to diverge here, but make this change to fix an
unavoidable EncodingWarning that is otherwise raised in pytest's test
suite. The behavior should be exactly the same besides the warning,
hopefully that won't cause confusion.
2023-04-29 11:37:22 +03:00
Sergey Kim
725de3a0d3 add flake8-pytest-style mention to goodpractices (#10939) 2023-04-28 23:47:47 +03:00
Ran Benita
fcada1ea47 nodes: change _prunetraceback to return the new traceback instead of modifying excinfo
This makes it usable as a general function, and just more understandable
in general.
2023-04-28 11:47:45 +03:00
Ran Benita
6f7f89f3c4 code: make TracebackEntry immutable
TracebackEntry being mutable caught me by surprise and makes reasoning
about the exception formatting code harder. Make it a proper value.
2023-04-28 11:47:45 +03:00
Ran Benita
0a20452f78 code: inline Traceback.getcrashentry into ExceptionInfo._getreprcrash
Since `Traceback.getcrashentry` takes the `ExceptionInfo`, it is not
really independent of it and is in the wrong layer. Prevent nonsensical
mistakes by inlining it.
2023-04-28 11:47:45 +03:00
Ran Benita
cc23ec91d0 code: stop storing weakref to ExceptionInfo on Traceback and TracebackEntry
TracebackEntry needs the excinfo for the `__tracebackhide__ = callback`
functionality, where `callback` accepts the excinfo.

Currently it achieves this by storing a weakref to the excinfo which
created it. I think this is not great, mixing layers and bloating the
objects.

Instead, have `ishidden` (and transitively, `Traceback.filter()`) take
the excinfo as a parameter.
2023-04-28 11:47:45 +03:00
Florian Bruhin
a15f544962 doc: Fix 2024 training location (#10947) 2023-04-26 08:13:06 +02:00
pre-commit-ci[bot]
3823ce60dd [pre-commit.ci] pre-commit autoupdate (#10941)
updates:
- [github.com/PyCQA/autoflake: v2.1.0 → v2.1.1](https://github.com/PyCQA/autoflake/compare/v2.1.0...v2.1.1)
- [github.com/asottile/pyupgrade: v3.3.1 → v3.3.2](https://github.com/asottile/pyupgrade/compare/v3.3.1...v3.3.2)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-04-25 10:52:15 +02:00
Miro Hrončok
e03f82c359 Filter new pkg_resources deprecations (#10938)
Fixes https://github.com/pytest-dev/pytest/issues/10815
2023-04-25 10:51:10 +02:00
Bryan Ricker
158f41fdf8 Fix documentation typo (#10942) 2023-04-25 10:49:16 +02:00
Bruno Oliveira
fd6a4507ac Merge pull request #10936 from pytest-dev/update-plugin-list/patch-0860f4e91
[automated] Update plugin list
2023-04-22 22:42:05 -03:00
pytest bot
15156757b6 [automated] Update plugin list 2023-04-23 00:21:26 +00:00
Florian Bruhin
0860f4e916 Add 2024 pytest training (#10933) 2023-04-22 21:22:25 +02:00
Ran Benita
11965d1c27 Merge pull request #10920 from bluetech/testing-no-testdir
testing: remove usages of testdir that sneaked back in
2023-04-18 22:49:23 +03:00
pre-commit-ci[bot]
14be71b234 [pre-commit.ci] pre-commit autoupdate (#10926)
updates:
- [github.com/PyCQA/autoflake: v2.0.2 → v2.1.0](https://github.com/PyCQA/autoflake/compare/v2.0.2...v2.1.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-04-18 07:03:27 +02:00
dependabot[bot]
2d6206b89a build(deps): Bump pytest-sugar in /testing/plugins_integration (#10922)
Bumps [pytest-sugar](https://github.com/Teemu/pytest-sugar) from 0.9.5 to 0.9.7.
- [Release notes](https://github.com/Teemu/pytest-sugar/releases)
- [Changelog](https://github.com/Teemu/pytest-sugar/blob/main/CHANGES.rst)
- [Commits](https://github.com/Teemu/pytest-sugar/commits/v0.9.7)

---
updated-dependencies:
- dependency-name: pytest-sugar
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-17 08:55:36 +02:00
Alex
41f57ef95d Fix pytrace=False and --tb=line reports None (#10905)
Closes #10831.

This fixes a small bug where running tests that contained
`pytest.fail(pytrace=False)` with the `--tb=line` flag set results in
 an output of "None" in the Failures section of the output, and adds
 a test to ensure the behavior is correct.
2023-04-16 20:31:45 +00:00
Ran Benita
4eca6063c8 Merge pull request #10918 from pytest-dev/update-plugin-list/patch-7834b3b07
[automated] Update plugin list
2023-04-16 19:40:51 +03:00
Ran Benita
819f5abd73 testing: remove usages of testdir that sneaked back in 2023-04-16 19:18:50 +03:00
Ran Benita
adf891ab1d Merge pull request #10919 from bluetech/missing-changelog
doc: add missing changelog for #10907
2023-04-16 18:41:45 +03:00
Ran Benita
f08184ba20 doc: add missing changelog for #10907 2023-04-16 18:22:51 +03:00
pytest bot
28783e5d23 [automated] Update plugin list 2023-04-16 00:21:39 +00:00
Bruno Oliveira
7834b3b07f Merge pull request #10914 from nicoddemus/cherry-pick-release-7.3.1
Merge pull request #10913 from pytest-dev/release-7.3.1
2023-04-14 15:19:13 -03:00
Bruno Oliveira
ece756fcb4 Merge pull request #10913 from pytest-dev/release-7.3.1
Prepare release 7.3.1

(cherry picked from commit a1f7a204df)
2023-04-14 15:15:28 -03:00
Bruno Oliveira
d380771065 Fix tmp_path regression introduced in 7.3.0 (#10911)
The problem is that we would loop over all directories of the basetemp directory searching for dead symlinks, for each test, which would compound over the test session run.

Doing the cleanup just once, at the end of the session, fixes the problem.

Fix #10896
2023-04-14 13:24:12 -03:00
Ran Benita
b893d2a0fe Merge pull request #10907 from bluetech/empty-traceback
code: handle repr'ing empty tracebacks gracefully
2023-04-13 19:36:09 +03:00
Ran Benita
e3b1799766 code: handle repr'ing empty tracebacks gracefully
By "empty traceback" I mean a traceback all of whose entries have been
filtered/cut/pruned out.

Currently, if an empty traceback needs to be repr'ed, the last entry
before the filtering is used instead (added in
accd962c9f).

Showing a hidden frame is not so good IMO. This commit does the
following instead:

1. Shows details of the exception.
2. Shows a message about how the full trace can be seen.

Example:

```
_____________ test _____________

E   ZeroDivisionError: division by zero
All traceback entries are hidden. Pass `--full-trace` to see hidden and internal frames.
```

Also handles `--tb=native`, though there the `--full-trace` bit is not
shown.

This commit contains some pieces from
431ec6d34e (which has been reverted).

Helps towards fixing issue # 1904.

Co-authored-by: Felix Hofstätter <Felhof1@hotmail.com>
2023-04-13 19:11:37 +03:00
Ran Benita
5d1385320f Merge pull request #10901 from bluetech/exceptioninfo-from-exception
code: add `ExceptionInfo.from_exception`
2023-04-13 16:04:48 +03:00
Ran Benita
eff54aece1 Merge pull request #10904 from bluetech/revert-10772
Revert "Correctly handle tracebackhide for chained exceptions (#10772)"
2023-04-13 14:51:57 +03:00
Ran Benita
bc1fc3f0fc Merge branch 'main' into exceptioninfo-from-exception 2023-04-13 14:51:07 +03:00
Ran Benita
784ffa0fad Merge pull request #10900 from bluetech/exceptioninfo-from_exc_info-exp
code: drop Experimental API label from ExceptionInfo.from_exc_info
2023-04-13 14:48:56 +03:00
Ran Benita
90412827c3 Revert "Correctly handle tracebackhide for chained exceptions (#10772)"
This reverts commit 431ec6d34e.

Fix #10903.
Reopen #1904.
2023-04-12 19:23:25 +03:00
Ran Benita
424c3eebde code: add ExceptionInfo.from_exception
The old-style `sys.exc_info()` triplet is redundant nowadays with
`(type(exc), exc, exc.__traceback__)`, and is beginning to get
soft-deprecated in Python 3.12.

Add a nicer API to ExceptionInfo which takes just the exc instead of the
triplet. There are already a few internal uses which benefit.
2023-04-12 13:16:48 +03:00
Ran Benita
9c2247ec1b code: drop Experimental API label from ExceptionInfo.from_exc_info
This API is OK, I don't think we're going to change something about it
at this point.
2023-04-12 12:46:29 +03:00
Ran Benita
61f7c27ec0 Merge pull request #10893 from bluetech/py312
Python 3.12 alpha fixes
2023-04-11 23:54:47 +03:00
Ran Benita
1b81d636e2 unittest: add addDuration function for Python 3.12 support
Fix #10875

Without this, fails with

```
...
E           AttributeError: 'TestCaseFunction' object has no attribute 'addDuration'
...
E           RuntimeWarning: TestResult has no addDuration method
```
2023-04-11 13:24:32 +03:00
Ran Benita
1b196fbeaf pathlib: fix Python 3.12 rmtree(onerror=...) deprecation
Fixes #10890
Ref: https://docs.python.org/3.12/library/shutil.html#shutil.rmtree
2023-04-11 13:24:32 +03:00
pre-commit-ci[bot]
22524046cf [pre-commit.ci] pre-commit autoupdate (#10891)
updates:
- [github.com/pre-commit/mirrors-mypy: v1.1.1 → v1.2.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.1.1...v1.2.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-04-11 07:08:45 +02:00
Bruno Oliveira
6dcd652d4a Amend changelog note for removal of attrs (#10888)
As discussed in https://github.com/pytest-dev/pytest/pull/10669#issuecomment-1501497729, we should
make the reasoning behind this change more clear, as well as thank the attrs maintainers for the
many years of cooperation and support.

Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
2023-04-10 13:04:02 -03:00
dependabot[bot]
be9faa68d8 build(deps): Bump peter-evans/create-pull-request from 4.2.4 to 5.0.0 (#10887)
Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4.2.4 to 5.0.0.
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](38e0b6e68b...5b4a9f6a9e)

---
updated-dependencies:
- dependency-name: peter-evans/create-pull-request
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-10 08:25:35 -03:00
dependabot[bot]
e4e13dd913 build(deps): Bump django in /testing/plugins_integration (#10886)
Bumps [django](https://github.com/django/django) from 4.1.7 to 4.2.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/compare/4.1.7...4.2)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-10 08:21:27 -03:00
Ran Benita
5a399030c1 Merge pull request #10884 from pytest-dev/update-plugin-list/patch-ec8e23951
[automated] Update plugin list
2023-04-09 11:11:02 +03:00
Ran Benita
19c8ef63a4 Merge pull request #10880 from bluetech/regen-rm-dataclasses-dep
tox: remove `dataclasses` dependency from `regen`
2023-04-09 10:46:11 +03:00
pytest bot
22951bba67 [automated] Update plugin list 2023-04-09 00:22:30 +00:00
Ran Benita
ec8e23951d Merge pull request #10883 from bluetech/cherry-pick-release
Cherry pick 7.3.0 release notes
2023-04-09 00:53:52 +03:00
Ran Benita
bf47357511 Merge pull request #10881 from pytest-dev/release-7.3.0
Prepare release 7.3.0

(cherry picked from commit cec5bfe058)
2023-04-09 00:50:37 +03:00
Ran Benita
2d2dc4a2a8 tox: remove dataclasses dependency from regen
This dep is not needed on newer Pythons.
2023-04-09 00:18:07 +03:00
Kodi Arfer
3683722bcb FormattedExcinfo.get_source: avoid crash when line number is out-of-bounds/negative
pytest could crash given pathological AST position attributes, which shouldn't happen when testing real Python code, but could happen when testing AST produced by e.g. Hylang.

Another example of the failure is in the nightly CI for the JAX project: https://github.com/google/jax/actions/runs/4607513902/jobs/8142126075

Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
Co-authored-by: Jake VanderPlas <jakevdp@google.com>
2023-04-05 22:48:24 -03:00
github-actions[bot]
31d0b51039 [automated] Update plugin list (#10857)
Co-authored-by: pytest bot <pytestbot@users.noreply.github.com>
2023-04-04 13:17:43 -03:00
Pierre Sassoulas
2d2f69dab5 Merge pull request #10862 from pytest-dev/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-04-04 09:04:47 +02:00
pre-commit-ci[bot]
2a39ed3461 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/psf/black: 23.1.0 → 23.3.0](https://github.com/psf/black/compare/23.1.0...23.3.0)
2023-04-04 06:34:42 +00:00
Stefanie Molin
f1c7585184 Update fixture scope in package/directory fixture example. 2023-03-31 10:00:45 -07:00
github-actions[bot]
a3b39069bc [automated] Update plugin list (#10838)
Co-authored-by: pytest bot <pytestbot@users.noreply.github.com>
2023-03-26 08:32:13 -03:00
github-actions[bot]
172c832cbd [automated] Update plugin list (#10823)
Co-authored-by: pytest bot <pytestbot@users.noreply.github.com>
2023-03-24 11:41:07 -03:00
dependabot[bot]
839b90db45 build(deps): Bump peter-evans/create-pull-request from 4.2.3 to 4.2.4 (#10828)
Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4.2.3 to 4.2.4.
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](2b011faafd...38e0b6e68b)

---
updated-dependencies:
- dependency-name: peter-evans/create-pull-request
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-24 11:40:34 -03:00
dependabot[bot]
549cc512f7 build(deps): Bump pytest-asyncio in /testing/plugins_integration (#10827)
Bumps [pytest-asyncio](https://github.com/pytest-dev/pytest-asyncio) from 0.20.2 to 0.21.0.
- [Release notes](https://github.com/pytest-dev/pytest-asyncio/releases)
- [Commits](https://github.com/pytest-dev/pytest-asyncio/compare/v0.20.2...v0.21.0)

---
updated-dependencies:
- dependency-name: pytest-asyncio
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-24 11:40:06 -03:00
Ronny Pfannschmidt
2369bed1db Merge pull request #10727 from RonnyPfannschmidt/ronny/split-report-header
split up report header lines for config, rootdir and testpaths
2023-03-18 22:06:46 +01:00
Ronny Pfannschmidt
54864f0c9b bugfix: fix imports for simple example 2023-03-17 21:58:26 +01:00
Ronny Pfannschmidt
ba969d2ae7 run regendoc 2023-03-17 21:58:26 +01:00
Ronny Pfannschmidt
407b330fe1 split up report header lines
i found it painful to read crammed in a single line
thus rootdir, config file and testpaths now have own lines
2023-03-17 21:58:26 +01:00
Felix Hofstätter
431ec6d34e Correctly handle tracebackhide for chained exceptions (#10772) 2023-03-15 08:10:25 -03:00
pre-commit-ci[bot]
eada68b2b3 [pre-commit.ci] pre-commit autoupdate (#10814)
updates:
- [github.com/PyCQA/autoflake: v2.0.1 → v2.0.2](https://github.com/PyCQA/autoflake/compare/v2.0.1...v2.0.2)
- [github.com/pre-commit/mirrors-mypy: v1.0.1 → v1.1.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.0.1...v1.1.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-03-14 08:17:13 -03:00
dependabot[bot]
ab069247cd build(deps): Bump pytest-rerunfailures in /testing/plugins_integration (#10812)
Bumps [pytest-rerunfailures](https://github.com/pytest-dev/pytest-rerunfailures) from 11.1.1 to 11.1.2.
- [Release notes](https://github.com/pytest-dev/pytest-rerunfailures/releases)
- [Changelog](https://github.com/pytest-dev/pytest-rerunfailures/blob/master/CHANGES.rst)
- [Commits](https://github.com/pytest-dev/pytest-rerunfailures/compare/11.1.1...11.1.2)

---
updated-dependencies:
- dependency-name: pytest-rerunfailures
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-13 08:00:07 -03:00
github-actions[bot]
7af1e4e4ed [automated] Update plugin list (#10810)
Co-authored-by: pytest bot <pytestbot@users.noreply.github.com>
2023-03-12 13:04:50 -03:00
Stefanie Molin
0ae04ae629 Include pyproject.toml in help section that lists out config files with ini-options (#10807) 2023-03-11 10:59:02 -03:00
Florian Bruhin
723035be7f doc: Remove done training (#10805) 2023-03-10 20:47:03 +00:00
Alessio Izzo
6e478b0947 Fix walrus operator support in assertion rewriting (#10758)
Closes #10743
2023-03-10 07:32:36 -03:00
Paul Kehrer
a869141b3d New option to allow a progress report even when capture=no (#10755) 2023-03-07 17:49:37 -03:00
Bruno Oliveira
5e98aefc92 Merge pull request #10794 from pytest-dev/update-plugin-list/patch-d5dda84ef
[automated] Update plugin list
2023-03-06 08:16:06 -03:00
Bruno Oliveira
5f47e423b2 Merge pull request #10795 from bluthej/fix-10782
Fix example in the documentation (#10782)
2023-03-05 13:49:35 -03:00
bluthej
5a61ec3d4a Fix example in the documentation (#10782) 2023-03-05 17:01:21 +01:00
Zac Hatfield-Dodds
b3b44ea814 Merge pull request #10766 from rdb/fix-10765 2023-03-04 23:34:35 -08:00
pytest bot
1d48b3021d [automated] Update plugin list 2023-03-05 00:25:29 +00:00
Bruno Oliveira
d5dda84ef3 Merge pull request #10793 from nicoddemus/cherry-pick-release
Merge pull request #10792 from pytest-dev/release-7.2.2
2023-03-03 16:22:47 -03:00
Bruno Oliveira
517e02e59e Merge pull request #10792 from pytest-dev/release-7.2.2
Prepare release 7.2.2

(cherry picked from commit 3ce6030f0c)
2023-03-03 16:14:11 -03:00
Bruno Oliveira
4e259590c9 Normalize how changelog entries are written (#10779)
Went over all changelog entries making sure they follow our guidelines as written at:

88c9e92258/.github/PULL_REQUEST_TEMPLATE.md (L18-L21)
2023-03-03 12:53:38 -03:00
Bruno Oliveira
97a2761d72 Fix test_cmdline_python_namespace_package (#10788)
pgk_resources.declare_namespace has been deprecated, so added an ignore warnings option
to the test.
2023-03-03 12:25:33 -03:00
Billy
88c9e92258 Minor updates to fixtures docs (#10724)
Updated the c fixture to be a little more consistent with other fixtures in the corresponding image. for example both e and g both have edges connected with the fixtures that they explicitly depend on.
2023-02-28 12:42:33 -03:00
Ronny Pfannschmidt
72ad32411f Docs: be more explicit about module level skip preventing collection (#10753) 2023-02-28 12:41:31 -03:00
Bruno Oliveira
cb9e8be301 Move logic to get_user_id in compat 2023-02-28 11:19:34 -03:00
Bruno Oliveira
d72da480c4 Apply suggestions from code review 2023-02-28 11:02:17 -03:00
Bruno Oliveira
07e7deb4a7 Update changelog/10765.bugfix.rst 2023-02-28 10:59:23 -03:00
Bruno Oliveira
572b5657d7 Merge pull request #10767 from alexhad6/fix-typo-in-python_api.py
Fix typo in python_api.py
2023-02-27 08:59:13 -03:00
dependabot[bot]
44afed9b13 build(deps): Bump pytest-rerunfailures in /testing/plugins_integration (#10754)
Bumps [pytest-rerunfailures](https://github.com/pytest-dev/pytest-rerunfailures) from 11.1 to 11.1.1.
- [Release notes](https://github.com/pytest-dev/pytest-rerunfailures/releases)
- [Changelog](https://github.com/pytest-dev/pytest-rerunfailures/blob/master/CHANGES.rst)
- [Commits](https://github.com/pytest-dev/pytest-rerunfailures/compare/11.1...11.1.1)

---
updated-dependencies:
- dependency-name: pytest-rerunfailures
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-27 08:56:25 -03:00
github-actions[bot]
13ea4780b8 [automated] Update plugin list (#10752)
Co-authored-by: pytest bot <pytestbot@users.noreply.github.com>
2023-02-27 08:55:33 -03:00
Alex Hadley
135600fca3 Fix typo in python_api.py 2023-02-24 15:04:42 -08:00
rdb
c237297b3d Fix OSError in tmpdir on emscripten due to missing getuid()
Fixes #10765
2023-02-24 23:23:44 +01:00
Ronny Pfannschmidt
9ccae9a8e3 Merge pull request #10756 from pytest-dev/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-02-22 15:10:09 +01:00
pre-commit-ci[bot]
77152d26e7 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/mirrors-mypy: v1.0.0 → v1.0.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.0.0...v1.0.1)
2023-02-21 03:03:45 +00:00
Manuel Jacob
da626e7186 Update import mode documentation to not refer to __import__() anymore. (#10747)
Nowadays, the prepend and append import modes use importlib.import_module() instead of __import__().

There was a phrase “which avoids having to use `__import__`”, in which I couldn’t just replace `__import__` by `importlib.import_module` because the latter is used (in insert_missing_modules()) also when using importlib mode. Therefore I removed the part from the sentence.
2023-02-18 18:55:46 -03:00
bitzge
051f8f1f0f Add CI and BUILD_NUMBER env var in docs (#10749) 2023-02-18 18:52:14 -03:00
Bruno Oliveira
31ad577325 Merge pull request #10741 from pytest-dev/dependabot/pip/testing/plugins_integration/django-4.1.7
build(deps): Bump django from 4.1.6 to 4.1.7 in /testing/plugins_integration
2023-02-16 06:50:38 -03:00
dependabot[bot]
835cac8d8b build(deps): Bump django in /testing/plugins_integration
Bumps [django](https://github.com/django/django) from 4.1.6 to 4.1.7.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/compare/4.1.6...4.1.7)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-15 20:40:12 +00:00
Florian Bruhin
464f29901f Update open training (#10739) 2023-02-15 14:06:24 +00:00
Garvit Shubham
aa72496d24 Fix entry-points declaration in the documentation example using Hatch
Closes #10721
2023-02-14 10:57:32 -03:00
pre-commit-ci[bot]
e9f3a01392 [pre-commit.ci] pre-commit autoupdate (#10733)
updates:
- [github.com/pre-commit/mirrors-mypy: v0.991 → v1.0.0](https://github.com/pre-commit/mirrors-mypy/compare/v0.991...v1.0.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-02-14 07:30:12 -03:00
dependabot[bot]
00c94ab01b build(deps): Bump pytest-rerunfailures in /testing/plugins_integration (#10731)
Bumps [pytest-rerunfailures](https://github.com/pytest-dev/pytest-rerunfailures) from 11.0 to 11.1.
- [Release notes](https://github.com/pytest-dev/pytest-rerunfailures/releases)
- [Changelog](https://github.com/pytest-dev/pytest-rerunfailures/blob/master/CHANGES.rst)
- [Commits](https://github.com/pytest-dev/pytest-rerunfailures/compare/11.0...11.1)

---
updated-dependencies:
- dependency-name: pytest-rerunfailures
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-13 08:05:47 -03:00
github-actions[bot]
9048621002 [automated] Update plugin list (#10726)
Co-authored-by: pytest bot <pytestbot@users.noreply.github.com>
2023-02-12 21:39:45 -03:00
Bruno Oliveira
27165cf8db Use build-and-inspect-python-package action (#10722)
This uses https://github.com/hynek/build-and-inspect-python-package to ensure our package is correct, both during testing and deploy,
2023-02-12 21:37:40 -03:00
Ilya Konstantinov
7a829cb57d Document the location tuple (#10700) 2023-02-12 11:20:53 -03:00
HTRafal
5e1c3d2477 Propagate timestamps from CallInfo to TestReport objects (#10711)
This makes it possible to correlate pytest stages with external events, and also makes it readable when TestReports are exported externall (for example with pytest-reportlog).

Closes #10710
2023-02-10 17:52:54 -03:00
pre-commit-ci[bot]
59e7d2bbc9 [pre-commit.ci] pre-commit autoupdate (#10712)
* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/psf/black: 22.12.0 → 23.1.0](https://github.com/psf/black/compare/22.12.0...23.1.0)
- [github.com/PyCQA/autoflake: v2.0.0 → v2.0.1](https://github.com/PyCQA/autoflake/compare/v2.0.0...v2.0.1)

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

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

* Update .pre-commit-config.yaml

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

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

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
2023-02-07 19:30:33 -03:00
Mahesh Vashishtha
af99040123 Add a note about -W vs filterwarnings. (#10713)
Closes #10687

Signed-off-by: mvashishtha <mahesh@ponder.io>
2023-02-07 19:27:34 -03:00
dependabot[bot]
a2b7db7655 build(deps): Bump django in /testing/plugins_integration (#10706)
Bumps [django](https://github.com/django/django) from 4.1.5 to 4.1.6.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/compare/4.1.5...4.1.6)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-07 08:00:16 -03:00
github-actions[bot]
9c93c96b14 [automated] Update plugin list (#10707)
Co-authored-by: pytest bot <pytestbot@users.noreply.github.com>
2023-02-07 07:59:57 -03:00
github-actions[bot]
4a46ee8bc9 [automated] Update plugin list (#10699)
Co-authored-by: pytest bot <pytestbot@users.noreply.github.com>
2023-01-30 10:15:02 -03:00
Teejay
5dbfb8e108 Fix fixtures named teardown being considered by nose (#10696)
Closes #10597
2023-01-27 14:33:46 -03:00
vin01
86a1beba07 Clarify docs for match regarding escaping (#10695)
Add example using `re.escape` to escape arbitrary literal strings which might contain regular expression characters like `.` or `)`.

Closes #10595
2023-01-27 08:11:00 -03:00
Jay
ca40380e99 Add check for zero denominator in approx (#10624)
Closes #10533
2023-01-24 07:07:42 -03:00
Bruno Oliveira
05eee78aaa Merge pull request #10599 from pytest-dev/fix-update-plugin-list-workflow
Fix update-plugin-list workflow due to new 'packaging'
2023-01-23 17:55:45 -03:00
Ran Benita
02893139f9 Merge pull request #10680 from bluetech/capture-typing
capture: improve typing
2023-01-23 14:38:28 +02:00
Ran Benita
8c53dbf9d7 capture: fix pyright type error
This is OK in mypy, but doesn't hurt to fix.
2023-01-23 14:12:01 +02:00
Ran Benita
54b8b40f83 capture: improve NoCapture typing 2023-01-23 14:12:01 +02:00
Ran Benita
54911acf8d capture: improve captureclass typing
Previously, the any `captureclass` arguments were Any. We need to
introduce another common base class to fix this.
2023-01-23 14:12:01 +02:00
Ran Benita
c746d2b016 capture: improve SysCapture/FDCapture typing
Instead of `SysCapture`/`FDCapture` inheriting from
`SysCaptureBinary`/`FDCaptureBinary`, have both inherit from a common
`SysCaptureBase`/`FDCaptureBase`. This fixes a Liskov substitution
violation.
2023-01-23 14:12:01 +02:00
Ran Benita
a3693ce503 capture: improve DontReadFromInput typing
Have `DontReadFromInput` inherit from `TextIO`, ensuring it's fully
compatible with `sys.stdin` (which has type `TextIO`).
2023-01-23 14:12:01 +02:00
Yannick PÉROUX
af4143729f Allow spaces in -p arguments (#10658) 2023-01-21 08:22:44 -03:00
q0w
bd7919e03d Initialize args and args_source during Config.__init__
Closes #10626

Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
2023-01-21 08:19:54 -03:00
Ran Benita
7d4b40337b capture: fix some Anys 2023-01-21 10:39:58 +02:00
Ran Benita
6a714d7b70 capture: CaptureResult can be a namedtuple again (#10678)
mypy now supports generic NamedTuple.
2023-01-21 09:39:58 +02:00
Ran Benita
5a23eeff7a Merge pull request #10668 from pytest-dev/dependabot/pip/testing/plugins_integration/pytest-rerunfailures-11.0
build(deps): Bump pytest-rerunfailures from 10.3 to 11.0 in /testing/plugins_integration
2023-01-20 11:23:54 +02:00
Ran Benita
310b67b227 Drop attrs dependency, use dataclasses instead (#10669)
Since pytest now requires Python>=3.7, we can use the stdlib attrs
clone, dataclasses, instead of the OG package.

attrs is still somewhat nicer than dataclasses and has some extra
functionality, but for pytest usage there's not really a justification
IMO to impose the extra dependency on users when a standard alternative
exists.
2023-01-20 11:13:36 +02:00
Ramsey
4d4ed42c34 Fix crash if --cache-show and --help are passed at the same time
Closes #10592
2023-01-19 09:44:57 -03:00
Ronny Pfannschmidt
096b942ec4 Merge pull request #10660 from ikonst/2023-01-13-raises-typing
Derive pytest.raises from AbstractContextManager
2023-01-18 06:42:46 +01:00
pre-commit-ci[bot]
61cfaacec6 [pre-commit.ci] pre-commit autoupdate (#10671)
updates:
- [github.com/asottile/blacken-docs: v1.12.1 → 1.13.0](https://github.com/asottile/blacken-docs/compare/v1.12.1...1.13.0)
- [github.com/pre-commit/pygrep-hooks: v1.9.0 → v1.10.0](https://github.com/pre-commit/pygrep-hooks/compare/v1.9.0...v1.10.0)
2023-01-17 12:09:19 +02:00
dependabot[bot]
95c62eb527 build(deps): Bump pytest-rerunfailures in /testing/plugins_integration
Bumps [pytest-rerunfailures](https://github.com/pytest-dev/pytest-rerunfailures) from 10.3 to 11.0.
- [Release notes](https://github.com/pytest-dev/pytest-rerunfailures/releases)
- [Changelog](https://github.com/pytest-dev/pytest-rerunfailures/blob/master/CHANGES.rst)
- [Commits](https://github.com/pytest-dev/pytest-rerunfailures/compare/10.3...11.0)

---
updated-dependencies:
- dependency-name: pytest-rerunfailures
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-16 03:00:46 +00:00
Bruno Oliveira
03b19945fb Merge pull request #10662 from nicoddemus/cherry-pick-release
Merge pull request #10659 from pytest-dev/release-7.2.1
2023-01-14 09:35:02 -03:00
Bruno Oliveira
b2ac31cc9f Merge pull request #10659 from pytest-dev/release-7.2.1
Prepare release 7.2.1

(cherry picked from commit 94c05bc2a4)
2023-01-14 09:21:43 -03:00
Ilya Konstantinov
1a96f16401 Derive pytest.raises from AbstractContextManager
Makes `AbstractContextManager` the shared base class between "raises" and other context managers.

The motivation is for type checkers to narrow `pytest.raises(...) if x else nullcontext()` to a `ContextManager` rather than `object`.
2023-01-13 13:58:49 -05:00
dependabot[bot]
7421f3bb94 build(deps): Bump django in /testing/plugins_integration (#10643)
Bumps [django](https://github.com/django/django) from 4.1.3 to 4.1.5.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/compare/4.1.3...4.1.5)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-13 07:16:43 -03:00
Bruno Oliveira
5e0583f4b9 Fix regen tox environment (#10640)
Since tox 4.0, the whitelist_external option has been renamed to allowlist_externals.

Co-authored-by: pytest bot <pytestbot@gmail.com>
2023-01-13 07:15:41 -03:00
s-padmanaban
8efb4bb9c1 Do not update cache from xdist worker (#10641) 2023-01-13 07:14:52 -03:00
Kadino
3ad4344656 Mitigate directory creation race condition (#10607)
Fixes https://github.com/pytest-dev/pytest/issues/10604 which could intermittently display unexpected behavior between checking if the path exists and requesting creation. This was fairly prevalent when pytest was being invoked in parallel by another test runner (CTest) and trying to create the same parent-folder for multiple XMLs. A modest amount of testing did not reproduce other filesystem race conditions.

This notably does not work around an edge case where the parent path of the XML could be created as a file instead of a folder or link. That vanishingly rare case should cause file creation to fail on the next line, with a fairly obvious exception message.
2023-01-06 09:12:24 -03:00
Bruno Oliveira
6bf7f55555 Merge pull request #10632 from danigm/fix-tests
Fix tests pygments 2.14.0
2023-01-05 12:59:24 -03:00
Daniel Garcia Moreno
61f70a5a75 Fix tests pygments 2.14.0
Fix https://github.com/pytest-dev/pytest/issues/10630
2023-01-04 10:30:28 +01:00
Bruno Oliveira
326ae0cd88 Merge pull request #10609 from yusuke-kadowaki/default_policy_all
Change the default `tmp_path_retention_policy` to `all`
2022-12-25 14:09:45 -03:00
Yusuke Kadowaki
10220d3f31 Change the default policy to all 2022-12-25 00:18:38 +09:00
Bruno Oliveira
a98b00cd09 Use a more descriptive job name for update-plugin-list workflow 2022-12-21 14:01:37 -03:00
Bruno Oliveira
215ea7fd03 Fix update-plugin-list workflow due to new 'packaging'
The latest 'packaging' release has dropped support for `LegacyVersion`, meaning
that version strings which do not conform to the standard now raise an error.
2022-12-21 14:01:37 -03:00
Pierre Sassoulas
b31db4809b Avoid truncation when truncating means longer output (#10446)
Fixes #6267
2022-12-17 10:24:46 -03:00
Bruno Oliveira
f6adebb990 Fix tox 4.0 support and docs
Also includes pre-commit autoupdate
2022-12-14 08:57:07 -03:00
Bruno Oliveira
b90e7b84d0 Remove reference to explicit pytest tox support
This reference no longer exists in tox 4.0 docs.
2022-12-14 08:38:11 -03:00
Bruno Oliveira
19807ab79a Pin packaging while building docs
Packaging >=22 no longer supports 'latest' versions, which is the version that is assigned when building the docs.

https://github.com/pytest-dev/pytest/pull/10578#issuecomment-1348249045

Also first install pytest, then `docs/en/requirements.txt`:

Installing the requirements last will honor pins of libraries
shared between pytest and the docs build.
2022-12-14 08:38:11 -03:00
Bruno Oliveira
3e52124185 Fix passenv setting for tox.ini
In tox 4.0 `passenv` is required to be one per-line, or comma-separated.
2022-12-14 08:35:39 -03:00
pre-commit-ci[bot]
1eca228bd5 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/psf/black: 22.10.0 → 22.12.0](https://github.com/psf/black/compare/22.10.0...22.12.0)
- [github.com/asottile/pyupgrade: v3.3.0 → v3.3.1](https://github.com/asottile/pyupgrade/compare/v3.3.0...v3.3.1)
2022-12-13 01:28:21 +00:00
Bruno Oliveira
cab02e67d7 Merge pull request #10559 from pytest-dev/update-plugin-list/patch-9fbd67dd4
[automated] Update plugin list
2022-12-06 11:17:45 -03:00
Bruno Oliveira
64dbc7a0a1 Merge pull request #10560 from pytest-dev/dependabot/github_actions/peter-evans/create-pull-request-4.2.3
build(deps): Bump peter-evans/create-pull-request from 3.7.0 to 4.2.3
2022-12-06 11:16:54 -03:00
Bruno Oliveira
60d992677d Merge pull request #10563 from pytest-dev/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2022-12-06 11:06:16 -03:00
pre-commit-ci[bot]
0079decf29 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v3.2.2 → v3.3.0](https://github.com/asottile/pyupgrade/compare/v3.2.2...v3.3.0)
2022-12-06 00:36:28 +00:00
dependabot[bot]
3a58fc2d44 build(deps): Bump peter-evans/create-pull-request from 3.7.0 to 4.2.3
Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 3.7.0 to 4.2.3.
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](2455e15969...2b011faafd)

---
updated-dependencies:
- dependency-name: peter-evans/create-pull-request
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-05 03:15:13 +00:00
pytest bot
39b6bb551c [automated] Update plugin list 2022-12-04 00:20:05 +00:00
Marko Pacak
9fbd67dd4b Class methods can now be discovered as tests (#10552)
Fix #10525
2022-12-02 15:53:04 +00:00
Zac Hatfield-Dodds
eca93db05b Merge pull request #10550 from danschef/use_os_sep
Use os.sep instead of os.path.sep.
2022-12-02 00:32:12 -08:00
Daniel Scheffler
fb701b538c Use os.sep instead of os.path.sep.
Signed-off-by: Daniel Scheffler <danschef@gfz-potsdam.de>
2022-12-01 18:16:02 +01:00
Bruno Oliveira
314e623304 Merge pull request #10549 from yusuke-kadowaki/more_doc_update
Documentation update for the tmp_path configurations
2022-12-01 12:23:43 -03:00
Yusuke Kadowaki
62e75c7d55 Documentation update for tmp_path configurations 2022-12-01 22:29:46 +09:00
github-actions[bot]
fd30759d94 [automated] Update plugin list (#10536)
Co-authored-by: pytest bot <pytestbot@users.noreply.github.com>
2022-11-29 09:14:38 +00:00
dependabot[bot]
eb984a717a build(deps): Bump pytest-rerunfailures in /testing/plugins_integration (#10537)
Bumps [pytest-rerunfailures](https://github.com/pytest-dev/pytest-rerunfailures) from 10.2 to 10.3.
- [Release notes](https://github.com/pytest-dev/pytest-rerunfailures/releases)
- [Changelog](https://github.com/pytest-dev/pytest-rerunfailures/blob/master/CHANGES.rst)
- [Commits](https://github.com/pytest-dev/pytest-rerunfailures/compare/10.2...10.3)

---
updated-dependencies:
- dependency-name: pytest-rerunfailures
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-29 09:55:24 +01:00
Anthony Sottile
54f0fb3c63 Merge pull request #10543 from pytest-dev/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2022-11-28 20:27:39 -08:00
pre-commit-ci[bot]
49a4ed14cf [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/pre-commit-hooks: v4.3.0 → v4.4.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.3.0...v4.4.0)
- [github.com/PyCQA/autoflake: v1.7.7 → v2.0.0](https://github.com/PyCQA/autoflake/compare/v1.7.7...v2.0.0)
- [github.com/PyCQA/flake8: 5.0.4 → 6.0.0](https://github.com/PyCQA/flake8/compare/5.0.4...6.0.0)
2022-11-29 02:01:34 +00:00
Yusuke Kadowaki
f513d33d5a Modify documentation to use .stash when storing test results. (#10535) 2022-11-27 22:09:56 +02:00
Prerak Patel
857e34ef85 Fix bug where file system root was erroneously be used as rootdir on Windows
Fix #10506
2022-11-23 14:46:00 -03:00
Yusuke Kadowaki
99dfc19fe6 Fix tmp_path_retention_policy crash when skipping from fixture (#10517)
Also uses the stash to save the test status.

Fix #10502
2022-11-23 10:48:29 -03:00
pre-commit-ci[bot]
56544c11b5 [pre-commit.ci] pre-commit autoupdate (#10522)
updates:
- [github.com/pre-commit/mirrors-mypy: v0.990 → v0.991](https://github.com/pre-commit/mirrors-mypy/compare/v0.990...v0.991)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-11-22 08:27:55 -03:00
github-actions[bot]
7710e18b4c [automated] Update plugin list (#10519)
Co-authored-by: pytest bot <pytestbot@users.noreply.github.com>
2022-11-22 08:05:40 -03:00
Daniel Valenzuela
791b51d0fa Show test name when skipping from fixture (#10482)
Fixes #10457
2022-11-18 09:20:38 -03:00
Anthony Sottile
bc4e70e048 Merge pull request #10504 from pytest-dev/all-repos_autofix_no-implicit-optional
remove no_implicit_optional
2022-11-17 00:30:51 -05:00
Anthony Sottile
b817aa457c remove no_implicit_optional
this is the default in mypy 0.990

Committed via https://github.com/asottile/all-repos
2022-11-16 19:20:16 -05:00
Anthony Sottile
66b28912ac Merge pull request #10497 from pytest-dev/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2022-11-16 13:18:59 -05:00
Yusuke Kadowaki
cca029d55e Add configuration options to control how tmp_path directories are kept (#10442)
Close #8141
2022-11-15 09:11:39 -03:00
Bruno Oliveira
d5466b3917 Fix typing errors after mypy update 2022-11-15 08:53:23 -03:00
pre-commit-ci[bot]
4fce29f15d [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v3.2.0 → v3.2.2](https://github.com/asottile/pyupgrade/compare/v3.2.0...v3.2.2)
- [github.com/pre-commit/mirrors-mypy: v0.982 → v0.990](https://github.com/pre-commit/mirrors-mypy/compare/v0.982...v0.990)
2022-11-15 00:17:06 +00:00
Bruno Oliveira
69e3973d86 Merge pull request #10468 from pytest-dev/dependabot/pip/testing/plugins_integration/django-4.1.3
build(deps): Bump django from 4.1.2 to 4.1.3 in /testing/plugins_integration
2022-11-14 09:25:28 -03:00
dependabot[bot]
c842893b02 build(deps): Bump django in /testing/plugins_integration
Bumps [django](https://github.com/django/django) from 4.1.2 to 4.1.3.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/compare/4.1.2...4.1.3)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-14 11:36:55 +00:00
dependabot[bot]
506b10d295 build(deps): Bump pytest-asyncio in /testing/plugins_integration (#10494)
Bumps [pytest-asyncio](https://github.com/pytest-dev/pytest-asyncio) from 0.20.1 to 0.20.2.
- [Release notes](https://github.com/pytest-dev/pytest-asyncio/releases)
- [Changelog](https://github.com/pytest-dev/pytest-asyncio/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-asyncio/compare/v0.20.1...v0.20.2)

---
updated-dependencies:
- dependency-name: pytest-asyncio
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
2022-11-14 11:36:00 +00:00
Bruno Oliveira
05061493cb Merge pull request #10495 from pytest-dev/dependabot/pip/testing/plugins_integration/pytest-bdd-6.1.1
build(deps): Bump pytest-bdd from 6.0.1 to 6.1.1 in /testing/plugins_integration
2022-11-14 08:09:07 -03:00
dependabot[bot]
f97f3dc3a3 build(deps): Bump pytest-bdd in /testing/plugins_integration
Bumps [pytest-bdd](https://github.com/pytest-dev/pytest-bdd) from 6.0.1 to 6.1.1.
- [Release notes](https://github.com/pytest-dev/pytest-bdd/releases)
- [Changelog](https://github.com/pytest-dev/pytest-bdd/blob/master/CHANGES.rst)
- [Commits](https://github.com/pytest-dev/pytest-bdd/compare/6.0.1...6.1.1)

---
updated-dependencies:
- dependency-name: pytest-bdd
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-14 03:00:43 +00:00
Bruno Oliveira
3c31b0132f Merge pull request #10493 from pytest-dev/update-plugin-list/patch-54d5a63d1
[automated] Update plugin list
2022-11-13 12:02:36 -03:00
pytest bot
593178d909 [automated] Update plugin list 2022-11-13 00:22:21 +00:00
Bruno Oliveira
54d5a63d14 Merge pull request #10488 from DanielVZ96/fix-test-raising-repr
Fix test_raising_repr test
2022-11-09 20:20:05 -03:00
Daniel Valenzuela
b55e264a67 Fix test_raising_repr test
Closes #10473

Python <3.11 versions depend on `exceptiongroup>=1.0.0rc8`, and they released version `1.0.1`
6 days ago (2022/11/03) that as a side-effect changed the output of exceptions.
2022-11-09 19:43:10 -03:00
Bruno Oliveira
13d6114c0a Merge pull request #10484 from joukewitteveen/patch-2
scripts/update-plugin-list: Improve requirement detection
2022-11-09 10:07:12 -03:00
Jouke Witteveen
b635e16d30 scripts/update-plugin-list: Improve requirement detection
PEP 566 does not require a space after the dependency name.
2022-11-08 19:05:10 +01:00
Bruno Oliveira
a092b3ab36 Merge pull request #10481 from pytest-dev/update-plugin-list/patch-aa7e9de91
[automated] Update plugin list
2022-11-08 09:07:00 -03:00
pytest bot
a006dabf6e [automated] Update plugin list 2022-11-08 11:13:43 +00:00
Bruno Oliveira
aa7e9de91d Merge pull request #10480 from joukewitteveen/patch-1
scripts/update-plugin-list: Be liberal in accepted summaries
2022-11-08 08:11:35 -03:00
Jouke Witteveen
6aec32163d scripts/update-plugin-list: Be liberal in accepted summaries
Misconfigured packages may have a null summary in the PyPI json response.
2022-11-07 21:00:31 +01:00
Florian Bruhin
2f33ea87c8 Remove done trainings (#10471) 2022-11-04 18:38:09 +01:00
Ashish Kurmi
1ada62e237 Update dependabot for GitHub Actions (#10464)
Signed-off-by: Ashish Kurmi <akurmi@stepsecurity.io>

Signed-off-by: Ashish Kurmi <akurmi@stepsecurity.io>
2022-11-04 07:41:34 -03:00
pre-commit-ci[bot]
50b232b0cb [pre-commit.ci] pre-commit autoupdate (#10454)
updates:
- [github.com/asottile/reorder_python_imports: v3.8.5 → v3.9.0](https://github.com/asottile/reorder_python_imports/compare/v3.8.5...v3.9.0)
- [github.com/asottile/pyupgrade: v3.1.0 → v3.2.0](https://github.com/asottile/pyupgrade/compare/v3.1.0...v3.2.0)
- [github.com/asottile/setup-cfg-fmt: v2.1.0 → v2.2.0](https://github.com/asottile/setup-cfg-fmt/compare/v2.1.0...v2.2.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-10-31 21:37:05 -03:00
dependabot[bot]
496196b15c build(deps): Bump pytest-html in /testing/plugins_integration (#10450)
Bumps [pytest-html](https://github.com/pytest-dev/pytest-html) from 3.1.1 to 3.2.0.
- [Release notes](https://github.com/pytest-dev/pytest-html/releases)
- [Changelog](https://github.com/pytest-dev/pytest-html/blob/master/docs/changelog.rst)
- [Commits](https://github.com/pytest-dev/pytest-html/compare/v3.1.1...v3.2.0)

---
updated-dependencies:
- dependency-name: pytest-html
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-31 12:51:34 -03:00
Hugo van Kemenade
0314b50c52 Fix 'importlib.abc.TraversableResources' deprecation warning in Python 3.12 (#10452) 2022-10-31 12:49:51 -03:00
wim glenn
8e2de91bf8 Merge pull request #10449 from cj81499/patch-1
fix type in python_api.py
2022-10-30 22:26:56 -05:00
Cal Jacobson
692ab1160b add Cal Jacobson to AUTHORS 2022-10-30 22:02:18 -05:00
Cal Jacobson
549839bac5 fix type in python_api.py
excpected -> expected
2022-10-30 21:59:09 -05:00
Florian Bruhin
646a46e5f4 Edit changelog for 7.2.0 (#10423)
Fix some typos, and consolidate two entries for the same feature into one.
2022-10-25 19:47:16 +02:00
Santiago Castro
f07017f91b Add the PyPI classifier for Python 3.11 (#10426) 2022-10-25 13:43:59 -03:00
Anthony Sottile
a17d3b0c44 Merge pull request #10425 from pytest-dev/asottile-patch-1
upgrade pygments-pytest for 7.2.x coloring
2022-10-25 11:27:36 -04:00
Anthony Sottile
bbec1ce67f upgrade pygments-pytest for 7.2.x coloring 2022-10-25 10:52:53 -04:00
Ronny Pfannschmidt
5a040aef97 Merge pull request #10412 from pytest-dev/release-7.2.0 (#10419)
Prepare release 7.2.0

(cherry picked from commit ac4e3cced9)
2022-10-25 13:12:55 +02:00
Bruno Oliveira
c1d2168df6 Merge pull request #10417 from nicoddemus/publish-action-pin
Use specific tag in the gh-action-pypi-publish action
2022-10-25 08:11:33 -03:00
pre-commit-ci[bot]
bbe7cbae4a [pre-commit.ci] pre-commit autoupdate (#10418)
updates:
- [github.com/PyCQA/autoflake: v1.7.6 → v1.7.7](https://github.com/PyCQA/autoflake/compare/v1.7.6...v1.7.7)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-10-24 20:25:15 -03:00
Bruno Oliveira
deae8f47f6 Use specific tag in the gh-action-pypi-publish action
Otherwise we see this warning:

Warning:  You are using "pypa/gh-action-pypi-publish@master". The "master" branch of this project has been sunset and will not receive any updates, not even security bug fixes. Please, make sure to use a supported version. If you want to pin to v1 major version, use "pypa/gh-action-pypi-publish@release/v1". If you feel adventurous, you may opt to use use "pypa/gh-action-pypi-publish@unstable/v1" instead. A more general recommendation is to pin to exact tags or commit shas.
2022-10-24 19:23:40 -03:00
Zac Hatfield-Dodds
10f55f79af Merge pull request #10226 from Zac-HD/use-exceptiongroup-for-teardown
Use exceptiongroup for multiple errors during teardown
2022-10-24 08:44:02 -07:00
Bruno Oliveira
a6d244343f Merge pull request #10416 from rettinghaus/update-actions
Update used GitHub Actions to latest versions
2022-10-24 10:32:58 -03:00
Klaus Rettinghaus
2b552c2240 update actions 2022-10-24 14:58:03 +02:00
Bruno Oliveira
54d7b9a08e Merge pull request #10415 from pytest-dev/dependabot/pip/testing/plugins_integration/pytest-asyncio-0.20.1
build(deps): Bump pytest-asyncio from 0.19.0 to 0.20.1 in /testing/plugins_integration
2022-10-24 08:12:12 -03:00
Bruno Oliveira
6afc02abca Merge pull request #10414 from pytest-dev/dependabot/pip/testing/plugins_integration/anyio-curiotrio--3.6.2
build(deps): Bump anyio[curio,trio] from 3.6.1 to 3.6.2 in /testing/plugins_integration
2022-10-24 08:11:49 -03:00
dependabot[bot]
e75e2d66a0 build(deps): Bump pytest-asyncio in /testing/plugins_integration
Bumps [pytest-asyncio](https://github.com/pytest-dev/pytest-asyncio) from 0.19.0 to 0.20.1.
- [Release notes](https://github.com/pytest-dev/pytest-asyncio/releases)
- [Changelog](https://github.com/pytest-dev/pytest-asyncio/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-asyncio/compare/v0.19.0...v0.20.1)

---
updated-dependencies:
- dependency-name: pytest-asyncio
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-24 03:04:18 +00:00
dependabot[bot]
66db0b7522 build(deps): Bump anyio[curio,trio] in /testing/plugins_integration
Bumps [anyio[curio,trio]](https://github.com/agronholm/anyio) from 3.6.1 to 3.6.2.
- [Release notes](https://github.com/agronholm/anyio/releases)
- [Changelog](https://github.com/agronholm/anyio/blob/3.6.2/docs/versionhistory.rst)
- [Commits](https://github.com/agronholm/anyio/compare/3.6.1...3.6.2)

---
updated-dependencies:
- dependency-name: anyio[curio,trio]
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-24 03:04:15 +00:00
Zac Hatfield-Dodds
3a68c08426 Use exceptiongroup for teardown errors 2022-10-23 15:45:50 -07:00
Itxaso Aizpurua
9e1804a6ee Add --log-disable CLI option (#10371)
Fixes #7431
2022-10-23 18:23:34 -03:00
pre-commit-ci[bot]
bb6155adfa [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2021-10-01 16:07:39 +00:00
Vijay Arora
5fefd7de96 Updated indentation and spaces in logging.py for #9146 2021-10-01 21:36:35 +05:30
Vijay Arora
750ce30392 Update 9146.doc.rst 2021-10-01 21:33:32 +05:30
pre-commit-ci[bot]
de1f378b60 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2021-10-01 14:53:26 +00:00
Vijay Arora
14d5b4ca6c Merge pull request #2 from Vijay-Arora/Vijay-Arora-patch-1
Updated AUTHORS and added changelog file
2021-10-01 20:19:23 +05:30
Vijay Arora
307dbf15c4 Add Myself as Authors 2021-10-01 20:16:10 +05:30
Vijay Arora
a8697601ad Create 9146.doc.rst
Create 9146.doc.rst
2021-10-01 20:10:46 +05:30
Vijay Arora
0ed1b0ac12 Merge pull request #1 from Vijay-Arora/Vijay-Arora-patch-1
Updated logging.py for #9146
2021-10-01 20:04:51 +05:30
Vijay Arora
26b0702b98 Updated logging.py
Updated logging.py for #9146
2021-10-01 19:34:59 +05:30
171 changed files with 7928 additions and 3126 deletions

View File

@@ -9,3 +9,9 @@ updates:
allow:
- dependency-type: direct
- dependency-type: indirect
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
time: "03:00"
open-pull-requests-limit: 10

View File

@@ -22,7 +22,7 @@ jobs:
pull-requests: write
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 0
persist-credentials: true

View File

@@ -1,52 +1,84 @@
name: deploy
on:
push:
tags:
# These tags are protected, see:
# https://github.com/pytest-dev/pytest/settings/tag_protection
- "[0-9]+.[0-9]+.[0-9]+"
- "[0-9]+.[0-9]+.[0-9]+rc[0-9]+"
workflow_dispatch:
inputs:
version:
description: 'Release version'
required: true
default: '1.2.3'
# Set permissions at the job level.
permissions: {}
jobs:
package:
runs-on: ubuntu-latest
env:
SETUPTOOLS_SCM_PRETEND_VERSION: ${{ github.event.inputs.version }}
timeout-minutes: 10
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
persist-credentials: false
- name: Build and Check Package
uses: hynek/build-and-inspect-python-package@v1.5
deploy:
if: github.repository == 'pytest-dev/pytest'
needs: [package]
runs-on: ubuntu-latest
environment: deploy
timeout-minutes: 30
permissions:
id-token: write
steps:
- uses: actions/checkout@v3
- name: Download Package
uses: actions/download-artifact@v3
with:
name: Packages
path: dist
- name: Publish package to PyPI
uses: pypa/gh-action-pypi-publish@v1.8.5
- name: Push tag
run: |
git config user.name "pytest bot"
git config user.email "pytestbot@gmail.com"
git tag --annotate --message=v${{ github.event.inputs.version }} v${{ github.event.inputs.version }} ${{ github.sha }}
git push origin v${{ github.event.inputs.version }}
release-notes:
# todo: generate the content in the build job
# the goal being of using a github action script to push the release data
# after success instead of creating a complete python/tox env
needs: [deploy]
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: write
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 0
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: "3.7"
python-version: "3.10"
- name: Install dependencies
- name: Install tox
run: |
python -m pip install --upgrade pip
pip install --upgrade build tox
- name: Build package
run: |
python -m build
- name: Publish package to PyPI
uses: pypa/gh-action-pypi-publish@master
with:
user: __token__
password: ${{ secrets.pypi_token }}
pip install --upgrade tox
- name: Publish GitHub release notes
env:

View File

@@ -27,12 +27,12 @@ jobs:
pull-requests: write
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: "3.8"

23
.github/workflows/stale.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
name: close needs-information issues
on:
schedule:
- cron: "30 1 * * *"
workflow_dispatch:
jobs:
close-issues:
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- uses: actions/stale@v8
with:
debug-only: false
days-before-issue-stale: 14
days-before-issue-close: 7
only-labels: "status: needs information"
stale-issue-label: "stale"
stale-issue-message: "This issue is stale because it has been open for 14 days with no activity."
close-issue-message: "This issue was closed because it has been inactive for 7 days since being marked as stale."
days-before-pr-stale: -1
days-before-pr-close: -1

View File

@@ -18,11 +18,28 @@ on:
env:
PYTEST_ADDOPTS: "--color=yes"
# Cancel running jobs for the same workflow and branch.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# Set permissions at the job level.
permissions: {}
jobs:
package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
persist-credentials: false
- name: Build and Check Package
uses: hynek/build-and-inspect-python-package@v1.5
build:
needs: [package]
runs-on: ${{ matrix.os }}
timeout-minutes: 45
permissions:
@@ -33,27 +50,28 @@ jobs:
matrix:
name: [
"windows-py37",
"windows-py37-pluggy",
"windows-py38",
"windows-py38-pluggy",
"windows-py39",
"windows-py310",
"windows-py311",
"windows-py312",
"ubuntu-py37",
"ubuntu-py37-pluggy",
"ubuntu-py37-freeze",
"ubuntu-py38",
"ubuntu-py38-pluggy",
"ubuntu-py39",
"ubuntu-py310",
"ubuntu-py311",
"ubuntu-py312",
"ubuntu-pypy3",
"macos-py37",
"macos-py38",
"macos-py39",
"macos-py310",
"macos-py312",
"docs",
"doctesting",
"plugins",
]
@@ -63,15 +81,15 @@ jobs:
python: "3.7"
os: windows-latest
tox_env: "py37-numpy"
- name: "windows-py37-pluggy"
python: "3.7"
os: windows-latest
tox_env: "py37-pluggymain-pylib-xdist"
- name: "windows-py38"
python: "3.8"
os: windows-latest
tox_env: "py38-unittestextras"
use_coverage: true
- name: "windows-py38-pluggy"
python: "3.8"
os: windows-latest
tox_env: "py38-pluggymain-pylib-xdist"
- name: "windows-py39"
python: "3.9"
os: windows-latest
@@ -81,19 +99,19 @@ jobs:
os: windows-latest
tox_env: "py310-xdist"
- name: "windows-py311"
python: "3.11-dev"
python: "3.11"
os: windows-latest
tox_env: "py311"
- name: "windows-py312"
python: "3.12-dev"
os: windows-latest
tox_env: "py312"
- name: "ubuntu-py37"
python: "3.7"
os: ubuntu-latest
tox_env: "py37-lsof-numpy-pexpect"
use_coverage: true
- name: "ubuntu-py37-pluggy"
python: "3.7"
os: ubuntu-latest
tox_env: "py37-pluggymain-pylib-xdist"
- name: "ubuntu-py37-freeze"
python: "3.7"
os: ubuntu-latest
@@ -102,6 +120,10 @@ jobs:
python: "3.8"
os: ubuntu-latest
tox_env: "py38-xdist"
- name: "ubuntu-py38-pluggy"
python: "3.8"
os: ubuntu-latest
tox_env: "py38-pluggymain-pylib-xdist"
- name: "ubuntu-py39"
python: "3.9"
os: ubuntu-latest
@@ -111,10 +133,15 @@ jobs:
os: ubuntu-latest
tox_env: "py310-xdist"
- name: "ubuntu-py311"
python: "3.11-dev"
python: "3.11"
os: ubuntu-latest
tox_env: "py311"
use_coverage: true
- name: "ubuntu-py312"
python: "3.12-dev"
os: ubuntu-latest
tox_env: "py312"
use_coverage: true
- name: "ubuntu-pypy3"
python: "pypy-3.7"
os: ubuntu-latest
@@ -124,29 +151,25 @@ jobs:
python: "3.7"
os: macos-latest
tox_env: "py37-xdist"
- name: "macos-py38"
python: "3.8"
os: macos-latest
tox_env: "py38-xdist"
use_coverage: true
- name: "macos-py39"
python: "3.9"
os: macos-latest
tox_env: "py39-xdist"
use_coverage: true
- name: "macos-py310"
python: "3.10"
os: macos-latest
tox_env: "py310-xdist"
- name: "macos-py312"
python: "3.12-dev"
os: macos-latest
tox_env: "py312-xdist"
- name: "plugins"
python: "3.9"
os: ubuntu-latest
tox_env: "plugins"
- name: "docs"
python: "3.7"
os: ubuntu-latest
tox_env: "docs"
- name: "doctesting"
python: "3.7"
os: ubuntu-latest
@@ -159,10 +182,17 @@ jobs:
fetch-depth: 0
persist-credentials: false
- name: Download Package
uses: actions/download-artifact@v3
with:
name: Packages
path: dist
- name: Set up Python ${{ matrix.python }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python }}
check-latest: ${{ endsWith(matrix.python, '-dev') }}
- name: Install dependencies
run: |
@@ -171,11 +201,13 @@ jobs:
- name: Test without coverage
if: "! matrix.use_coverage"
run: "tox -e ${{ matrix.tox_env }}"
shell: bash
run: tox run -e ${{ matrix.tox_env }} --installpkg `find dist/*.tar.gz`
- name: Test with coverage
if: "matrix.use_coverage"
run: "tox -e ${{ matrix.tox_env }}-coverage"
shell: bash
run: tox run -e ${{ matrix.tox_env }}-coverage --installpkg `find dist/*.tar.gz`
- name: Generate coverage report
if: "matrix.use_coverage"

View File

@@ -11,7 +11,7 @@ on:
permissions: {}
jobs:
createPullRequest:
update-plugin-list:
if: github.repository_owner == 'pytest-dev'
runs-on: ubuntu-latest
permissions:
@@ -20,12 +20,12 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: 3.8
@@ -38,7 +38,7 @@ jobs:
run: python scripts/update-plugin-list.py
- name: Create Pull Request
uses: peter-evans/create-pull-request@2455e1596942c2902952003bbb574afbbe2ab2e6
uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38
with:
commit-message: '[automated] Update plugin list'
author: 'pytest bot <pytestbot@users.noreply.github.com>'

View File

@@ -1,18 +1,16 @@
default_language_version:
python: "3.10"
repos:
- repo: https://github.com/psf/black
rev: 22.10.0
rev: 23.3.0
hooks:
- id: black
args: [--safe, --quiet]
- repo: https://github.com/asottile/blacken-docs
rev: v1.12.1
rev: 1.14.0
hooks:
- id: blacken-docs
additional_dependencies: [black==20.8b1]
additional_dependencies: [black==23.1.0]
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
@@ -23,7 +21,7 @@ repos:
exclude: _pytest/(debugging|hookspec).py
language_version: python3
- repo: https://github.com/PyCQA/autoflake
rev: v1.7.6
rev: v2.1.1
hooks:
- id: autoflake
name: autoflake
@@ -31,34 +29,34 @@ repos:
language: python
files: \.py$
- repo: https://github.com/PyCQA/flake8
rev: 5.0.4
rev: 6.0.0
hooks:
- id: flake8
language_version: python3
additional_dependencies:
- flake8-typing-imports==1.12.0
- flake8-docstrings==1.5.0
- repo: https://github.com/asottile/reorder_python_imports
rev: v3.8.5
- repo: https://github.com/asottile/reorder-python-imports
rev: v3.10.0
hooks:
- id: reorder-python-imports
args: ['--application-directories=.:src', --py37-plus]
- repo: https://github.com/asottile/pyupgrade
rev: v3.1.0
rev: v3.7.0
hooks:
- id: pyupgrade
args: [--py37-plus]
- repo: https://github.com/asottile/setup-cfg-fmt
rev: v2.1.0
rev: v2.3.0
hooks:
- id: setup-cfg-fmt
args: ["--max-py-version=3.10", "--include-version-classifiers"]
args: ["--max-py-version=3.12", "--include-version-classifiers"]
- repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.9.0
rev: v1.10.0
hooks:
- id: python-use-type-annotations
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.982
rev: v1.3.0
hooks:
- id: mypy
files: ^(src/|testing/)

View File

@@ -2,9 +2,12 @@ version: 2
python:
install:
- requirements: doc/en/requirements.txt
- method: pip
path: .
# Install pytest first, then doc/en/requirements.txt.
# This order is important to honor any pins in doc/en/requirements.txt
# when the pinned library is also a dependency of pytest.
- method: pip
path: .
- requirements: doc/en/requirements.txt
build:
os: ubuntu-20.04

37
AUTHORS
View File

@@ -8,10 +8,14 @@ Abdeali JK
Abdelrahman Elbehery
Abhijeet Kasurde
Adam Johnson
Adam Stewart
Adam Uhlir
Ahn Ki-Wook
Akiomi Kamakura
Alan Velasco
Alessio Izzo
Alex Jones
Alex Lambson
Alexander Johnson
Alexander King
Alexei Kozlenok
@@ -43,6 +47,7 @@ Ariel Pillemer
Armin Rigo
Aron Coyle
Aron Curzon
Ashish Kurmi
Aviral Verma
Aviv Palivoda
Babak Keyvani
@@ -53,10 +58,12 @@ Benjamin Peterson
Bernard Pratz
Bob Ippolito
Brian Dorsey
Brian Larsen
Brian Maissy
Brian Okken
Brianna Laugher
Bruno Oliveira
Cal Jacobson
Cal Leeming
Carl Friedrich Bolz
Carlos Jenkins
@@ -65,6 +72,7 @@ Charles Cloud
Charles Machalow
Charnjit SiNGH (CCSJ)
Cheuk Ting Ho
Chris Mahoney
Chris Lamb
Chris NeJame
Chris Rose
@@ -88,6 +96,7 @@ Daniel Grana
Daniel Hahler
Daniel Nuri
Daniel Sánchez Castelló
Daniel Valenzuela Zenteno
Daniel Wandschneider
Daniele Procida
Danielle Jenkins
@@ -122,8 +131,10 @@ Eric Siegerman
Erik Aronesty
Erik M. Bray
Evan Kepner
Evgeny Seliverstov
Fabien Zarifian
Fabio Zadrozny
Felix Hofstätter
Felix Nieuwenhuizen
Feng Ma
Florian Bruhin
@@ -155,9 +166,12 @@ Ian Bicking
Ian Lesperance
Ilya Konstantinov
Ionuț Turturică
Isaac Virshup
Israel Fruchter
Itxaso Aizpurua
Iwan Briquemont
Jaap Broekhuizen
Jake VanderPlas
Jakob van Santen
Jakub Mitoraj
James Bourbeau
@@ -182,10 +196,11 @@ Joseph Hunkeler
Josh Karpel
Joshua Bronson
Jurko Gospodnetić
Justyna Janczyszyn
Justice Ndou
Justyna Janczyszyn
Kale Kundert
Kamran Ahmad
Kenny Y
Karl O. Pinc
Karthikeyan Singaravelan
Katarzyna Jachim
@@ -222,6 +237,7 @@ Marcin Bachry
Marco Gorelli
Mark Abramowitz
Mark Dickinson
Marko Pacak
Markus Unterwaditzer
Martijn Faassen
Martin Altmayer
@@ -235,7 +251,6 @@ Matthias Hafner
Maxim Filipenko
Maximilian Cosmo Sitter
mbyt
Mickey Pashov
Michael Aquilina
Michael Birtwell
Michael Droettboom
@@ -244,6 +259,7 @@ Michael Krebs
Michael Seifert
Michal Wajszczuk
Michał Zięba
Mickey Pashov
Mihai Capotă
Mike Hoyle (hoylemd)
Mike Lundy
@@ -258,9 +274,9 @@ Niclas Olofsson
Nicolas Delaby
Nikolay Kondratyev
Nipunn Koorapati
Olga Matoula
Oleg Pidsadnyi
Oleg Sushchenko
Olga Matoula
Oliver Bestwalter
Omar Kohl
Omer Hadari
@@ -276,6 +292,7 @@ Paweł Adamczak
Pedro Algarvio
Petter Strandmark
Philipp Loose
Pierre Sassoulas
Pieter Mulder
Piotr Banaszkiewicz
Piotr Helm
@@ -285,15 +302,18 @@ Prashant Sharma
Pulkit Goyal
Punyashloka Biswal
Quentin Pradet
q0w
Ralf Schmitt
Ram Rachum
Ralph Giles
Ram Rachum
Ran Benita
Raphael Castaneda
Raphael Pierzina
Rafal Semik
Raquel Alegre
Ravi Chandra
Robert Holt
Roberto Aldera
Roberto Polli
Roland Puntaier
Romain Dorgueil
@@ -310,6 +330,7 @@ Samuel Searles-Bryant
Samuele Pedroni
Sanket Duthade
Sankt Petersbug
Saravanan Padmanaban
Segev Finer
Serhii Mozghovyi
Seth Junot
@@ -320,9 +341,11 @@ Simon Holesch
Simon Kerr
Skylar Downes
Srinivas Reddy Thatiparthy
Stefaan Lippens
Stefan Farmbauer
Stefan Scherfke
Stefan Zimmermann
Stefanie Molin
Stefano Taschini
Steffen Allner
Stephan Obermann
@@ -341,6 +364,7 @@ Thomas Grainger
Thomas Hisch
Tim Hoffmann
Tim Strazny
TJ Bruno
Tobias Diez
Tom Dalton
Tom Viner
@@ -350,12 +374,14 @@ Tony Narlock
Tor Colvin
Trevor Bekolay
Tyler Goodlet
Tyler Smart
Tzu-ping Chung
Vasily Kuznetsov
Victor Maryama
Victor Rodriguez
Victor Uriarte
Vidar T. Fauske
Vijay Arora
Virgil Dupras
Vitaly Lashmanov
Vivaan Verma
@@ -371,7 +397,10 @@ Wouter van Ackooy
Xixi Zhao
Xuan Luong
Xuecong Liao
Yannick Péroux
Yoav Caspi
Yuliang Shao
Yusuke Kadowaki
Yuval Shimon
Zac Hatfield-Dodds
Zachary Kneupper

View File

@@ -50,7 +50,7 @@ Fix bugs
--------
Look through the `GitHub issues for bugs <https://github.com/pytest-dev/pytest/labels/type:%20bug>`_.
See also the `"status: easy" issues <https://github.com/pytest-dev/pytest/labels/status%3A%20easy>`_
See also the `"good first issue" issues <https://github.com/pytest-dev/pytest/labels/good%20first%20issue>`_
that are friendly to new contributors.
:ref:`Talk <contact>` to developers to find out how you can fix specific bugs. To indicate that you are going

View File

@@ -133,14 +133,11 @@ Releasing
Both automatic and manual processes described above follow the same steps from this point onward.
#. After all tests pass and the PR has been approved, tag the release commit
in the ``release-MAJOR.MINOR.PATCH`` branch and push it. This will publish to PyPI::
#. After all tests pass and the PR has been approved, trigger the ``deploy`` job
in https://github.com/pytest-dev/pytest/actions/workflows/deploy.yml.
git fetch upstream
git tag MAJOR.MINOR.PATCH upstream/release-MAJOR.MINOR.PATCH
git push upstream MAJOR.MINOR.PATCH
Wait for the deploy to complete, then make sure it is `available on PyPI <https://pypi.org/project/pytest>`_.
This job will require approval from ``pytest-dev/core``, after which it will publish to PyPI
and tag the repository.
#. Merge the PR. **Make sure it's not squash-merged**, so that the tagged commit ends up in the main branch.

View File

@@ -1 +0,0 @@
Update :class:`pytest.PytestUnhandledCoroutineWarning` to a deprecation; it will raise an error in pytest 8.

View File

@@ -1 +0,0 @@
:data:`sys.stdin` now contains all expected methods of a file-like object when capture is enabled.

View File

@@ -1 +0,0 @@
:class:`~pytest.PytestReturnNotNoneWarning` is now a subclass of :class:`~pytest.PytestRemovedIn8Warning`: the plan is to make returning non-``None`` from tests an error in the future.

View File

@@ -1,5 +0,0 @@
``@pytest.mark.parametrize()`` (and similar functions) now accepts any ``Sequence[str]`` for the argument names,
instead of just ``list[str]`` and ``tuple[str, ...]``.
(Note that ``str``, which is itself a ``Sequence[str]``, is still treated as a
comma-delimited name list, as before).

View File

@@ -1,3 +0,0 @@
Made ``_pytest.doctest.DoctestItem`` export ``pytest.DoctestItem`` for
type check and runtime purposes. Made `_pytest.doctest` use internal APIs
to avoid circular imports.

View File

@@ -1 +0,0 @@
Update information on writing plugins to use ``pyproject.toml`` instead of ``setup.py``.

View File

@@ -1 +0,0 @@
The ``--no-showlocals`` flag has been added. This can be passed directly to tests to override ``--showlocals`` declared through ``addopts``.

View File

@@ -1 +0,0 @@
Do not break into pdb when ``raise unittest.SkipTest()`` appears top-level in a file.

View File

@@ -1 +0,0 @@
pytest no longer depends on the ``py`` library. ``pytest`` provides a vendored copy of ``py.error`` and ``py.path`` modules but will use the ``py`` library if it is installed. If you need other ``py.*`` modules, continue to install the deprecated ``py`` library separately, otherwise it can usually be removed as a dependency.

View File

@@ -1 +0,0 @@
Assertion failures with strings in NFC and NFD forms that normalize to the same string now have a dedicated error message detailing the issue, and their utf-8 representation is expresed instead.

View File

@@ -1,4 +0,0 @@
Deprecate configuring hook specs/impls using attributes/marks.
Instead use :py:func:`pytest.hookimpl` and :py:func:`pytest.hookspec`.
For more details, see the :ref:`docs <legacy-path-hooks-deprecated>`.

View File

@@ -1 +0,0 @@
A warning is now emitted if a test function returns something other than `None`. This prevents a common mistake among beginners that expect that returning a `bool` (for example `return foo(a, b) == result`) would cause a test to pass or fail, instead of using `assert`.

View File

@@ -1,5 +0,0 @@
Marks are now inherited according to the full MRO in test classes. Previously, if a test class inherited from two or more classes, only marks from the first super-class would apply.
When inheriting marks from super-classes, marks from the sub-classes are now ordered before marks from the super-classes, in MRO order. Previously it was the reverse.
When inheriting marks from super-classes, the `pytestmark` attribute of the sub-class now only contains the marks directly applied to it. Previously, it also contained marks from its super-classes. Please note that this attribute should not normally be accessed directly; use :func:`pytest.Node.iter_markers` instead.

View File

@@ -1,2 +0,0 @@
Introduce multiline display for warning matching via :py:func:`pytest.warns` and
enhance match comparison for :py:func:`_pytest._code.ExceptionInfo.match` as returned by :py:func:`pytest.raises`.

View File

@@ -1,2 +0,0 @@
Improve :py:func:`pytest.raises`. Previously passing an empty tuple would give a confusing
error. We now raise immediately with a more helpful message.

View File

@@ -1 +0,0 @@
Showing inner exceptions by forcing native display in ``ExceptionGroups`` even when using display options other than ``--tb=native``. A temporary step before full implementation of pytest-native display for inner exceptions in ``ExceptionGroups``.

View File

@@ -1 +0,0 @@
The documentation is now built using Sphinx 5.x (up from 3.x previously).

View File

@@ -1 +0,0 @@
Update documentation on how :func:`pytest.warns` affects :class:`DeprecationWarning`.

View File

@@ -1,3 +0,0 @@
On Python 3.11, use the standard library's :mod:`tomllib` to parse TOML.
:mod:`tomli`` is no longer a dependency on Python 3.11.

View File

@@ -1 +0,0 @@
Display assertion message without escaped newline characters with ``-vv``.

View File

@@ -1 +0,0 @@
Improved error message that is shown when no collector is found for a given file.

View File

@@ -1 +0,0 @@
Some coloring has been added to the short test summary.

View File

@@ -1 +0,0 @@
Ensure ``caplog.get_records(when)`` returns current/correct data after invoking ``caplog.clear()``.

View File

@@ -1 +0,0 @@
Normalize the help description of all command-line options.

View File

@@ -1,10 +0,0 @@
The functionality for running tests written for ``nose`` has been officially deprecated.
This includes:
* Plain ``setup`` and ``teardown`` functions and methods: this might catch users by surprise, as ``setup()`` and ``teardown()`` are not pytest idioms, but part of the ``nose`` support.
* Setup/teardown using the `@with_setup <with-setup-nose>`_ decorator.
For more details, consult the :ref:`deprecation docs <nose-deprecation>`.
.. _`with-setup-nose`: https://nose.readthedocs.io/en/latest/testing_tools.html?highlight=with_setup#nose.tools.with_setup

View File

@@ -1 +0,0 @@
Added shell-style wildcard support to ``testpaths``.

View File

@@ -1 +0,0 @@
Made ``_pytest.compat`` re-export ``importlib_metadata`` in the eyes of type checkers.

View File

@@ -1 +0,0 @@
Fix default encoding warning (``EncodingWarning``) in ``cacheprovider``

View File

@@ -1 +0,0 @@
Display full crash messages in ``short test summary info``, when runng in a CI environment.

View File

@@ -1,4 +0,0 @@
Improve the error message when we attempt to access a fixture that has been
torn down.
Add an additional sentence to the docstring explaining when it's not a good
idea to call getfixturevalue.

View File

@@ -1 +0,0 @@
Added support for hidden configuration file by allowing ``.pytest.ini`` as an alternative to ``pytest.ini``.

View File

@@ -6,6 +6,15 @@ Release announcements
:maxdepth: 2
release-7.4.2
release-7.4.1
release-7.4.0
release-7.3.2
release-7.3.1
release-7.3.0
release-7.2.2
release-7.2.1
release-7.2.0
release-7.1.3
release-7.1.2
release-7.1.1

View File

@@ -0,0 +1,93 @@
pytest-7.2.0
=======================================
The pytest team is proud to announce the 7.2.0 release!
This release contains new features, improvements, and bug fixes,
the full list of changes is available in the changelog:
https://docs.pytest.org/en/stable/changelog.html
For complete documentation, please visit:
https://docs.pytest.org/en/stable/
As usual, you can upgrade from PyPI via:
pip install -U pytest
Thanks to all of the contributors to this release:
* Aaron Berdy
* Adam Turner
* Albert Villanova del Moral
* Alice Purcell
* Anthony Sottile
* Anton Yakutovich
* Babak Keyvani
* Brandon Chinn
* Bruno Oliveira
* Chanvin Xiao
* Cheuk Ting Ho
* Chris Wheeler
* EmptyRabbit
* Ezio Melotti
* Florian Best
* Florian Bruhin
* Fredrik Berndtsson
* Gabriel Landau
* Gergely Kalmár
* Hugo van Kemenade
* James Gerity
* John Litborn
* Jon Parise
* Kevin C
* Kian Eliasi
* MatthewFlamm
* Miro Hrončok
* Nate Meyvis
* Neil Girdhar
* Nhieuvu1802
* Nipunn Koorapati
* Ofek Lev
* Paul Müller
* Paul Reece
* Pax
* Pete Baughman
* Peyman Salehi
* Philipp A
* Ran Benita
* Robert O'Shea
* Ronny Pfannschmidt
* Rowin
* Ruth Comer
* Samuel Colvin
* Samuel Gaist
* Sandro Tosi
* Shantanu
* Simon K
* Stephen Rosen
* Sviatoslav Sydorenko
* Tatiana Ovary
* Thierry Moisan
* Thomas Grainger
* Tim Hoffmann
* Tobias Diez
* Tony Narlock
* Vivaan Verma
* Wolfremium
* Zac Hatfield-Dodds
* Zach OBrien
* aizpurua23a
* gresm
* holesch
* itxasos23
* johnkangw
* skhomuti
* sommersoft
* wodny
* zx.qiu
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,25 @@
pytest-7.2.1
=======================================
pytest 7.2.1 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
Thanks to all of the contributors to this release:
* Anthony Sottile
* Bruno Oliveira
* Daniel Valenzuela
* Kadino
* Prerak Patel
* Ronny Pfannschmidt
* Santiago Castro
* s-padmanaban
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,25 @@
pytest-7.2.2
=======================================
pytest 7.2.2 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
Thanks to all of the contributors to this release:
* Bruno Oliveira
* Garvit Shubham
* Mahesh Vashishtha
* Ramsey
* Ronny Pfannschmidt
* Teejay
* q0w
* vin01
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,130 @@
pytest-7.3.0
=======================================
The pytest team is proud to announce the 7.3.0 release!
This release contains new features, improvements, and bug fixes,
the full list of changes is available in the changelog:
https://docs.pytest.org/en/stable/changelog.html
For complete documentation, please visit:
https://docs.pytest.org/en/stable/
As usual, you can upgrade from PyPI via:
pip install -U pytest
Thanks to all of the contributors to this release:
* Aaron Berdy
* Adam Turner
* Albert Villanova del Moral
* Alessio Izzo
* Alex Hadley
* Alice Purcell
* Anthony Sottile
* Anton Yakutovich
* Ashish Kurmi
* Babak Keyvani
* Billy
* Brandon Chinn
* Bruno Oliveira
* Cal Jacobson
* Chanvin Xiao
* Cheuk Ting Ho
* Chris Wheeler
* Daniel Garcia Moreno
* Daniel Scheffler
* Daniel Valenzuela
* EmptyRabbit
* Ezio Melotti
* Felix Hofstätter
* Florian Best
* Florian Bruhin
* Fredrik Berndtsson
* Gabriel Landau
* Garvit Shubham
* Gergely Kalmár
* HTRafal
* Hugo van Kemenade
* Ilya Konstantinov
* Itxaso Aizpurua
* James Gerity
* Jay
* John Litborn
* Jon Parise
* Jouke Witteveen
* Kadino
* Kevin C
* Kian Eliasi
* Klaus Rettinghaus
* Kodi Arfer
* Mahesh Vashishtha
* Manuel Jacob
* Marko Pacak
* MatthewFlamm
* Miro Hrončok
* Nate Meyvis
* Neil Girdhar
* Nhieuvu1802
* Nipunn Koorapati
* Ofek Lev
* Paul Kehrer
* Paul Müller
* Paul Reece
* Pax
* Pete Baughman
* Peyman Salehi
* Philipp A
* Pierre Sassoulas
* Prerak Patel
* Ramsey
* Ran Benita
* Robert O'Shea
* Ronny Pfannschmidt
* Rowin
* Ruth Comer
* Samuel Colvin
* Samuel Gaist
* Sandro Tosi
* Santiago Castro
* Shantanu
* Simon K
* Stefanie Molin
* Stephen Rosen
* Sviatoslav Sydorenko
* Tatiana Ovary
* Teejay
* Thierry Moisan
* Thomas Grainger
* Tim Hoffmann
* Tobias Diez
* Tony Narlock
* Vivaan Verma
* Wolfremium
* Yannick PÉROUX
* Yusuke Kadowaki
* Zac Hatfield-Dodds
* Zach OBrien
* aizpurua23a
* bitzge
* bluthej
* gresm
* holesch
* itxasos23
* johnkangw
* q0w
* rdb
* s-padmanaban
* skhomuti
* sommersoft
* vin01
* wim glenn
* wodny
* zx.qiu
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,18 @@
pytest-7.3.1
=======================================
pytest 7.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/stable/changelog.html.
Thanks to all of the contributors to this release:
* Ran Benita
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,21 @@
pytest-7.3.2
=======================================
pytest 7.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/stable/changelog.html.
Thanks to all of the contributors to this release:
* Adam J. Stewart
* Alessio Izzo
* Bruno Oliveira
* Ran Benita
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,49 @@
pytest-7.4.0
=======================================
The pytest team is proud to announce the 7.4.0 release!
This release contains new features, improvements, and bug fixes,
the full list of changes is available in the changelog:
https://docs.pytest.org/en/stable/changelog.html
For complete documentation, please visit:
https://docs.pytest.org/en/stable/
As usual, you can upgrade from PyPI via:
pip install -U pytest
Thanks to all of the contributors to this release:
* Adam J. Stewart
* Alessio Izzo
* Alex
* Alex Lambson
* Brian Larsen
* Bruno Oliveira
* Bryan Ricker
* Chris Mahoney
* Facundo Batista
* Florian Bruhin
* Jarrett Keifer
* Kenny Y
* Miro Hrončok
* Ran Benita
* Roberto Aldera
* Ronny Pfannschmidt
* Sergey Kim
* Stefanie Molin
* Vijay Arora
* Ville Skyttä
* Zac Hatfield-Dodds
* bzoracler
* leeyueh
* nondescryptid
* theirix
Happy testing,
The pytest Development Team

View File

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

View File

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

View File

@@ -92,3 +92,5 @@ pytest version min. Python version
5.0 - 6.1 3.5+
3.3 - 4.6 2.7, 3.4+
============== ===================
`Status of Python Versions <https://devguide.python.org/versions/>`__.

View File

@@ -22,7 +22,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
cachedir: .pytest_cache
rootdir: /home/sweet/project
collected 0 items
cache -- .../_pytest/cacheprovider.py:510
cache -- .../_pytest/cacheprovider.py:532
Return a cache object that can persist state between testing sessions.
cache.get(key, default)
@@ -33,25 +33,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
Values can be any object handled by the json stdlib module.
capsys -- .../_pytest/capture.py:878
Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``.
The captured output is made available via ``capsys.readouterr()`` method
calls, which return a ``(out, err)`` namedtuple.
``out`` and ``err`` will be ``text`` objects.
Returns an instance of :class:`CaptureFixture[str] <pytest.CaptureFixture>`.
Example:
.. code-block:: python
def test_output(capsys):
print("hello")
captured = capsys.readouterr()
assert captured.out == "hello\n"
capsysbinary -- .../_pytest/capture.py:906
capsysbinary -- .../_pytest/capture.py:1001
Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``.
The captured output is made available via ``capsysbinary.readouterr()``
@@ -69,7 +51,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
captured = capsysbinary.readouterr()
assert captured.out == b"hello\n"
capfd -- .../_pytest/capture.py:934
capfd -- .../_pytest/capture.py:1029
Enable text capturing of writes to file descriptors ``1`` and ``2``.
The captured output is made available via ``capfd.readouterr()`` method
@@ -87,7 +69,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
captured = capfd.readouterr()
assert captured.out == "hello\n"
capfdbinary -- .../_pytest/capture.py:962
capfdbinary -- .../_pytest/capture.py:1057
Enable bytes capturing of writes to file descriptors ``1`` and ``2``.
The captured output is made available via ``capfd.readouterr()`` method
@@ -105,7 +87,25 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
captured = capfdbinary.readouterr()
assert captured.out == b"hello\n"
doctest_namespace [session scope] -- .../_pytest/doctest.py:735
capsys -- .../_pytest/capture.py:973
Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``.
The captured output is made available via ``capsys.readouterr()`` method
calls, which return a ``(out, err)`` namedtuple.
``out`` and ``err`` will be ``text`` objects.
Returns an instance of :class:`CaptureFixture[str] <pytest.CaptureFixture>`.
Example:
.. code-block:: python
def test_output(capsys):
print("hello")
captured = capsys.readouterr()
assert captured.out == "hello\n"
doctest_namespace [session scope] -- .../_pytest/doctest.py:757
Fixture that returns a :py:class:`dict` that will be injected into the
namespace of doctests.
@@ -119,7 +119,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
For more details: :ref:`doctest_namespace`.
pytestconfig [session scope] -- .../_pytest/fixtures.py:1344
pytestconfig [session scope] -- .../_pytest/fixtures.py:1353
Session-scoped fixture that returns the session's :class:`pytest.Config`
object.
@@ -163,7 +163,10 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
record_testsuite_property("ARCH", "PPC")
record_testsuite_property("STORAGE_TYPE", "CEPH")
``name`` must be a string, ``value`` will be converted to a string and properly xml-escaped.
:param name:
The property name.
:param value:
The property value. Will be converted to a string.
.. warning::
@@ -193,7 +196,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
.. _legacy_path: https://py.readthedocs.io/en/latest/path.html
caplog -- .../_pytest/logging.py:487
caplog -- .../_pytest/logging.py:570
Access and control log capturing.
Captured logs are available through the following properties/methods::
@@ -204,7 +207,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
* caplog.record_tuples -> list of (logger_name, level, message) tuples
* caplog.clear() -> clear captured records and formatted log output string
monkeypatch -- .../_pytest/monkeypatch.py:29
monkeypatch -- .../_pytest/monkeypatch.py:30
A convenient fixture for monkey-patching.
The fixture provides these methods to modify objects, dictionaries, or
@@ -228,23 +231,25 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
To undo modifications done by the fixture in a contained scope,
use :meth:`context() <pytest.MonkeyPatch.context>`.
recwarn -- .../_pytest/recwarn.py:29
recwarn -- .../_pytest/recwarn.py:30
Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions.
See https://docs.python.org/library/how-to/capture-warnings.html for information
See https://docs.pytest.org/en/latest/how-to/capture-warnings.html for information
on warning categories.
tmp_path_factory [session scope] -- .../_pytest/tmpdir.py:184
tmp_path_factory [session scope] -- .../_pytest/tmpdir.py:245
Return a :class:`pytest.TempPathFactory` instance for the test session.
tmp_path -- .../_pytest/tmpdir.py:199
tmp_path -- .../_pytest/tmpdir.py:260
Return a temporary directory path object which is unique to each test
function invocation, created as a sub directory of the base temporary
directory.
By default, a new base temporary directory is created each test session,
and old bases are removed after 3 sessions, to aid in debugging. If
``--basetemp`` is used then it is cleared each session. See :ref:`base
and old bases are removed after 3 sessions, to aid in debugging.
This behavior can be configured with :confval:`tmp_path_retention_count` and
:confval:`tmp_path_retention_policy`.
If ``--basetemp`` is used then it is cleared each session. See :ref:`base
temporary directory`.
The returned object is a :class:`pathlib.Path` object.

View File

@@ -28,6 +28,463 @@ with advance notice in the **Deprecations** section of releases.
.. towncrier release notes start
pytest 7.4.2 (2023-09-07)
=========================
Bug Fixes
---------
- `#11237 <https://github.com/pytest-dev/pytest/issues/11237>`_: Fix doctest collection of `functools.cached_property` objects.
- `#11306 <https://github.com/pytest-dev/pytest/issues/11306>`_: Fixed bug using ``--importmode=importlib`` which would cause package ``__init__.py`` files to be imported more than once in some cases.
- `#11367 <https://github.com/pytest-dev/pytest/issues/11367>`_: Fixed bug where `user_properties` where not being saved in the JUnit XML file if a fixture failed during teardown.
- `#11394 <https://github.com/pytest-dev/pytest/issues/11394>`_: Fixed crash when parsing long command line arguments that might be interpreted as files.
Improved Documentation
----------------------
- `#11391 <https://github.com/pytest-dev/pytest/issues/11391>`_: Improved disclaimer on pytest plugin reference page to better indicate this is an automated, non-curated listing.
pytest 7.4.1 (2023-09-02)
=========================
Bug Fixes
---------
- `#10337 <https://github.com/pytest-dev/pytest/issues/10337>`_: Fixed bug where fake intermediate modules generated by ``--import-mode=importlib`` would not include the
child modules as attributes of the parent modules.
- `#10702 <https://github.com/pytest-dev/pytest/issues/10702>`_: Fixed error assertion handling in :func:`pytest.approx` when ``None`` is an expected or received value when comparing dictionaries.
- `#10811 <https://github.com/pytest-dev/pytest/issues/10811>`_: Fixed issue when using ``--import-mode=importlib`` together with ``--doctest-modules`` that caused modules
to be imported more than once, causing problems with modules that have import side effects.
pytest 7.4.0 (2023-06-23)
=========================
Features
--------
- `#10901 <https://github.com/pytest-dev/pytest/issues/10901>`_: Added :func:`ExceptionInfo.from_exception() <pytest.ExceptionInfo.from_exception>`, a simpler way to create an :class:`~pytest.ExceptionInfo` from an exception.
This can replace :func:`ExceptionInfo.from_exc_info() <pytest.ExceptionInfo.from_exc_info()>` for most uses.
Improvements
------------
- `#10872 <https://github.com/pytest-dev/pytest/issues/10872>`_: Update test log report annotation to named tuple and fixed inconsistency in docs for :hook:`pytest_report_teststatus` hook.
- `#10907 <https://github.com/pytest-dev/pytest/issues/10907>`_: When an exception traceback to be displayed is completely filtered out (by mechanisms such as ``__tracebackhide__``, internal frames, and similar), now only the exception string and the following message are shown:
"All traceback entries are hidden. Pass `--full-trace` to see hidden and internal frames.".
Previously, the last frame of the traceback was shown, even though it was hidden.
- `#10940 <https://github.com/pytest-dev/pytest/issues/10940>`_: Improved verbose output (``-vv``) of ``skip`` and ``xfail`` reasons by performing text wrapping while leaving a clear margin for progress output.
Added ``TerminalReporter.wrap_write()`` as a helper for that.
- `#10991 <https://github.com/pytest-dev/pytest/issues/10991>`_: Added handling of ``%f`` directive to print microseconds in log format options, such as ``log-date-format``.
- `#11005 <https://github.com/pytest-dev/pytest/issues/11005>`_: Added the underlying exception to the cache provider's path creation and write warning messages.
- `#11013 <https://github.com/pytest-dev/pytest/issues/11013>`_: Added warning when :confval:`testpaths` is set, but paths are not found by glob. In this case, pytest will fall back to searching from the current directory.
- `#11043 <https://github.com/pytest-dev/pytest/issues/11043>`_: When `--confcutdir` is not specified, and there is no config file present, the conftest cutoff directory (`--confcutdir`) is now set to the :ref:`rootdir <rootdir>`.
Previously in such cases, `conftest.py` files would be probed all the way to the root directory of the filesystem.
If you are badly affected by this change, consider adding an empty config file to your desired cutoff directory, or explicitly set `--confcutdir`.
- `#11081 <https://github.com/pytest-dev/pytest/issues/11081>`_: The :confval:`norecursedirs` check is now performed in a :hook:`pytest_ignore_collect` implementation, so plugins can affect it.
If after updating to this version you see that your `norecursedirs` setting is not being respected,
it means that a conftest or a plugin you use has a bad `pytest_ignore_collect` implementation.
Most likely, your hook returns `False` for paths it does not want to ignore,
which ends the processing and doesn't allow other plugins, including pytest itself, to ignore the path.
The fix is to return `None` instead of `False` for paths your hook doesn't want to ignore.
- `#8711 <https://github.com/pytest-dev/pytest/issues/8711>`_: :func:`caplog.set_level() <pytest.LogCaptureFixture.set_level>` and :func:`caplog.at_level() <pytest.LogCaptureFixture.at_level>`
will temporarily enable the requested ``level`` if ``level`` was disabled globally via
``logging.disable(LEVEL)``.
Bug Fixes
---------
- `#10831 <https://github.com/pytest-dev/pytest/issues/10831>`_: Terminal Reporting: Fixed bug when running in ``--tb=line`` mode where ``pytest.fail(pytrace=False)`` tests report ``None``.
- `#11068 <https://github.com/pytest-dev/pytest/issues/11068>`_: Fixed the ``--last-failed`` whole-file skipping functionality ("skipped N files") for :ref:`non-python test files <non-python tests>`.
- `#11104 <https://github.com/pytest-dev/pytest/issues/11104>`_: Fixed a regression in pytest 7.3.2 which caused to :confval:`testpaths` to be considered for loading initial conftests,
even when it was not utilized (e.g. when explicit paths were given on the command line).
Now the ``testpaths`` are only considered when they are in use.
- `#1904 <https://github.com/pytest-dev/pytest/issues/1904>`_: Fixed traceback entries hidden with ``__tracebackhide__ = True`` still being shown for chained exceptions (parts after "... the above exception ..." message).
- `#7781 <https://github.com/pytest-dev/pytest/issues/7781>`_: Fix writing non-encodable text to log file when using ``--debug``.
Improved Documentation
----------------------
- `#9146 <https://github.com/pytest-dev/pytest/issues/9146>`_: Improved documentation for :func:`caplog.set_level() <pytest.LogCaptureFixture.set_level>`.
Trivial/Internal Changes
------------------------
- `#11031 <https://github.com/pytest-dev/pytest/issues/11031>`_: Enhanced the CLI flag for ``-c`` to now include ``--config-file`` to make it clear that this flag applies to the usage of a custom config file.
pytest 7.3.2 (2023-06-10)
=========================
Bug Fixes
---------
- `#10169 <https://github.com/pytest-dev/pytest/issues/10169>`_: Fix bug where very long option names could cause pytest to break with ``OSError: [Errno 36] File name too long`` on some systems.
- `#10894 <https://github.com/pytest-dev/pytest/issues/10894>`_: Support for Python 3.12 (beta at the time of writing).
- `#10987 <https://github.com/pytest-dev/pytest/issues/10987>`_: :confval:`testpaths` is now honored to load root ``conftests``.
- `#10999 <https://github.com/pytest-dev/pytest/issues/10999>`_: The `monkeypatch` `setitem`/`delitem` type annotations now allow `TypedDict` arguments.
- `#11028 <https://github.com/pytest-dev/pytest/issues/11028>`_: Fixed bug in assertion rewriting where a variable assigned with the walrus operator could not be used later in a function call.
- `#11054 <https://github.com/pytest-dev/pytest/issues/11054>`_: Fixed ``--last-failed``'s "(skipped N files)" functionality for files inside of packages (directories with `__init__.py` files).
pytest 7.3.1 (2023-04-14)
=========================
Improvements
------------
- `#10875 <https://github.com/pytest-dev/pytest/issues/10875>`_: Python 3.12 support: fixed ``RuntimeError: TestResult has no addDuration method`` when running ``unittest`` tests.
- `#10890 <https://github.com/pytest-dev/pytest/issues/10890>`_: Python 3.12 support: fixed ``shutil.rmtree(onerror=...)`` deprecation warning when using :fixture:`tmp_path`.
Bug Fixes
---------
- `#10896 <https://github.com/pytest-dev/pytest/issues/10896>`_: Fixed performance regression related to :fixture:`tmp_path` and the new :confval:`tmp_path_retention_policy` option.
- `#10903 <https://github.com/pytest-dev/pytest/issues/10903>`_: Fix crash ``INTERNALERROR IndexError: list index out of range`` which happens when displaying an exception where all entries are hidden.
This reverts the change "Correctly handle ``__tracebackhide__`` for chained exceptions." introduced in version 7.3.0.
pytest 7.3.0 (2023-04-08)
=========================
Features
--------
- `#10525 <https://github.com/pytest-dev/pytest/issues/10525>`_: Test methods decorated with ``@classmethod`` can now be discovered as tests, following the same rules as normal methods. This fills the gap that static methods were discoverable as tests but not class methods.
- `#10755 <https://github.com/pytest-dev/pytest/issues/10755>`_: :confval:`console_output_style` now supports ``progress-even-when-capture-no`` to force the use of the progress output even when capture is disabled. This is useful in large test suites where capture may have significant performance impact.
- `#7431 <https://github.com/pytest-dev/pytest/issues/7431>`_: ``--log-disable`` CLI option added to disable individual loggers.
- `#8141 <https://github.com/pytest-dev/pytest/issues/8141>`_: Added :confval:`tmp_path_retention_count` and :confval:`tmp_path_retention_policy` configuration options to control how directories created by the :fixture:`tmp_path` fixture are kept.
Improvements
------------
- `#10226 <https://github.com/pytest-dev/pytest/issues/10226>`_: If multiple errors are raised in teardown, we now re-raise an ``ExceptionGroup`` of them instead of discarding all but the last.
- `#10658 <https://github.com/pytest-dev/pytest/issues/10658>`_: Allow ``-p`` arguments to include spaces (eg: ``-p no:logging`` instead of
``-pno:logging``). Mostly useful in the ``addopts`` section of the configuration
file.
- `#10710 <https://github.com/pytest-dev/pytest/issues/10710>`_: Added ``start`` and ``stop`` timestamps to ``TestReport`` objects.
- `#10727 <https://github.com/pytest-dev/pytest/issues/10727>`_: Split the report header for ``rootdir``, ``config file`` and ``testpaths`` so each has its own line.
- `#10840 <https://github.com/pytest-dev/pytest/issues/10840>`_: pytest should no longer crash on AST with pathological position attributes, for example testing AST produced by `Hylang <https://github.com/hylang/hy>__`.
- `#6267 <https://github.com/pytest-dev/pytest/issues/6267>`_: The full output of a test is no longer truncated if the truncation message would be longer than
the hidden text. The line number shown has also been fixed.
Bug Fixes
---------
- `#10743 <https://github.com/pytest-dev/pytest/issues/10743>`_: The assertion rewriting mechanism now works correctly when assertion expressions contain the walrus operator.
- `#10765 <https://github.com/pytest-dev/pytest/issues/10765>`_: Fixed :fixture:`tmp_path` fixture always raising :class:`OSError` on ``emscripten`` platform due to missing :func:`os.getuid`.
- `#1904 <https://github.com/pytest-dev/pytest/issues/1904>`_: Correctly handle ``__tracebackhide__`` for chained exceptions.
NOTE: This change was reverted in version 7.3.1.
Improved Documentation
----------------------
- `#10782 <https://github.com/pytest-dev/pytest/issues/10782>`_: Fixed the minimal example in :ref:`goodpractices`: ``pip install -e .`` requires a ``version`` entry in ``pyproject.toml`` to run successfully.
Trivial/Internal Changes
------------------------
- `#10669 <https://github.com/pytest-dev/pytest/issues/10669>`_: pytest no longer directly depends on the `attrs <https://www.attrs.org/en/stable/>`__ package. While
we at pytest all love the package dearly and would like to thank the ``attrs`` team for many years of cooperation and support,
it makes sense for ``pytest`` to have as little external dependencies as possible, as this helps downstream projects.
With that in mind, we have replaced the pytest's limited internal usage to use the standard library's ``dataclasses`` instead.
Nice diffs for ``attrs`` classes are still supported though.
pytest 7.2.2 (2023-03-03)
=========================
Bug Fixes
---------
- `#10533 <https://github.com/pytest-dev/pytest/issues/10533>`_: Fixed :func:`pytest.approx` handling of dictionaries containing one or more values of `0.0`.
- `#10592 <https://github.com/pytest-dev/pytest/issues/10592>`_: Fixed crash if `--cache-show` and `--help` are passed at the same time.
- `#10597 <https://github.com/pytest-dev/pytest/issues/10597>`_: Fixed bug where a fixture method named ``teardown`` would be called as part of ``nose`` teardown stage.
- `#10626 <https://github.com/pytest-dev/pytest/issues/10626>`_: Fixed crash if ``--fixtures`` and ``--help`` are passed at the same time.
- `#10660 <https://github.com/pytest-dev/pytest/issues/10660>`_: Fixed :py:func:`pytest.raises` to return a 'ContextManager' so that type-checkers could narrow
:code:`pytest.raises(...) if ... else nullcontext()` down to 'ContextManager' rather than 'object'.
Improved Documentation
----------------------
- `#10690 <https://github.com/pytest-dev/pytest/issues/10690>`_: Added `CI` and `BUILD_NUMBER` environment variables to the documentation.
- `#10721 <https://github.com/pytest-dev/pytest/issues/10721>`_: Fixed entry-points declaration in the documentation example using Hatch.
- `#10753 <https://github.com/pytest-dev/pytest/issues/10753>`_: Changed wording of the module level skip to be very explicit
about not collecting tests and not executing the rest of the module.
pytest 7.2.1 (2023-01-13)
=========================
Bug Fixes
---------
- `#10452 <https://github.com/pytest-dev/pytest/issues/10452>`_: Fix 'importlib.abc.TraversableResources' deprecation warning in Python 3.12.
- `#10457 <https://github.com/pytest-dev/pytest/issues/10457>`_: If a test is skipped from inside a fixture, the test summary now shows the test location instead of the fixture location.
- `#10506 <https://github.com/pytest-dev/pytest/issues/10506>`_: Fix bug where sometimes pytest would use the file system root directory as :ref:`rootdir <rootdir>` on Windows.
- `#10607 <https://github.com/pytest-dev/pytest/issues/10607>`_: Fix a race condition when creating junitxml reports, which could occur when multiple instances of pytest execute in parallel.
- `#10641 <https://github.com/pytest-dev/pytest/issues/10641>`_: Fix a race condition when creating or updating the stepwise plugin's cache, which could occur when multiple xdist worker nodes try to simultaneously update the stepwise plugin's cache.
pytest 7.2.0 (2022-10-23)
=========================
Deprecations
------------
- `#10012 <https://github.com/pytest-dev/pytest/issues/10012>`_: Update :class:`pytest.PytestUnhandledCoroutineWarning` to a deprecation; it will raise an error in pytest 8.
- `#10396 <https://github.com/pytest-dev/pytest/issues/10396>`_: pytest no longer depends on the ``py`` library. ``pytest`` provides a vendored copy of ``py.error`` and ``py.path`` modules but will use the ``py`` library if it is installed. If you need other ``py.*`` modules, continue to install the deprecated ``py`` library separately, otherwise it can usually be removed as a dependency.
- `#4562 <https://github.com/pytest-dev/pytest/issues/4562>`_: Deprecate configuring hook specs/impls using attributes/marks.
Instead use :py:func:`pytest.hookimpl` and :py:func:`pytest.hookspec`.
For more details, see the :ref:`docs <legacy-path-hooks-deprecated>`.
- `#9886 <https://github.com/pytest-dev/pytest/issues/9886>`_: The functionality for running tests written for ``nose`` has been officially deprecated.
This includes:
* Plain ``setup`` and ``teardown`` functions and methods: this might catch users by surprise, as ``setup()`` and ``teardown()`` are not pytest idioms, but part of the ``nose`` support.
* Setup/teardown using the `@with_setup <with-setup-nose>`_ decorator.
For more details, consult the :ref:`deprecation docs <nose-deprecation>`.
.. _`with-setup-nose`: https://nose.readthedocs.io/en/latest/testing_tools.html?highlight=with_setup#nose.tools.with_setup
- `#7337 <https://github.com/pytest-dev/pytest/issues/7337>`_: A deprecation warning is now emitted if a test function returns something other than `None`. This prevents a common mistake among beginners that expect that returning a `bool` (for example `return foo(a, b) == result`) would cause a test to pass or fail, instead of using `assert`. The plan is to make returning non-`None` from tests an error in the future.
Features
--------
- `#9897 <https://github.com/pytest-dev/pytest/issues/9897>`_: Added shell-style wildcard support to ``testpaths``.
Improvements
------------
- `#10218 <https://github.com/pytest-dev/pytest/issues/10218>`_: ``@pytest.mark.parametrize()`` (and similar functions) now accepts any ``Sequence[str]`` for the argument names,
instead of just ``list[str]`` and ``tuple[str, ...]``.
(Note that ``str``, which is itself a ``Sequence[str]``, is still treated as a
comma-delimited name list, as before).
- `#10381 <https://github.com/pytest-dev/pytest/issues/10381>`_: The ``--no-showlocals`` flag has been added. This can be passed directly to tests to override ``--showlocals`` declared through ``addopts``.
- `#3426 <https://github.com/pytest-dev/pytest/issues/3426>`_: Assertion failures with strings in NFC and NFD forms that normalize to the same string now have a dedicated error message detailing the issue, and their utf-8 representation is expressed instead.
- `#8508 <https://github.com/pytest-dev/pytest/issues/8508>`_: Introduce multiline display for warning matching via :py:func:`pytest.warns` and
enhance match comparison for :py:func:`_pytest._code.ExceptionInfo.match` as returned by :py:func:`pytest.raises`.
- `#8646 <https://github.com/pytest-dev/pytest/issues/8646>`_: Improve :py:func:`pytest.raises`. Previously passing an empty tuple would give a confusing
error. We now raise immediately with a more helpful message.
- `#9741 <https://github.com/pytest-dev/pytest/issues/9741>`_: On Python 3.11, use the standard library's :mod:`tomllib` to parse TOML.
:mod:`tomli` is no longer a dependency on Python 3.11.
- `#9742 <https://github.com/pytest-dev/pytest/issues/9742>`_: Display assertion message without escaped newline characters with ``-vv``.
- `#9823 <https://github.com/pytest-dev/pytest/issues/9823>`_: Improved error message that is shown when no collector is found for a given file.
- `#9873 <https://github.com/pytest-dev/pytest/issues/9873>`_: Some coloring has been added to the short test summary.
- `#9883 <https://github.com/pytest-dev/pytest/issues/9883>`_: Normalize the help description of all command-line options.
- `#9920 <https://github.com/pytest-dev/pytest/issues/9920>`_: Display full crash messages in ``short test summary info``, when running in a CI environment.
- `#9987 <https://github.com/pytest-dev/pytest/issues/9987>`_: Added support for hidden configuration file by allowing ``.pytest.ini`` as an alternative to ``pytest.ini``.
Bug Fixes
---------
- `#10150 <https://github.com/pytest-dev/pytest/issues/10150>`_: :data:`sys.stdin` now contains all expected methods of a file-like object when capture is enabled.
- `#10382 <https://github.com/pytest-dev/pytest/issues/10382>`_: Do not break into pdb when ``raise unittest.SkipTest()`` appears top-level in a file.
- `#7792 <https://github.com/pytest-dev/pytest/issues/7792>`_: Marks are now inherited according to the full MRO in test classes. Previously, if a test class inherited from two or more classes, only marks from the first super-class would apply.
When inheriting marks from super-classes, marks from the sub-classes are now ordered before marks from the super-classes, in MRO order. Previously it was the reverse.
When inheriting marks from super-classes, the `pytestmark` attribute of the sub-class now only contains the marks directly applied to it. Previously, it also contained marks from its super-classes. Please note that this attribute should not normally be accessed directly; use :func:`pytest.Node.iter_markers` instead.
- `#9159 <https://github.com/pytest-dev/pytest/issues/9159>`_: Showing inner exceptions by forcing native display in ``ExceptionGroups`` even when using display options other than ``--tb=native``. A temporary step before full implementation of pytest-native display for inner exceptions in ``ExceptionGroups``.
- `#9877 <https://github.com/pytest-dev/pytest/issues/9877>`_: Ensure ``caplog.get_records(when)`` returns current/correct data after invoking ``caplog.clear()``.
Improved Documentation
----------------------
- `#10344 <https://github.com/pytest-dev/pytest/issues/10344>`_: Update information on writing plugins to use ``pyproject.toml`` instead of ``setup.py``.
- `#9248 <https://github.com/pytest-dev/pytest/issues/9248>`_: The documentation is now built using Sphinx 5.x (up from 3.x previously).
- `#9291 <https://github.com/pytest-dev/pytest/issues/9291>`_: Update documentation on how :func:`pytest.warns` affects :class:`DeprecationWarning`.
Trivial/Internal Changes
------------------------
- `#10313 <https://github.com/pytest-dev/pytest/issues/10313>`_: Made ``_pytest.doctest.DoctestItem`` export ``pytest.DoctestItem`` for
type check and runtime purposes. Made `_pytest.doctest` use internal APIs
to avoid circular imports.
- `#9906 <https://github.com/pytest-dev/pytest/issues/9906>`_: Made ``_pytest.compat`` re-export ``importlib_metadata`` in the eyes of type checkers.
- `#9910 <https://github.com/pytest-dev/pytest/issues/9910>`_: Fix default encoding warning (``EncodingWarning``) in ``cacheprovider``
- `#9984 <https://github.com/pytest-dev/pytest/issues/9984>`_: Improve the error message when we attempt to access a fixture that has been
torn down.
Add an additional sentence to the docstring explaining when it's not a good
idea to call ``getfixturevalue``.
pytest 7.1.3 (2022-08-31)
=========================
@@ -268,7 +725,7 @@ Breaking Changes
- `#7259 <https://github.com/pytest-dev/pytest/issues/7259>`_: The :ref:`Node.reportinfo() <non-python tests>` function first return value type has been expanded from `py.path.local | str` to `os.PathLike[str] | str`.
Most plugins which refer to `reportinfo()` only define it as part of a custom :class:`pytest.Item` implementation.
Since `py.path.local` is a `os.PathLike[str]`, these plugins are unaffacted.
Since `py.path.local` is an `os.PathLike[str]`, these plugins are unaffacted.
Plugins and users which call `reportinfo()`, use the first return value and interact with it as a `py.path.local`, would need to adjust by calling `py.path.local(fspath)`.
Although preferably, avoid the legacy `py.path.local` and use `pathlib.Path`, or use `item.location` or `item.path`, instead.
@@ -3768,7 +4225,7 @@ Removals
See our :ref:`docs <calling fixtures directly deprecated>` on information on how to update your code.
- :issue:`4546`: Remove ``Node.get_marker(name)`` the return value was not usable for more than a existence check.
- :issue:`4546`: Remove ``Node.get_marker(name)`` the return value was not usable for more than an existence check.
Use ``Node.get_closest_marker(name)`` as a replacement.

View File

@@ -341,7 +341,7 @@ epub_copyright = "2013, holger krekel et alii"
# The scheme of the identifier. Typical schemes are ISBN or URL.
# epub_scheme = ''
# The unique identifier of the text. This can be a ISBN number
# The unique identifier of the text. This can be an ISBN number
# or the project homepage.
# epub_identifier = ''

View File

@@ -1052,7 +1052,7 @@ that are then turned into proper test methods. Example:
.. code-block:: python
def check(x, y):
assert x ** x == y
assert x**x == y
def test_squared():
@@ -1067,7 +1067,7 @@ This form of test function doesn't support fixtures properly, and users should s
@pytest.mark.parametrize("x, y", [(2, 4), (3, 9)])
def test_squared(x, y):
assert x ** x == y
assert x**x == y
.. _internal classes accessed through node deprecated:

View File

@@ -17,7 +17,7 @@ def b(a, order):
@pytest.fixture
def c(a, b, order):
def c(b, order):
order.append("c")

View File

@@ -246,9 +246,9 @@ You can ask which markers exist for your test suite - the list includes our just
@pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see https://docs.pytest.org/en/stable/explanation/fixtures.html#usefixtures
@pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
@pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible. DEPRECATED, use @pytest.hookimpl(tryfirst=True) instead.
@pytest.mark.trylast: mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
@pytest.mark.trylast: mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible. DEPRECATED, use @pytest.hookimpl(trylast=True) instead.
For an example on how to add and work with markers from a plugin, see
@@ -438,9 +438,9 @@ The ``--markers`` option always gives you a list of available markers:
@pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see https://docs.pytest.org/en/stable/explanation/fixtures.html#usefixtures
@pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
@pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible. DEPRECATED, use @pytest.hookimpl(tryfirst=True) instead.
@pytest.mark.trylast: mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
@pytest.mark.trylast: mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible. DEPRECATED, use @pytest.hookimpl(trylast=True) instead.
.. _`passing callables to custom markers`:
@@ -611,7 +611,7 @@ then you will see two tests skipped and two executed tests as expected:
test_plat.py s.s. [100%]
========================= short test summary info ==========================
SKIPPED [2] conftest.py:12: cannot run on platform linux
SKIPPED [2] conftest.py:13: cannot run on platform linux
======================= 2 passed, 2 skipped in 0.12s =======================
Note that if you specify a platform via the marker-command line option like this:

View File

@@ -12,7 +12,7 @@ class YamlFile(pytest.File):
# We need a yaml parser, e.g. PyYAML.
import yaml
raw = yaml.safe_load(self.path.open())
raw = yaml.safe_load(self.path.open(encoding="utf-8"))
for name, spec in sorted(raw.items()):
yield YamlItem.from_parent(self, name=name, spec=spec)
@@ -38,6 +38,7 @@ class YamlItem(pytest.Item):
" no further details known at this point.",
]
)
return super().repr_failure(excinfo)
def reportinfo(self):
return self.path, 0, f"usecase: {self.name}"

View File

@@ -504,9 +504,9 @@ Running it results in some skips if we don't have all the python interpreters in
. $ pytest -rs -q multipython.py
sssssssssssssssssssssssssss [100%]
========================= short test summary info ==========================
SKIPPED [9] multipython.py:29: 'python3.5' not found
SKIPPED [9] multipython.py:29: 'python3.6' not found
SKIPPED [9] multipython.py:29: 'python3.7' not found
SKIPPED [9] multipython.py:69: 'python3.5' not found
SKIPPED [9] multipython.py:69: 'python3.6' not found
SKIPPED [9] multipython.py:69: 'python3.7' not found
27 skipped in 0.12s
Indirect parametrization of optional implementations/imports
@@ -574,7 +574,7 @@ If you run this with reporting for skips enabled:
test_module.py .s [100%]
========================= short test summary info ==========================
SKIPPED [1] conftest.py:12: could not import 'opt2': No module named 'opt2'
SKIPPED [1] test_module.py:3: could not import 'opt2': No module named 'opt2'
======================= 1 passed, 1 skipped in 0.12s =======================
You'll see that we don't have an ``opt2`` module and thus the second test run

View File

@@ -148,7 +148,8 @@ The test collection would look like this:
$ pytest --collect-only
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
rootdir: /home/sweet/project, configfile: pytest.ini
rootdir: /home/sweet/project
configfile: pytest.ini
collected 2 items
<Module check_myapp.py>
@@ -209,7 +210,8 @@ You can always peek at the collection tree without running tests like this:
. $ pytest --collect-only pythoncollection.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
rootdir: /home/sweet/project, configfile: pytest.ini
rootdir: /home/sweet/project
configfile: pytest.ini
collected 3 items
<Module CWD/pythoncollection.py>
@@ -290,7 +292,8 @@ file will be left out:
$ pytest --collect-only
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
rootdir: /home/sweet/project, configfile: pytest.ini
rootdir: /home/sweet/project
configfile: pytest.ini
collected 0 items
======================= no tests collected in 0.12s ========================

View File

@@ -144,7 +144,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
E 1
E 1...
E
E ...Full output truncated (7 lines hidden), use '-vv' to show
E ...Full output truncated (6 lines hidden), use '-vv' to show
failure_demo.py:60: AssertionError
_________________ TestSpecialisedExplanations.test_eq_list _________________
@@ -184,9 +184,8 @@ Here is a nice run of several failures and how ``pytest`` presents things:
E Left contains 1 more item:
E {'c': 0}
E Right contains 1 more item:
E {'d': 0}...
E
E ...Full output truncated (2 lines hidden), use '-vv' to show
E {'d': 0}
E Use -v to get more diff
failure_demo.py:71: AssertionError
_________________ TestSpecialisedExplanations.test_eq_set __________________
@@ -195,16 +194,15 @@ Here is a nice run of several failures and how ``pytest`` presents things:
def test_eq_set(self):
> assert {0, 10, 11, 12} == {0, 20, 21}
E AssertionError: assert {0, 10, 11, 12} == {0, 20, 21}
E assert {0, 10, 11, 12} == {0, 20, 21}
E Extra items in the left set:
E 10
E 11
E 12
E Extra items in the right set:
E 20
E 21...
E
E ...Full output truncated (2 lines hidden), use '-vv' to show
E 21
E Use -v to get more diff
failure_demo.py:74: AssertionError
_____________ TestSpecialisedExplanations.test_eq_longer_list ______________
@@ -241,9 +239,8 @@ Here is a nice run of several failures and how ``pytest`` presents things:
E which
E includes foo
E ? +++
E and a...
E
E ...Full output truncated (2 lines hidden), use '-vv' to show
E and a
E tail
failure_demo.py:84: AssertionError
___________ TestSpecialisedExplanations.test_not_in_text_single ____________
@@ -307,9 +304,9 @@ Here is a nice run of several failures and how ``pytest`` presents things:
E ['b']
E
E Drill down into differing attribute b:
E b: 'b' != 'c'...
E
E ...Full output truncated (3 lines hidden), use '-vv' to show
E b: 'b' != 'c'
E - c
E + b
failure_demo.py:108: AssertionError
________________ TestSpecialisedExplanations.test_eq_attrs _________________
@@ -334,9 +331,9 @@ Here is a nice run of several failures and how ``pytest`` presents things:
E ['b']
E
E Drill down into differing attribute b:
E b: 'b' != 'c'...
E
E ...Full output truncated (3 lines hidden), use '-vv' to show
E b: 'b' != 'c'
E - c
E + b
failure_demo.py:120: AssertionError
______________________________ test_attribute ______________________________
@@ -673,7 +670,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
FAILED failure_demo.py::TestSpecialisedExplanations::test_eq_list - asser...
FAILED failure_demo.py::TestSpecialisedExplanations::test_eq_list_long - ...
FAILED failure_demo.py::TestSpecialisedExplanations::test_eq_dict - Asser...
FAILED failure_demo.py::TestSpecialisedExplanations::test_eq_set - Assert...
FAILED failure_demo.py::TestSpecialisedExplanations::test_eq_set - assert...
FAILED failure_demo.py::TestSpecialisedExplanations::test_eq_longer_list
FAILED failure_demo.py::TestSpecialisedExplanations::test_in_list - asser...
FAILED failure_demo.py::TestSpecialisedExplanations::test_not_in_text_multiline

View File

@@ -661,8 +661,7 @@ If we run this:
test_step.py:11: AssertionError
========================= short test summary info ==========================
XFAIL test_step.py::TestUserHandling::test_deletion
reason: previous test failed (test_modification)
XFAIL test_step.py::TestUserHandling::test_deletion - reason: previous test failed (test_modification)
================== 1 failed, 2 passed, 1 xfailed in 0.12s ==================
We'll see that ``test_deletion`` was not executed because ``test_modification``
@@ -692,7 +691,7 @@ Here is an example for making a ``db`` fixture available in a directory:
pass
@pytest.fixture(scope="session")
@pytest.fixture(scope="package")
def db():
return DB()
@@ -818,7 +817,7 @@ case we just write some information out to a ``failures`` file:
# we only look at actual failing test calls, not setup/teardown
if rep.when == "call" and rep.failed:
mode = "a" if os.path.exists("failures") else "w"
with open("failures", mode) as f:
with open("failures", mode, encoding="utf-8") as f:
# let's also access a fixture for the fun of it
if "tmp_path" in item.fixturenames:
extra = " ({})".format(item.funcargs["tmp_path"])
@@ -893,8 +892,11 @@ here is a little example implemented via a local plugin:
.. code-block:: python
# content of conftest.py
from typing import Dict
import pytest
from pytest import StashKey, CollectReport
phase_report_key = StashKey[Dict[str, CollectReport]]()
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
@@ -903,10 +905,9 @@ here is a little example implemented via a local plugin:
outcome = yield
rep = outcome.get_result()
# set a report attribute for each phase of a call, which can
# store test results for each phase of a call, which can
# be "setup", "call", "teardown"
setattr(item, "rep_" + rep.when, rep)
item.stash.setdefault(phase_report_key, {})[rep.when] = rep
@pytest.fixture
@@ -914,11 +915,11 @@ here is a little example implemented via a local plugin:
yield
# request.node is an "item" because we use the default
# "function" scope
if request.node.rep_setup.failed:
print("setting up a test failed!", request.node.nodeid)
elif request.node.rep_setup.passed:
if request.node.rep_call.failed:
print("executing test failed", request.node.nodeid)
report = request.node.stash[phase_report_key]
if report["setup"].failed:
print("setting up a test failed or skipped", request.node.nodeid)
elif ("call" not in report) or report["call"].failed:
print("executing test failed or skipped", request.node.nodeid)
if you then have failing tests:
@@ -956,8 +957,8 @@ and run it:
rootdir: /home/sweet/project
collected 3 items
test_module.py Esetting up a test failed! test_module.py::test_setup_fails
Fexecuting test failed test_module.py::test_call_fails
test_module.py Esetting up a test failed or skipped test_module.py::test_setup_fails
Fexecuting test failed or skipped test_module.py::test_call_fails
F
================================== ERRORS ==================================

View File

@@ -24,8 +24,9 @@ The first few lines should look like this:
[project]
name = "PACKAGENAME"
version = "PACKAGEVERSION"
where ``PACKAGENAME`` is the name of your package.
where ``PACKAGENAME`` and ``PACKAGEVERSION`` are the name and version of your package respectively.
You can then install your package in "editable" mode by running from the same directory:
@@ -50,8 +51,8 @@ Conventions for Python test discovery
* In those directories, search for ``test_*.py`` or ``*_test.py`` files, imported by their `test package name`_.
* From those files, collect test items:
* ``test`` prefixed test functions or methods outside of class
* ``test`` prefixed test functions or methods inside ``Test`` prefixed test classes (without an ``__init__`` method)
* ``test`` prefixed test functions or methods outside of class.
* ``test`` prefixed test functions or methods inside ``Test`` prefixed test classes (without an ``__init__`` method). Methods decorated with ``@staticmethod`` and ``@classmethods`` are also considered.
For examples of how to customize your test discovery :doc:`/example/pythoncollection`.
@@ -270,8 +271,8 @@ tox
Once you are done with your work and want to make sure that your actual
package passes all tests you may want to look into :doc:`tox <tox:index>`, the
virtualenv test automation tool and its :doc:`pytest support <tox:example/pytest>`.
tox helps you to setup virtualenv environments with pre-defined
virtualenv test automation tool.
``tox`` helps you to setup virtualenv environments with pre-defined
dependencies and then executing a pre-configured test command with
options. It will run tests against the installed package and not
against your source code checkout, helping to detect packaging
@@ -293,3 +294,20 @@ See also `pypa/setuptools#1684 <https://github.com/pypa/setuptools/issues/1684>`
setuptools intends to
`remove the test command <https://github.com/pypa/setuptools/issues/931>`_.
Checking with flake8-pytest-style
---------------------------------
In order to ensure that pytest is being used correctly in your project,
it can be helpful to use the `flake8-pytest-style <https://github.com/m-burst/flake8-pytest-style>`_ flake8 plugin.
flake8-pytest-style checks for common mistakes and coding style violations in pytest code,
such as incorrect use of fixtures, test function names, and markers.
By using this plugin, you can catch these errors early in the development process
and ensure that your pytest code is consistent and easy to maintain.
A list of the lints detected by flake8-pytest-style can be found on its `PyPI page <https://pypi.org/project/flake8-pytest-style/>`_.
.. note::
flake8-pytest-style is not an official pytest project. Some of the rules enforce certain style choices, such as using `@pytest.fixture()` over `@pytest.fixture`, but you can configure the plugin to fit your preferred style.

View File

@@ -16,7 +16,7 @@ import process can be controlled through the ``--import-mode`` command-line flag
these values:
* ``prepend`` (default): the directory path containing each module will be inserted into the *beginning*
of :py:data:`sys.path` if not already there, and then imported with the :func:`__import__ <__import__>` builtin.
of :py:data:`sys.path` if not already there, and then imported with the :func:`importlib.import_module <importlib.import_module>` function.
This requires test module names to be unique when the test directory tree is not arranged in
packages, because the modules will put in :py:data:`sys.modules` after importing.
@@ -24,7 +24,7 @@ these values:
This is the classic mechanism, dating back from the time Python 2 was still supported.
* ``append``: the directory containing each module is appended to the end of :py:data:`sys.path` if not already
there, and imported with ``__import__``.
there, and imported with :func:`importlib.import_module <importlib.import_module>`.
This better allows to run test modules against installed versions of a package even if the
package under test has the same import root. For example:
@@ -43,7 +43,7 @@ these values:
Same as ``prepend``, requires test module names to be unique when the test directory tree is
not arranged in packages, because the modules will put in :py:data:`sys.modules` after importing.
* ``importlib``: new in pytest-6.0, this mode uses :mod:`importlib` to import test modules. This gives full control over the import process, and doesn't require changing :py:data:`sys.path`.
* ``importlib``: new in pytest-6.0, this mode uses more fine control mechanisms provided by :mod:`importlib` to import test modules. This gives full control over the import process, and doesn't require changing :py:data:`sys.path`.
For this reason this doesn't require test module names to be unique.

View File

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

View File

@@ -54,14 +54,13 @@ operators. (See :ref:`tbreportdemo`). This allows you to use the
idiomatic python constructs without boilerplate code while not losing
introspection information.
However, if you specify a message with the assertion like this:
If a message is specified with the assertion like this:
.. code-block:: python
assert a % 2 == 0, "value was odd, should be even"
then no assertion introspection takes places at all and the message
will be simply shown in the traceback.
it is printed alongside the assertion introspection in the traceback.
See :ref:`assert-details` for more information on assertion introspection.

View File

@@ -176,14 +176,21 @@ with more recent files coming first.
Behavior when no tests failed in the last run
---------------------------------------------
When no tests failed in the last run, or when no cached ``lastfailed`` data was
found, ``pytest`` can be configured either to run all of the tests or no tests,
using the ``--last-failed-no-failures`` option, which takes one of the following values:
The ``--lfnf/--last-failed-no-failures`` option governs the behavior of ``--last-failed``.
Determines whether to execute tests when there are no previously (known)
failures or when no cached ``lastfailed`` data was found.
There are two options:
* ``all``: when there are no known test failures, runs all tests (the full test suite). This is the default.
* ``none``: when there are no known test failures, just emits a message stating this and exit successfully.
Example:
.. code-block:: bash
pytest --last-failed --last-failed-no-failures all # run all tests (default behavior)
pytest --last-failed --last-failed-no-failures none # run no tests and exit
pytest --last-failed --last-failed-no-failures all # runs the full test suite (default behavior)
pytest --last-failed --last-failed-no-failures none # runs no tests and exits successfully
The new config.cache object
--------------------------------
@@ -233,7 +240,7 @@ If you run this command for the first time, you can see the print statement:
> assert mydata == 23
E assert 42 == 23
test_caching.py:20: AssertionError
test_caching.py:19: AssertionError
-------------------------- Captured stdout setup ---------------------------
running expensive computation...
========================= short test summary info ==========================
@@ -256,7 +263,7 @@ the cache and nothing will be printed:
> assert mydata == 23
E assert 42 == 23
test_caching.py:20: AssertionError
test_caching.py:19: AssertionError
========================= short test summary info ==========================
FAILED test_caching.py::test_function - assert 42 == 23
1 failed in 0.12s

View File

@@ -109,6 +109,18 @@ When a warning matches more than one option in the list, the action for the last
is performed.
.. note::
The ``-W`` flag and the ``filterwarnings`` ini option use warning filters that are
similar in structure, but each configuration option interprets its filter
differently. For example, *message* in ``filterwarnings`` is a string containing a
regular expression that the start of the warning message must match,
case-insensitively, while *message* in ``-W`` is a literal string that the start of
the warning message must contain (case-insensitively), ignoring any whitespace at
the start or end of message. Consult the `warning filter`_ documentation for more
details.
.. _`filterwarnings`:
``@pytest.mark.filterwarnings``
@@ -270,20 +282,34 @@ which works in a similar manner to :ref:`raises <assertraises>` (except that
warnings.warn("my warning", UserWarning)
The test will fail if the warning in question is not raised. Use the keyword
argument ``match`` to assert that the warning matches a text or regex::
argument ``match`` to assert that the warning matches a text or regex.
To match a literal string that may contain regular expression metacharacters like ``(`` or ``.``, the pattern can
first be escaped with ``re.escape``.
>>> with warns(UserWarning, match='must be 0 or None'):
Some examples:
.. code-block:: pycon
>>> with warns(UserWarning, match="must be 0 or None"):
... warnings.warn("value must be 0 or None", UserWarning)
...
>>> with warns(UserWarning, match=r'must be \d+$'):
>>> with warns(UserWarning, match=r"must be \d+$"):
... warnings.warn("value must be 42", UserWarning)
...
>>> with warns(UserWarning, match=r'must be \d+$'):
>>> with warns(UserWarning, match=r"must be \d+$"):
... warnings.warn("this is not here", UserWarning)
...
Traceback (most recent call last):
...
Failed: DID NOT WARN. No warnings of type ...UserWarning... were emitted...
>>> with warns(UserWarning, match=re.escape("issue with foo() func")):
... warnings.warn("issue with foo() func")
...
You can also call :func:`pytest.warns` on a function or code string:
.. code-block:: python

View File

@@ -1237,7 +1237,6 @@ If the data created by the factory requires managing, the fixture can take care
@pytest.fixture
def make_customer_record():
created_records = []
def _make_customer_record(name):
@@ -1699,7 +1698,7 @@ and declare its use in a test module via a ``usefixtures`` marker:
class TestDirectoryInit:
def test_cwd_starts_empty(self):
assert os.listdir(os.getcwd()) == []
with open("myfile", "w") as f:
with open("myfile", "w", encoding="utf-8") as f:
f.write("hello")
def test_cwd_again_starts_empty(self):

View File

@@ -55,6 +55,13 @@ These options can also be customized through ``pytest.ini`` file:
log_format = %(asctime)s %(levelname)s %(message)s
log_date_format = %Y-%m-%d %H:%M:%S
Specific loggers can be disabled via ``--log-disable={logger_name}``.
This argument can be passed multiple times:
.. code-block:: bash
pytest --log-disable=main --log-disable=testing
Further it is possible to disable reporting of captured content (stdout,
stderr and logs) on failed tests completely with:

View File

@@ -135,10 +135,10 @@ This can be done in our test file by defining a class to represent ``r``.
# this is the previous code block example
import app
# custom class to be the mock return value
# will override the requests.Response returned from requests.get
class MockResponse:
# mock json() method always returns a specific testing dictionary
@staticmethod
def json():
@@ -146,7 +146,6 @@ This can be done in our test file by defining a class to represent ``r``.
def test_get_json(monkeypatch):
# Any arguments may be passed and mock_get() will always return our
# mocked object, which only has the .json() method.
def mock_get(*args, **kwargs):
@@ -181,6 +180,7 @@ This mock can be shared across tests using a ``fixture``:
# app.py that includes the get_json() function
import app
# custom class to be the mock return value of requests.get()
class MockResponse:
@staticmethod
@@ -358,7 +358,6 @@ For testing purposes we can patch the ``DEFAULT_CONFIG`` dictionary to specific
def test_connection(monkeypatch):
# Patch the values of DEFAULT_CONFIG to specific
# testing values only for this test.
monkeypatch.setitem(app.DEFAULT_CONFIG, "user", "test_user")
@@ -383,7 +382,6 @@ You can use the :py:meth:`monkeypatch.delitem <MonkeyPatch.delitem>` to remove v
def test_missing_user(monkeypatch):
# patch the DEFAULT_CONFIG t be missing the 'user' key
monkeypatch.delitem(app.DEFAULT_CONFIG, "user", raising=False)
@@ -404,6 +402,7 @@ separate fixtures for each potential mock and reference them in the needed tests
# app.py with the connection string function
import app
# all of the mocks are moved into separated fixtures
@pytest.fixture
def mock_test_user(monkeypatch):
@@ -425,7 +424,6 @@ separate fixtures for each potential mock and reference them in the needed tests
# tests reference only the fixture mocks that are needed
def test_connection(mock_test_user, mock_test_database):
expected = "User Id=test_user; Location=test_db;"
result = app.create_connection_string()
@@ -433,7 +431,6 @@ separate fixtures for each potential mock and reference them in the needed tests
def test_missing_user(mock_missing_default_user):
with pytest.raises(KeyError):
_ = app.create_connection_string()

View File

@@ -167,9 +167,9 @@ Now we can increase pytest's verbosity:
E Right contains 4 more items:
E {'10': 10, '20': 20, '30': 30, '40': 40}
E Full diff:
E - {'0': 0, '10': 10, '20': 20, '30': 30, '40': 40}...
E
E ...Full output truncated (3 lines hidden), use '-vv' to show
E - {'0': 0, '10': 10, '20': 20, '30': 30, '40': 40}
E ? - - - - - - - -
E + {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4}
test_verbosity_example.py:14: AssertionError
___________________________ test_long_text_fail ____________________________
@@ -349,8 +349,7 @@ Example:
test_example.py:14: AssertionError
========================= short test summary info ==========================
SKIPPED [1] test_example.py:22: skipping this test
XFAIL test_example.py::test_xfail
reason: xfailing this test
XFAIL test_example.py::test_xfail - reason: xfailing this test
XPASS test_example.py::test_xpass always xfail
ERROR test_example.py::test_error - assert 0
FAILED test_example.py::test_fail - assert 0

View File

@@ -24,8 +24,8 @@ created in the `base temporary directory`_.
d = tmp_path / "sub"
d.mkdir()
p = d / "hello.txt"
p.write_text(CONTENT)
assert p.read_text() == CONTENT
p.write_text(CONTENT, encoding="utf-8")
assert p.read_text(encoding="utf-8") == CONTENT
assert len(list(tmp_path.iterdir())) == 1
assert 0
@@ -51,8 +51,8 @@ Running this would result in a passed test except for the last
d = tmp_path / "sub"
d.mkdir()
p = d / "hello.txt"
p.write_text(CONTENT)
assert p.read_text() == CONTENT
p.write_text(CONTENT, encoding="utf-8")
assert p.read_text(encoding="utf-8") == CONTENT
assert len(list(tmp_path.iterdir())) == 1
> assert 0
E assert 0
@@ -131,10 +131,12 @@ The default base temporary directory
Temporary directories are by default created as sub-directories of
the system temporary directory. The base name will be ``pytest-NUM`` where
``NUM`` will be incremented with each test run. Moreover, entries older
than 3 temporary directories will be removed.
``NUM`` will be incremented with each test run.
By default, entries older than 3 temporary directories will be removed.
This behavior can be configured with :confval:`tmp_path_retention_count` and
:confval:`tmp_path_retention_policy`.
The number of entries currently cannot be changed, but using the ``--basetemp``
Using the ``--basetemp``
option will remove the directory before every run, effectively meaning the temporary directories
of only the most recent run will be kept.

View File

@@ -157,7 +157,7 @@ the ``self.db`` values in the traceback:
E AssertionError: <conftest.db_class.<locals>.DummyDB object at 0xdeadbeef0001>
E assert 0
test_unittest_db.py:10: AssertionError
test_unittest_db.py:11: AssertionError
___________________________ MyTest.test_method2 ____________________________
self = <test_unittest_db.MyTest testMethod=test_method2>
@@ -167,7 +167,7 @@ the ``self.db`` values in the traceback:
E AssertionError: <conftest.db_class.<locals>.DummyDB object at 0xdeadbeef0001>
E assert 0
test_unittest_db.py:13: AssertionError
test_unittest_db.py:14: AssertionError
========================= short test summary info ==========================
FAILED test_unittest_db.py::MyTest::test_method1 - AssertionError: <conft...
FAILED test_unittest_db.py::MyTest::test_method2 - AssertionError: <conft...
@@ -207,10 +207,10 @@ creation of a per-test temporary directory:
@pytest.fixture(autouse=True)
def initdir(self, tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path) # change to pytest-provided temporary directory
tmp_path.joinpath("samplefile.ini").write_text("# testdata")
tmp_path.joinpath("samplefile.ini").write_text("# testdata", encoding="utf-8")
def test_method(self):
with open("samplefile.ini") as f:
with open("samplefile.ini", encoding="utf-8") as f:
s = f.read()
assert "testdata" in s

View File

@@ -35,11 +35,12 @@ Pytest supports several ways to run and select tests from the command-line.
.. code-block:: bash
pytest -k "MyClass and not method"
pytest -k 'MyClass and not method'
This will run tests which contain names that match the given *string expression* (case-insensitive),
which can include Python operators that use filenames, class names and function names as variables.
The example above will run ``TestMyClass.test_something`` but not ``TestMyClass.test_method_simple``.
Use ``""`` instead of ``''`` in expression when running this on Windows
.. _nodeids:
@@ -172,7 +173,8 @@ You can invoke ``pytest`` from Python code directly:
this acts as if you would call "pytest" from the command line.
It will not raise :class:`SystemExit` but return the :ref:`exit code <exit-codes>` instead.
You can pass in options and arguments:
If you don't pass it any arguments, ``main`` reads the arguments from the command line arguments of the process (:data:`sys.argv`), which may be undesirable.
You can pass in options and arguments explicitly:
.. code-block:: python

View File

@@ -249,6 +249,7 @@ and use pytest_addoption as follows:
# contents of hooks.py
# Use firstresult=True because we only want one plugin to define this
# default value
@hookspec(firstresult=True)

View File

@@ -167,13 +167,8 @@ it in your ``pyproject.toml`` file.
"Framework :: Pytest",
]
[tool.setuptools]
packages = ["myproject"]
[project.entry_points]
pytest11 = [
"myproject = myproject.pluginmodule",
]
[project.entry-points.pytest11]
myproject = "myproject.pluginmodule"
If a package is installed this way, ``pytest`` will load
``myproject.pluginmodule`` as a plugin which can define
@@ -454,7 +449,8 @@ in our ``pytest.ini`` to tell pytest where to look for example files.
$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
rootdir: /home/sweet/project, configfile: pytest.ini
rootdir: /home/sweet/project
configfile: pytest.ini
collected 2 items
test_example.py .. [100%]

View File

@@ -2,8 +2,7 @@
.. sidebar:: Next Open Trainings
- Professionelles Testen für Python mit pytest, part of `enterPy <https://www.enterpy.de/>`__ (German), `October 28th <https://www.enterpy.de/veranstaltung-15409-se-0-professionelles-testen-fuer-python-mit-pytest.html>`__ (sold out) and `November 4th <https://www.enterpy.de/veranstaltung-15557-se-0-professionelles-testen-fuer-python-mit-pytest-zusatztermin.html>`__, online
- `Professional Testing with Python <https://python-academy.com/courses/python_course_testing.html>`_, via `Python Academy <https://www.python-academy.com/>`_, March 7th to 9th 2023 (3 day in-depth training), Remote and Leipzig, Germany
- `Professional Testing with Python <https://python-academy.com/courses/python_course_testing.html>`_, via `Python Academy <https://www.python-academy.com/>`_, **March 5th to 7th 2024** (3 day in-depth training), **Leipzig, Germany / Remote**
Also see :doc:`previous talks and blogposts <talks>`.

View File

@@ -335,7 +335,7 @@ For example:
.. literalinclude:: /example/fixtures/test_fixtures_order_dependencies.py
If we map out what depends on what, we get something that look like this:
If we map out what depends on what, we get something that looks like this:
.. image:: /example/fixtures/test_fixtures_order_dependencies.*
:align: center

File diff suppressed because it is too large Load Diff

View File

@@ -82,6 +82,8 @@ pytest.exit
pytest.main
~~~~~~~~~~~
**Tutorial**: :ref:`pytest.main-usage`
.. autofunction:: pytest.main
pytest.param
@@ -783,18 +785,66 @@ reporting or interaction with exceptions:
.. autofunction:: pytest_leave_pdb
Objects
-------
Collection tree objects
-----------------------
Full reference to objects accessible from :ref:`fixtures <fixture>` or :ref:`hooks <hook-reference>`.
These are the collector and item classes (collectively called "nodes") which
make up the collection tree.
Node
~~~~
CallInfo
~~~~~~~~
.. autoclass:: pytest.CallInfo()
.. autoclass:: _pytest.nodes.Node()
:members:
Collector
~~~~~~~~~
.. autoclass:: pytest.Collector()
:members:
:show-inheritance:
Item
~~~~
.. autoclass:: pytest.Item()
:members:
:show-inheritance:
File
~~~~
.. autoclass:: pytest.File()
:members:
:show-inheritance:
FSCollector
~~~~~~~~~~~
.. autoclass:: _pytest.nodes.FSCollector()
:members:
:show-inheritance:
Session
~~~~~~~
.. autoclass:: pytest.Session()
:members:
:show-inheritance:
Package
~~~~~~~
.. autoclass:: pytest.Package()
:members:
:show-inheritance:
Module
~~~~~~
.. autoclass:: pytest.Module()
:members:
:show-inheritance:
Class
~~~~~
@@ -803,13 +853,34 @@ Class
:members:
:show-inheritance:
Collector
~~~~~~~~~
Function
~~~~~~~~
.. autoclass:: pytest.Collector()
.. autoclass:: pytest.Function()
:members:
:show-inheritance:
FunctionDefinition
~~~~~~~~~~~~~~~~~~
.. autoclass:: _pytest.python.FunctionDefinition()
:members:
:show-inheritance:
Objects
-------
Objects accessible from :ref:`fixtures <fixture>` or :ref:`hooks <hook-reference>`
or importable from ``pytest``.
CallInfo
~~~~~~~~
.. autoclass:: pytest.CallInfo()
:members:
CollectReport
~~~~~~~~~~~~~
@@ -837,13 +908,6 @@ ExitCode
.. autoclass:: pytest.ExitCode
:members:
File
~~~~
.. autoclass:: pytest.File()
:members:
:show-inheritance:
FixtureDef
~~~~~~~~~~
@@ -852,34 +916,6 @@ FixtureDef
:members:
:show-inheritance:
FSCollector
~~~~~~~~~~~
.. autoclass:: _pytest.nodes.FSCollector()
:members:
:show-inheritance:
Function
~~~~~~~~
.. autoclass:: pytest.Function()
:members:
:show-inheritance:
FunctionDefinition
~~~~~~~~~~~~~~~~~~
.. autoclass:: _pytest.python.FunctionDefinition()
:members:
:show-inheritance:
Item
~~~~
.. autoclass:: pytest.Item()
:members:
:show-inheritance:
MarkDecorator
~~~~~~~~~~~~~
@@ -907,19 +943,6 @@ Metafunc
.. autoclass:: pytest.Metafunc()
:members:
Module
~~~~~~
.. autoclass:: pytest.Module()
:members:
:show-inheritance:
Node
~~~~
.. autoclass:: _pytest.nodes.Node()
:members:
Parser
~~~~~~
@@ -941,13 +964,6 @@ PytestPluginManager
:inherited-members:
:show-inheritance:
Session
~~~~~~~
.. autoclass:: pytest.Session()
:members:
:show-inheritance:
TestReport
~~~~~~~~~~
@@ -956,10 +972,16 @@ TestReport
:show-inheritance:
:inherited-members:
_Result
TestShortLogReport
~~~~~~~~~~~~~~~~~~
.. autoclass:: pytest.TestShortLogReport()
:members:
Result
~~~~~~~
Result object used within :ref:`hook wrappers <hookwrapper>`, see :py:class:`_Result in the pluggy documentation <pluggy._callers._Result>` for more information.
Result object used within :ref:`hook wrappers <hookwrapper>`, see :py:class:`Result in the pluggy documentation <pluggy.Result>` for more information.
Stash
~~~~~
@@ -1047,6 +1069,14 @@ Environment Variables
Environment variables that can be used to change pytest's behavior.
.. envvar:: CI
When set (regardless of value), pytest acknowledges that is running in a CI process. Alternative to ``BUILD_NUMBER`` variable.
.. envvar:: BUILD_NUMBER
When set (regardless of value), pytest acknowledges that is running in a CI process. Alternative to CI variable.
.. envvar:: PYTEST_ADDOPTS
This contains a command-line (parsed by the py:mod:`shlex` module) that will be **prepended** to the command line given
@@ -1212,6 +1242,7 @@ passed multiple times. The expected format is ``name=value``. For example::
* ``classic``: classic pytest output.
* ``progress``: like classic pytest output, but with a progress indicator.
* ``progress-even-when-capture-no``: allows the use of the progress indicator even when ``capture=no``.
* ``count``: like progress, but shows progress as the number of tests completed instead of a percent.
The default is ``progress``, but you can fallback to ``classic`` if you prefer or
@@ -1688,6 +1719,11 @@ passed multiple times. The expected format is ``name=value``. For example::
[pytest]
pythonpath = src1 src2
.. note::
``pythonpath`` does not affect some imports that happen very early,
most notably plugins loaded using the ``-p`` command line option.
.. confval:: required_plugins
@@ -1704,13 +1740,12 @@ passed multiple times. The expected format is ``name=value``. For example::
.. confval:: testpaths
Sets list of directories that should be searched for tests when
no specific directories, files or test ids are given in the command line when
executing pytest from the :ref:`rootdir <rootdir>` directory.
File system paths may use shell-style wildcards, including the recursive
``**`` pattern.
Useful when all project tests are in a known location to speed up
test collection and to avoid picking up undesired tests by accident.
@@ -1719,8 +1754,51 @@ passed multiple times. The expected format is ``name=value``. For example::
[pytest]
testpaths = testing doc
This tells pytest to only look for tests in ``testing`` and ``doc``
directories when executing from the root directory.
This configuration means that executing:
.. code-block:: console
pytest
has the same practical effects as executing:
.. code-block:: console
pytest testing doc
.. confval:: tmp_path_retention_count
How many sessions should we keep the `tmp_path` directories,
according to `tmp_path_retention_policy`.
.. code-block:: ini
[pytest]
tmp_path_retention_count = 3
Default: ``3``
.. confval:: tmp_path_retention_policy
Controls which directories created by the `tmp_path` fixture are kept around,
based on test outcome.
* `all`: retains directories for all tests, regardless of the outcome.
* `failed`: retains directories only for tests with outcome `error` or `failed`.
* `none`: directories are always removed after each test ends, regardless of the outcome.
.. code-block:: ini
[pytest]
tmp_path_retention_policy = "all"
Default: ``all``
.. confval:: usefixtures
@@ -1759,12 +1837,12 @@ All the command-line flags can be obtained by running ``pytest --help``::
$ pytest --help
usage: pytest [options] [file_or_dir] [file_or_dir] [...]
Positional arguments:
positional arguments:
file_or_dir
General:
general:
-k EXPRESSION Only run tests which match the given substring
expression. An expression is a python evaluatable
expression. An expression is a Python evaluatable
expression where all names are substring-matched
against test names and their parent classes.
Example: -k 'test_method or test_other' matches all
@@ -1778,9 +1856,9 @@ All the command-line flags can be obtained by running ``pytest --help``::
'extra_keyword_matches' set, as well as functions
which have names assigned directly to them. The
matching is case-insensitive.
-m MARKEXPR Only run tests matching given mark expression.
For example: -m 'mark1 and not mark2'.
--markers Show markers (builtin, plugin and per-project ones)
-m MARKEXPR Only run tests matching given mark expression. For
example: -m 'mark1 and not mark2'.
--markers show markers (builtin, plugin and per-project ones).
-x, --exitfirst Exit instantly on first error or failed test
--fixtures, --funcargs
Show available fixtures, sorted by plugin appearance
@@ -1790,18 +1868,18 @@ All the command-line flags can be obtained by running ``pytest --help``::
KeyboardInterrupt
--pdbcls=modulename:classname
Specify a custom interactive Python debugger for use
with --pdb. For example:
with --pdb.For example:
--pdbcls=IPython.terminal.debugger:TerminalPdb
--trace Immediately break when running each test
--capture=method Per-test capturing method: one of fd|sys|no|tee-sys.
-s Shortcut for --capture=no.
--capture=method Per-test capturing method: one of fd|sys|no|tee-sys
-s Shortcut for --capture=no
--runxfail Report the results of xfail tests as if they were
not marked
--lf, --last-failed Rerun only the tests that failed at the last run (or
all if none failed)
--ff, --failed-first Run all tests, but run the last failures first
This may re-order tests and thus lead to repeated
fixture setup/teardown
--ff, --failed-first Run all tests, but run the last failures first. This
may re-order tests and thus lead to repeated fixture
setup/teardown.
--nf, --new-first Run tests from new files first, then the rest of the
tests sorted by file mtime
--cache-show=[CACHESHOW]
@@ -1809,17 +1887,20 @@ All the command-line flags can be obtained by running ``pytest --help``::
tests. Optional argument: glob (default: '*').
--cache-clear Remove all cache contents at start of test run
--lfnf={all,none}, --last-failed-no-failures={all,none}
Which tests to run with no previously (known)
failures
With ``--lf``, determines whether to execute tests
when there are no previously (known) failures or
when no cached ``lastfailed`` data was found.
``all`` (the default) runs the full test suite
again. ``none`` just emits a message about no known
failures and exits successfully.
--sw, --stepwise Exit on test failure and continue from last failing
test next time
--sw-skip, --stepwise-skip
Ignore the first failing test but stop on the next
failing test.
implicitly enables --stepwise.
failing test. Implicitly enables --stepwise.
Reporting:
--durations=N show N slowest setup/test durations (N=0 for all)
--durations=N Show N slowest setup/test durations (N=0 for all)
--durations-min=N Minimal duration in seconds for inclusion in slowest
list. Default: 0.005.
-v, --verbose Increase verbosity
@@ -1836,8 +1917,10 @@ All the command-line flags can be obtained by running ``pytest --help``::
--disable-warnings, --disable-pytest-warnings
Disable warnings summary
-l, --showlocals Show locals in tracebacks (disabled by default)
--no-showlocals Hide locals in tracebacks (negate --showlocals
passed through addopts)
--tb=style Traceback print mode
(auto/long/short/line/native/no).
(auto/long/short/line/native/no)
--show-capture={no,stdout,stderr,log,all}
Controls how captured stdout/stderr/log is shown on
failed tests. Default: all.
@@ -1860,18 +1943,18 @@ All the command-line flags can be obtained by running ``pytest --help``::
--strict-markers Markers not registered in the `markers` section of
the configuration file raise errors
--strict (Deprecated) alias to --strict-markers
-c file Load configuration from `file` instead of trying to
locate one of the implicit configuration files
-c FILE, --config-file=FILE
Load configuration from `FILE` instead of trying to
locate one of the implicit configuration files.
--continue-on-collection-errors
Force test execution even if collection errors
occur
Force test execution even if collection errors occur
--rootdir=ROOTDIR Define root directory for tests. Can be relative
path: 'root_dir', './root_dir',
'root_dir/another_dir/'; absolute path:
'/home/user/root_dir'; path with variables:
'$HOME/root_dir'.
Collection:
collection:
--collect-only, --co Only collect tests, don't execute them
--pyargs Try to interpret all arguments as Python packages
--ignore=path Ignore path during collection (multi-allowed)
@@ -1899,27 +1982,24 @@ All the command-line flags can be obtained by running ``pytest --help``::
For a given doctest, continue to run after the first
failure
Test session debugging and configuration:
--basetemp=dir Base temporary directory for this test run. (Warning:
this directory is removed if it exists.)
test session debugging and configuration:
--basetemp=dir Base temporary directory for this test run.
(Warning: this directory is removed if it exists.)
-V, --version Display pytest version and information about
plugins. When given twice, also display information
about plugins.
-h, --help Show help message and configuration info
-p name Early-load given plugin module name or entry point
(multi-allowed)
To avoid loading of plugins, use the `no:` prefix,
e.g. `no:doctest`
(multi-allowed). To avoid loading of plugins, use
the `no:` prefix, e.g. `no:doctest`.
--trace-config Trace considerations of conftest.py files
--debug=[DEBUG_FILE_NAME]
Store internal tracing debug information in this log
file.
This file is opened with 'w' and truncated as a
result, care advised.
Default: pytestdebug.log.
file. This file is opened with 'w' and truncated as
a result, care advised. Default: pytestdebug.log.
-o OVERRIDE_INI, --override-ini=OVERRIDE_INI
Override ini option with "option=value" style, e.g.
`-o xfail_strict=True -o cache_dir=cache`
`-o xfail_strict=True -o cache_dir=cache`.
--assert=MODE Control assertion debugging tools.
'plain' performs no assertion debugging.
'rewrite' (the default) rewrites assert statements
@@ -1930,11 +2010,11 @@ All the command-line flags can be obtained by running ``pytest --help``::
--setup-plan Show what fixtures and tests would be executed but
don't execute anything
Logging:
--log-level=LEVEL Level of messages to catch/display.
Not set by default, so it depends on the root/parent
log handler's effective level, where it is "WARNING"
by default.
logging:
--log-level=LEVEL Level of messages to catch/display. Not set by
default, so it depends on the root/parent log
handler's effective level, where it is "WARNING" by
default.
--log-format=LOG_FORMAT
Log format used by the logging module
--log-date-format=LOG_DATE_FORMAT
@@ -1955,15 +2035,18 @@ All the command-line flags can be obtained by running ``pytest --help``::
--log-auto-indent=LOG_AUTO_INDENT
Auto-indent multiline messages passed to the logging
module. Accepts true|on, false|off or an integer.
--log-disable=LOGGER_DISABLE
Disable a logger by name. Can be passed multiple
times.
[pytest] ini-options in the first pytest.ini|tox.ini|setup.cfg file found:
[pytest] ini-options in the first pytest.ini|tox.ini|setup.cfg|pyproject.toml file found:
markers (linelist): Markers for test functions
empty_parameter_set_mark (string):
Default marker for empty parametersets
norecursedirs (args): Directory patterns to avoid for recursion
testpaths (args): Directories to search for tests when no files or
directories are given in the command line
directories are given on the command line
filterwarnings (linelist):
Each line specifies a pattern for
warnings.filterwarnings. Processed after
@@ -1984,9 +2067,18 @@ All the command-line flags can be obtained by running ``pytest --help``::
console_output_style (string):
Console output: "classic", or with additional
progress information ("progress" (percentage) |
"count")
"count" | "progress-even-when-capture-no" (forces
progress even when capture=no)
xfail_strict (bool): Default for the strict parameter of xfail markers
when not given explicitly (default: False)
tmp_path_retention_count (string):
How many sessions should we keep the `tmp_path`
directories, according to
`tmp_path_retention_policy`.
tmp_path_retention_policy (string):
Controls which directories created by the `tmp_path`
fixture are kept around, based on test outcome.
(all/failed/none)
enable_assertion_pass_hook (bool):
Enables the pytest_assertion_pass hook. Make sure to
delete any previously generated pyc cache files.

View File

@@ -1,7 +1,11 @@
pallets-sphinx-themes
pluggy>=1.0
pygments-pytest>=2.2.0
pygments-pytest>=2.3.0
sphinx-removed-in>=0.2.0
sphinx>=5,<6
sphinxcontrib-trio
sphinxcontrib-svg2pdfconverter
# Pin packaging because it no longer handles 'latest' version, which
# is the version that is assigned to the docs.
# See https://github.com/pytest-dev/pytest/pull/10578#issuecomment-1348249045.
packaging <22

View File

@@ -114,3 +114,8 @@ template = "changelog/_template.rst"
[tool.black]
target-version = ['py37']
# check-wheel-contents is executed by the build-and-inspect-python-package action.
[tool.check-wheel-contents]
# W009: Wheel contains multiple toplevel library entries
ignore = "W009"

View File

@@ -7,7 +7,9 @@ def main():
Platform agnostic wrapper script for towncrier.
Fixes the issue (#7251) where windows users are unable to natively run tox -e docs to build pytest docs.
"""
with open("doc/en/_changelog_towncrier_draft.rst", "w") as draft_file:
with open(
"doc/en/_changelog_towncrier_draft.rst", "w", encoding="utf-8"
) as draft_file:
return call(("towncrier", "--draft"), stdout=draft_file)

View File

@@ -13,11 +13,25 @@ from tqdm import tqdm
FILE_HEAD = r"""
.. _plugin-list:
Plugin List
===========
Pytest Plugin List
==================
Below is an automated compilation of ``pytest``` plugins available on `PyPI <https://pypi.org>`_.
It includes PyPI projects whose names begin with "pytest-" and a handful of manually selected projects.
Packages classified as inactive are excluded.
For detailed insights into how this list is generated,
please refer to `the update script <https://github.com/pytest-dev/pytest/blob/main/scripts/update-plugin-list.py>`_.
.. warning::
Please be aware that this list is not a curated collection of projects
and does not undergo a systematic review process.
It serves purely as an informational resource to aid in the discovery of ``pytest`` plugins.
Do not presume any endorsement from the ``pytest`` project or its developers,
and always conduct your own quality assessment before incorporating any of these plugins into your own projects.
PyPI projects that match "pytest-\*" are considered plugins and are listed
automatically. Packages classified as inactive are excluded.
.. The following conditional uses a different format for this list when
creating a PDF, because otherwise the table gets far too wide for the
@@ -33,6 +47,9 @@ DEVELOPMENT_STATUS_CLASSIFIERS = (
"Development Status :: 6 - Mature",
"Development Status :: 7 - Inactive",
)
ADDITIONAL_PROJECTS = { # set of additional projects to consider as plugins
"logassert",
}
def escape_rst(text: str) -> str:
@@ -52,18 +69,18 @@ def iter_plugins():
regex = r">([\d\w-]*)</a>"
response = requests.get("https://pypi.org/simple")
matches = list(
match
for match in re.finditer(regex, response.text)
if match.groups()[0].startswith("pytest-")
)
match_names = (match.groups()[0] for match in re.finditer(regex, response.text))
plugin_names = [
name
for name in match_names
if name.startswith("pytest-") or name in ADDITIONAL_PROJECTS
]
for match in tqdm(matches, smoothing=0):
name = match.groups()[0]
for name in tqdm(plugin_names, smoothing=0):
response = requests.get(f"https://pypi.org/pypi/{name}/json")
if response.status_code == 404:
# Some packages, like pytest-azurepipelines42, are included in https://pypi.org/simple but
# return 404 on the JSON API. Skip.
# Some packages, like pytest-azurepipelines42, are included in https://pypi.org/simple
# but return 404 on the JSON API. Skip.
continue
response.raise_for_status()
info = response.json()["info"]
@@ -78,11 +95,23 @@ def iter_plugins():
requires = "N/A"
if info["requires_dist"]:
for requirement in info["requires_dist"]:
if requirement == "pytest" or "pytest " in requirement:
if re.match(r"pytest(?![-.\w])", requirement):
requires = requirement
break
def version_sort_key(version_string):
"""
Return the sort key for the given version string
returned by the API.
"""
try:
return packaging.version.parse(version_string)
except packaging.version.InvalidVersion:
# Use a hard-coded pre-release version.
return packaging.version.Version("0.0.0alpha")
releases = response.json()["releases"]
for release in sorted(releases, key=packaging.version.parse, reverse=True):
for release in sorted(releases, key=version_sort_key, reverse=True):
if releases[release]:
release_date = datetime.date.fromisoformat(
releases[release][-1]["upload_time_iso_8601"].split("T")[0]
@@ -90,7 +119,9 @@ def iter_plugins():
last_release = release_date.strftime("%b %d, %Y")
break
name = f':pypi:`{info["name"]}`'
summary = escape_rst(info["summary"].replace("\n", ""))
summary = ""
if info["summary"]:
summary = escape_rst(info["summary"].replace("\n", ""))
yield {
"name": name,
"summary": summary.strip(),
@@ -122,7 +153,7 @@ def main():
reference_dir = pathlib.Path("doc", "en", "reference")
plugin_list = reference_dir / "plugin_list.rst"
with plugin_list.open("w") as f:
with plugin_list.open("w", encoding="UTF-8") as f:
f.write(FILE_HEAD)
f.write(f"This list contains {len(plugins)} plugins.\n\n")
f.write(".. only:: not latex\n\n")

View File

@@ -6,7 +6,7 @@ long_description_content_type = text/x-rst
url = https://docs.pytest.org/en/latest/
author = Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin and others
license = MIT
license_file = LICENSE
license_files = LICENSE
platforms = unix, linux, osx, cygwin, win32
classifiers =
Development Status :: 6 - Mature
@@ -21,6 +21,8 @@ classifiers =
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
Programming Language :: Python :: 3.12
Topic :: Software Development :: Libraries
Topic :: Software Development :: Testing
Topic :: Utilities
@@ -43,7 +45,6 @@ packages =
pytest
py_modules = py
install_requires =
attrs>=19.2.0
iniconfig
packaging
pluggy>=0.12,<2.0
@@ -67,11 +68,13 @@ console_scripts =
[options.extras_require]
testing =
argcomplete
attrs>=19.2.0
hypothesis>=3.56
mock
nose
pygments>=2.7.2
requests
setuptools
xmlschema
[options.package_data]
@@ -95,7 +98,6 @@ mypy_path = src
check_untyped_defs = True
disallow_any_generics = True
ignore_missing_imports = True
no_implicit_optional = True
show_error_codes = True
strict_equality = True
warn_redundant_casts = True

View File

@@ -78,15 +78,15 @@ class FastFilesCompleter:
def __call__(self, prefix: str, **kwargs: Any) -> List[str]:
# Only called on non option completions.
if os.path.sep in prefix[1:]:
prefix_dir = len(os.path.dirname(prefix) + os.path.sep)
if os.sep in prefix[1:]:
prefix_dir = len(os.path.dirname(prefix) + os.sep)
else:
prefix_dir = 0
completion = []
globbed = []
if "*" not in prefix and "?" not in prefix:
# We are on unix, otherwise no bash.
if not prefix or prefix[-1] == os.path.sep:
if not prefix or prefix[-1] == os.sep:
globbed.extend(glob(prefix + ".*"))
prefix += "*"
globbed.extend(glob(prefix))

View File

@@ -1,4 +1,5 @@
import ast
import dataclasses
import inspect
import os
import re
@@ -30,9 +31,7 @@ from typing import Type
from typing import TYPE_CHECKING
from typing import TypeVar
from typing import Union
from weakref import ref
import attr
import pluggy
import _pytest
@@ -50,9 +49,9 @@ from _pytest.pathlib import absolutepath
from _pytest.pathlib import bestrelpath
if TYPE_CHECKING:
from typing_extensions import Final
from typing_extensions import Literal
from typing_extensions import SupportsIndex
from weakref import ReferenceType
_TracebackStyle = Literal["long", "short", "line", "no", "native", "value", "auto"]
@@ -194,25 +193,25 @@ class Frame:
class TracebackEntry:
"""A single entry in a Traceback."""
__slots__ = ("_rawentry", "_excinfo", "_repr_style")
__slots__ = ("_rawentry", "_repr_style")
def __init__(
self,
rawentry: TracebackType,
excinfo: Optional["ReferenceType[ExceptionInfo[BaseException]]"] = None,
repr_style: Optional['Literal["short", "long"]'] = None,
) -> None:
self._rawentry = rawentry
self._excinfo = excinfo
self._repr_style: Optional['Literal["short", "long"]'] = None
self._rawentry: "Final" = rawentry
self._repr_style: "Final" = repr_style
def with_repr_style(
self, repr_style: Optional['Literal["short", "long"]']
) -> "TracebackEntry":
return TracebackEntry(self._rawentry, repr_style)
@property
def lineno(self) -> int:
return self._rawentry.tb_lineno - 1
def set_repr_style(self, mode: "Literal['short', 'long']") -> None:
assert mode in ("short", "long")
self._repr_style = mode
@property
def frame(self) -> Frame:
return Frame(self._rawentry.tb_frame)
@@ -272,7 +271,7 @@ class TracebackEntry:
source = property(getsource)
def ishidden(self) -> bool:
def ishidden(self, excinfo: Optional["ExceptionInfo[BaseException]"]) -> bool:
"""Return True if the current frame has a var __tracebackhide__
resolving to True.
@@ -296,7 +295,7 @@ class TracebackEntry:
else:
break
if tbh and callable(tbh):
return tbh(None if self._excinfo is None else self._excinfo())
return tbh(excinfo)
return tbh
def __str__(self) -> str:
@@ -329,16 +328,14 @@ class Traceback(List[TracebackEntry]):
def __init__(
self,
tb: Union[TracebackType, Iterable[TracebackEntry]],
excinfo: Optional["ReferenceType[ExceptionInfo[BaseException]]"] = None,
) -> None:
"""Initialize from given python traceback object and ExceptionInfo."""
self._excinfo = excinfo
if isinstance(tb, TracebackType):
def f(cur: TracebackType) -> Iterable[TracebackEntry]:
cur_: Optional[TracebackType] = cur
while cur_ is not None:
yield TracebackEntry(cur_, excinfo=excinfo)
yield TracebackEntry(cur_)
cur_ = cur_.tb_next
super().__init__(f(tb))
@@ -378,7 +375,7 @@ class Traceback(List[TracebackEntry]):
continue
if firstlineno is not None and x.frame.code.firstlineno != firstlineno:
continue
return Traceback(x._rawentry, self._excinfo)
return Traceback(x._rawentry)
return self
@overload
@@ -398,26 +395,27 @@ class Traceback(List[TracebackEntry]):
return super().__getitem__(key)
def filter(
self, fn: Callable[[TracebackEntry], bool] = lambda x: not x.ishidden()
self,
# TODO(py38): change to positional only.
_excinfo_or_fn: Union[
"ExceptionInfo[BaseException]",
Callable[[TracebackEntry], bool],
],
) -> "Traceback":
"""Return a Traceback instance with certain items removed
"""Return a Traceback instance with certain items removed.
fn is a function that gets a single argument, a TracebackEntry
instance, and should return True when the item should be added
to the Traceback, False when not.
If the filter is an `ExceptionInfo`, removes all the ``TracebackEntry``s
which are hidden (see ishidden() above).
By default this removes all the TracebackEntries which are hidden
(see ishidden() above).
Otherwise, the filter is a function that gets a single argument, a
``TracebackEntry`` instance, and should return True when the item should
be added to the ``Traceback``, False when not.
"""
return Traceback(filter(fn, self), self._excinfo)
def getcrashentry(self) -> TracebackEntry:
"""Return last non-hidden traceback entry that lead to the exception of a traceback."""
for i in range(-1, -len(self) - 1, -1):
entry = self[i]
if not entry.ishidden():
return entry
return self[-1]
if isinstance(_excinfo_or_fn, ExceptionInfo):
fn = lambda x: not x.ishidden(_excinfo_or_fn) # noqa: E731
else:
fn = _excinfo_or_fn
return Traceback(filter(fn, self))
def recursionindex(self) -> Optional[int]:
"""Return the index of the frame/TracebackEntry where recursion originates if
@@ -445,7 +443,7 @@ E = TypeVar("E", bound=BaseException, covariant=True)
@final
@attr.s(repr=False, init=False, auto_attribs=True)
@dataclasses.dataclass
class ExceptionInfo(Generic[E]):
"""Wraps sys.exc_info() objects and offers help for navigating the traceback."""
@@ -469,22 +467,41 @@ class ExceptionInfo(Generic[E]):
self._traceback = traceback
@classmethod
def from_exc_info(
def from_exception(
cls,
exc_info: Tuple[Type[E], E, TracebackType],
# Ignoring error: "Cannot use a covariant type variable as a parameter".
# This is OK to ignore because this class is (conceptually) readonly.
# See https://github.com/python/mypy/issues/7049.
exception: E, # type: ignore[misc]
exprinfo: Optional[str] = None,
) -> "ExceptionInfo[E]":
"""Return an ExceptionInfo for an existing exc_info tuple.
"""Return an ExceptionInfo for an existing exception.
.. warning::
Experimental API
The exception must have a non-``None`` ``__traceback__`` attribute,
otherwise this function fails with an assertion error. This means that
the exception must have been raised, or added a traceback with the
:py:meth:`~BaseException.with_traceback()` method.
:param exprinfo:
A text string helping to determine if we should strip
``AssertionError`` from the output. Defaults to the exception
message/``__str__()``.
.. versionadded:: 7.4
"""
assert (
exception.__traceback__
), "Exceptions passed to ExcInfo.from_exception(...) must have a non-None __traceback__."
exc_info = (type(exception), exception, exception.__traceback__)
return cls.from_exc_info(exc_info, exprinfo)
@classmethod
def from_exc_info(
cls,
exc_info: Tuple[Type[E], E, TracebackType],
exprinfo: Optional[str] = None,
) -> "ExceptionInfo[E]":
"""Like :func:`from_exception`, but using old-style exc_info tuple."""
_striptext = ""
if exprinfo is None and isinstance(exc_info[1], AssertionError):
exprinfo = getattr(exc_info[1], "msg", None)
@@ -563,7 +580,7 @@ class ExceptionInfo(Generic[E]):
def traceback(self) -> Traceback:
"""The traceback."""
if self._traceback is None:
self._traceback = Traceback(self.tb, excinfo=ref(self))
self._traceback = Traceback(self.tb)
return self._traceback
@traceback.setter
@@ -602,18 +619,25 @@ class ExceptionInfo(Generic[E]):
"""
return isinstance(self.value, exc)
def _getreprcrash(self) -> "ReprFileLocation":
exconly = self.exconly(tryshort=True)
entry = self.traceback.getcrashentry()
path, lineno = entry.frame.code.raw.co_filename, entry.lineno
return ReprFileLocation(path, lineno + 1, exconly)
def _getreprcrash(self) -> Optional["ReprFileLocation"]:
# Find last non-hidden traceback entry that led to the exception of the
# traceback, or None if all hidden.
for i in range(-1, -len(self.traceback) - 1, -1):
entry = self.traceback[i]
if not entry.ishidden(self):
path, lineno = entry.frame.code.raw.co_filename, entry.lineno
exconly = self.exconly(tryshort=True)
return ReprFileLocation(path, lineno + 1, exconly)
return None
def getrepr(
self,
showlocals: bool = False,
style: "_TracebackStyle" = "long",
abspath: bool = False,
tbfilter: bool = True,
tbfilter: Union[
bool, Callable[["ExceptionInfo[BaseException]"], Traceback]
] = True,
funcargs: bool = False,
truncate_locals: bool = True,
chain: bool = True,
@@ -625,14 +649,20 @@ class ExceptionInfo(Generic[E]):
Ignored if ``style=="native"``.
:param str style:
long|short|no|native|value traceback style.
long|short|line|no|native|value traceback style.
:param bool abspath:
If paths should be changed to absolute or left unchanged.
:param bool tbfilter:
Hide entries that contain a local variable ``__tracebackhide__==True``.
Ignored if ``style=="native"``.
:param tbfilter:
A filter for traceback entries.
* If false, don't hide any entries.
* If true, hide internal entries and entries that contain a local
variable ``__tracebackhide__ = True``.
* If a callable, delegates the filtering to the callable.
Ignored if ``style`` is ``"native"``.
:param bool funcargs:
Show fixtures ("funcargs" for legacy purposes) per traceback entry.
@@ -649,12 +679,14 @@ class ExceptionInfo(Generic[E]):
"""
if style == "native":
return ReprExceptionInfo(
ReprTracebackNative(
reprtraceback=ReprTracebackNative(
traceback.format_exception(
self.type, self.value, self.traceback[0]._rawentry
self.type,
self.value,
self.traceback[0]._rawentry if self.traceback else None,
)
),
self._getreprcrash(),
reprcrash=self._getreprcrash(),
)
fmt = FormattedExcinfo(
@@ -684,7 +716,7 @@ class ExceptionInfo(Generic[E]):
return True
@attr.s(auto_attribs=True)
@dataclasses.dataclass
class FormattedExcinfo:
"""Presenting information about failing Functions and Generators."""
@@ -695,12 +727,12 @@ class FormattedExcinfo:
showlocals: bool = False
style: "_TracebackStyle" = "long"
abspath: bool = True
tbfilter: bool = True
tbfilter: Union[bool, Callable[[ExceptionInfo[BaseException]], Traceback]] = True
funcargs: bool = False
truncate_locals: bool = True
chain: bool = True
astcache: Dict[Union[str, Path], ast.AST] = attr.ib(
factory=dict, init=False, repr=False
astcache: Dict[Union[str, Path], ast.AST] = dataclasses.field(
default_factory=dict, init=False, repr=False
)
def _getindent(self, source: "Source") -> int:
@@ -741,11 +773,13 @@ class FormattedExcinfo:
) -> List[str]:
"""Return formatted and marked up source lines."""
lines = []
if source is None or line_index >= len(source.lines):
if source is not None and line_index < 0:
line_index += len(source)
if source is None or line_index >= len(source.lines) or line_index < 0:
# `line_index` could still be outside `range(len(source.lines))` if
# we're processing AST with pathological position attributes.
source = Source("???")
line_index = 0
if line_index < 0:
line_index += len(source)
space_prefix = " "
if short:
lines.append(space_prefix + source.lines[line_index].strip())
@@ -805,12 +839,16 @@ class FormattedExcinfo:
def repr_traceback_entry(
self,
entry: TracebackEntry,
entry: Optional[TracebackEntry],
excinfo: Optional[ExceptionInfo[BaseException]] = None,
) -> "ReprEntry":
lines: List[str] = []
style = entry._repr_style if entry._repr_style is not None else self.style
if style in ("short", "long"):
style = (
entry._repr_style
if entry is not None and entry._repr_style is not None
else self.style
)
if style in ("short", "long") and entry is not None:
source = self._getentrysource(entry)
if source is None:
source = Source("???")
@@ -851,25 +889,31 @@ class FormattedExcinfo:
def repr_traceback(self, excinfo: ExceptionInfo[BaseException]) -> "ReprTraceback":
traceback = excinfo.traceback
if self.tbfilter:
traceback = traceback.filter()
if callable(self.tbfilter):
traceback = self.tbfilter(excinfo)
elif self.tbfilter:
traceback = traceback.filter(excinfo)
if isinstance(excinfo.value, RecursionError):
traceback, extraline = self._truncate_recursive_traceback(traceback)
else:
extraline = None
if not traceback:
if extraline is None:
extraline = "All traceback entries are hidden. Pass `--full-trace` to see hidden and internal frames."
entries = [self.repr_traceback_entry(None, excinfo)]
return ReprTraceback(entries, extraline, style=self.style)
last = traceback[-1]
entries = []
if self.style == "value":
reprentry = self.repr_traceback_entry(last, excinfo)
entries.append(reprentry)
entries = [self.repr_traceback_entry(last, excinfo)]
return ReprTraceback(entries, None, style=self.style)
for index, entry in enumerate(traceback):
einfo = (last == entry) and excinfo or None
reprentry = self.repr_traceback_entry(entry, einfo)
entries.append(reprentry)
entries = [
self.repr_traceback_entry(entry, excinfo if last == entry else None)
for entry in traceback
]
return ReprTraceback(entries, extraline, style=self.style)
def _truncate_recursive_traceback(
@@ -926,6 +970,7 @@ class FormattedExcinfo:
seen: Set[int] = set()
while e is not None and id(e) not in seen:
seen.add(id(e))
if excinfo_:
# Fall back to native traceback as a temporary workaround until
# full support for exception groups added to ExceptionInfo.
@@ -942,9 +987,7 @@ class FormattedExcinfo:
)
else:
reprtraceback = self.repr_traceback(excinfo_)
reprcrash: Optional[ReprFileLocation] = (
excinfo_._getreprcrash() if self.style != "value" else None
)
reprcrash = excinfo_._getreprcrash()
else:
# Fallback to native repr if the exception doesn't have a traceback:
# ExceptionInfo objects require a full traceback to work.
@@ -952,25 +995,17 @@ class FormattedExcinfo:
traceback.format_exception(type(e), e, None)
)
reprcrash = None
repr_chain += [(reprtraceback, reprcrash, descr)]
if e.__cause__ is not None and self.chain:
e = e.__cause__
excinfo_ = (
ExceptionInfo.from_exc_info((type(e), e, e.__traceback__))
if e.__traceback__
else None
)
excinfo_ = ExceptionInfo.from_exception(e) if e.__traceback__ else None
descr = "The above exception was the direct cause of the following exception:"
elif (
e.__context__ is not None and not e.__suppress_context__ and self.chain
):
e = e.__context__
excinfo_ = (
ExceptionInfo.from_exc_info((type(e), e, e.__traceback__))
if e.__traceback__
else None
)
excinfo_ = ExceptionInfo.from_exception(e) if e.__traceback__ else None
descr = "During handling of the above exception, another exception occurred:"
else:
e = None
@@ -978,7 +1013,7 @@ class FormattedExcinfo:
return ExceptionChainRepr(repr_chain)
@attr.s(eq=False, auto_attribs=True)
@dataclasses.dataclass(eq=False)
class TerminalRepr:
def __str__(self) -> str:
# FYI this is called from pytest-xdist's serialization of exception
@@ -996,14 +1031,14 @@ class TerminalRepr:
# This class is abstract -- only subclasses are instantiated.
@attr.s(eq=False)
@dataclasses.dataclass(eq=False)
class ExceptionRepr(TerminalRepr):
# Provided by subclasses.
reprcrash: Optional["ReprFileLocation"]
reprtraceback: "ReprTraceback"
def __attrs_post_init__(self) -> None:
self.sections: List[Tuple[str, str, str]] = []
reprcrash: Optional["ReprFileLocation"]
sections: List[Tuple[str, str, str]] = dataclasses.field(
init=False, default_factory=list
)
def addsection(self, name: str, content: str, sep: str = "-") -> None:
self.sections.append((name, content, sep))
@@ -1014,16 +1049,23 @@ class ExceptionRepr(TerminalRepr):
tw.line(content)
@attr.s(eq=False, auto_attribs=True)
@dataclasses.dataclass(eq=False)
class ExceptionChainRepr(ExceptionRepr):
chain: Sequence[Tuple["ReprTraceback", Optional["ReprFileLocation"], Optional[str]]]
def __attrs_post_init__(self) -> None:
super().__attrs_post_init__()
def __init__(
self,
chain: Sequence[
Tuple["ReprTraceback", Optional["ReprFileLocation"], Optional[str]]
],
) -> None:
# reprcrash and reprtraceback of the outermost (the newest) exception
# in the chain.
self.reprtraceback = self.chain[-1][0]
self.reprcrash = self.chain[-1][1]
super().__init__(
reprtraceback=chain[-1][0],
reprcrash=chain[-1][1],
)
self.chain = chain
def toterminal(self, tw: TerminalWriter) -> None:
for element in self.chain:
@@ -1034,17 +1076,17 @@ class ExceptionChainRepr(ExceptionRepr):
super().toterminal(tw)
@attr.s(eq=False, auto_attribs=True)
@dataclasses.dataclass(eq=False)
class ReprExceptionInfo(ExceptionRepr):
reprtraceback: "ReprTraceback"
reprcrash: "ReprFileLocation"
reprcrash: Optional["ReprFileLocation"]
def toterminal(self, tw: TerminalWriter) -> None:
self.reprtraceback.toterminal(tw)
super().toterminal(tw)
@attr.s(eq=False, auto_attribs=True)
@dataclasses.dataclass(eq=False)
class ReprTraceback(TerminalRepr):
reprentries: Sequence[Union["ReprEntry", "ReprEntryNative"]]
extraline: Optional[str]
@@ -1073,12 +1115,12 @@ class ReprTraceback(TerminalRepr):
class ReprTracebackNative(ReprTraceback):
def __init__(self, tblines: Sequence[str]) -> None:
self.style = "native"
self.reprentries = [ReprEntryNative(tblines)]
self.extraline = None
self.style = "native"
@attr.s(eq=False, auto_attribs=True)
@dataclasses.dataclass(eq=False)
class ReprEntryNative(TerminalRepr):
lines: Sequence[str]
@@ -1088,7 +1130,7 @@ class ReprEntryNative(TerminalRepr):
tw.write("".join(self.lines))
@attr.s(eq=False, auto_attribs=True)
@dataclasses.dataclass(eq=False)
class ReprEntry(TerminalRepr):
lines: Sequence[str]
reprfuncargs: Optional["ReprFuncArgs"]
@@ -1142,8 +1184,8 @@ class ReprEntry(TerminalRepr):
def toterminal(self, tw: TerminalWriter) -> None:
if self.style == "short":
assert self.reprfileloc is not None
self.reprfileloc.toterminal(tw)
if self.reprfileloc:
self.reprfileloc.toterminal(tw)
self._write_entry_lines(tw)
if self.reprlocals:
self.reprlocals.toterminal(tw, indent=" " * 8)
@@ -1168,12 +1210,15 @@ class ReprEntry(TerminalRepr):
)
@attr.s(eq=False, auto_attribs=True)
@dataclasses.dataclass(eq=False)
class ReprFileLocation(TerminalRepr):
path: str = attr.ib(converter=str)
path: str
lineno: int
message: str
def __post_init__(self) -> None:
self.path = str(self.path)
def toterminal(self, tw: TerminalWriter) -> None:
# Filename and lineno output for each entry, using an output format
# that most editors understand.
@@ -1185,7 +1230,7 @@ class ReprFileLocation(TerminalRepr):
tw.line(f":{self.lineno}: {msg}")
@attr.s(eq=False, auto_attribs=True)
@dataclasses.dataclass(eq=False)
class ReprLocals(TerminalRepr):
lines: Sequence[str]
@@ -1194,7 +1239,7 @@ class ReprLocals(TerminalRepr):
tw.line(indent + line)
@attr.s(eq=False, auto_attribs=True)
@dataclasses.dataclass(eq=False)
class ReprFuncArgs(TerminalRepr):
args: Sequence[Tuple[str, object]]

View File

@@ -24,6 +24,7 @@ from stat import S_ISLNK
from stat import S_ISREG
from typing import Any
from typing import Callable
from typing import cast
from typing import overload
from typing import TYPE_CHECKING
@@ -146,7 +147,7 @@ class Visitor:
self.fil = fil
self.ignore = ignore
self.breadthfirst = bf
self.optsort = sort and sorted or (lambda x: x)
self.optsort = cast(Callable[[Any], Any], sorted) if sort else (lambda x: x)
def gen(self, path):
try:
@@ -224,7 +225,7 @@ class Stat:
raise NotImplementedError("XXX win32")
import pwd
entry = error.checked_call(pwd.getpwuid, self.uid)
entry = error.checked_call(pwd.getpwuid, self.uid) # type:ignore[attr-defined]
return entry[0]
@property
@@ -234,7 +235,7 @@ class Stat:
raise NotImplementedError("XXX win32")
import grp
entry = error.checked_call(grp.getgrgid, self.gid)
entry = error.checked_call(grp.getgrgid, self.gid) # type:ignore[attr-defined]
return entry[0]
def isdir(self):
@@ -252,7 +253,7 @@ def getuserid(user):
import pwd
if not isinstance(user, int):
user = pwd.getpwnam(user)[2]
user = pwd.getpwnam(user)[2] # type:ignore[attr-defined]
return user
@@ -260,7 +261,7 @@ def getgroupid(group):
import grp
if not isinstance(group, int):
group = grp.getgrnam(group)[2]
group = grp.getgrnam(group)[2] # type:ignore[attr-defined]
return group
@@ -795,7 +796,7 @@ class LocalPath:
kw = {"exists": 1}
return Checkers(self)._evaluate(kw)
_patternchars = set("*?[" + os.path.sep)
_patternchars = set("*?[" + os.sep)
def listdir(self, fil=None, sort=None):
"""List directory contents, possibly filter by the given fil func
@@ -952,7 +953,7 @@ class LocalPath:
else:
p.dirpath()._ensuredirs()
if not p.check(file=1):
p.open("w").close()
p.open("wb").close()
return p
@overload
@@ -1127,7 +1128,7 @@ class LocalPath:
modfile = modfile[:-1]
elif modfile.endswith("$py.class"):
modfile = modfile[:-9] + ".py"
if modfile.endswith(os.path.sep + "__init__.py"):
if modfile.endswith(os.sep + "__init__.py"):
if self.basename != "__init__.py":
modfile = modfile[:-12]
try:

View File

@@ -44,10 +44,20 @@ from _pytest.stash import StashKey
if TYPE_CHECKING:
from _pytest.assertion import AssertionState
if sys.version_info >= (3, 8):
namedExpr = ast.NamedExpr
astNameConstant = ast.Constant
astStr = ast.Constant
astNum = ast.Constant
else:
namedExpr = ast.Expr
astNameConstant = ast.NameConstant
astStr = ast.Str
astNum = ast.Num
assertstate_key = StashKey["AssertionState"]()
# pytest caches rewritten pycs in pycache dirs
PYTEST_TAG = f"{sys.implementation.cache_tag}-pytest-{version}"
PYC_EXT = ".py" + (__debug__ and "c" or "o")
@@ -180,7 +190,7 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader)
for initial_path in self.session._initialpaths:
# Make something as c:/projects/my_project/path.py ->
# ['c:', 'projects', 'my_project', 'path.py']
parts = str(initial_path).split(os.path.sep)
parts = str(initial_path).split(os.sep)
# add 'path' to basenames to be checked.
self._basenames_to_check_rewrite.add(os.path.splitext(parts[-1])[0])
@@ -274,8 +284,12 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader)
return f.read()
if sys.version_info >= (3, 10):
if sys.version_info >= (3, 12):
from importlib.resources.abc import TraversableResources
else:
from importlib.abc import TraversableResources
def get_resource_reader(self, name: str) -> importlib.abc.TraversableResources: # type: ignore
def get_resource_reader(self, name: str) -> TraversableResources: # type: ignore
if sys.version_info < (3, 11):
from importlib.readers import FileReader
else:
@@ -631,8 +645,12 @@ class AssertionRewriter(ast.NodeVisitor):
.push_format_context() and .pop_format_context() which allows
to build another %-formatted string while already building one.
This state is reset on every new assert statement visited and used
by the other visitors.
:variables_overwrite: A dict filled with references to variables
that change value within an assert. This happens when a variable is
reassigned with the walrus operator
This state, except the variables_overwrite, is reset on every new assert
statement visited and used by the other visitors.
"""
def __init__(
@@ -648,6 +666,7 @@ class AssertionRewriter(ast.NodeVisitor):
else:
self.enable_assertion_pass_hook = False
self.source = source
self.variables_overwrite: Dict[str, str] = {}
def run(self, mod: ast.Module) -> None:
"""Find all assert statements in *mod* and rewrite them."""
@@ -662,14 +681,17 @@ class AssertionRewriter(ast.NodeVisitor):
if doc is not None and self.is_rewrite_disabled(doc):
return
pos = 0
lineno = 1
item = None
for item in mod.body:
if (
expect_docstring
and isinstance(item, ast.Expr)
and isinstance(item.value, ast.Str)
and isinstance(item.value, astStr)
):
doc = item.value.s
if sys.version_info >= (3, 8):
doc = item.value.value
else:
doc = item.value.s
if self.is_rewrite_disabled(doc):
return
expect_docstring = False
@@ -801,7 +823,7 @@ class AssertionRewriter(ast.NodeVisitor):
current = self.stack.pop()
if self.stack:
self.explanation_specifiers = self.stack[-1]
keys = [ast.Str(key) for key in current.keys()]
keys = [astStr(key) for key in current.keys()]
format_dict = ast.Dict(keys, list(current.values()))
form = ast.BinOp(expl_expr, ast.Mod(), format_dict)
name = "@py_format" + str(next(self.variable_counter))
@@ -855,16 +877,16 @@ class AssertionRewriter(ast.NodeVisitor):
negation = ast.UnaryOp(ast.Not(), top_condition)
if self.enable_assertion_pass_hook: # Experimental pytest_assertion_pass hook
msg = self.pop_format_context(ast.Str(explanation))
msg = self.pop_format_context(astStr(explanation))
# Failed
if assert_.msg:
assertmsg = self.helper("_format_assertmsg", assert_.msg)
gluestr = "\n>assert "
else:
assertmsg = ast.Str("")
assertmsg = astStr("")
gluestr = "assert "
err_explanation = ast.BinOp(ast.Str(gluestr), ast.Add(), msg)
err_explanation = ast.BinOp(astStr(gluestr), ast.Add(), msg)
err_msg = ast.BinOp(assertmsg, ast.Add(), err_explanation)
err_name = ast.Name("AssertionError", ast.Load())
fmt = self.helper("_format_explanation", err_msg)
@@ -880,8 +902,8 @@ class AssertionRewriter(ast.NodeVisitor):
hook_call_pass = ast.Expr(
self.helper(
"_call_assertion_pass",
ast.Num(assert_.lineno),
ast.Str(orig),
astNum(assert_.lineno),
astStr(orig),
fmt_pass,
)
)
@@ -900,7 +922,7 @@ class AssertionRewriter(ast.NodeVisitor):
variables = [
ast.Name(name, ast.Store()) for name in self.format_variables
]
clear_format = ast.Assign(variables, ast.NameConstant(None))
clear_format = ast.Assign(variables, astNameConstant(None))
self.statements.append(clear_format)
else: # Original assertion rewriting
@@ -911,9 +933,9 @@ class AssertionRewriter(ast.NodeVisitor):
assertmsg = self.helper("_format_assertmsg", assert_.msg)
explanation = "\n>assert " + explanation
else:
assertmsg = ast.Str("")
assertmsg = astStr("")
explanation = "assert " + explanation
template = ast.BinOp(assertmsg, ast.Add(), ast.Str(explanation))
template = ast.BinOp(assertmsg, ast.Add(), astStr(explanation))
msg = self.pop_format_context(template)
fmt = self.helper("_format_explanation", msg)
err_name = ast.Name("AssertionError", ast.Load())
@@ -925,7 +947,7 @@ class AssertionRewriter(ast.NodeVisitor):
# Clear temporary variables by setting them to None.
if self.variables:
variables = [ast.Name(name, ast.Store()) for name in self.variables]
clear = ast.Assign(variables, ast.NameConstant(None))
clear = ast.Assign(variables, astNameConstant(None))
self.statements.append(clear)
# Fix locations (line numbers/column offsets).
for stmt in self.statements:
@@ -933,14 +955,26 @@ class AssertionRewriter(ast.NodeVisitor):
ast.copy_location(node, assert_)
return self.statements
def visit_NamedExpr(self, name: namedExpr) -> Tuple[namedExpr, str]:
# This method handles the 'walrus operator' repr of the target
# name if it's a local variable or _should_repr_global_name()
# thinks it's acceptable.
locs = ast.Call(self.builtin("locals"), [], [])
target_id = name.target.id # type: ignore[attr-defined]
inlocs = ast.Compare(astStr(target_id), [ast.In()], [locs])
dorepr = self.helper("_should_repr_global_name", name)
test = ast.BoolOp(ast.Or(), [inlocs, dorepr])
expr = ast.IfExp(test, self.display(name), astStr(target_id))
return name, self.explanation_param(expr)
def visit_Name(self, name: ast.Name) -> Tuple[ast.Name, str]:
# Display the repr of the name if it's a local variable or
# _should_repr_global_name() thinks it's acceptable.
locs = ast.Call(self.builtin("locals"), [], [])
inlocs = ast.Compare(ast.Str(name.id), [ast.In()], [locs])
inlocs = ast.Compare(astStr(name.id), [ast.In()], [locs])
dorepr = self.helper("_should_repr_global_name", name)
test = ast.BoolOp(ast.Or(), [inlocs, dorepr])
expr = ast.IfExp(test, self.display(name), ast.Str(name.id))
expr = ast.IfExp(test, self.display(name), astStr(name.id))
return name, self.explanation_param(expr)
def visit_BoolOp(self, boolop: ast.BoolOp) -> Tuple[ast.Name, str]:
@@ -959,10 +993,26 @@ class AssertionRewriter(ast.NodeVisitor):
# cond is set in a prior loop iteration below
self.expl_stmts.append(ast.If(cond, fail_inner, [])) # noqa
self.expl_stmts = fail_inner
# Check if the left operand is a namedExpr and the value has already been visited
if (
isinstance(v, ast.Compare)
and isinstance(v.left, namedExpr)
and v.left.target.id
in [
ast_expr.id
for ast_expr in boolop.values[:i]
if hasattr(ast_expr, "id")
]
):
pytest_temp = self.variable()
self.variables_overwrite[
v.left.target.id
] = v.left # type:ignore[assignment]
v.left.target.id = pytest_temp
self.push_format_context()
res, expl = self.visit(v)
body.append(ast.Assign([ast.Name(res_var, ast.Store())], res))
expl_format = self.pop_format_context(ast.Str(expl))
expl_format = self.pop_format_context(astStr(expl))
call = ast.Call(app, [expl_format], [])
self.expl_stmts.append(ast.Expr(call))
if i < levels:
@@ -974,7 +1024,7 @@ class AssertionRewriter(ast.NodeVisitor):
self.statements = body = inner
self.statements = save
self.expl_stmts = fail_save
expl_template = self.helper("_format_boolop", expl_list, ast.Num(is_or))
expl_template = self.helper("_format_boolop", expl_list, astNum(is_or))
expl = self.pop_format_context(expl_template)
return ast.Name(res_var, ast.Load()), self.explanation_param(expl)
@@ -998,10 +1048,19 @@ class AssertionRewriter(ast.NodeVisitor):
new_args = []
new_kwargs = []
for arg in call.args:
if isinstance(arg, ast.Name) and arg.id in self.variables_overwrite:
arg = self.variables_overwrite[arg.id] # type:ignore[assignment]
res, expl = self.visit(arg)
arg_expls.append(expl)
new_args.append(res)
for keyword in call.keywords:
if (
isinstance(keyword.value, ast.Name)
and keyword.value.id in self.variables_overwrite
):
keyword.value = self.variables_overwrite[
keyword.value.id
] # type:ignore[assignment]
res, expl = self.visit(keyword.value)
new_kwargs.append(ast.keyword(keyword.arg, res))
if keyword.arg:
@@ -1034,6 +1093,15 @@ class AssertionRewriter(ast.NodeVisitor):
def visit_Compare(self, comp: ast.Compare) -> Tuple[ast.expr, str]:
self.push_format_context()
# We first check if we have overwritten a variable in the previous assert
if isinstance(comp.left, ast.Name) and comp.left.id in self.variables_overwrite:
comp.left = self.variables_overwrite[
comp.left.id
] # type:ignore[assignment]
if isinstance(comp.left, namedExpr):
self.variables_overwrite[
comp.left.target.id
] = comp.left # type:ignore[assignment]
left_res, left_expl = self.visit(comp.left)
if isinstance(comp.left, (ast.Compare, ast.BoolOp)):
left_expl = f"({left_expl})"
@@ -1045,14 +1113,23 @@ class AssertionRewriter(ast.NodeVisitor):
syms = []
results = [left_res]
for i, op, next_operand in it:
if (
isinstance(next_operand, namedExpr)
and isinstance(left_res, ast.Name)
and next_operand.target.id == left_res.id
):
next_operand.target.id = self.variable()
self.variables_overwrite[
left_res.id
] = next_operand # type:ignore[assignment]
next_res, next_expl = self.visit(next_operand)
if isinstance(next_operand, (ast.Compare, ast.BoolOp)):
next_expl = f"({next_expl})"
results.append(next_res)
sym = BINOP_MAP[op.__class__]
syms.append(ast.Str(sym))
syms.append(astStr(sym))
expl = f"{left_expl} {sym} {next_expl}"
expls.append(ast.Str(expl))
expls.append(astStr(expl))
res_expr = ast.Compare(left_res, [op], [next_res])
self.statements.append(ast.Assign([store_names[i]], res_expr))
left_res, left_expl = next_res, next_expl
@@ -1068,6 +1145,7 @@ class AssertionRewriter(ast.NodeVisitor):
res: ast.expr = ast.BoolOp(ast.And(), load_names)
else:
res = load_names[0]
return res, self.explanation_param(self.pop_format_context(expl_call))

View File

@@ -38,9 +38,9 @@ def _truncate_explanation(
"""Truncate given list of strings that makes up the assertion explanation.
Truncates to either 8 lines, or 640 characters - whichever the input reaches
first. The remaining lines will be replaced by a usage message.
first, taking the truncation explanation into account. The remaining lines
will be replaced by a usage message.
"""
if max_lines is None:
max_lines = DEFAULT_MAX_LINES
if max_chars is None:
@@ -48,35 +48,56 @@ def _truncate_explanation(
# Check if truncation required
input_char_count = len("".join(input_lines))
if len(input_lines) <= max_lines and input_char_count <= max_chars:
# The length of the truncation explanation depends on the number of lines
# removed but is at least 68 characters:
# The real value is
# 64 (for the base message:
# '...\n...Full output truncated (1 line hidden), use '-vv' to show")'
# )
# + 1 (for plural)
# + int(math.log10(len(input_lines) - max_lines)) (number of hidden line, at least 1)
# + 3 for the '...' added to the truncated line
# But if there's more than 100 lines it's very likely that we're going to
# truncate, so we don't need the exact value using log10.
tolerable_max_chars = (
max_chars + 70 # 64 + 1 (for plural) + 2 (for '99') + 3 for '...'
)
# The truncation explanation add two lines to the output
tolerable_max_lines = max_lines + 2
if (
len(input_lines) <= tolerable_max_lines
and input_char_count <= tolerable_max_chars
):
return input_lines
# Truncate first to max_lines, and then truncate to max_chars if max_chars
# is exceeded.
# Truncate first to max_lines, and then truncate to max_chars if necessary
truncated_explanation = input_lines[:max_lines]
truncated_explanation = _truncate_by_char_count(truncated_explanation, max_chars)
# Add ellipsis to final line
truncated_explanation[-1] = truncated_explanation[-1] + "..."
# Append useful message to explanation
truncated_line_count = len(input_lines) - len(truncated_explanation)
truncated_line_count += 1 # Account for the part-truncated final line
msg = "...Full output truncated"
if truncated_line_count == 1:
msg += f" ({truncated_line_count} line hidden)"
truncated_char = True
# We reevaluate the need to truncate chars following removal of some lines
if len("".join(truncated_explanation)) > tolerable_max_chars:
truncated_explanation = _truncate_by_char_count(
truncated_explanation, max_chars
)
else:
msg += f" ({truncated_line_count} lines hidden)"
msg += f", {USAGE_MSG}"
truncated_explanation.extend(["", str(msg)])
return truncated_explanation
truncated_char = False
truncated_line_count = len(input_lines) - len(truncated_explanation)
if truncated_explanation[-1]:
# Add ellipsis and take into account part-truncated final line
truncated_explanation[-1] = truncated_explanation[-1] + "..."
if truncated_char:
# It's possible that we did not remove any char from this line
truncated_line_count += 1
else:
# Add proper ellipsis when we were able to fit a full line exactly
truncated_explanation[-1] = "..."
return truncated_explanation + [
"",
f"...Full output truncated ({truncated_line_count} line"
f"{'' if truncated_line_count == 1 else 's'} hidden), {USAGE_MSG}",
]
def _truncate_by_char_count(input_lines: List[str], max_chars: int) -> List[str]:
# Check if truncation required
if len("".join(input_lines)) <= max_chars:
return input_lines
# Find point at which input length exceeds total allowed length
iterated_char_count = 0
for iterated_index, input_line in enumerate(input_lines):

View File

@@ -1,6 +1,7 @@
"""Implementation of the cache provider."""
# This plugin was not named "cache" to avoid conflicts with the external
# pytest-cache version.
import dataclasses
import json
import os
from pathlib import Path
@@ -12,8 +13,6 @@ from typing import Optional
from typing import Set
from typing import Union
import attr
from .pathlib import resolve_from_str
from .pathlib import rm_rf
from .reports import CollectReport
@@ -28,11 +27,10 @@ from _pytest.deprecated import check_ispytest
from _pytest.fixtures import fixture
from _pytest.fixtures import FixtureRequest
from _pytest.main import Session
from _pytest.python import Module
from _pytest.nodes import File
from _pytest.python import Package
from _pytest.reports import TestReport
README_CONTENT = """\
# pytest cache directory #
@@ -53,10 +51,12 @@ Signature: 8a477f597d28d172789f06886806bc55
@final
@attr.s(init=False, auto_attribs=True)
@dataclasses.dataclass
class Cache:
_cachedir: Path = attr.ib(repr=False)
_config: Config = attr.ib(repr=False)
"""Instance of the `cache` fixture."""
_cachedir: Path = dataclasses.field(repr=False)
_config: Config = dataclasses.field(repr=False)
# Sub-directory under cache-dir for directories created by `mkdir()`.
_CACHE_PREFIX_DIRS = "d"
@@ -179,16 +179,22 @@ class Cache:
else:
cache_dir_exists_already = self._cachedir.exists()
path.parent.mkdir(exist_ok=True, parents=True)
except OSError:
self.warn("could not create cache path {path}", path=path, _ispytest=True)
except OSError as exc:
self.warn(
f"could not create cache path {path}: {exc}",
_ispytest=True,
)
return
if not cache_dir_exists_already:
self._ensure_supporting_files()
data = json.dumps(value, ensure_ascii=False, indent=2)
try:
f = path.open("w", encoding="UTF-8")
except OSError:
self.warn("cache could not write path {path}", path=path, _ispytest=True)
except OSError as exc:
self.warn(
f"cache could not write path {path}: {exc}",
_ispytest=True,
)
else:
with f:
f.write(data)
@@ -213,22 +219,30 @@ class LFPluginCollWrapper:
@hookimpl(hookwrapper=True)
def pytest_make_collect_report(self, collector: nodes.Collector):
if isinstance(collector, Session):
if isinstance(collector, (Session, Package)):
out = yield
res: CollectReport = out.get_result()
# Sort any lf-paths to the beginning.
lf_paths = self.lfplugin._last_failed_paths
# Use stable sort to priorize last failed.
def sort_key(node: Union[nodes.Item, nodes.Collector]) -> bool:
# Package.path is the __init__.py file, we need the directory.
if isinstance(node, Package):
path = node.path.parent
else:
path = node.path
return path in lf_paths
res.result = sorted(
res.result,
# use stable sort to priorize last failed
key=lambda x: x.path in lf_paths,
key=sort_key,
reverse=True,
)
return
elif isinstance(collector, Module):
elif isinstance(collector, File):
if collector.path in self.lfplugin._last_failed_paths:
out = yield
res = out.get_result()
@@ -266,10 +280,9 @@ class LFPluginCollSkipfiles:
def pytest_make_collect_report(
self, collector: nodes.Collector
) -> Optional[CollectReport]:
# Packages are Modules, but _last_failed_paths only contains
# test-bearing paths and doesn't try to include the paths of their
# packages, so don't filter them.
if isinstance(collector, Module) and not isinstance(collector, Package):
# Packages are Files, but we only want to skip test-bearing Files,
# so don't filter Packages.
if isinstance(collector, File) and not isinstance(collector, Package):
if collector.path not in self.lfplugin._last_failed_paths:
self.lfplugin._skipped_files += 1
@@ -299,9 +312,14 @@ class LFPlugin:
)
def get_last_failed_paths(self) -> Set[Path]:
"""Return a set with all Paths()s of the previously failed nodeids."""
"""Return a set with all Paths of the previously failed nodeids and
their parents."""
rootpath = self.config.rootpath
result = {rootpath / nodeid.split("::")[0] for nodeid in self.lastfailed}
result = set()
for nodeid in self.lastfailed:
path = rootpath / nodeid.split("::")[0]
result.add(path)
result.update(path.parents)
return {x for x in result if x.exists()}
def pytest_report_collectionfinish(self) -> Optional[str]:
@@ -487,12 +505,16 @@ def pytest_addoption(parser: Parser) -> None:
dest="last_failed_no_failures",
choices=("all", "none"),
default="all",
help="Which tests to run with no previously (known) failures",
help="With ``--lf``, determines whether to execute tests when there "
"are no previously (known) failures or when no "
"cached ``lastfailed`` data was found. "
"``all`` (the default) runs the full test suite again. "
"``none`` just emits a message about no known failures and exits successfully.",
)
def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]:
if config.option.cacheshow:
if config.option.cacheshow and not config.option.help:
from _pytest.main import wrap_session
return wrap_session(config, cacheshow)

View File

@@ -1,19 +1,26 @@
"""Per-test stdout/stderr capturing mechanism."""
import abc
import collections
import contextlib
import functools
import io
import os
import sys
from io import UnsupportedOperation
from tempfile import TemporaryFile
from types import TracebackType
from typing import Any
from typing import AnyStr
from typing import BinaryIO
from typing import Generator
from typing import Generic
from typing import Iterable
from typing import Iterator
from typing import List
from typing import NamedTuple
from typing import Optional
from typing import TextIO
from typing import Tuple
from typing import Type
from typing import TYPE_CHECKING
from typing import Union
@@ -29,6 +36,7 @@ from _pytest.nodes import File
from _pytest.nodes import Item
if TYPE_CHECKING:
from typing_extensions import Final
from typing_extensions import Literal
_CaptureMethod = Literal["fd", "sys", "no", "tee-sys"]
@@ -185,19 +193,27 @@ class TeeCaptureIO(CaptureIO):
return self._other.write(s)
class DontReadFromInput:
encoding = None
class DontReadFromInput(TextIO):
@property
def encoding(self) -> str:
return sys.__stdin__.encoding
def read(self, *args):
def read(self, size: int = -1) -> str:
raise OSError(
"pytest: reading from stdin while output is captured! Consider using `-s`."
)
readline = read
readlines = read
__next__ = read
def __iter__(self):
def __next__(self) -> str:
return self.readline()
def readlines(self, hint: Optional[int] = -1) -> List[str]:
raise OSError(
"pytest: reading from stdin while output is captured! Consider using `-s`."
)
def __iter__(self) -> Iterator[str]:
return self
def fileno(self) -> int:
@@ -215,7 +231,7 @@ class DontReadFromInput:
def readable(self) -> bool:
return False
def seek(self, offset: int) -> int:
def seek(self, offset: int, whence: int = 0) -> int:
raise UnsupportedOperation("redirected stdin is pseudofile, has no seek(int)")
def seekable(self) -> bool:
@@ -224,41 +240,104 @@ class DontReadFromInput:
def tell(self) -> int:
raise UnsupportedOperation("redirected stdin is pseudofile, has no tell()")
def truncate(self, size: int) -> None:
raise UnsupportedOperation("cannont truncate stdin")
def truncate(self, size: Optional[int] = None) -> int:
raise UnsupportedOperation("cannot truncate stdin")
def write(self, *args) -> None:
def write(self, data: str) -> int:
raise UnsupportedOperation("cannot write to stdin")
def writelines(self, *args) -> None:
def writelines(self, lines: Iterable[str]) -> None:
raise UnsupportedOperation("Cannot write to stdin")
def writable(self) -> bool:
return False
@property
def buffer(self):
def __enter__(self) -> "DontReadFromInput":
return self
def __exit__(
self,
type: Optional[Type[BaseException]],
value: Optional[BaseException],
traceback: Optional[TracebackType],
) -> None:
pass
@property
def buffer(self) -> BinaryIO:
# The str/bytes doesn't actually matter in this type, so OK to fake.
return self # type: ignore[return-value]
# Capture classes.
class CaptureBase(abc.ABC, Generic[AnyStr]):
EMPTY_BUFFER: AnyStr
@abc.abstractmethod
def __init__(self, fd: int) -> None:
raise NotImplementedError()
@abc.abstractmethod
def start(self) -> None:
raise NotImplementedError()
@abc.abstractmethod
def done(self) -> None:
raise NotImplementedError()
@abc.abstractmethod
def suspend(self) -> None:
raise NotImplementedError()
@abc.abstractmethod
def resume(self) -> None:
raise NotImplementedError()
@abc.abstractmethod
def writeorg(self, data: AnyStr) -> None:
raise NotImplementedError()
@abc.abstractmethod
def snap(self) -> AnyStr:
raise NotImplementedError()
patchsysdict = {0: "stdin", 1: "stdout", 2: "stderr"}
class NoCapture:
EMPTY_BUFFER = None
__init__ = start = done = suspend = resume = lambda *args: None
class NoCapture(CaptureBase[str]):
EMPTY_BUFFER = ""
def __init__(self, fd: int) -> None:
pass
def start(self) -> None:
pass
def done(self) -> None:
pass
def suspend(self) -> None:
pass
def resume(self) -> None:
pass
def snap(self) -> str:
return ""
def writeorg(self, data: str) -> None:
pass
class SysCaptureBinary:
EMPTY_BUFFER = b""
def __init__(self, fd: int, tmpfile=None, *, tee: bool = False) -> None:
class SysCaptureBase(CaptureBase[AnyStr]):
def __init__(
self, fd: int, tmpfile: Optional[TextIO] = None, *, tee: bool = False
) -> None:
name = patchsysdict[fd]
self._old = getattr(sys, name)
self._old: TextIO = getattr(sys, name)
self.name = name
if tmpfile is None:
if name == "stdin":
@@ -298,14 +377,6 @@ class SysCaptureBinary:
setattr(sys, self.name, self.tmpfile)
self._state = "started"
def snap(self):
self._assert_state("snap", ("started", "suspended"))
self.tmpfile.seek(0)
res = self.tmpfile.buffer.read()
self.tmpfile.seek(0)
self.tmpfile.truncate()
return res
def done(self) -> None:
self._assert_state("done", ("initialized", "started", "suspended", "done"))
if self._state == "done":
@@ -327,36 +398,43 @@ class SysCaptureBinary:
setattr(sys, self.name, self.tmpfile)
self._state = "started"
def writeorg(self, data) -> None:
class SysCaptureBinary(SysCaptureBase[bytes]):
EMPTY_BUFFER = b""
def snap(self) -> bytes:
self._assert_state("snap", ("started", "suspended"))
self.tmpfile.seek(0)
res = self.tmpfile.buffer.read()
self.tmpfile.seek(0)
self.tmpfile.truncate()
return res
def writeorg(self, data: bytes) -> None:
self._assert_state("writeorg", ("started", "suspended"))
self._old.flush()
self._old.buffer.write(data)
self._old.buffer.flush()
class SysCapture(SysCaptureBinary):
EMPTY_BUFFER = "" # type: ignore[assignment]
class SysCapture(SysCaptureBase[str]):
EMPTY_BUFFER = ""
def snap(self):
def snap(self) -> str:
self._assert_state("snap", ("started", "suspended"))
assert isinstance(self.tmpfile, CaptureIO)
res = self.tmpfile.getvalue()
self.tmpfile.seek(0)
self.tmpfile.truncate()
return res
def writeorg(self, data):
def writeorg(self, data: str) -> None:
self._assert_state("writeorg", ("started", "suspended"))
self._old.write(data)
self._old.flush()
class FDCaptureBinary:
"""Capture IO to/from a given OS-level file descriptor.
snap() produces `bytes`.
"""
EMPTY_BUFFER = b""
class FDCaptureBase(CaptureBase[AnyStr]):
def __init__(self, targetfd: int) -> None:
self.targetfd = targetfd
@@ -382,7 +460,7 @@ class FDCaptureBinary:
if targetfd == 0:
self.tmpfile = open(os.devnull, encoding="utf-8")
self.syscapture = SysCapture(targetfd)
self.syscapture: CaptureBase[str] = SysCapture(targetfd)
else:
self.tmpfile = EncodedFile(
TemporaryFile(buffering=0),
@@ -394,7 +472,7 @@ class FDCaptureBinary:
if targetfd in patchsysdict:
self.syscapture = SysCapture(targetfd, self.tmpfile)
else:
self.syscapture = NoCapture()
self.syscapture = NoCapture(targetfd)
self._state = "initialized"
@@ -421,14 +499,6 @@ class FDCaptureBinary:
self.syscapture.start()
self._state = "started"
def snap(self):
self._assert_state("snap", ("started", "suspended"))
self.tmpfile.seek(0)
res = self.tmpfile.buffer.read()
self.tmpfile.seek(0)
self.tmpfile.truncate()
return res
def done(self) -> None:
"""Stop capturing, restore streams, return original capture file,
seeked to position zero."""
@@ -461,22 +531,38 @@ class FDCaptureBinary:
os.dup2(self.tmpfile.fileno(), self.targetfd)
self._state = "started"
def writeorg(self, data):
class FDCaptureBinary(FDCaptureBase[bytes]):
"""Capture IO to/from a given OS-level file descriptor.
snap() produces `bytes`.
"""
EMPTY_BUFFER = b""
def snap(self) -> bytes:
self._assert_state("snap", ("started", "suspended"))
self.tmpfile.seek(0)
res = self.tmpfile.buffer.read()
self.tmpfile.seek(0)
self.tmpfile.truncate()
return res
def writeorg(self, data: bytes) -> None:
"""Write to original file descriptor."""
self._assert_state("writeorg", ("started", "suspended"))
os.write(self.targetfd_save, data)
class FDCapture(FDCaptureBinary):
class FDCapture(FDCaptureBase[str]):
"""Capture IO to/from a given OS-level file descriptor.
snap() produces text.
"""
# Ignore type because it doesn't match the type in the superclass (bytes).
EMPTY_BUFFER = "" # type: ignore
EMPTY_BUFFER = ""
def snap(self):
def snap(self) -> str:
self._assert_state("snap", ("started", "suspended"))
self.tmpfile.seek(0)
res = self.tmpfile.read()
@@ -484,77 +570,49 @@ class FDCapture(FDCaptureBinary):
self.tmpfile.truncate()
return res
def writeorg(self, data):
def writeorg(self, data: str) -> None:
"""Write to original file descriptor."""
super().writeorg(data.encode("utf-8")) # XXX use encoding of original stream
self._assert_state("writeorg", ("started", "suspended"))
# XXX use encoding of original stream
os.write(self.targetfd_save, data.encode("utf-8"))
# MultiCapture
# This class was a namedtuple, but due to mypy limitation[0] it could not be
# made generic, so was replaced by a regular class which tries to emulate the
# pertinent parts of a namedtuple. If the mypy limitation is ever lifted, can
# make it a namedtuple again.
# [0]: https://github.com/python/mypy/issues/685
@final
@functools.total_ordering
class CaptureResult(Generic[AnyStr]):
"""The result of :method:`CaptureFixture.readouterr`."""
# Generic NamedTuple only supported since Python 3.11.
if sys.version_info >= (3, 11) or TYPE_CHECKING:
__slots__ = ("out", "err")
@final
class CaptureResult(NamedTuple, Generic[AnyStr]):
"""The result of :method:`CaptureFixture.readouterr`."""
def __init__(self, out: AnyStr, err: AnyStr) -> None:
self.out: AnyStr = out
self.err: AnyStr = err
out: AnyStr
err: AnyStr
def __len__(self) -> int:
return 2
else:
def __iter__(self) -> Iterator[AnyStr]:
return iter((self.out, self.err))
class CaptureResult(
collections.namedtuple("CaptureResult", ["out", "err"]), Generic[AnyStr]
):
"""The result of :method:`CaptureFixture.readouterr`."""
def __getitem__(self, item: int) -> AnyStr:
return tuple(self)[item]
def _replace(
self, *, out: Optional[AnyStr] = None, err: Optional[AnyStr] = None
) -> "CaptureResult[AnyStr]":
return CaptureResult(
out=self.out if out is None else out, err=self.err if err is None else err
)
def count(self, value: AnyStr) -> int:
return tuple(self).count(value)
def index(self, value) -> int:
return tuple(self).index(value)
def __eq__(self, other: object) -> bool:
if not isinstance(other, (CaptureResult, tuple)):
return NotImplemented
return tuple(self) == tuple(other)
def __hash__(self) -> int:
return hash(tuple(self))
def __lt__(self, other: object) -> bool:
if not isinstance(other, (CaptureResult, tuple)):
return NotImplemented
return tuple(self) < tuple(other)
def __repr__(self) -> str:
return f"CaptureResult(out={self.out!r}, err={self.err!r})"
__slots__ = ()
class MultiCapture(Generic[AnyStr]):
_state = None
_in_suspended = False
def __init__(self, in_, out, err) -> None:
self.in_ = in_
self.out = out
self.err = err
def __init__(
self,
in_: Optional[CaptureBase[AnyStr]],
out: Optional[CaptureBase[AnyStr]],
err: Optional[CaptureBase[AnyStr]],
) -> None:
self.in_: Optional[CaptureBase[AnyStr]] = in_
self.out: Optional[CaptureBase[AnyStr]] = out
self.err: Optional[CaptureBase[AnyStr]] = err
def __repr__(self) -> str:
return "<MultiCapture out={!r} err={!r} in_={!r} _state={!r} _in_suspended={!r}>".format(
@@ -578,8 +636,10 @@ class MultiCapture(Generic[AnyStr]):
"""Pop current snapshot out/err capture and flush to orig streams."""
out, err = self.readouterr()
if out:
assert self.out is not None
self.out.writeorg(out)
if err:
assert self.err is not None
self.err.writeorg(err)
return out, err
@@ -600,6 +660,7 @@ class MultiCapture(Generic[AnyStr]):
if self.err:
self.err.resume()
if self._in_suspended:
assert self.in_ is not None
self.in_.resume()
self._in_suspended = False
@@ -622,7 +683,8 @@ class MultiCapture(Generic[AnyStr]):
def readouterr(self) -> CaptureResult[AnyStr]:
out = self.out.snap() if self.out else ""
err = self.err.snap() if self.err else ""
return CaptureResult(out, err)
# TODO: This type error is real, need to fix.
return CaptureResult(out, err) # type: ignore[arg-type]
def _get_multicapture(method: "_CaptureMethod") -> MultiCapture[str]:
@@ -662,7 +724,7 @@ class CaptureManager:
"""
def __init__(self, method: "_CaptureMethod") -> None:
self._method = method
self._method: Final = method
self._global_capturing: Optional[MultiCapture[str]] = None
self._capture_fixture: Optional[CaptureFixture[Any]] = None
@@ -831,14 +893,18 @@ class CaptureFixture(Generic[AnyStr]):
:fixture:`capfd` and :fixture:`capfdbinary` fixtures."""
def __init__(
self, captureclass, request: SubRequest, *, _ispytest: bool = False
self,
captureclass: Type[CaptureBase[AnyStr]],
request: SubRequest,
*,
_ispytest: bool = False,
) -> None:
check_ispytest(_ispytest)
self.captureclass = captureclass
self.captureclass: Type[CaptureBase[AnyStr]] = captureclass
self.request = request
self._capture: Optional[MultiCapture[AnyStr]] = None
self._captured_out = self.captureclass.EMPTY_BUFFER
self._captured_err = self.captureclass.EMPTY_BUFFER
self._captured_out: AnyStr = self.captureclass.EMPTY_BUFFER
self._captured_err: AnyStr = self.captureclass.EMPTY_BUFFER
def _start(self) -> None:
if self._capture is None:
@@ -893,7 +959,9 @@ class CaptureFixture(Generic[AnyStr]):
@contextlib.contextmanager
def disabled(self) -> Generator[None, None, None]:
"""Temporarily disable capturing while inside the ``with`` block."""
capmanager = self.request.config.pluginmanager.getplugin("capturemanager")
capmanager: CaptureManager = self.request.config.pluginmanager.getplugin(
"capturemanager"
)
with capmanager.global_and_fixture_disabled():
yield
@@ -920,8 +988,8 @@ def capsys(request: SubRequest) -> Generator[CaptureFixture[str], None, None]:
captured = capsys.readouterr()
assert captured.out == "hello\n"
"""
capman = request.config.pluginmanager.getplugin("capturemanager")
capture_fixture = CaptureFixture[str](SysCapture, request, _ispytest=True)
capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager")
capture_fixture = CaptureFixture(SysCapture, request, _ispytest=True)
capman.set_fixture(capture_fixture)
capture_fixture._start()
yield capture_fixture
@@ -948,8 +1016,8 @@ def capsysbinary(request: SubRequest) -> Generator[CaptureFixture[bytes], None,
captured = capsysbinary.readouterr()
assert captured.out == b"hello\n"
"""
capman = request.config.pluginmanager.getplugin("capturemanager")
capture_fixture = CaptureFixture[bytes](SysCaptureBinary, request, _ispytest=True)
capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager")
capture_fixture = CaptureFixture(SysCaptureBinary, request, _ispytest=True)
capman.set_fixture(capture_fixture)
capture_fixture._start()
yield capture_fixture
@@ -976,8 +1044,8 @@ def capfd(request: SubRequest) -> Generator[CaptureFixture[str], None, None]:
captured = capfd.readouterr()
assert captured.out == "hello\n"
"""
capman = request.config.pluginmanager.getplugin("capturemanager")
capture_fixture = CaptureFixture[str](FDCapture, request, _ispytest=True)
capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager")
capture_fixture = CaptureFixture(FDCapture, request, _ispytest=True)
capman.set_fixture(capture_fixture)
capture_fixture._start()
yield capture_fixture
@@ -1005,8 +1073,8 @@ def capfdbinary(request: SubRequest) -> Generator[CaptureFixture[bytes], None, N
assert captured.out == b"hello\n"
"""
capman = request.config.pluginmanager.getplugin("capturemanager")
capture_fixture = CaptureFixture[bytes](FDCaptureBinary, request, _ispytest=True)
capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager")
capture_fixture = CaptureFixture(FDCaptureBinary, request, _ispytest=True)
capman.set_fixture(capture_fixture)
capture_fixture._start()
yield capture_fixture

View File

@@ -1,4 +1,7 @@
"""Python version compatibility code."""
from __future__ import annotations
import dataclasses
import enum
import functools
import inspect
@@ -11,13 +14,8 @@ from typing import Any
from typing import Callable
from typing import Generic
from typing import NoReturn
from typing import Optional
from typing import Tuple
from typing import TYPE_CHECKING
from typing import TypeVar
from typing import Union
import attr
import py
@@ -47,7 +45,7 @@ LEGACY_PATH = py.path. local
# fmt: on
def legacy_path(path: Union[str, "os.PathLike[str]"]) -> LEGACY_PATH:
def legacy_path(path: str | os.PathLike[str]) -> LEGACY_PATH:
"""Internal wrapper to prepare lazy proxies for legacy_path instances"""
return LEGACY_PATH(path)
@@ -57,7 +55,7 @@ def legacy_path(path: Union[str, "os.PathLike[str]"]) -> LEGACY_PATH:
# https://www.python.org/dev/peps/pep-0484/#support-for-singleton-types-in-unions
class NotSetType(enum.Enum):
token = 0
NOTSET: "Final" = NotSetType.token # noqa: E305
NOTSET: Final = NotSetType.token # noqa: E305
# fmt: on
if sys.version_info >= (3, 8):
@@ -95,7 +93,7 @@ def is_async_function(func: object) -> bool:
return iscoroutinefunction(func) or inspect.isasyncgenfunction(func)
def getlocation(function, curdir: Optional[str] = None) -> str:
def getlocation(function, curdir: str | None = None) -> str:
function = get_real_func(function)
fn = Path(inspect.getfile(function))
lineno = function.__code__.co_firstlineno
@@ -133,8 +131,8 @@ def getfuncargnames(
*,
name: str = "",
is_method: bool = False,
cls: Optional[type] = None,
) -> Tuple[str, ...]:
cls: type | None = None,
) -> tuple[str, ...]:
"""Return the names of a function's mandatory arguments.
Should return the names of all function arguments that:
@@ -198,7 +196,7 @@ def getfuncargnames(
return arg_names
def get_default_arg_names(function: Callable[..., Any]) -> Tuple[str, ...]:
def get_default_arg_names(function: Callable[..., Any]) -> tuple[str, ...]:
# Note: this code intentionally mirrors the code at the beginning of
# getfuncargnames, to get the arguments which were excluded from its result
# because they had default values.
@@ -229,7 +227,7 @@ def _bytes_to_ascii(val: bytes) -> str:
return val.decode("ascii", "backslashreplace")
def ascii_escaped(val: Union[bytes, str]) -> str:
def ascii_escaped(val: bytes | str) -> str:
r"""If val is pure ASCII, return it as an str, otherwise, escape
bytes objects into a sequence of escaped bytes:
@@ -253,7 +251,7 @@ def ascii_escaped(val: Union[bytes, str]) -> str:
return _translate_non_printable(ret)
@attr.s
@dataclasses.dataclass
class _PytestWrapper:
"""Dummy wrapper around a function object for internal use only.
@@ -262,7 +260,7 @@ class _PytestWrapper:
decorator to issue warnings when the fixture function is called directly.
"""
obj = attr.ib()
obj: Any
def get_real_func(obj):
@@ -356,7 +354,6 @@ else:
if sys.version_info >= (3, 8):
from functools import cached_property as cached_property
else:
from typing import Type
class cached_property(Generic[_S, _T]):
__slots__ = ("func", "__doc__")
@@ -367,12 +364,12 @@ else:
@overload
def __get__(
self, instance: None, owner: Optional[Type[_S]] = ...
) -> "cached_property[_S, _T]":
self, instance: None, owner: type[_S] | None = ...
) -> cached_property[_S, _T]:
...
@overload
def __get__(self, instance: _S, owner: Optional[Type[_S]] = ...) -> _T:
def __get__(self, instance: _S, owner: type[_S] | None = ...) -> _T:
...
def __get__(self, instance, owner=None):
@@ -382,6 +379,18 @@ else:
return value
def get_user_id() -> int | None:
"""Return the current user id, or None if we cannot get it reliably on the current platform."""
# win32 does not have a getuid() function.
# On Emscripten, getuid() is a stub that always returns 0.
if sys.platform in ("win32", "emscripten"):
return None
# getuid shouldn't fail, but cpython defines such a case.
# Let's hope for the best.
uid = os.getuid()
return uid if uid != -1 else None
# Perform exhaustiveness checking.
#
# Consider this example:

View File

@@ -2,6 +2,7 @@
import argparse
import collections.abc
import copy
import dataclasses
import enum
import glob
import inspect
@@ -34,7 +35,6 @@ from typing import Type
from typing import TYPE_CHECKING
from typing import Union
import attr
from pluggy import HookimplMarker
from pluggy import HookspecMarker
from pluggy import PluginManager
@@ -49,7 +49,7 @@ from _pytest._code import ExceptionInfo
from _pytest._code import filter_traceback
from _pytest._io import TerminalWriter
from _pytest.compat import final
from _pytest.compat import importlib_metadata
from _pytest.compat import importlib_metadata # type: ignore[attr-defined]
from _pytest.outcomes import fail
from _pytest.outcomes import Skipped
from _pytest.pathlib import absolutepath
@@ -57,12 +57,12 @@ from _pytest.pathlib import bestrelpath
from _pytest.pathlib import import_path
from _pytest.pathlib import ImportMode
from _pytest.pathlib import resolve_package_path
from _pytest.pathlib import safe_exists
from _pytest.stash import Stash
from _pytest.warning_types import PytestConfigWarning
from _pytest.warning_types import warn_explicit_for
if TYPE_CHECKING:
from _pytest._code.code import _TracebackStyle
from _pytest.terminal import TerminalReporter
from .argparsing import Argument
@@ -138,7 +138,9 @@ def main(
) -> Union[int, ExitCode]:
"""Perform an in-process test run.
:param args: List of command line arguments.
:param args:
List of command line arguments. If `None` or not given, defaults to reading
arguments directly from the process command line (:data:`sys.argv`).
:param plugins: List of plugin objects to be auto-registered during initialization.
:returns: An exit code.
@@ -443,10 +445,10 @@ class PytestPluginManager(PluginManager):
# so we avoid accessing possibly non-readable attributes
# (see issue #1073).
if not name.startswith("pytest_"):
return
return None
# Ignore names which can not be hooks.
if name == "pytest_plugins":
return
return None
opts = super().parse_hookimpl_opts(plugin, name)
if opts is not None:
@@ -455,9 +457,9 @@ class PytestPluginManager(PluginManager):
method = getattr(plugin, name)
# Consider only actual functions for hooks (#3775).
if not inspect.isroutine(method):
return
return None
# Collect unmarked hooks as long as they have the `pytest_' prefix.
return _get_legacy_hook_marks(
return _get_legacy_hook_marks( # type: ignore[return-value]
method, "impl", ("tryfirst", "trylast", "optionalhook", "hookwrapper")
)
@@ -466,7 +468,7 @@ class PytestPluginManager(PluginManager):
if opts is None:
method = getattr(module_or_class, name)
if name.startswith("pytest_"):
opts = _get_legacy_hook_marks(
opts = _get_legacy_hook_marks( # type: ignore[assignment]
method,
"spec",
("firstresult", "historic"),
@@ -527,7 +529,13 @@ class PytestPluginManager(PluginManager):
# Internal API for local conftest plugin handling.
#
def _set_initial_conftests(
self, namespace: argparse.Namespace, rootpath: Path
self,
args: Sequence[Union[str, Path]],
pyargs: bool,
noconftest: bool,
rootpath: Path,
confcutdir: Optional[Path],
importmode: Union[ImportMode, str],
) -> None:
"""Load initial conftest files given a preparsed "namespace".
@@ -537,27 +545,25 @@ class PytestPluginManager(PluginManager):
common options will not confuse our logic here.
"""
current = Path.cwd()
self._confcutdir = (
absolutepath(current / namespace.confcutdir)
if namespace.confcutdir
else None
)
self._noconftest = namespace.noconftest
self._using_pyargs = namespace.pyargs
testpaths = namespace.file_or_dir
self._confcutdir = absolutepath(current / confcutdir) if confcutdir else None
self._noconftest = noconftest
self._using_pyargs = pyargs
foundanchor = False
for testpath in testpaths:
path = str(testpath)
for intitial_path in args:
path = str(intitial_path)
# remove node-id syntax
i = path.find("::")
if i != -1:
path = path[:i]
anchor = absolutepath(current / path)
if anchor.exists(): # we found some file object
self._try_load_conftest(anchor, namespace.importmode, rootpath)
# Ensure we do not break if what appears to be an anchor
# is in fact a very long option (#10169, #11394).
if safe_exists(anchor):
self._try_load_conftest(anchor, importmode, rootpath)
foundanchor = True
if not foundanchor:
self._try_load_conftest(current, namespace.importmode, rootpath)
self._try_load_conftest(current, importmode, rootpath)
def _is_in_confcutdir(self, path: Path) -> bool:
"""Whether a path is within the confcutdir.
@@ -697,6 +703,7 @@ class PytestPluginManager(PluginManager):
parg = opt[2:]
else:
continue
parg = parg.strip()
if exclude_only and not parg.startswith("no:"):
continue
self.consider_pluginarg(parg)
@@ -886,10 +893,6 @@ def _iter_rewritable_modules(package_files: Iterable[str]) -> Iterator[str]:
yield from _iter_rewritable_modules(new_package_files)
def _args_converter(args: Iterable[str]) -> Tuple[str, ...]:
return tuple(args)
@final
class Config:
"""Access to configuration values, pluginmanager and plugin hooks.
@@ -903,7 +906,7 @@ class Config:
"""
@final
@attr.s(frozen=True, auto_attribs=True)
@dataclasses.dataclass(frozen=True)
class InvocationParams:
"""Holds parameters passed during :func:`pytest.main`.
@@ -919,13 +922,24 @@ class Config:
Plugins accessing ``InvocationParams`` must be aware of that.
"""
args: Tuple[str, ...] = attr.ib(converter=_args_converter)
args: Tuple[str, ...]
"""The command-line arguments as passed to :func:`pytest.main`."""
plugins: Optional[Sequence[Union[str, _PluggyPlugin]]]
"""Extra plugins, might be `None`."""
dir: Path
"""The directory from which :func:`pytest.main` was invoked."""
def __init__(
self,
*,
args: Iterable[str],
plugins: Optional[Sequence[Union[str, _PluggyPlugin]]],
dir: Path,
) -> None:
object.__setattr__(self, "args", tuple(args))
object.__setattr__(self, "plugins", plugins)
object.__setattr__(self, "dir", dir)
class ArgsSource(enum.Enum):
"""Indicates the source of the test arguments.
@@ -998,6 +1012,8 @@ class Config:
self.hook.pytest_addoption.call_historic(
kwargs=dict(parser=self._parser, pluginmanager=self.pluginmanager)
)
self.args_source = Config.ArgsSource.ARGS
self.args: List[str] = []
if TYPE_CHECKING:
from _pytest.cacheprovider import Cache
@@ -1046,9 +1062,10 @@ class Config:
fin()
def get_terminal_writer(self) -> TerminalWriter:
terminalreporter: TerminalReporter = self.pluginmanager.get_plugin(
terminalreporter: Optional[TerminalReporter] = self.pluginmanager.get_plugin(
"terminalreporter"
)
assert terminalreporter is not None
return terminalreporter._tw
def pytest_cmdline_parse(
@@ -1057,7 +1074,6 @@ class Config:
try:
self.parse(args)
except UsageError:
# Handle --version and --help here in a minimal fashion.
# This gets done via helpconfig normally, but its
# pytest_cmdline_main is not called in case of errors.
@@ -1122,8 +1138,25 @@ class Config:
@hookimpl(trylast=True)
def pytest_load_initial_conftests(self, early_config: "Config") -> None:
# We haven't fully parsed the command line arguments yet, so
# early_config.args it not set yet. But we need it for
# discovering the initial conftests. So "pre-run" the logic here.
# It will be done for real in `parse()`.
args, args_source = early_config._decide_args(
args=early_config.known_args_namespace.file_or_dir,
pyargs=early_config.known_args_namespace.pyargs,
testpaths=early_config.getini("testpaths"),
invocation_dir=early_config.invocation_params.dir,
rootpath=early_config.rootpath,
warn=False,
)
self.pluginmanager._set_initial_conftests(
early_config.known_args_namespace, rootpath=early_config.rootpath
args=args,
pyargs=early_config.known_args_namespace.pyargs,
noconftest=early_config.known_args_namespace.noconftest,
rootpath=early_config.rootpath,
confcutdir=early_config.known_args_namespace.confcutdir,
importmode=early_config.known_args_namespace.importmode,
)
def _initini(self, args: Sequence[str]) -> None:
@@ -1203,6 +1236,49 @@ class Config:
return args
def _decide_args(
self,
*,
args: List[str],
pyargs: List[str],
testpaths: List[str],
invocation_dir: Path,
rootpath: Path,
warn: bool,
) -> Tuple[List[str], ArgsSource]:
"""Decide the args (initial paths/nodeids) to use given the relevant inputs.
:param warn: Whether can issue warnings.
"""
if args:
source = Config.ArgsSource.ARGS
result = args
else:
if invocation_dir == rootpath:
source = Config.ArgsSource.TESTPATHS
if pyargs:
result = testpaths
else:
result = []
for path in testpaths:
result.extend(sorted(glob.iglob(path, recursive=True)))
if testpaths and not result:
if warn:
warning_text = (
"No files were found in testpaths; "
"consider removing or adjusting your testpaths configuration. "
"Searching recursively from the current directory instead."
)
self.issue_config_time_warning(
PytestConfigWarning(warning_text), stacklevel=3
)
else:
result = []
if not result:
source = Config.ArgsSource.INCOVATION_DIR
result = [str(invocation_dir)]
return result, source
def _preparse(self, args: List[str], addopts: bool = True) -> None:
if addopts:
env_addopts = os.environ.get("PYTEST_ADDOPTS", "")
@@ -1241,8 +1317,11 @@ class Config:
_pytest.deprecated.STRICT_OPTION, stacklevel=2
)
if self.known_args_namespace.confcutdir is None and self.inipath is not None:
confcutdir = str(self.inipath.parent)
if self.known_args_namespace.confcutdir is None:
if self.inipath is not None:
confcutdir = str(self.inipath.parent)
else:
confcutdir = str(self.rootpath)
self.known_args_namespace.confcutdir = confcutdir
try:
self.hook.pytest_load_initial_conftests(
@@ -1337,8 +1416,8 @@ class Config:
def parse(self, args: List[str], addopts: bool = True) -> None:
# Parse given cmdline arguments into this config object.
assert not hasattr(
self, "args"
assert (
self.args == []
), "can only parse cmdline args at most once per Config object"
self.hook.pytest_addhooks.call_historic(
kwargs=dict(pluginmanager=self.pluginmanager)
@@ -1348,25 +1427,17 @@ class Config:
self.hook.pytest_cmdline_preparse(config=self, args=args)
self._parser.after_preparse = True # type: ignore
try:
source = Config.ArgsSource.ARGS
args = self._parser.parse_setoption(
args, self.option, namespace=self.option
)
if not args:
if self.invocation_params.dir == self.rootpath:
source = Config.ArgsSource.TESTPATHS
testpaths: List[str] = self.getini("testpaths")
if self.known_args_namespace.pyargs:
args = testpaths
else:
args = []
for path in testpaths:
args.extend(sorted(glob.iglob(path, recursive=True)))
if not args:
source = Config.ArgsSource.INCOVATION_DIR
args = [str(self.invocation_params.dir)]
self.args = args
self.args_source = source
self.args, self.args_source = self._decide_args(
args=args,
pyargs=self.known_args_namespace.pyargs,
testpaths=self.getini("testpaths"),
invocation_dir=self.invocation_params.dir,
rootpath=self.rootpath,
warn=True,
)
except PrintHelp:
pass

View File

@@ -43,7 +43,6 @@ class PathAwareHookProxy:
@_wraps(hook)
def fixed_hook(**kw):
path_value: Optional[Path] = kw.pop(path_var, None)
fspath_value: Optional[LEGACY_PATH] = kw.pop(fspath_var, None)
if fspath_value is not None:

View File

@@ -16,6 +16,7 @@ from .exceptions import UsageError
from _pytest.outcomes import fail
from _pytest.pathlib import absolutepath
from _pytest.pathlib import commonpath
from _pytest.pathlib import safe_exists
if TYPE_CHECKING:
from . import Config
@@ -151,14 +152,6 @@ def get_dirs_from_args(args: Iterable[str]) -> List[Path]:
return path
return path.parent
def safe_exists(path: Path) -> bool:
# This can throw on paths that contain characters unrepresentable at the OS level,
# or with invalid syntax on Windows (https://bugs.python.org/issue35306)
try:
return path.exists()
except OSError:
return False
# These look like paths but may not exist
possible_paths = (
absolutepath(get_file_part_from_node_id(arg))
@@ -203,8 +196,7 @@ def determine_setup(
else:
cwd = Path.cwd()
rootdir = get_common_ancestor([cwd, ancestor])
is_fs_root = os.path.splitdrive(str(rootdir))[1] == "/"
if is_fs_root:
if is_fs_root(rootdir):
rootdir = ancestor
if rootdir_cmd_arg:
rootdir = absolutepath(os.path.expandvars(rootdir_cmd_arg))
@@ -216,3 +208,11 @@ def determine_setup(
)
assert rootdir is not None
return rootdir, inipath, inicfg or {}
def is_fs_root(p: Path) -> bool:
r"""
Return True if the given path is pointing to the root of the
file system ("/" on Unix and "C:\\" on Windows for example).
"""
return os.path.splitdrive(str(p))[1] == os.sep

Some files were not shown because too many files have changed in this diff Show More