Compare commits

...

924 Commits
4.3.0 ... 5.0.0

Author SHA1 Message Date
Anthony Sottile
58bfc7736f Use shutil.which to avoid distutils+imp warning 2019-06-28 14:44:49 -07:00
Anthony Sottile
97f0a20ca9 Add notice about py35+ and move ExitCode changelog entry 2019-06-28 14:40:10 -07:00
Anthony Sottile
55d2fe076f Use importlib instead of imp in demo 2019-06-28 14:40:10 -07:00
Anthony Sottile
5e39eb91bb Correct Zac-HD's name in changelogs 2019-06-28 14:40:10 -07:00
Anthony Sottile
fd2f320485 Preparing release version 5.0.0 2019-06-28 14:39:53 -07:00
Bruno Oliveira
73d918db55 Remove astor and reproduce the original assertion expression (#5512)
Remove astor and reproduce the original assertion expression
2019-06-28 18:36:11 -03:00
Anthony Sottile
7ee244476a Remove astor and reproduce the original assertion expression 2019-06-28 13:38:52 -07:00
Bruno Oliveira
3c9b46f781 Remove stray comment from tox.ini (#5507)
Remove stray comment from tox.ini
2019-06-27 15:12:57 -03:00
Anthony Sottile
f7bfbb557e Merge pull request #5506 from asottile/fix_no_terminal
Fix crash when discovery fails while using `-p no:terminal`
2019-06-27 10:46:43 -07:00
Anthony Sottile
45af361a67 Remove stray comment from tox.ini 2019-06-27 10:46:08 -07:00
Anthony Sottile
4e723d6750 Fix crash when discovery fails while using -p no:terminal 2019-06-27 10:24:29 -07:00
Bruno Oliveira
65fbdf2568 Don't crash with --pyargs and a filename that looks like a modu… (#5503)
Don't crash with --pyargs and a filename that looks like a module
2019-06-27 13:53:02 -03:00
Bruno Oliveira
5ef507982c Add Open Collective to FUNDING.yml (#5504)
Add Open Collective to FUNDING.yml
2019-06-27 13:49:58 -03:00
Florian Bruhin
2479a91e92 Add Open Collective to FUNDING.yml 2019-06-27 17:53:03 +02:00
Anthony Sottile
3e0e31a364 Don't crash with --pyargs and a filename that looks like a module 2019-06-27 08:32:32 -07:00
Anthony Sottile
c9923a3a5c Merge pull request #5499 from nicoddemus/cleanup-py35-rewrite
Remove py<35 compatibility code from rewrite.py
2019-06-26 21:31:54 -07:00
Bruno Oliveira
1be49e713a Remove py<35 compatibility code from rewrite.py 2019-06-26 21:44:10 -03:00
Bruno Oliveira
37fb50a3ed Features assertion pass hook (#3479)
Features assertion pass hook
2019-06-26 21:14:19 -03:00
Bruno Oliveira
790806e865 Merge pull request #5494 from Zac-HD/funcargnames-to-fixturenames
Deprecate funcargnames alias for fixturenames
2019-06-26 21:13:59 -03:00
Bruno Oliveira
2ea22218ff Cover assertions with messages when enable_assertion_pass_hook is enabled 2019-06-26 20:46:31 -03:00
Bruno Oliveira
629eb3ec6a Move formatting variables under the "has impls" if
Small optimization, move the generation of the intermediate
formatting variables inside the 'if _check_if_assertion_pass_impl():'
block.
2019-06-26 19:26:12 -03:00
Bruno Oliveira
8edf68f3c0 Add a trivial note about astor 2019-06-26 19:21:12 -03:00
Bruno Oliveira
3afee36ebb Improve docs and reference 2019-06-26 19:15:00 -03:00
Bruno Oliveira
fcbe66feba Restore proper handling of '%' in assertion messages 2019-06-26 18:51:27 -03:00
Bruno Oliveira
eb90f3d1c8 Fix default value of 'enable_assertion_pass_hook' 2019-06-26 17:54:24 -03:00
Victor Maryama
6854ff2acc Fixed import order pep8. 2019-06-26 19:05:17 +02:00
Victor Maryama
53234bf613 Added config back to AssertionWriter and fixed typo in check_if_assertion_pass_impl function call. 2019-06-26 19:00:31 +02:00
Victor Maryama
6f851e6cbb Merge remote-tracking branch 'upstream/master' into features-assertion-pass-hook-master
# Conflicts:
#	src/_pytest/assertion/rewrite.py
2019-06-26 18:12:56 +02:00
Victor Maryama
9a34d88c8d Explanation variables only defined if failed or passed with plugins implementing the hook. 2019-06-26 18:10:16 +02:00
Victor Maryama
d91a5d3cd7 Further reverting changes. 2019-06-26 18:10:16 +02:00
Victor Maryama
f755ff6af1 Black formatting. 2019-06-26 18:10:16 +02:00
Victor Maryama
d638da5821 Using ini-file option instead of cmd option. 2019-06-26 18:10:16 +02:00
Victor Maryama
0fb52416b1 Reverted changes. 2019-06-26 18:10:15 +02:00
Bruno Oliveira
6a2d844c5d Fix typo replace circuting with circuiting. (#5497)
Fix typo replace `circuting` with `circuiting`.
2019-06-26 11:09:22 -03:00
Bruno Oliveira
a48feb3261 Delete 5497.trivial.rst
Just a typo, no need for a changelog entry. :)
2019-06-26 11:09:04 -03:00
Anthony Sottile
6cf8e71dca Merge pull request #5496 from pytest-dev/stray_comment
Delete stray comment
2019-06-26 05:59:38 -07:00
Tim Gates
994c32235c Fix rst support 2019-06-26 20:46:09 +10:00
Tim Gates
d81f758285 Update changelog with trivial as per ./CONTRIBUTING.rst 2019-06-26 20:31:45 +10:00
Tim Gates
fdb6e35b1b Fix typo replace circuting with circuiting. 2019-06-26 20:23:35 +10:00
Victor Maryama
7efdd5063b Update src/_pytest/assertion/rewrite.py
Co-Authored-By: Bruno Oliveira <nicoddemus@gmail.com>
2019-06-26 10:50:27 +02:00
Anthony Sottile
bfba33ec9e Delete stray comment 2019-06-25 20:24:13 -07:00
Anthony Sottile
3f3f3e7a29 Merge pull request #5495 from nicoddemus/blueyed/cmp-bytes
Improve comparison of byte strings (supersedes #5267)
2019-06-25 19:16:24 -07:00
Bruno Oliveira
aaf40314d1 Do not attempt to rewrite non-source files (#5490)
Do not attempt to rewrite non-source files
2019-06-25 20:43:23 -03:00
Bruno Oliveira
3f2344e8f7 Show bytes ascii representation instead of numeric value 2019-06-25 20:40:25 -03:00
Daniel Hahler
8c7eb82363 Fix/improve comparison of byte strings
Fixes https://github.com/pytest-dev/pytest/issues/5260.
2019-06-25 20:15:45 -03:00
Bruno Oliveira
1aefb24b37 Merge features into master (#5491)
Merge features into master
2019-06-25 20:02:02 -03:00
Zac Hatfield-Dodds
ed85c83154 Deprecate funcargnames alias 2019-06-26 08:53:17 +10:00
Anthony Sottile
bd647fdd8b Revert allow_abbrev=False in helper scripts 2019-06-25 14:50:07 -07:00
Victor Maryama
80ac910a24 Added msg to docstring for cleaning pyc. 2019-06-25 19:49:57 +02:00
Victor Maryama
4db5488ed8 Now dependent on command line option. 2019-06-25 19:49:05 +02:00
Victor Maryama
cfbfa53f2b Using pytester subprocess to avoid keeping references in the HookRecorder. 2019-06-25 17:46:56 +02:00
Victor Maryama
db50a975fd Reverted leak fixture test. 2019-06-25 17:23:14 +02:00
Bruno Oliveira
019455298b Merge remote-tracking branch 'upstream/features' into merge-features-into-master 2019-06-25 12:07:54 -03:00
Bruno Oliveira
64a6365227 Include pytest version in the cached pyc tags (#5484)
Include pytest version in the cached pyc tags
2019-06-25 12:07:01 -03:00
Anthony Sottile
b991810f32 Do not attempt to rewrite non-source files 2019-06-25 08:00:20 -07:00
Zac Hatfield-Dodds
4d5780facf Merge pull request #5469 from Zac-HD/disallow-abbrev
Disallow abbreviated command-line options
2019-06-25 22:38:52 +10:00
Victor Maryama
81e3f3cf95 Black formatting 2019-06-25 10:41:11 +02:00
Victor Maryama
2280f28596 Black formatting. 2019-06-25 10:36:01 +02:00
Victor Maryama
f8c9a7b86d Formatting and removed py2 support. 2019-06-25 10:35:42 +02:00
Victor Maryama
98b212cbfb Added "experimental" note. 2019-06-25 10:35:09 +02:00
Zac Hatfield-Dodds
d72fb73fa0 Do not allow abbreviated arguments 2019-06-25 13:51:33 +10:00
Bruno Oliveira
a54e2e19f5 Clarify changelog entries should be rst files (#5485)
Clarify changelog entries should be rst files
2019-06-24 22:33:40 -03:00
Bruno Oliveira
37fce6c6e5 Pickup addition positional args passed to _parse_parametrize_ar… (#5483)
Pickup addition positional args passed to _parse_parametrize_args
2019-06-24 22:05:19 -03:00
Kevin J. Foley
23aa3bb0ae Clarify changelog entries should be rst files 2019-06-24 20:55:51 -04:00
Kevin J. Foley
95714436a1 Pickup additional positional args passed to _parse_parametrize_args 2019-06-24 20:43:52 -04:00
Bruno Oliveira
f43fb13179 Include pytest version in the cached pyc tags
Fix #1671
2019-06-24 20:37:07 -03:00
Bruno Oliveira
3f5b078462 Merge pull request #5441 from nicoddemus/faulthandler-5440
Integrate pytest-faulthandler into the core
2019-06-24 20:01:15 -03:00
Bruno Oliveira
63c4802be0 minor: clarify help with reportchars (#5475)
minor: clarify help with reportchars
2019-06-24 19:59:15 -03:00
Anthony Sottile
a24933b0a6 Merge pull request #5481 from asottile/minor_py3_cleanup
Use new raise syntax in one case
2019-06-24 12:06:53 -07:00
Anthony Sottile
380ca8f880 Use new raise syntax in one case 2019-06-24 11:24:03 -07:00
Anthony Sottile
61dcb84f0d Merge pull request #5468 from asottile/switch_importlib_to_imp
Switch from deprecated imp to importlib
2019-06-24 11:22:01 -07:00
Anthony Sottile
4cd08f9b52 Switch from deprecated imp to importlib 2019-06-24 09:48:38 -07:00
Victor Maryama
52e695b329 Removed debug code. 2019-06-24 17:47:48 +02:00
Victor Maryama
9a89783fbb Assertion passed hook 2019-06-24 16:09:39 +02:00
Daniel Hahler
01a094cc43 minor: clarify help with reportchars
`-ra` / `-rA` will include "w" also.  This does not explicitly mention
it (allowing for change the behavior), but makes it a) clearer that "w"
is a recognized reportchar, and b) less confusing that `-ra
--disable-warnings` still displays them.
2019-06-24 06:07:52 +02:00
Bruno Oliveira
3ce31b6370 Change pytest-faulthandler for simplification
* The --no-faulthandler option is not necessary given that we can use
  `-p no:faulthandler`.

* The `--faulthandler-timeout` command-line option has become an ini
  option, for the reasons described in
  https://github.com/pytest-dev/pytest-faulthandler/issues/34 and
  users can still set it from the command-line.

Fix pytest-dev/pytest-faulthandler#34
2019-06-22 19:22:43 -03:00
Bruno Oliveira
3d01dd3adf Update cache.rst (#5465)
Update cache.rst
2019-06-21 21:12:30 -03:00
Bruno Oliveira
a37b902afe Integrate pytest-faulthandler into the core
* Add pytest-faulthandler files unchanged
* Adapt imports and tests
* Add code to skip registration of the external `pytest_faulthandler`
  to avoid conflicts

Fix #5440
2019-06-21 21:02:24 -03:00
curiousjazz77
12b76b6261 Update cache.rst 2019-06-21 15:48:59 -07:00
Bruno Oliveira
b713460cc7 fix typos in the resolution of #5125 (#5458)
fix typos in the resolution of #5125
2019-06-17 16:55:26 -03:00
Ronny Pfannschmidt
0627d92df2 fix typos in the resolution of #5125 2019-06-17 20:35:23 +02:00
Bruno Oliveira
b38a4e8e11 Fix stepwise crash when first collected module fails (#5446)
Fix stepwise crash when first collected module fails
2019-06-16 10:43:18 -03:00
Bruno Oliveira
689ce112e7 Small cleanups on _pytest.compat (#5451)
Small cleanups on _pytest.compat
2019-06-16 10:42:07 -03:00
Bruno Oliveira
87fc5a5455 Make pytest warnings show as from 'pytest' module instead of '_pytest.warning_types' (#5452)
Make pytest warnings show as from 'pytest' module instead of '_pytest.warning_types'
2019-06-16 10:41:40 -03:00
Bruno Oliveira
bbfc8d1501 conversion of exit codes to enum + exposure (#5420)
conversion of exit codes to enum  + exposure
2019-06-16 10:41:18 -03:00
Bruno Oliveira
ab6ed381ac Improve ExitCode docstring 2019-06-15 20:53:46 -03:00
Ronny Pfannschmidt
1cfea5f1b3 add ExitCode reference in usage 2019-06-15 21:41:55 +02:00
Daniel Hahler
cf27af734e Merge pull request #5404 from Zac-HD/helpful-mock-unwrapper
Emit warning for broken object
2019-06-15 18:54:03 +02:00
Ronny Pfannschmidt
8b3b10b14b pre-commit 2019-06-15 17:41:13 +02:00
Ronny Pfannschmidt
103d6146b0 document exitcode members 2019-06-15 17:18:21 +02:00
Ronny Pfannschmidt
065fa17124 update cangelog to fit review suggestion 2019-06-15 17:03:40 +02:00
Bruno Oliveira
43e8576ca3 Make pytest warnings show as from 'pytest' module instead of '_pytest.warning_types'
When we configure warnings as errors, users see error messages like this:

        def test():
    >       warnings.warn(pytest.PytestWarning("some warning"))
    E       _pytest.warning_types.PytestWarning: some warning

This is a problem because suggests the user should use `_pytest.warning_types.PytestWarning` to configure
their warning filters, which is not nice.

This commit changes the message to:

        def test():
    >       warnings.warn(pytest.PytestWarning("some warning"))
    E       pytest.PytestWarning: some warning
2019-06-15 11:48:34 -03:00
Bruno Oliveira
d8fa434d39 Remove Python 2-only workaround 2019-06-15 10:45:16 -03:00
Bruno Oliveira
177af032d2 Remove unused/unneeded code 2019-06-15 10:45:16 -03:00
Bruno Oliveira
355eb5adfb Small cleanups on _pytest.compat
Small improvements and cleanups
2019-06-15 10:06:37 -03:00
Ronny Pfannschmidt
2bd619ecb0 add changelog 2019-06-15 07:05:18 +02:00
Ronny Pfannschmidt
2b92fee1c3 initial conversion of exit codes to enum 2019-06-15 06:48:00 +02:00
Anthony Sottile
240828d912 Merge pull request #5448 from nicoddemus/add-hooks
Remove broken/unused PytestPluginManager.addhooks
2019-06-14 21:45:11 -07:00
Bruno Oliveira
c94e9b6145 Fix test docstring 2019-06-13 23:10:13 -03:00
Bruno Oliveira
7513d87b15 Remove broken/unused PytestPluginManager.addhooks
The function has been deprecated for ages and
the PLUGIN_MANAGER_ADDHOOKS constant doesn't even exist since 3.0.

Because the function is clearly broken, this change doesn't even
require a CHANGELOG.
2019-06-13 19:55:13 -03:00
Bruno Oliveira
bc345ac980 Remove handling of collection errors by --sw
Since then pytest itself adopted the behavior of interrupting
the test session on collection errors, so --sw no longer needs
to handle this.

The --sw behavior seems have been implemented when pytest
would continue execution even if there were collection errors.
2019-06-13 17:19:36 -03:00
Bruno Oliveira
4cc05a657d Fix --sw crash when first file in cmdline fails to collect
Fix #5444
2019-06-13 16:47:01 -03:00
Thomas Grainger
ad15efc7ea add test for stepwise attribute error Refs: #5444 2019-06-13 12:39:49 +01:00
Bruno Oliveira
e3dcf1f39d Merge pull request #5442 from patriksevallius/master
Add missing 'e' to test_mod(e).
2019-06-13 08:23:53 -03:00
patriksevallius
701d0351a6 Add missing 'e' to test_mod(e). 2019-06-13 06:01:30 +02:00
Bruno Oliveira
bad7ab721a Minor: tox: coverage: use -m with coverage-report (#5427)
Minor:  tox: coverage: use -m with coverage-report
2019-06-12 19:44:15 -03:00
Anthony Sottile
ede8b87560 Merge pull request #5438 from nicoddemus/result-log-deprecation
Postpone removal of --result-log to pytest 6.0
2019-06-12 10:45:02 -07:00
Bruno Oliveira
52780f39ce Postpone removal of --result-log to pytest 6.0
As we did not provide an alternative yet, it is better to postpone
the actual removal until we have provided a suitable and stable
alternative.

Related to #4488
2019-06-12 08:26:11 -03:00
Bruno Oliveira
d637d1b2ac Merge pull request #5435 from asottile/release-4.6.3 (#5436)
Merge pull request #5435 from asottile/release-4.6.3
2019-06-11 21:24:45 -03:00
Anthony Sottile
47447963b7 Merge pull request #5437 from asottile/reenable_pypy3
re-enable pypy3
2019-06-11 12:21:53 -07:00
Anthony Sottile
f586d627b3 re-enable pypy3 now that importlib-metadata 0.18 is released 2019-06-11 10:54:16 -07:00
Anthony Sottile
108fad1ac0 Revert "ci: Travis: remove pypy3 job for now"
This reverts commit 47022b36cb.
2019-06-11 10:53:32 -07:00
Anthony Sottile
18c2ff6625 Merge pull request #5435 from asottile/release-4.6.3
Preparing release version 4.6.3
2019-06-11 09:59:05 -07:00
Zac-HD
c5a549b5bb Emit warning for broken object 2019-06-09 22:17:40 +10:00
Daniel Hahler
75cda6de53 tox: coverage: use -m with coverage-report 2019-06-09 12:51:32 +02:00
Daniel Hahler
e868bb647d remove commented code 2019-06-09 12:51:32 +02:00
Daniel Hahler
40c5a9d9f2 Merge pull request #5409 from blueyed/twisted
ci: optimize twisted/numpy related jobs
2019-06-09 12:49:41 +02:00
Anthony Sottile
83d18096d4 Merge pull request #5421 from nicoddemus/raises-warning-message
Link deprecation docs pytest.raises 'message' warning
2019-06-08 12:44:40 -07:00
Daniel Hahler
28aa38ece6 ci: optimize twisted/pexpect related jobs
- tox: use twisted as dep only
- Azure: move twisted/numpy to main py37 job
- Travis: move twisted to main py37 build
2019-06-08 19:21:56 +02:00
Bruno Oliveira
f0cee593f2 Link deprecation docs pytest.raises 'message' warning
As commented in https://github.com/pytest-dev/pytest/issues/3974#issuecomment-499870914
2019-06-07 14:14:46 -03:00
Daniel Hahler
4f597f011d Merge pull request #5419 from blueyed/remove-pypy3
ci: Travis: remove pypy3 job for now
2019-06-07 18:05:59 +02:00
Bruno Oliveira
9f5c356784 Remove ExceptionInfo.__str__, falling back to __repr__ (#5413)
Remove ExceptionInfo.__str__, falling back to __repr__
2019-06-07 08:48:03 -03:00
Daniel Hahler
47022b36cb ci: Travis: remove pypy3 job for now
Ref: https://github.com/pytest-dev/pytest/issues/5317#issuecomment-499019928
2019-06-07 11:01:23 +02:00
Bruno Oliveira
042a10f2e2 Add slow marker in run/skip option example. (#5416)
Add `slow` marker in run/skip option example.
2019-06-06 15:00:04 -03:00
Anthony Sottile
0405697a78 Merge pull request #5415 from asottile/small_mypy_fixes
small mypy fixes
2019-06-06 10:47:58 -07:00
Ralph Giles
918268774b Add slow marker in run/skip option example.
The example implementation of a `--runslow` option results in
a `PytestUnknownMarkWarning`. Include registering the custom
mark in the example, based on the documentation in markers.rst.
2019-06-06 10:15:43 -07:00
Anthony Sottile
ccd87f9e80 small mypy fixes 2019-06-06 09:13:02 -07:00
Bruno Oliveira
65c2a81924 Remove ExceptionInfo.__str__, falling back to __repr__
Fix #5412
2019-06-06 12:20:32 -03:00
Bruno Oliveira
450d264623 ci: move coverage reporting to shared script (#5403)
ci: move coverage reporting to shared script
2019-06-06 12:04:32 -03:00
Daniel Hahler
0fd1f3038c ci: move coverage reporting to shared script 2019-06-06 16:31:39 +02:00
Bruno Oliveira
9f8b566ea9 Turn PytestDeprecationWarning into error (#5410)
Turn PytestDeprecationWarning into error
2019-06-05 22:20:14 -03:00
Bruno Oliveira
3656885d0d Fix verbosity bug in --collect-only (#5391)
Fix verbosity bug in --collect-only
2019-06-05 20:47:59 -03:00
Thomas Hisch
577b0dffe7 Fix verbosity bug in --collect-only 2019-06-05 20:10:16 -03:00
Bruno Oliveira
8f5cb461a8 Turn PytestDeprecationWarning into error
Fix #5402
2019-06-05 19:02:52 -03:00
Daniel Hahler
be84ba8a33 Merge pull request #5396 from blueyed/travis-py37-nocov
Revert "Enable coverage for 'py37' environment"
2019-06-05 12:40:46 +02:00
Daniel Hahler
d9eafbdee3 Revert "Enable coverage for 'py37' environment"
This reverts commit 6d393c5dc8.

It should not be necessary, because we have it via other jobs already.
2019-06-05 12:12:58 +02:00
Daniel Hahler
190456aeda Merge pull request #5406 from blueyed/restore-tracing
tests: restore tracing function
2019-06-05 11:50:56 +02:00
Daniel Hahler
aab5687093 tests: restore tracing function
Without this, `testing/test_pdb.py` (already without pexpect) will cause
missing test coverage afterwards (for the same process).
2019-06-05 11:26:48 +02:00
Anthony Sottile
5fdc2d7744 Merge pull request #5400 from asottile/prefixes
Clean up u' prefixes and py2 bytes conversions
2019-06-04 19:03:12 -07:00
Anthony Sottile
25cef55e4a Merge pull request #5398 from nicoddemus/kwargs-refactoring
Use keyword-only arguments in a few places
2019-06-04 18:21:44 -07:00
Anthony Sottile
be2be040f9 Clean up u' prefixes and py2 bytes conversions 2019-06-04 17:50:34 -07:00
Anthony Sottile
0f4992c223 Merge pull request #5393 from nicoddemus/unittest-self-5390
item.obj is again a bound method on TestCase function items
2019-06-04 17:48:48 -07:00
Bruno Oliveira
23cd68b667 Use keyword-only arguments in a few places 2019-06-04 21:10:59 -03:00
Bruno Oliveira
79ef04888e Merge pull request #5389 from dirk-thomas/patch-1
fix logic if importlib_metadata.PathDistribution.files is None [breaks pytest 4.6.0|1|2]
2019-06-04 20:25:21 -03:00
Bruno Oliveira
1f8fd421c4 item.obj is again a bound method on TestCase function items
Fix #5390
2019-06-04 20:22:49 -03:00
Bruno Oliveira
76d50801fd Allow pypy3 failures again (#5394)
Allow pypy3 failures again
2019-06-04 20:22:25 -03:00
Bruno Oliveira
9349c72cac Allow pypy3 failures again
Related to #5317
2019-06-04 20:17:51 -03:00
Bruno Oliveira
883db6a216 Add test for packages with broken metadata
Related to #5389
2019-06-04 17:04:15 -03:00
Dirk Thomas
898e869bcd add changelog file for #5389 2019-06-04 10:55:38 -07:00
Dirk Thomas
0a91e181af fix logic if importlib_metadata.PathDistribution.files is None 2019-06-04 10:41:58 -07:00
Bruno Oliveira
6a6b6d8195 Remove --recreate from .travis.yml (#5384)
Remove --recreate from .travis.yml
2019-06-03 22:18:17 -03:00
Anthony Sottile
b95bb29fc2 Remove --recreate from .travis.yml
Looks like this has been in the history since the beginning of time, but we should always get a blank slate anyway

Noticed this in https://github.com/crsmithdev/arrow/pull/597
2019-06-03 16:31:06 -07:00
Bruno Oliveira
2a3d643bdf Documentation: expansion of Monkeypatch to include mocked classes and dictionaries (#5315)
Documentation: expansion of Monkeypatch to include mocked classes and dictionaries
2019-06-03 19:30:29 -03:00
Bruno Oliveira
2cfd9c259f Revert "ci: Travis: add pypy3 to allowed failures temporarily" (#5378)
Revert "ci: Travis: add pypy3 to allowed failures temporarily"
2019-06-03 17:59:24 -03:00
Bruno Oliveira
927e95c65a [release notes] Merge pull request #5379 from asottile/release-4.6.2 (#5380)
[release notes] Merge pull request #5379 from asottile/release-4.6.2
2019-06-03 16:35:21 -03:00
Anthony Sottile
9657166a22 Merge pull request #5379 from asottile/release-4.6.2
Preparing release version 4.6.2
2019-06-03 12:19:42 -07:00
Bruno Oliveira
f223d26111 Add a new Exceptions section in documentation and document UsageError (#5231)
Add a new Exceptions section in documentation and document UsageError
2019-06-03 14:41:04 -03:00
Pulkit Goyal
1d6bbab2b0 Add a new Exceptions section in documentation and document UsageError 2019-06-03 14:23:56 -03:00
Daniel Hahler
606d728697 Revert "Revert "Revert "ci: Travis: add pypy3 to allowed failures temporarily""" 2019-06-03 18:54:40 +02:00
Daniel Hahler
3a8b08275d Merge pull request #5377 from blueyed/pypy3
Revert "Revert "ci: Travis: add pypy3 to allowed failures temporarily""
2019-06-03 18:33:57 +02:00
Daniel Hahler
e770db4c91 Revert "Revert "ci: Travis: add pypy3 to allowed failures temporarily""
This reverts commit a6dc283133.
2019-06-03 18:30:50 +02:00
Anthony Sottile
63099bc282 Merge pull request #5373 from asottile/revert_all_handling
Revert unrolling of `all()`
2019-06-03 09:18:47 -07:00
Bruno Oliveira
4f57d40a43 Merge master into features (#5374)
Merge master into features
2019-06-03 13:14:31 -03:00
Anthony Sottile
230f736fcd Add changelog entries for reverting all() handling 2019-06-03 08:42:03 -07:00
Anthony Sottile
1b381d5277 Revert "Unroll calls to any #5062 (#5103)"
This reverts commit 2b9ca34280, reversing
changes made to 0a57124063.
2019-06-03 08:42:03 -07:00
Anthony Sottile
2125d04501 Revert "Fix all() unroll for non-generators/non-list comprehensions (#5360)"
This reverts commit 733f43b02e, reversing
changes made to e4fe41ebb7.
2019-06-03 08:42:03 -07:00
Anthony Sottile
5976f36240 Merge pull request #5368 from asottile/pyupgrade
Automated / semi-automated python3 upgrades
2019-06-03 08:41:37 -07:00
Bruno Oliveira
ce78c9adef Fix linting 2019-06-03 12:08:50 -03:00
Bruno Oliveira
6d393c5dc8 Enable coverage for 'py37' environment 2019-06-03 12:08:02 -03:00
Anthony Sottile
8292644015 Allow multiple positions for the SyntaxError in pypy3.6 2019-06-03 12:08:02 -03:00
Anthony Sottile
96fd44e040 Update line number for warning 2019-06-03 12:08:02 -03:00
Anthony Sottile
5dcf85c17e manual: remove dependence on six 2019-06-03 12:08:02 -03:00
Anthony Sottile
ca1efd57bd git rm src/_pytest/_code/_py2traceback.py 2019-06-03 12:08:02 -03:00
Anthony Sottile
c63320cc31 codecs.open / io.open 2019-06-03 12:08:02 -03:00
Anthony Sottile
4df529e5b9 Clean up __future__ and coding: in tests 2019-06-03 12:08:02 -03:00
Anthony Sottile
a91fe1fedd pre-commit run pyupgrade --all-files 2019-06-03 12:08:02 -03:00
Anthony Sottile
3f1ec520fc pre-commit run reorder-python-imports --all-files 2019-06-03 12:08:01 -03:00
Anthony Sottile
5034399d7a pre-commit run fix-encoding-pragma --all-files 2019-06-03 12:08:01 -03:00
Anthony Sottile
99057555e1 manual changes to .pre-commit-config.yaml 2019-06-03 12:08:01 -03:00
Thomas Hisch
b8781ff868 Merge pull request #5335 from thisch/fmtregex
logging: Extend LEVELNAME_FMT_REGEX
2019-06-03 05:55:36 +02:00
Thomas Hisch
ef053193b5 logging: Extend LEVELNAME_FMT_REGEX 2019-06-03 05:53:19 +02:00
Bruno Oliveira
28ac469eaa Merge pull request #5367 from apollovy/master
Fix typo about interpreters count in doc/en/example/parametrize.html
2019-06-02 20:43:53 -03:00
Bruno Oliveira
5999782768 Use python 3 interpreters in example 2019-06-02 19:20:00 -03:00
Bruno Oliveira
6e68532bb3 Merge pull request #5362 from asottile/release-4.6.1 (#5365)
Merge pull request #5362 from asottile/release-4.6.1
2019-06-02 19:17:29 -03:00
Bruno Oliveira
602a290fcc Drop py27 and py34 support (#5318)
Drop py27 and py34 support
2019-06-02 19:14:54 -03:00
Bruno Oliveira
876df87672 Update release instructions for 4.6-maintenance (#5363)
Update release instructions for 4.6-maintenance
2019-06-02 19:14:26 -03:00
Bruno Oliveira
b7f8f53127 Revert "ci: Travis: add pypy3 to allowed failures temporarily" (#5364)
Revert "ci: Travis: add pypy3 to allowed failures temporarily"
2019-06-02 19:12:39 -03:00
Yuri Apollov
5c016d1021 Fix typo about interpreters count in doc/en/example/parametrize.html 2019-06-02 23:57:22 +03:00
Anthony Sottile
25e728c78b Update release instructions for 4.6-maintenance 2019-06-02 11:50:07 -07:00
Anthony Sottile
642ef6dc1b Merge pull request #5362 from asottile/release-4.6.1
Preparing release version 4.6.1
2019-06-02 11:47:26 -07:00
Daniel Hahler
a6dc283133 Revert "ci: Travis: add pypy3 to allowed failures temporarily"
This reverts commit 5ac498ea96.

The idea is that maybe https://github.com/pytest-dev/pytest/pull/5360
fixes the failures here also.
2019-06-02 20:19:34 +02:00
Bruno Oliveira
4d49ba6529 Drop Python 2.7 and 3.4 support
* Update setup.py requires and classifiers
* Drop Python 2.7 and 3.4 from CI
* Update docs dropping 2.7 and 3.4 support
* Fix mock imports and remove tests related to pypi's mock module
* Add py27 and 34 support docs to the sidebar
* Remove usage of six from tmpdir
* Remove six.PY* code blocks
* Remove sys.version_info related code
* Cleanup compat
* Remove obsolete safe_str
* Remove obsolete __unicode__ methods
* Remove compat.PY35 and compat.PY36: not really needed anymore
* Remove unused UNICODE_TYPES
* Remove Jython specific code
* Remove some Python 2 references from docs

Related to #5275
2019-06-02 14:39:11 -03:00
Bruno Oliveira
733f43b02e Fix all() unroll for non-generators/non-list comprehensions (#5360)
Fix all() unroll for non-generators/non-list comprehensions
2019-06-02 12:51:43 -03:00
Bruno Oliveira
acdf30a523 Fix all() unroll for non-generators/non-list comprehensions
Fix #5358
2019-06-02 12:12:14 -03:00
Anthony Sottile
e4fe41ebb7 Merge pull request #5356 from asottile/fix_parametrize_iterator
Fix `pytest.mark.parametrize` when the argvalue is an iterator
2019-06-01 15:09:43 -07:00
Anthony Sottile
cafb13c95f Fix pytest.mark.parametrize when the argvalue is an iterator 2019-06-01 15:09:17 -07:00
Bruno Oliveira
a8f4e56d8c Docs: Fix link anchor (#5355)
Docs: Fix link anchor
2019-06-01 19:09:04 -03:00
Hugo
e2ac4b782a Fix link anchor 2019-06-01 23:27:20 +03:00
Anthony Sottile
917195ea8e Merge pull request #5350 from asottile/release-4.6.0
Release 4.6.0
2019-06-01 11:10:57 -07:00
Anthony Sottile
e7cd00ac92 Preparing release version 4.6.0 2019-05-31 23:12:57 -07:00
Daniel Hahler
693c3b7f61 Merge pull request #5349 from asottile/mm
Merge master into features
2019-05-31 06:35:00 +02:00
Anthony Sottile
fb3ae5eaa9 Merge remote-tracking branch 'origin/master' into mm 2019-05-30 20:23:38 -07:00
Bruno Oliveira
c8d23c206b logging: Improve formatting of multiline message (#5312)
logging: Improve formatting of multiline message
2019-05-30 21:42:41 -03:00
Bruno Oliveira
c5de8e8c50 ci: Travis: add pypy3 to allowed failures temporarily (#5340)
ci: Travis: add pypy3 to allowed failures temporarily
2019-05-30 21:37:55 -03:00
Bruno Oliveira
f360147758 Avoiding looking upwards for parameter argnames when generating fixtu… (#5254)
Avoiding looking upwards for parameter argnames when generating fixtu…
2019-05-30 21:08:15 -03:00
Victor Maryama
65bd1b8a93 Avoiding looking upwards for parameter argnames when generating fixtureinfo. 2019-05-30 23:42:04 +02:00
Daniel Hahler
882f3a4cd7 Merge pull request #5306 from blueyed/spawn-home
pytester: use temporary HOME with spawn
2019-05-30 20:46:17 +02:00
Anthony Sottile
56e430f74e Merge pull request #5338 from nicoddemus/junit_log_passing_tests-ref-docs
Add missing junitxml ini options to the reference docs
2019-05-30 10:29:56 -07:00
Anthony Sottile
654d8da9f7 Merge pull request #5346 from nicoddemus/improve-collect-warn-message
Show test module in the PytestCollectionWarning message
2019-05-30 10:27:52 -07:00
Daniel Hahler
ace3a02cd4 pytester: factor out testdir._env_run_update 2019-05-30 17:39:44 +02:00
Daniel Hahler
f013a5e8c1 pytester: use temporary HOME with spawn
Followup to https://github.com/pytest-dev/pytest/issues/4956.
2019-05-30 17:39:44 +02:00
Bruno Oliveira
fbd8ff9502 Merge master into features (#5339)
Merge master into features
2019-05-30 12:21:51 -03:00
Daniel Hahler
737a1bf947 Merge pull request #5341 from blueyed/auto-slow
tests: conftest: auto-add slow marker
2019-05-30 17:01:43 +02:00
Bruno Oliveira
843ca03770 Merge pull request #5343 from blueyed/remove-winpymap
pytester: remove unused winpymap
2019-05-30 09:21:51 -03:00
Bruno Oliveira
5cbc06a453 Show test module in the PytestCollectionWarning message
Related to #5330
2019-05-30 08:18:28 -03:00
Daniel Hahler
da23aa3419 pytester: remove unused winpymap
Follow-up to c86d2daf8.
2019-05-30 08:09:49 +02:00
Daniel Hahler
28bf3816e7 tests: conftest: auto-add slow marker 2019-05-30 06:55:38 +02:00
Daniel Hahler
d6ce2e5858 Merge pull request #5322 from blueyed/pdb-wrapper
pdb: move/refactor initialization of PytestPdbWrapper
2019-05-30 06:39:25 +02:00
Daniel Hahler
5ac498ea96 ci: Travis: add pypy3 to allowed failures temporarily
Ref: https://github.com/pytest-dev/pytest/pull/5334
Ref: https://github.com/pytest-dev/pytest/issues/5317
2019-05-30 06:37:53 +02:00
Daniel Hahler
6765aca0d1 Merge master into features 2019-05-30 06:33:04 +02:00
Daniel Hahler
72fc43952b Merge pull request #5336 from blueyed/fix-pexpect
Fix pexpect tests on MacOS
2019-05-30 06:31:11 +02:00
Daniel Hahler
6896dbc5ca tox: pexpect: use "-m uses_pexpect" 2019-05-30 03:52:41 +02:00
Daniel Hahler
49c6aebbc7 ci: Travis: test with pexpect on macos
Uses `PYTEST_COVERAGE=1` already, which will come in via dropping
Python 2 soon anyway.
2019-05-30 03:51:33 +02:00
Daniel Hahler
fb12d2a612 test_enter_leave_pdb_hooks_are_called: remove child.sendeof() 2019-05-30 03:09:36 +02:00
Daniel Hahler
8e51563384 tests: pdb: flush also on MacOS, but read() before
Ref: https://github.com/pytest-dev/pytest/issues/2022
2019-05-30 03:09:36 +02:00
Daniel Hahler
61dfd0a94f pdb: move/refactor initialization of PytestPdbWrapper 2019-05-30 03:00:07 +02:00
Bruno Oliveira
f9cafd1c94 Add missing junitxml ini options to the reference docs 2019-05-29 21:13:16 -03:00
Bruno Oliveira
b10f28949d Add the junit_log_passing_tests ini flag to skip logging output for passing tests. (#5052)
Add the junit_log_passing_tests ini flag to skip logging output for passing tests.
2019-05-29 20:56:13 -03:00
Bruno Oliveira
b0f090890c Merge master into features (#5332)
Merge master into features
2019-05-29 20:39:27 -03:00
Daniel Hahler
f0a4a13e48 Merge pull request #5333 from blueyed/fix-lf
Fix regression with --lf and non-selected failures
2019-05-30 01:24:07 +02:00
Daniel Hahler
ff80464b47 last-failed: display skipped-files msg always 2019-05-29 23:32:52 +02:00
Daniel Hahler
480dd9e6d6 last_failed_paths: improve caching 2019-05-29 23:28:58 +02:00
Daniel Hahler
ceb4f3f701 fixup! Fix regression with --lf and non-selected failures 2019-05-29 23:24:52 +02:00
Thomas Hisch
ea3ebec117 logging: Improve formatting of multiline message 2019-05-29 23:21:14 +02:00
Daniel Hahler
bf3b26b3f7 Fix regression with --lf and non-selected failures 2019-05-29 22:44:43 +02:00
Thomas Hisch
84569ca4da Merge pull request #5311 from thisch/coloredcaplog
Use same code for setting up cli/non-cli formatter
2019-05-29 22:18:43 +02:00
Daniel Hahler
af21e6b45c Merge master into features
(dropping the temporary pinning of pluggy (2e2e895b4))
2019-05-29 22:15:25 +02:00
Daniel Hahler
5c5966f62d Merge pull request #5331 from blueyed/conftest
tests: conftest: fix collection of slow/slowest items
2019-05-29 22:13:52 +02:00
Thomas Hisch
31b1c4ca0c Update changelog/5311.feature.rst
Co-Authored-By: Daniel Hahler <github@thequod.de>
2019-05-29 22:00:34 +02:00
Daniel Hahler
61b76c7f5f tests: conftest: fix collection of slow/slowest items 2019-05-29 21:35:05 +02:00
Bruno Oliveira
c35544a0f7 conftest: add uses_pexpect mark (#5314)
conftest: add uses_pexpect mark
2019-05-28 20:00:36 -03:00
Bruno Oliveira
5bc3ad8e27 minor: getbasetemp: dedent, improve assert (#5327)
minor: getbasetemp: dedent, improve assert
2019-05-28 19:59:43 -03:00
Bruno Oliveira
a930ca0b45 Remove _pytest.compat.NoneType (#5326)
Remove _pytest.compat.NoneType
2019-05-28 19:58:17 -03:00
Thomas Hisch
d4b85da8c7 Use same code for setting up cli/non-cli formatter
A method _create_formatter was introduced that is used for both the
log_cli_formatter and the log_formatter.

Consequences of this commit are:
* Captured logs that are output for each failing test are formatted
  using the ColoredLevelFromatter.
* The formatter used for writing to a file still uses the non-colored
  logging.Formatter class.
2019-05-28 21:30:34 +02:00
Daniel Hahler
0025e4408f conftest: add uses_pexpect mark 2019-05-28 18:41:01 +02:00
Anthony Sottile
81cc73103a Merge pull request #5325 from asottile/temporary
Temporarily pin pluggy on master
2019-05-28 09:37:04 -07:00
Daniel Hahler
97d8e9fbec minor: getbasetemp: dedent, improve assert 2019-05-28 18:26:10 +02:00
Daniel Hahler
220a2a1bc9 Remove _pytest.compat.NoneType 2019-05-28 18:16:18 +02:00
Anthony Sottile
2e2e895b4b Temporarily pin pluggy on master 2019-05-28 09:02:23 -07:00
Bruno Oliveira
bce45052a6 Correct some tiny typos in changelog (#5323)
Correct some tiny typos in changelog
2019-05-28 12:41:05 -03:00
Anthony Sottile
2f48ae4e66 Correct some tiny typos in changelog 2019-05-28 08:11:18 -07:00
Evan Kepner
2dfbed11b4 fix path expansion example 2019-05-27 23:23:18 -04:00
Evan Kepner
24c95c78e7 add updated monkeypatch examples 2019-05-27 20:55:15 -04:00
Daniel Hahler
6061ecf95a Merge master into features (#5319) 2019-05-28 02:15:15 +02:00
Daniel Hahler
e032904413 Merge master into features 2019-05-28 01:46:16 +02:00
Bruno Oliveira
ece774f0eb pdb: import pdbcls lazily (#5307)
pdb: import pdbcls lazily
2019-05-27 20:23:39 -03:00
Bruno Oliveira
bc49d6ff99 Update docs about py27 and py34 support plans (#5316)
Update docs about py27 and py34 support plans
2019-05-27 20:22:31 -03:00
Bruno Oliveira
2b9ca34280 Unroll calls to any #5062 (#5103)
Unroll calls to any #5062
2019-05-27 20:17:44 -03:00
Bruno Oliveira
e38561037d Update docs about py27 and py34 support plans
As discussed in #5275
2019-05-27 19:28:27 -03:00
Anthony Sottile
0a57124063 Merge pull request #5063 from asottile/importlib_metadata_v2
Switch to importlib-metadata
2019-05-27 15:00:12 -07:00
Anthony Sottile
13f02af97d Switch to importlib-metadata 2019-05-27 13:24:08 -07:00
Daniel Hahler
f2ed796c41 pdb: import pdbcls lazily
Fixes https://github.com/pytest-dev/pytest/issues/2064.
2019-05-26 15:56:38 +02:00
Anthony Sottile
b3f8fabac8 Merge pull request #5308 from blueyed/minor
Minor fixes
2019-05-25 21:58:39 -07:00
Tomer Keren
22d91a3c3a Unroll calls to all on python 2 2019-05-25 19:34:08 +03:00
Tomer Keren
322a0f0a33 Fix mention of issue #5062 in docstrings 2019-05-25 19:34:08 +03:00
Tomer Keren
58149459a5 Mark visit_all as a private method 2019-05-25 19:34:08 +03:00
Tomer Keren
852fb6a4ae Change basic test case to be consistent with existing assertion rewriting
The code
```
x = 0
assert x == 1
```
will give the failure message 0 == 1, so it shouldn't be different as
part of an unroll
2019-05-25 19:34:08 +03:00
Tomer Keren
437d6452c1 Expand list comprehensions as well 2019-05-25 19:34:08 +03:00
Tomer Keren
e37ff3042e Check calls to all only if it's a name and not an attribute 2019-05-25 19:34:08 +03:00
Tomer Keren
ecd2de25a1 Revert "Displaying pip list command's packages and versions #5062"
This reverts commit 043fdb7c40.
These tests were part of the PR #5155 but weren't relevant to #5602
2019-05-25 19:34:08 +03:00
danielx123
c607697400 Fixed test case 2019-05-25 19:34:08 +03:00
danielx123
0996f3dbc5 Displaying pip list command's packages and versions #5062 2019-05-25 19:34:08 +03:00
danielx123
a0dbf2ab99 Adding test cases for unrolling an iterable #5062 2019-05-25 19:34:08 +03:00
Tomer Keren
ddbe733666 Add changelog entry for 5062 2019-05-25 19:34:08 +03:00
Tomer Keren
470e686a70 Rewrite unrolled assertion with a new rewriter,correctly append the unrolled for loop 2019-05-25 19:34:08 +03:00
Tomer Keren
765f75a8f1 Replace asserts of any with an assert in a for 2019-05-25 19:34:08 +03:00
Tomer Keren
6b5152ae13 Sanity tests for loop unrolling 2019-05-25 19:34:08 +03:00
Daniel Hahler
10ca84ffc5 Merge pull request #5119 from blueyed/reportopts-A-order
reportopts: A: put "Pp" in front
2019-05-24 18:28:43 +02:00
Bruno Oliveira
e393a73890 Small change to test if pushing directly to master is disabled 2019-05-24 12:11:15 -03:00
Thomas Hisch
bed3918cbc Revert "logging: Simple impl of multline pretty printing"
This reverts commit 31dfbb4668.
2019-05-24 09:11:03 +02:00
Thomas Hisch
31dfbb4668 logging: Simple impl of multline pretty printing 2019-05-24 09:09:09 +02:00
Bruno Oliveira
b4d75ad31d Merge pull request #5091 from blueyed/showhelp-ini-options
Improve output of ini options in --help
2019-05-23 21:10:43 -03:00
Daniel Hahler
ec6d0fa4d7 Merge pull request #5268 from blueyed/harden-test
tests: harden test_nonascii_text
2019-05-24 01:51:35 +02:00
Daniel Hahler
fa8a658458 Merge pull request #4908 from blueyed/pdb-pm-enter-hook
pdb: trigger pytest_enter_pdb hook with post-mortem
2019-05-24 01:50:24 +02:00
Bruno Oliveira
66f20b6f5e Fix invalid Python file encoding "utf8" (#5252)
Fix invalid Python file encoding "utf8"
2019-05-23 20:24:23 -03:00
Bruno Oliveira
6ba5e3c071 tests: fix test_trace_after_runpytest (#5245)
tests: fix test_trace_after_runpytest
2019-05-23 19:58:33 -03:00
Bruno Oliveira
b900b4155f importorskip: display/include ImportError (#5269)
importorskip: display/include ImportError
2019-05-23 19:25:34 -03:00
Bruno Oliveira
364ae5d723 changelog: Fix typo (#5304)
changelog: Fix typo
2019-05-23 14:16:49 -03:00
David Röthlisberger
84c7fef836 changelog: Fix typo 2019-05-23 16:48:39 +01:00
Bruno Oliveira
aaea4e52ef Fix whitespace in FUNDING.yml 2019-05-23 12:44:55 -03:00
Bruno Oliveira
0cacdadc97 Create FUNDING.yml
Following instructions from:

* https://tidelift.com/subscription/how-to-connect-tidelift-with-github
* https://help.github.com/en/articles/displaying-a-sponsor-button-in-your-repository#displaying-a-sponsor-button-in-your-repository
2019-05-23 12:36:46 -03:00
Daniel Hahler
6c56070df1 Merge pull request #5278 from blueyed/disable-python-plugin
Allow disabling of python plugin
2019-05-23 15:52:19 +02:00
Daniel Hahler
f9f41e69a8 reportopts: A: put "Pp" in front 2019-05-23 09:40:16 +02:00
Daniel Hahler
b2ce6f3200 Improve output of ini options in --help
Do not cut long help texts, but wrap them the same way as argparse wraps
the other help items.
2019-05-23 09:33:07 +02:00
Daniel Hahler
e0b584d048 CaptureFixture: do not crash in _suspend when not started
This happened in test_pdb_with_caplog_on_pdb_invocation.
2019-05-23 09:09:53 +02:00
Daniel Hahler
a0ff5deabf pdb: trigger pytest_enter_pdb hook with post-mortem
This is required for pytest-pdb to be called with `--pdb`.

TODO:

- [ ] test
- [ ] pass mode to hook, e.g. "post_mortem" in this case?
2019-05-23 09:09:53 +02:00
Daniel Hahler
97b85a17ae changelog [ci skip] 2019-05-23 08:38:54 +02:00
Anthony Sottile
de7ba5958b Merge pull request #5294 from akiomik/fix-disable_test_id_escaping-option
Fix `disable_test_id_escaping_and_forfeit_all_rights_to_community_support` option when using a list of test IDs
2019-05-21 15:14:45 -07:00
Akiomi Kamakura
8a498700da Add tests for #5286 2019-05-22 03:56:52 +09:00
Anthony Sottile
45c894b73f Merge pull request #5297 from nicoddemus/tidelift-contents
Add link to tidelift to the main docs
2019-05-21 08:18:07 -07:00
Bruno Oliveira
65342db7a4 Update fixture.rst (#5295)
Update fixture.rst
2019-05-21 11:57:15 -03:00
Bruno Oliveira
d391274f39 Add link to tidelift to the main docs
As commented in:

https://github.com/pytest-dev/pytest/pull/5240#issuecomment-494175451
2019-05-21 11:12:07 -03:00
oleg-yegorov
c5fa1d1c3e Update fixture.rst
add a missing import
2019-05-21 09:40:31 +03:00
Akiomi Kamakura
a304dbb519 Fix disable_test_id_escaping_and_forfeit_all_rights_to_community_support option when using a list of test IDs 2019-05-21 09:48:22 +09:00
Daniel Hahler
d19df5efa2 importorskip: display/include ImportError
This can provide useful information, e.g.

> could not import 'pyrepl.readline': curses library not found
2019-05-20 14:13:33 +02:00
Bruno Oliveira
6663cb054c Fix documentation of pytest.raises(match=...) (#5288)
Fix documentation of pytest.raises(match=...)
2019-05-19 12:29:02 -03:00
Tim Hoffmann
b27e40cbf1 Fix documentation of pytest.raises(match=...) 2019-05-19 16:01:48 +02:00
Anthony Sottile
ee52a8a5f8 Merge pull request #5287 from clickthisnick/chore-fix-misspellings
Fix one misspelling in changelog
2019-05-18 15:30:35 -07:00
MyComputer
8f04bd003c Fix misspellings 2019-05-18 17:11:23 -04:00
Daniel Hahler
93fd9debe3 Allow disabling of python plugin
Fixes https://github.com/pytest-dev/pytest/issues/5277.
2019-05-17 18:17:14 +02:00
Daniel Hahler
ff428bfee1 tests: harden test_nonascii_text 2019-05-17 18:06:32 +02:00
Daniel Hahler
0f7c7a99bf Merge pull request #5281 from nicoddemus/merge-master-into-features
Merge master into features (make CI green again)
2019-05-17 16:58:16 +02:00
Bruno Oliveira
041ea3704b Merge remote-tracking branch 'upstream/master' into merge-master-into-features 2019-05-16 20:10:44 -03:00
Anthony Sottile
d94b4b031f Merge pull request #5271 from nicoddemus/lone-surrogate-jython-5256
Handle lone surrogate unicode character not being representable in Jython
2019-05-16 15:02:08 -07:00
Bruno Oliveira
e253029ad0 Handle lone surrogate unicode character not being representable in Jython
No tests for this because we don't test with Jython currently.

Fix #5256
2019-05-16 17:59:31 -03:00
Bruno Oliveira
43617a8c47 Disable PyPy on Azure (#5274)
Disable PyPy on Azure
2019-05-16 17:57:32 -03:00
Bruno Oliveira
56bf7446f6 Disable pypy2 and pypy3 on Azure [travis skip]
Related to #5279
2019-05-16 12:20:17 -03:00
Bruno Oliveira
8e42c5b7db Make it clear that small typos in docs don't require a CHANGELOG entry (#5276)
Make it clear that small typos in docs don't require a CHANGELOG entry
2019-05-16 12:15:15 -03:00
Bruno Oliveira
0571e1ee8e Make it clear that small typos in docs don't require a CHANGELOG entry
From: https://github.com/pytest-dev/pytest/pull/5273#issuecomment-493076587
2019-05-16 10:54:50 -03:00
Bruno Oliveira
b0a6161d41 Fixed double be word on monkeypatch docs page. (#5273)
Fixed double `be` word on monkeypatch docs page.
2019-05-16 10:53:08 -03:00
Bruno Oliveira
06fa2bc0b8 Re-enable pypy3 to see how it fares on Azure 2019-05-16 08:10:53 -03:00
Bruno Oliveira
8abd4aec6e Remove customization of (python.exe) from Azure [skip travis]
This is no longer needed it seems
2019-05-16 08:07:59 -03:00
Nikita Krokosh
858010e214 Fixed double be word on monkeypatch docs page. 2019-05-16 18:22:41 +10:00
Bruno Oliveira
96424272a1 Improve mark docs (#5265)
Improve mark docs
2019-05-15 20:52:21 -03:00
Bruno Oliveira
e44a2ef653 Apply suggestions from code review
Co-Authored-By: Daniel Hahler <github@thequod.de>
2019-05-15 20:45:22 -03:00
Bruno Oliveira
c6e3ff3ce5 Mention "-m" in the main mark docs 2019-05-15 20:45:22 -03:00
Bruno Oliveira
a31098a74e Move section about mark revamp and iteration to historical notes
This has been in place for a long time now, since 3.6.
2019-05-15 20:45:22 -03:00
Bruno Oliveira
7e8044f9b8 Revamp the mark section in the docs
Add an introductory section about how to register marks,
including doing so programatically (#5255).
2019-05-15 20:45:22 -03:00
Anthony Sottile
b81173ea0c Upgrade pre-commit/pre-commit-hooks 2019-05-15 10:19:12 -07:00
Daniel Hahler
c8f7e50c47 Merge pull request #5262 from asottile/mode_non_binary
Remove 'b' from sys.stdout.mode
2019-05-15 10:56:36 +02:00
Daniel Hahler
c0e53a61e6 Merge pull request #5263 from blueyed/mm
Merge master to fix CI
2019-05-15 10:55:54 +02:00
Bruno Oliveira
494ac28a32 Merge pull request #5250 from EvanKepner/master
Documentation: add setenv/delenv examples to monkeypatch docs
2019-05-14 23:10:58 -03:00
Anthony Sottile
dc75b6af47 Use fix-encoding-pragma pre-commit hook 2019-05-14 15:56:31 -07:00
Bruno Oliveira
7573747cda Normalize all source encoding declarations 2019-05-14 19:42:44 -03:00
Daniel Hahler
2db05b6582 Merge master into features 2019-05-15 00:11:37 +02:00
Anthony Sottile
4318698bae Remove 'b' from sys.stdout.mode 2019-05-14 15:00:59 -07:00
Bruno Oliveira
e668aaf885 Fix indentation and use code-block directives
The code-block directives are required by our blacken-docs hook
2019-05-14 18:59:27 -03:00
Daniel Hahler
58e6a09db4 Merge pull request #5261 from blueyed/fix-py37-freeze-remove-no-use-pep517
ci: tox: fix py37-freeze
2019-05-14 23:57:09 +02:00
Daniel Hahler
6718a2f028 ci: tox: fix py37-freeze
Remove ``--no-use-pep517``, which appears to not be supported anymore,
and PyInstaller works without it by now.
2019-05-14 23:49:57 +02:00
Daniel Hahler
c081c01eb1 minor: s/no covers/no cover/ 2019-05-14 06:51:49 +02:00
Daniel Hahler
f8e1d58e8f minor: settrace != set_trace 2019-05-14 06:51:30 +02:00
Raul Tambre
18024467ff Fix invalid Python file encoding "utf8"
Since Python 3 it must be "utf-8", which is the official name.
This is backwards compatible with Python 2.
2019-05-12 19:14:06 +03:00
Evan Kepner
2ad36b1402 add #5250 changelog entry 2019-05-12 10:08:46 -04:00
Evan Kepner
6ca3e1e425 add @EvanKepner to authors 2019-05-12 10:04:24 -04:00
Evan Kepner
ecd072ea94 add env example to monkeypatch docs 2019-05-12 09:52:22 -04:00
Daniel Hahler
dda21935a7 tests: fix test_trace_after_runpytest
It was not really testing what it was supposed to test (e.g. the inner
test was not run in the first place).
2019-05-12 09:33:34 +02:00
Bruno Oliveira
cc464f6b96 Merge master into features (#5248)
Merge master into features
2019-05-11 22:33:44 -03:00
Bruno Oliveira
6a43c8cd94 Release 4.5.0 (#5246)
Release 4.5.0
2019-05-11 21:40:27 -03:00
Bruno Oliveira
63fe547d9f Preparing release version 4.5.0 2019-05-11 16:35:32 +00:00
Bruno Oliveira
b709e61892 Merge remote-tracking branch 'upstream/master' into release-4.5.0 2019-05-11 16:30:23 +00:00
Bruno Oliveira
465b2d998a Further "unknown marks warning" improvements (#5178)
Further "unknown marks warning" improvements
2019-05-11 13:28:10 -03:00
Bruno Oliveira
184ef92f0b Introduce record_testsuite_property fixture (#5205)
Introduce record_testsuite_property fixture
2019-05-11 13:27:14 -03:00
Bruno Oliveira
73bbff2b74 Introduce record_testsuite_property fixture
This exposes the functionality introduced in fa6acdc as a session-scoped fixture.

Plugins that want to remain compatible with the `xunit2`
standard should use this fixture instead of `record_property`.

Fix #5202
2019-05-10 19:44:27 -03:00
Floris Bruynooghe
4ccaa987d4 Merge pull request #5240 from nicoddemus/tidelift
Add Tidelift management docs and blurb on README, as discussed in the ML
2019-05-10 13:27:57 +02:00
Bruno Oliveira
3a4a815c41 Merge master into features (#5233)
Merge master into features
2019-05-09 20:13:01 -03:00
Bruno Oliveira
dae455e8a3 Add Tidelift management docs and blurb on README, as discussed in the ML 2019-05-09 20:10:30 -03:00
Bruno Oliveira
0594dba5ce Remove unused markers and enable --strict-markers 2019-05-09 19:36:39 -03:00
Bruno Oliveira
f1183c2422 Remove the 'issue' marker from test suite
It doesn't seem to add much value (why would one execute tests
based on that marker?), plus using the docstring for that
encourages one to write a more descriptive message about the test
2019-05-09 19:36:38 -03:00
Bruno Oliveira
685ca96c71 Change `--strict to --strict-markers`, preserving the old one
Fix #5023
2019-05-09 19:36:38 -03:00
Bruno Oliveira
ccf6c3cb46 Merge remote-tracking branch 'upstream/master' into merge-master-into-features 2019-05-09 19:22:40 -03:00
Daniel Hahler
ceca35b94a Merge pull request #5236 from blueyed/pdb-eoferror-quit
pdb: only use outcomes.exit via do_quit
2019-05-09 17:10:36 +02:00
Bruno Oliveira
803d68847b Pin pluggy to <1.0 (#5239)
Pin pluggy to <1.0
2019-05-09 11:03:36 -03:00
Bruno Oliveira
0bd02cd1bc Ignore PytestUnknownMark warning when generating docs (#5234)
Ignore PytestUnknownMark warning when generating docs
2019-05-09 11:03:20 -03:00
Daniel Hahler
0b8b006db4 minor: improve formatting 2019-05-09 15:36:49 +02:00
Daniel Hahler
73b74c74c9 pdb: only use outcomes.exit via do_quit
Fixes https://github.com/pytest-dev/pytest/issues/5235.
2019-05-09 14:55:55 +02:00
Bruno Oliveira
4d782dc13f Add CHANGELOG for #5239 2019-05-09 09:00:39 -03:00
Bruno Oliveira
e1756fc631 Pin pluggy to <1.0
Make sure we don't update to pluggy 1.0 automatically, as there are planned breaking
changes in the 1.0 release.
2019-05-09 08:47:01 -03:00
Bruno Oliveira
5d7686951c Run regendoc 2019-05-08 21:50:08 +00:00
Bruno Oliveira
80c5f6e609 Ignore PytestUnknownMark warnings when regen docs
A lot of our examples use custom markers to make a point and showcase
features, which generates a lot of warnings
2019-05-08 21:46:26 +00:00
Bruno Oliveira
0b47e51d08 Release version 4.4.2 (#5230)
Release version 4.4.2
2019-05-08 18:07:05 -03:00
Daniel Hahler
5eeb5ee960 Merge pull request #5013 from blueyed/short-summary-message
Display message from reprcrash in short test summary
2019-05-08 22:01:04 +02:00
Daniel Hahler
ed2b715f4c Merge pull request #5227 from Pulkit07/issue5214
logging: improve default logging format (issue5214)
2019-05-08 21:51:54 +02:00
Pulkit Goyal
7e08e09473 logging: improve default logging format (issue5214)
We improve the following things in the logging format:

  * Show module name instead of just the filename
  * show level of logging as the first thing
  * show lineno attached to module:file details

Thanks to @blueyed who suggested this on the github issue.

It's my first contribution and I have added myself to AUTHORS.

I also added to a changelog file.
2019-05-08 21:37:59 +03:00
Bruno Oliveira
3ac43314ee Preparing release version 4.4.2 2019-05-08 12:46:14 -03:00
Bruno Oliveira
972410f8b6 Require pluggy>=0.11 (#5229)
Require pluggy>=0.11
2019-05-08 12:40:49 -03:00
Romain Chossart
0cf267f187 Add changelog entry for #5229 2019-05-08 16:13:28 +01:00
Sitaktif
7161f5b372 Require pluggy>=0.11
Pluggy 0.10.0 introduced a new dependency `importlib-metadata` but that
package cannot be imported when installed as an egg and that is causing
problems for users relying on `setup.py` to install packages present in
`test_require`. This change was quickly reverted in pluggy 0.11.0.

See https://github.com/pytest-dev/pluggy/issues/205
2019-05-08 16:06:41 +01:00
Bruno Oliveira
2051e30b9b Show fixture scopes with `--fixtures`, except for "function" scope (#5221)
Show fixture scopes with ``--fixtures``, except for "function" scope
2019-05-07 19:50:53 -03:00
Bruno Oliveira
f339147d12 Add CHANGELOG entry about depending on wcwidth 2019-05-07 19:34:57 -03:00
Bruno Oliveira
c04767f946 Use msg.rstrip() as suggested in review 2019-05-07 15:20:00 -03:00
Bruno Oliveira
6d040370ed Show fixture scopes with `--fixtures`, except for "function" scope
Fix #5220
2019-05-07 14:32:20 -03:00
Bruno Oliveira
1d466d0aa7 Use exec directly (#5224)
Use exec directly
2019-05-07 10:02:12 -03:00
Bruno Oliveira
ef4dec0bcf Skip test_source_mtime_long_long on 32bit and lower platforms (#5045)
Skip test_source_mtime_long_long on 32bit and lower platforms
2019-05-07 09:42:18 -03:00
Bruno Oliveira
d76735f9e5 Restore usedevelop=True now that pip 19.1.1 restored the old behavior (#5222)
Restore usedevelop=True now that pip 19.1.1 restored the old behavior
2019-05-07 08:46:47 -03:00
Anthony Sottile
d1a48ad68f Use exec directly 2019-05-06 23:07:39 -07:00
Bruno Oliveira
6d259c400e Merge pull request #5223 from 5uper5hoot/patch-1
Clarify docs for pytest.raises `match`.
2019-05-06 21:57:47 -03:00
Bruno Oliveira
d3686361ba Fix linting 2019-05-07 00:52:40 +00:00
Peter Schutt
8605ed2a15 Update python_api.py 2019-05-07 10:10:25 +10:00
Peter Schutt
8b34d981fc Clarify docs for pytest.raises match.
For #5208.

Document explicit behavior of `match` and brief note on how to handle matching a string that may contain special re chars.
2019-05-07 09:56:13 +10:00
Bruno Oliveira
2795689435 Restore usedevelop=True now that pip 19.1.1 restored the old behavior
Fix #5167
2019-05-06 19:42:06 -03:00
Bruno Oliveira
dcf65a9643 Add links to pytest.raises message workaround (#5218)
Add links to pytest.raises `message` workaround
2019-05-06 18:56:40 -03:00
Bruno Oliveira
784e1e3b7e Add links to pytest.raises message workaround
Related to #3974
2019-05-06 12:35:27 -03:00
Bruno Oliveira
4a2fdce62b Emit a warning for record_property when used with xunit2 (#5204)
Emit a warning for record_property when used with xunit2
2019-05-05 21:33:31 -03:00
Bruno Oliveira
32a5e80a6d Add encoding: header and fix rep mock in test_line_with_reprcrash on py27 2019-05-05 09:33:37 -03:00
Bruno Oliveira
0e8a8f94f6 Add encoding header to test_terminal.py 2019-05-05 09:14:07 -03:00
Bruno Oliveira
8f23e19bcb Emit a warning for record_property when used with xunit2
"property" elements cannot be children of "testsuite" according to the schema, so it is incompatible with xunit2

Related to #5202
2019-05-05 09:01:13 -03:00
Daniel Hahler
f5c1f3df71 Merge pull request #5210 from nicoddemus/merge-master-into-features
Merge master into features
2019-05-05 08:02:06 +02:00
Daniel Hahler
b25802eca7 Merge pull request #5212 from jdufresne/new-black
Update Black URLs
2019-05-05 07:58:27 +02:00
Daniel Hahler
7f7f84757d Merge pull request #5211 from hugovk/upgrade-pypy
Upgrade PyPy to 7.1.1
2019-05-05 04:50:11 +02:00
Jon Dufresne
143499d041 Update Black URLs
> Black, your uncompromising #Python code formatter, was a project
> created with the community in mind from Day 1. Today we moved it under
> the PSF umbrella. It's now available on GitHub under
> https://github.com/python/black/ . You install and use it just like
> before.

https://twitter.com/llanga/status/1123980466292445190
2019-05-04 08:28:50 -07:00
Hugo
26b41a5914 Upgrade PyPy to 7.1.1 2019-05-04 17:54:57 +03:00
Bruno Oliveira
9f66102869 Merge remote-tracking branch 'upstream/master' into merge-master-into-features 2019-05-04 10:47:57 -03:00
Bruno Oliveira
e1a426c067 Unify pypy (#5209)
Unify pypy
2019-05-04 10:46:14 -03:00
Bruno Oliveira
2bd97ebaf7 Use 'pypy' as executable on PyPy env [skip travis] 2019-05-04 10:27:06 -03:00
Bruno Oliveira
45eeb53c98 Fix a typo (#5207)
Fix a typo
2019-05-04 10:04:50 -03:00
Don Kirkby
254b195f50 Fix a typo 2019-05-03 22:30:20 -07:00
Daniel Hahler
865e84d206 Merge pull request #5109 from blueyed/summary_passes-green
summary_passes: use bold green for report headers
2019-05-04 00:46:23 +02:00
Bruno Oliveira
1c13418a8b doc: fix hooks 'path' parameter doc type (#5198)
doc: fix hooks 'path' parameter doc type
2019-05-02 21:05:07 -03:00
DamianSkrzypczak
299e6479c1 add DamianSkrzypczak to AUTHORS 2019-05-02 23:45:34 +02:00
DamianSkrzypczak
6e81c3df92 add changelog for issue #5171 fixes 2019-05-02 21:30:03 +02:00
DamianSkrzypczak
1bd7d287a7 doc: fix hooks 'path' parameter doc type
by changing it from str to py.path.local
(#5171)
2019-05-02 20:51:11 +02:00
Anthony Sottile
b82e1b87cc Merge pull request #5195 from nicoddemus/pytest-config-docs
Add quick note about accessing config through session or item objects
2019-05-02 09:52:42 -07:00
Bruno Oliveira
af40473c9a Add quick note about accessing config through session or item objects
Fix #5030
2019-05-01 11:46:35 -03:00
Bruno Oliveira
f17b734989 Improve help for --runxfail flag (#5188)
Improve help for --runxfail flag
2019-05-01 11:00:31 -03:00
Allan Lewis
f050203f5d Improve help for --runxfail flag
The help for the '--runxfail' flag is somewhat misleading. The default
behaviour is to run tests marked as 'xfail' but to ignore the results. This
flag alters that behaviour by running these tests as if they weren't marked
'xfail', i.e. their results are not ignored.
2019-05-01 10:04:32 +01:00
Bruno Oliveira
3f5622c577 Use config_invocation_dir for startdirs (#5151)
Use config_invocation_dir for startdirs
2019-04-29 18:14:51 -03:00
Bruno Oliveira
1fb3f63f35 debugging: rename internal wrapper for pdb.Pdb (#5186)
debugging: rename internal wrapper for pdb.Pdb
2019-04-29 18:03:32 -03:00
Bruno Oliveira
fc2ad1dbed Warning subclasses (#5179)
Warning subclasses
2019-04-29 17:57:49 -03:00
Matt Cooper
bc6450773a remove PyPy special casing 2019-04-29 08:39:48 -04:00
Bruno Oliveira
204004c8b8 Review doctest docs (#5183)
Review doctest docs
2019-04-29 08:02:59 -03:00
Bruno Oliveira
772a4a5cf3 Remove all version references to obsolete pytest versions (#5184)
Remove all version references to obsolete pytest versions
2019-04-29 08:02:32 -03:00
Bruno Oliveira
fb6dad60a0 terminal: use pytest_collection_finish for reporting (#5113)
terminal: use pytest_collection_finish for reporting
2019-04-29 08:00:48 -03:00
Bruno Oliveira
d9cad1e759 Merge pull request #5176 from nicoddemus/initial-conftests-note
Add note about pytest_load_initial_conftests working only from plugins
2019-04-29 07:59:27 -03:00
Daniel Hahler
02053bf556 debugging: rename internal wrapper for pdb.Pdb
This is useful/clearer in case of errors / tracebacks - i.e. you see
clearly that it is coming from pytest.
2019-04-29 05:46:21 +02:00
Anthony Sottile
6fb3baf071 Merge pull request #5182 from nicoddemus/removed-unused-deprecation-msgs
Removed unused warning message
2019-04-28 20:26:11 -07:00
Daniel Hahler
ff5317a7f3 terminal: use pytest_collection_finish for reporting 2019-04-29 05:20:04 +02:00
Bruno Oliveira
766fc23151 Merge pull request #5185 from nicoddemus/lf-skip-files
--lf now skips colletion of files without failed tests
2019-04-28 21:48:53 -03:00
Bruno Oliveira
08734bdd18 --lf now skips colletion of files without failed tests
Fix #5172
2019-04-28 20:23:46 -03:00
Bruno Oliveira
e943aff995 Update blurb about Sybil 2019-04-28 20:01:22 -03:00
Bruno Oliveira
9c5da9c0d1 Remove all version references to obsolete pytest versions
Remove version references to pytest 2 and 3.

Just like Python 3 no longer has references to Python 2, I think
we should follow the same approach in pytest.
2019-04-28 12:37:58 -03:00
Bruno Oliveira
f6ab6d71ad Run regendoc 2019-04-28 12:17:15 -03:00
Bruno Oliveira
67755d67fb Review doctest docs
* Add pytest.skip() example
* Add blurb about Sybil
* Create a subsection for doctest-options
* Create a subsection for pytest-specific features
2019-04-28 12:17:15 -03:00
Bruno Oliveira
a3c2ec3c5b Add CHANGELOG for #5182 2019-04-28 11:10:21 -03:00
Bruno Oliveira
9742f11d37 Removed unused warning message 2019-04-28 11:06:47 -03:00
Bruno Oliveira
915ecb0dac Add CHANGELOG for #5177 2019-04-28 10:47:19 -03:00
Bruno Oliveira
53cd7fd2ea Introduce new warning subclasses
Fix #5177
2019-04-28 10:38:25 -03:00
Bruno Oliveira
8532e991a5 Publish UnknownMarkWarning as part of the public API and docs 2019-04-28 10:16:07 -03:00
Bruno Oliveira
50937fe622 Add note about pytest_load_initial_conftests working only from plugins
Fix #5175
2019-04-27 10:12:27 -03:00
Daniel Hahler
ebc0cea226 Merge pull request #5174 from blueyed/no-terminal
Terminal plugin is not semi-essential anymore
2019-04-27 09:51:32 +02:00
Daniel Hahler
65133018f3 Terminal plugin is not semi-essential anymore
Thanks to https://github.com/pytest-dev/pytest/pull/5138.
2019-04-27 03:39:00 +02:00
Anthony Sottile
ac7ee40aaf Merge pull request #5173 from blueyed/revert
Revert "Merge pull request #4854 from blueyed/pdb-skip"
2019-04-26 17:48:04 -07:00
Daniel Hahler
308b733b9d Revert "Merge pull request #4854 from blueyed/pdb-skip"
This reverts commit e88aa957ae, reversing
changes made to 1410d3dc9a.

I do not think it warrants an option anymore, and there is a way to
achieve this via `--pdbcls` if needed.
2019-04-27 02:25:38 +02:00
Daniel Hahler
76c6ed8b05 Merge pull request #5162 from blueyed/mm
Merge master into features
2019-04-27 01:42:42 +02:00
Daniel Hahler
7f519f8ab7 Merge master into features 2019-04-27 00:43:00 +02:00
Bruno Oliveira
9c700d1fd5 Workaround for editable installs with pip 19.1 (#5168)
Workaround for editable installs with pip 19.1
2019-04-26 18:29:28 -03:00
Bruno Oliveira
19cd4d0af7 Workaround for editable installs with pip 19.1
Related to #5167
2019-04-25 19:40:01 -03:00
Bruno Oliveira
bf0fe1a1fa GitHub: revisit issue/PR templates [ci skip] (#5158)
GitHub: revisit issue/PR templates  [ci skip]
2019-04-24 12:09:55 -03:00
Daniel Hahler
3df32e2732 GitHub: revisit issue/PR templates [ci skip]
The motivation here is to not have the same boilerplate with PRs.
2019-04-23 18:01:13 +02:00
Daniel Hahler
0bf363472e Use config_invocation_dir for startdirs
`Session.startdir` and `TerminalReporter.startdir` appear to be
redundant given `Config.invocation_dir`.
Keep them for backward compatibility reasons, but use
`config.invocation_dir` for them.
2019-04-22 02:05:59 +02:00
Daniel Hahler
34bc594beb Merge pull request #5133 from kondratyev-nv/fix-handle-repr-error-with-showlocals
Fix handle repr error with showlocals and verbose output
2019-04-19 19:23:39 +02:00
Nikolay Kondratyev
bc00d0f7db Fix handle repr error with showlocals and verbose output 2019-04-19 18:54:21 +03:00
Daniel Hahler
e3e57a755b Merge pull request #5083 from blueyed/capture-_suspended
capture: store _state
2019-04-19 17:32:54 +02:00
Daniel Hahler
79d5fc3a0b Merge pull request #5144 from blueyed/sessionfinish-exitstatus
pytest_sessionfinish: preset exitstatus with UsageErrors
2019-04-19 17:30:01 +02:00
Bruno Oliveira
322d686ab4 Merge pull request #5145 from blueyed/prefer-fast-wrapper
conftest: use a hookwrapper with sorting faster tests first
2019-04-19 12:25:55 -03:00
Daniel Hahler
f75f7c1925 conftest: use a hookwrapper with sorting faster tests first 2019-04-19 01:23:34 +02:00
Daniel Hahler
698c4e75fd capture: track current state in _state attributes
This is meant for debugging, and making assertions later.
2019-04-19 00:35:15 +02:00
Daniel Hahler
4749dca764 changelog [ci skip] 2019-04-18 23:54:20 +02:00
Daniel Hahler
649d23c8a8 pytest_sessionfinish: preset exitstatus with UsageErrors
Previously it would be 0.  Setting it to the expected outcome
(EXIT_USAGEERROR) here already helps `pytest_sessionfinish` hooks.
2019-04-18 23:18:59 +02:00
Daniel Hahler
e87d3d70e2 Merge pull request #5138 from ikonst/notify_exception_without_terminal
Fix dependencies on 'terminal' plugin
2019-04-17 23:20:49 +02:00
Daniel Hahler
1460ad6027 Merge pull request #5111 from blueyed/deselect_by_keyword-skip
deselect_by_keyword: skip without expression
2019-04-17 23:20:02 +02:00
Daniel Hahler
78cd1a07d0 Merge pull request #5140 from blueyed/rewrite-name
assertion rewriting: use actual helper name
2019-04-17 23:19:38 +02:00
Ilya Konstantinov
d67d68f6d3 Eliminate core dependency on 'terminal' plugin 2019-04-17 11:19:19 -07:00
Bruno Oliveira
008d04398a Merge pull request #5135 from dougthor42/patch-1
[Docs] Fix typo in skipping.rst
2019-04-17 14:36:57 -03:00
Daniel Hahler
c3178a176d move test 2019-04-17 15:30:34 +02:00
Daniel Hahler
df1d1105b0 Merge remote-tracking branch 'origin/features' into short-summary-message
Conflicts:
	src/_pytest/skipping.py
2019-04-17 15:30:19 +02:00
Daniel Hahler
2b11b2c093 Merge pull request #5126 from blueyed/report-get_verbose_word
terminal/reports: use get_verbose_word method
2019-04-17 14:43:54 +02:00
Daniel Hahler
adb8edbae1 assertion rewriting: use actual helper name
This makes it easier / possible to grep.
2019-04-17 14:41:34 +02:00
Douglas Thor
0e651d7297 [Docs] Fix typo in skipping.rst
One of the `pytest.mark.skipif` blocks does not use the `reason` kwarg.
2019-04-16 09:30:13 -07:00
Anthony Sottile
3cff5e252d Merge pull request #5117 from blueyed/cov-terminal
Minor coverage improvements for terminal plugin
2019-04-15 14:30:18 -07:00
Daniel Hahler
7412df0920 fixup! terminal: summary_errors: replace if with assert 2019-04-15 22:53:31 +02:00
Anthony Sottile
990133f804 Merge pull request #5132 from nicoddemus/merge-master-into-features
Merge master into features
2019-04-15 13:11:33 -07:00
Bruno Oliveira
df68808d29 Merge pull request #5116 from blueyed/cov-pytester
Minor coverage improvements for pytester
2019-04-15 14:49:28 -03:00
Bruno Oliveira
b64d9402ca Merge pull request #5108 from blueyed/summary
terminal summary: display passes after warnings
2019-04-15 14:49:01 -03:00
Bruno Oliveira
8d3a5dcd1b Merge remote-tracking branch 'upstream/master' into merge-master-into-features 2019-04-15 14:47:45 -03:00
Bruno Oliveira
9eac4733c5 Merge pull request #5129 from nicoddemus/release-4.4.1
Release version 4.4.1
2019-04-15 14:41:36 -03:00
Bruno Oliveira
24bd51bda0 Merge pull request #5131 from blueyed/_getfailureheadline
_getfailureheadline: get head_line property only once
2019-04-15 12:23:41 -03:00
Daniel Hahler
9374114370 terminal/reports: add/use _get_verbose_word method 2019-04-15 17:06:18 +02:00
Daniel Hahler
eb13530560 _getfailureheadline: get head_line property only once 2019-04-15 17:04:51 +02:00
Daniel Hahler
5530d3e15d Merge pull request #5120 from blueyed/minor
A collection of minor code tweaks
2019-04-15 16:32:20 +02:00
Daniel Hahler
ea79eb5c3f terminal summary: display passes after warnings
This displays passes (with output, `-rP`) before the short summary, and
before any other output from other plugins also.
2019-04-15 16:31:42 +02:00
Bruno Oliveira
c8b904a406 Merge pull request #5124 from blueyed/terminal-summary_failures
terminal: revisit summary_failures
2019-04-15 11:26:52 -03:00
Bruno Oliveira
d45fa7b212 Merge pull request #5123 from blueyed/clsname
TestReport: use class name in repr
2019-04-15 11:25:42 -03:00
Bruno Oliveira
852ca7ad59 Merge pull request #5114 from blueyed/fixtest
test_pytest_exit_returncode: ignore ResourceWarnings
2019-04-15 11:25:16 -03:00
Bruno Oliveira
006dc30476 Preparing release version 4.4.1 2019-04-15 14:24:17 +00:00
Daniel Hahler
45ba736c81 Merge pull request #5128 from blueyed/mark-is
Fix error message with unregistered markers
2019-04-15 16:23:54 +02:00
Bruno Oliveira
c453fe7053 Merge pull request #5118 from blueyed/mm
Merge master into features
2019-04-15 11:21:59 -03:00
Daniel Hahler
cc005af47e Fix error message with unregistered markers 2019-04-15 10:15:37 +02:00
Daniel Hahler
20c624efcf terminal: revisit summary_failures
- get the list of reports for teardown sections only once
- do not check option in loop
2019-04-15 06:47:51 +02:00
Daniel Hahler
b2be6c1a30 TestReport: use class name in repr 2019-04-15 05:47:45 +02:00
Daniel Hahler
c43a9c83ee doc: pytest_deselected: not only via keywords 2019-04-14 23:53:06 +02:00
Daniel Hahler
e804e419bc remove unnecessary newline 2019-04-14 23:53:06 +02:00
Daniel Hahler
992e7f7771 rename variable 2019-04-14 23:53:06 +02:00
Daniel Hahler
bd1a2e6435 fix typo 2019-04-14 23:53:06 +02:00
Daniel Hahler
1d137fd2fe minor: LFPlugin: de-indent code by returning if not active 2019-04-14 23:53:06 +02:00
Daniel Hahler
f1f1862b19 Update testing/test_runner.py 2019-04-14 23:26:56 +02:00
Daniel Hahler
6f0a5789fb Merge master into features 2019-04-14 23:22:21 +02:00
Daniel Hahler
cc78a533ae terminal: summary_errors: replace if with assert 2019-04-14 23:17:27 +02:00
Daniel Hahler
fd0b3e2e8b getreportopt: remove needless if 2019-04-14 23:17:27 +02:00
Daniel Hahler
f3dbe5a308 pytester: listoutcomes: assert instead of implicit if 2019-04-14 23:00:38 +02:00
Daniel Hahler
1da8ce65a6 pytester: raise_on_kwargs: ignore branch coverage 2019-04-14 23:00:38 +02:00
Daniel Hahler
1dd5f088fa test_pytest_exit_returncode: ignore ResourceWarnings
Fixes https://github.com/pytest-dev/pytest/issues/5088.
2019-04-14 21:55:15 +02:00
Daniel Hahler
6a73714b00 deselect_by_keyword: skip without expression
There is no need to iterate over all items always, if `-k` is not
specified.
2019-04-14 19:01:33 +02:00
Daniel Hahler
6371243c10 summary_passes: use bold green for report headers 2019-04-14 15:21:11 +02:00
Daniel Hahler
533e610a35 Merge pull request #5098 from blueyed/fix-syspath_prepend
monkeypatch.syspath_prepend: invalidate import cache
2019-04-13 18:46:16 +02:00
Daniel Hahler
8fd5a658eb monkeypatch.syspath_prepend: invalidate import cache
This was done with testdir only, and uses the fixed monkeypatch method
there now.
2019-04-13 16:15:44 +02:00
Anthony Sottile
c3b7efc818 Merge pull request #5097 from asottile/unknown_kwargs_param
Produce a warning when unknown arguments are passed to pytest.param()
2019-04-12 13:30:07 -07:00
Anthony Sottile
43e7401c91 Produce a warning when unknown arguments are passed to pytest.param() 2019-04-12 13:00:39 -07:00
Bruno Oliveira
a9e850f749 Merge pull request #5096 from asottile/docs_highlight
blacken-docs more code samples in docs
2019-04-12 10:00:26 -03:00
Anthony Sottile
da2e092163 pre-commit autoupdate 2019-04-12 04:52:47 -07:00
Anthony Sottile
8449294e5d blacken-docs more code samples in docs 2019-04-12 04:50:26 -07:00
Bruno Oliveira
19035f4b55 Merge pull request #5068 from blueyed/reportchars
Add support for reportchars=A (`-rA`)
2019-04-12 08:13:22 -03:00
Bruno Oliveira
48ed437e70 Merge pull request #5087 from samueljsb/issue/4907
Show XFail reason as part of JUnitXML message field
2019-04-11 19:20:21 -03:00
Bruno Oliveira
14d3d9187f Remove partial unicode characters from summary messages in Python 2 2019-04-11 19:01:21 -03:00
Samuel Searles-Bryant
a37d1df089 Show XFail reason as part of JUnitXML message field
Fixes #4907
2019-04-11 22:26:05 +01:00
Daniel Hahler
97cd5f0deb Merge pull request #5094 from blueyed/merge-master
Merge master into festures
2019-04-11 23:05:29 +02:00
Bruno Oliveira
1dafe969d1 Merge pull request #5093 from blueyed/tbreportdemo
docs: tbreportdemo: remove obsolete comment
2019-04-11 13:07:02 -03:00
Daniel Hahler
8b2fcf517c Merge master 2019-04-11 13:28:36 +02:00
Daniel Hahler
42e60d935a doc/changelog 2019-04-11 11:44:04 +02:00
Daniel Hahler
1f66e3b0d0 docs: tbreportdemo: remove obsolete comment 2019-04-11 11:39:23 +02:00
Daniel Hahler
b3759372ad Merge pull request #5082 from blueyed/pytester-raise_on_kwargs
pytester: improve/fix kwargs validation
2019-04-11 00:46:18 +02:00
Bruno Oliveira
f5d2b199e2 Merge pull request #5069 from blueyed/cleanup-summary-to-terminal
cleanup: move terminal summary code to terminal plugin
2019-04-10 19:32:01 -03:00
Bruno Oliveira
bc157417e1 Merge pull request #5051 from blueyed/fix-test
Fix test_conftest when run via pytest-randomly
2019-04-10 19:08:28 -03:00
Daniel Hahler
12133d4eb7 changelog [ci skip] 2019-04-10 23:15:25 +02:00
Daniel Hahler
148f2fc72c Fix test_error_during_readouterr: syspathinsert is unused 2019-04-10 21:57:45 +02:00
Daniel Hahler
5d9d12a6be pytester: improve/fix kwargs validation 2019-04-10 21:57:45 +02:00
Daniel Hahler
dde27a2305 changelog [ci skip] 2019-04-10 21:39:51 +02:00
Daniel Hahler
10fa66e5b5 Merge pull request #5059 from blueyed/pytester-popen-run-stdin
pytester: allow passing in stdin to run/popen
2019-04-10 20:32:22 +02:00
Bruno Oliveira
5e26304d81 Merge pull request #5075 from blueyed/console_output_style
terminal: console_output_style: document "count" with help
2019-04-09 20:54:12 -03:00
Bruno Oliveira
d0860a339b Merge pull request #5076 from blueyed/_show_progress_info
terminal: store console_output_style in _show_progress_info
2019-04-09 20:37:50 -03:00
Daniel Hahler
fcbfdef11b Merge pull request #5071 from blueyed/prefer-fast
Add a conftest to prefer faster tests
2019-04-10 01:22:24 +02:00
Daniel Hahler
b84f826fc8 test_run_stdin: add sleep 2019-04-10 00:03:49 +02:00
Daniel Hahler
ec46864922 run: pass through stdin, just close then 2019-04-10 00:02:38 +02:00
Daniel Hahler
c36a90531a Move CLOSE_STDIN to class 2019-04-10 00:01:15 +02:00
Daniel Hahler
3fa329c9e9 Merge pull request #5074 from blueyed/trace
pdb: add test for --trace with --pdbcls
2019-04-08 18:48:44 +02:00
Daniel Hahler
a70e5f119e terminal: store console_output_style in _show_progress_info
Avoids ini lookups.
2019-04-08 04:34:26 +02:00
Daniel Hahler
b6b7185b7b terminal: console_output_style: document "count" with help 2019-04-08 04:32:53 +02:00
Daniel Hahler
4fb7a91a5e pdb: add test for --trace with --pdbcls
Ensures that https://github.com/pytest-dev/pytest/issues/4111 is fixed,
which happened in 92a2884b as a byproduct.
2019-04-08 03:49:03 +02:00
Daniel Hahler
ff5e98c654 Change noqa comment to pragma 2019-04-08 03:48:42 +02:00
Daniel Hahler
2662c400ba dedent 2019-04-07 20:04:31 +02:00
Daniel Hahler
d8d835c1f5 minor: use functools.partial 2019-04-07 19:49:18 +02:00
Daniel Hahler
06029d11d3 Refactor into TerminalReporter.short_test_summary 2019-04-07 19:49:18 +02:00
Daniel Hahler
4c0ba6017d Add a conftest to prefer faster tests
This uses pytest_collection_modifyitems for pytest's own tests to order
them, preferring faster ones via quick'n'dirty heuristics only for now.
2019-04-07 19:11:17 +02:00
Daniel Hahler
c70ecd49ca cleanup: move terminal summary code to terminal plugin
Fixes https://github.com/pytest-dev/pytest/issues/5067.
2019-04-07 18:22:04 +02:00
Daniel Hahler
50edab8004 Add tests for reportchars=a
Ref: https://github.com/pytest-dev/pytest/issues/5066
2019-04-07 18:05:33 +02:00
Daniel Hahler
b4b9f788af Support reportchars=A (really all, including passed) 2019-04-07 18:05:33 +02:00
Daniel Hahler
a7e49e6c07 reportchars: fix/improve help message 2019-04-07 18:05:33 +02:00
Daniel Hahler
2b1ae8a66d __tracebackhide__ for check 2019-04-06 15:00:23 +02:00
Daniel Hahler
2ebb69b50a py2 fixes 2019-04-06 15:00:12 +02:00
Daniel Hahler
4fca86e2af testdir.popen: use kwargs with defaults for stdout/stderr 2019-04-06 12:13:48 +02:00
Daniel Hahler
9ad00714ba pytester: allow passing in stdin to run/popen 2019-04-06 12:13:28 +02:00
Bruno Oliveira
b549438423 Merge pull request #5058 from blueyed/merge-master-into-features
Merge master into features
2019-04-05 14:28:48 -03:00
Daniel Hahler
377888140f Merge master into features
Conflicts:
	testing/test_pdb.py
2019-04-05 17:50:32 +02:00
Daniel Hahler
df377b589f use wcwidth 2019-04-05 17:43:11 +02:00
Bruno Oliveira
87ce586d29 Merge pull request #5055 from blueyed/showhelp-lazy-fullwidth
showhelp: move tw.fullwidth out of the loop
2019-04-05 12:11:47 -03:00
Daniel Hahler
f599172add test with 😄 in message 2019-04-05 16:08:11 +02:00
Daniel Hahler
e20b39d928 showhelp: move tw.fullwidth out of the loop 2019-04-05 15:14:02 +02:00
Bruno Oliveira
4f33f46a02 Merge pull request #5026 from blueyed/compare-len-diff
Display number of different items with sequence/dict comparisons
2019-04-05 08:53:48 -03:00
Daniel Hahler
159704421e change separator to hyphen 2019-04-05 12:21:24 +02:00
Daniel Hahler
0f965e57a2 changelog, fix branch coverage 2019-04-05 12:12:29 +02:00
Daniel Hahler
8011ff5bda Add _sys_snapshot fixture and use it with more tests 2019-04-05 11:55:23 +02:00
Daniel Hahler
899e74aa14 tests: harden test_immediate_initialiation_and_incremental_are_the_same 2019-04-05 11:55:23 +02:00
Daniel Hahler
66f743c45a Fix test_conftest when run via pytest-randomly 2019-04-05 11:55:23 +02:00
Daniel Hahler
8ad99c5cab Merge pull request #5035 from blueyed/cache-glob
Support glob argument with ``--cache-show``
2019-04-05 10:25:19 +02:00
Jeffrey Rackauckas
da3f836ee3 Added the junit_log_passing_tests ini flag. 2019-04-04 20:26:48 -07:00
Daniel Hahler
37ecca3ba9 factor out _get_line_with_reprcrash_message 2019-04-04 22:13:28 +02:00
Daniel Hahler
3d0ecd03ed Display message from reprcrash in short test summary
This is useful to see common patterns easily, but also for single
failures already.
2019-04-04 18:49:13 +02:00
Daniel Hahler
eb5b2e0db5 Support glob argument with `--cache-show` 2019-04-04 18:10:39 +02:00
Daniel Hahler
47d92a0d96 Add tests and improve messages 2019-04-04 17:53:39 +02:00
Daniel Hahler
7f1bf44aa8 _compare_eq_dict: display number of different items 2019-04-04 17:16:21 +02:00
Daniel Hahler
5fec793bc7 _compare_eq_sequence: display number of extra items 2019-04-04 17:16:21 +02:00
Bruno Oliveira
e04936fc29 Merge pull request #5042 from blueyed/merge-regen-master-into-features
Merge master into features (with regen)
2019-04-04 10:44:30 -03:00
Daniel Hahler
13a9d876f7 Merge pull request #5037 from blueyed/regen
docs: revisit/fix regen
2019-04-04 13:36:47 +02:00
Ondřej Súkup
77526f412c Skip test_source_mtime_long_long on 32bit and lower platforms 2019-04-04 13:14:46 +02:00
Daniel Hahler
0d4636b056 Merge pull request #5034 from blueyed/run-last-failure-report
run-last-failure: improve reporting
2019-04-04 00:40:40 +02:00
Daniel Hahler
a65edf6711 Merge pull request #5028 from blueyed/fix-wrap_session-exit-code
wrap_session: restore old behavior for initstate=1
2019-04-04 00:39:38 +02:00
Bruno Oliveira
973301b675 Merge pull request #5041 from blueyed/fix-pdbcls-regression
pdb: try to import --pdbcls in pytest_configure only
2019-04-03 19:23:43 -03:00
Daniel Hahler
757ada2fd2 pdb: try to import --pdbcls in pytest_configure only
Fixes https://github.com/pytest-dev/pytest/issues/5039.
2019-04-03 22:35:18 +02:00
Daniel Hahler
e88aa957ae Merge pull request #4854 from blueyed/pdb-skip
pdb: add option to skip `pdb.set_trace()`
2019-04-03 22:25:38 +02:00
Daniel Hahler
8907fedc79 Merge master into features (with regen branch) 2019-04-03 22:11:00 +02:00
Daniel Hahler
befc8a3f10 Update via https://github.com/pytest-dev/regendoc/pull/8 2019-04-03 22:08:44 +02:00
Daniel Hahler
5935fbaa7a doc/en/Makefile: stabilize output between regendoc versions 2019-04-03 22:07:33 +02:00
Daniel Hahler
e8eaebe595 tox.ini: regen: clear pytest cache for stable outcome 2019-04-03 19:53:00 +02:00
Daniel Hahler
cec2dd6a7c doc/en/Makefile: allow passing in REGENDOC_FILES
This allows for:

> make -C doc/en regen REGENDOC_FILES=example/parametrize.rst
2019-04-03 19:53:00 +02:00
Daniel Hahler
266bf2c007 doc: make regen: replace trailing spaces already 2019-04-03 17:34:26 +02:00
Daniel Hahler
1f5a61e4ef run-last-failure: improve reporting 2019-04-03 16:27:12 +02:00
Bruno Oliveira
46df1d5fcf Merge pull request #5031 from blueyed/pytester-addopts
pytester: use monkeypatch with Testdir
2019-04-03 11:26:49 -03:00
Daniel Hahler
d91527599a pytester: use monkeypatch with Testdir 2019-04-03 15:02:53 +02:00
Bruno Oliveira
1410d3dc9a Merge pull request #5029 from blueyed/minor
Minor: doc, import
2019-04-03 08:58:12 -03:00
Daniel Hahler
7538aa7bb9 minor: check_interactive_exception: use Skipped 2019-04-03 11:45:45 +02:00
Daniel Hahler
db34bf01b6 doc: minor whitespace, punctuation 2019-04-03 11:45:45 +02:00
Daniel Hahler
9434541090 doc: mention that pytest.fixture's param is in request.param 2019-04-03 11:45:45 +02:00
Daniel Hahler
cc90bcce4c wrap_session: restore old behavior for initstate=1 2019-04-03 05:09:21 +02:00
Bruno Oliveira
15ea5cef46 Merge pull request #4943 from blueyed/remove-silence_logging_at_shutdown
capture: do not set logging.raiseExceptions = False
2019-04-02 22:46:15 -03:00
Daniel Hahler
32a8d503a2 Merge pull request #5005 from blueyed/essential_plugins
Split out list of essential plugins
2019-04-03 02:45:19 +02:00
Daniel Hahler
8c734dfc2f Split out list of essential plugins
Fixes https://github.com/pytest-dev/pytest/issues/4976.
2019-04-02 14:39:56 +02:00
Daniel Hahler
08ded2927a capture: do not set logging.raiseExceptions = False
Ref: https://github.com/pytest-dev/pytest/issues/4942
2019-04-02 13:20:39 +02:00
Zac Hatfield-Dodds
1c9dcf1f39 Merge pull request #4935 from Zac-HD/warn-unknown-marks
Emit warning on unknown marks via decorator
2019-04-02 21:29:09 +11:00
Zac Hatfield-Dodds
cab4069f42 Clarify mark.__getattr__ 2019-04-02 12:31:42 +11:00
Zac Hatfield-Dodds
4f6c67658c Use mark-specific warning type
So that we can ignore it in self-tests.
2019-04-01 12:38:33 +11:00
Zac Hatfield-Dodds
cda9ce198a Register marks from self-tests 2019-04-01 10:52:43 +11:00
Zac Hatfield-Dodds
9121138a1b Emit warning for unknown marks 2019-04-01 10:40:18 +11:00
Zac Hatfield-Dodds
407d74be27 Merge pull request #5015 from Zac-HD/mark-warning-prep
Pre-PR for warnings on unknown markers
2019-04-01 10:07:43 +11:00
Bruno Oliveira
49d690d137 Merge pull request #5014 from nicoddemus/release-4.4.0
Release 4.4.0
2019-03-31 18:14:27 -03:00
Daniel Hahler
deade370b9 Update doc/en/mark.rst
Co-Authored-By: Zac-HD <Zac-HD@users.noreply.github.com>
2019-04-01 07:13:45 +11:00
Zac-HD
00810b9b2a Register "issue" mark for self-tests 2019-03-31 14:22:30 +11:00
Zac-HD
bcc08ffe4d More docs on registering marks 2019-03-31 14:22:30 +11:00
Zac-HD
ba1fc02a9b Register mark used by pytester 2019-03-31 14:22:30 +11:00
Zac-HD
38d687f7c7 Fix typos in comments 2019-03-31 14:22:30 +11:00
Daniel Hahler
4621638f07 Update CHANGELOG.rst
Co-Authored-By: nicoddemus <nicoddemus@gmail.com>
2019-03-29 20:29:40 -03:00
Bruno Oliveira
8881b201aa Preparing release version 4.4.0 2019-03-29 20:49:18 +00:00
Bruno Oliveira
278b289f37 Merge pull request #4968 from blueyed/pdb-do_debug-quit
pdb: do not raise outcomes.Exit with quit in debug
2019-03-29 16:22:02 -03:00
Daniel Hahler
e7ade066b6 Merge pull request #5011 from blueyed/merge-master-into-features
Merge master into features
2019-03-29 18:35:13 +01:00
Daniel Hahler
dee520e310 Merge pull request #5008 from blueyed/setup-cfg-tool-pytest
setup.cfg: use existing [tool:pytest] (ignoring [pytest])
2019-03-29 16:47:31 +01:00
Daniel Hahler
adebfd0a84 pdb: add option to skip pdb.set_trace() 2019-03-29 11:13:38 +01:00
Daniel Hahler
4e931b258d Merge master into features 2019-03-29 11:05:46 +01:00
Daniel Hahler
4011021823 pdb: do not raise outcomes.Exit with quit in debug 2019-03-29 11:02:34 +01:00
Daniel Hahler
bfda2a0050 setup.cfg: use existing [tool:pytest] (ignoring [pytest]) 2019-03-29 10:59:27 +01:00
Bruno Oliveira
2812c087ec Merge pull request #5010 from garytyler/docupdate
Update docs for 'pytest_cmdline_parse' hook to note availability limi…
2019-03-28 22:32:50 -03:00
Bruno Oliveira
6b5cddc48a Merge pull request #4951 from blueyed/fix-pdb-capfix
pdb: handle capturing with fixtures only
2019-03-28 20:34:28 -03:00
Gary Tyler
403f556928 Update docs for 'pytest_cmdline_parse' hook to note availability liminations 2019-03-28 19:25:55 -04:00
Bruno Oliveira
d8ef86aadf Merge pull request #4993 from blueyed/stepwise-report
stepwise: report status via pytest_report_collectionfinish
2019-03-28 20:24:18 -03:00
Bruno Oliveira
a9fe1e159a Merge pull request #4965 from nicoddemus/serialization-hooks
Serialization hooks
2019-03-28 20:22:19 -03:00
Bruno Oliveira
65c8e8a09e Rename hooks: to/from_serializable 2019-03-28 13:41:56 -03:00
Daniel Hahler
46d9243eb0 changelog 2019-03-28 11:56:53 +01:00
Daniel Hahler
63a01bdb33 Factor out pytestPDB._is_capturing 2019-03-28 11:49:01 +01:00
Daniel Hahler
d53209956b test_pdb_continue_with_recursive_debug: mock pdb.set_trace 2019-03-28 11:49:01 +01:00
Daniel Hahler
951213ee09 Use new suspend/resume in global_and_fixture_disabled 2019-03-28 11:49:01 +01:00
Daniel Hahler
ae067df941 add test_pdb_continue_with_recursive_debug 2019-03-28 11:49:01 +01:00
Daniel Hahler
40718efacc Fix/revisit do_continue with regard to conditions 2019-03-28 11:49:01 +01:00
Daniel Hahler
d406786a8d pdb: handle capturing with fixtures only 2019-03-28 11:49:01 +01:00
Daniel Hahler
0ac069da13 Merge pull request #5006 from blueyed/capture-clean
tests: ensure cleanup with configs via get_config()
2019-03-28 11:38:30 +01:00
Daniel Hahler
d17ea7a9c0 tests: ensure cleanup with configs via get_config()
Also done in test_pluginmanager, although no resource warnings are
there at least.

Fixes https://github.com/pytest-dev/pytest/issues/4355.
2019-03-28 00:14:13 +01:00
Daniel Hahler
c92021fc4f Merge pull request #5003 from blueyed/off
Fix off-by-one error with lineno in mark collection error
2019-03-28 00:09:53 +01:00
Daniel Hahler
50a5cebba8 Merge pull request #5002 from blueyed/report
skipping: factor out _get_pos, pass only config to _get_report_str
2019-03-27 23:07:11 +01:00
Bruno Oliveira
6c602c2282 Merge pull request #4995 from youknowone/disble_test_id_escaping
add ini option to disable string escape for parametrization
2019-03-27 17:34:35 -03:00
Daniel Hahler
76c70cbf4c Fix off-by-one error with lineno in mark collection error 2019-03-27 17:44:52 +01:00
Bruno Oliveira
3d9e68ecfd Update doc/en/parametrize.rst 2019-03-28 00:07:28 +09:00
Jeong YunWon
8b0b7156d9 Fix glitches of original patch of disable-test-id-escaping 2019-03-28 00:07:28 +09:00
ApaDoctor
cf6e2ceafd add ini option to disable string escape for parametrization 2019-03-28 00:07:28 +09:00
Bruno Oliveira
69a55d334a Merge pull request #5004 from blueyed/doc-pdb
doc: fix note about output capturing with pdb
2019-03-26 20:20:05 -03:00
Bruno Oliveira
241b7433cd Merge pull request #4978 from blueyed/exit-from-from_assertrepr_compare
Do not swallow outcomes.Exit in assertrepr_compare
2019-03-26 18:39:13 -03:00
Bruno Oliveira
057c97812b Merge pull request #4975 from blueyed/verbose-fixes
Fix usages of "verbose" option
2019-03-26 18:38:39 -03:00
Bruno Oliveira
02188e399d Merge pull request #4987 from blueyed/collect-tbstyle-repr_failure
CollectError.repr_failure: honor explicit tbstyle option
2019-03-26 18:37:49 -03:00
Bruno Oliveira
aae02863db Merge pull request #4999 from nicoddemus/cmdline_parse-early
Docs: modules implementing pytest_cmdline_parse can be early-loaded
2019-03-26 18:14:31 -03:00
Daniel Hahler
49f36bb028 Merge pull request #4988 from blueyed/logging-close
logging: close log_file_handler
2019-03-26 19:57:57 +01:00
Daniel Hahler
52730f6330 doc: fix note about output capturing with pdb
[skip travis]
2019-03-26 18:33:00 +01:00
Daniel Hahler
538efef1ba logging: close log_file_handler
While it should be closed in logging's shutdown [1], the following would
still issue a ResourceWarning:

```
import logging

log_file_handler = logging.FileHandler("temp.log", mode="w", encoding="UTF-8")

root_logger = logging.getLogger()
root_logger.addHandler(log_file_handler)
root_logger.removeHandler(log_file_handler)
root_logger.error("error")

del log_file_handler
```

It looks like the weakref might get lost for some reason.

See https://github.com/pytest-dev/pytest/pull/4981/commits/92ffe42b45 / #4981
for more information.

1: c1419578a1/Lib/logging/__init__.py (L2107-L2139)
2019-03-26 18:24:19 +01:00
Bruno Oliveira
9311d822c7 Fix assertion in pytest_report_unserialize 2019-03-26 12:47:31 -03:00
Daniel Hahler
351529cb50 skipping: factor out _get_pos, pass only config to _get_report_str 2019-03-26 16:29:16 +01:00
Daniel Hahler
94a2e3dddc stepwise: report status via pytest_report_collectionfinish 2019-03-26 13:20:33 +01:00
Bruno Oliveira
ee96214a8d Merge pull request #5000 from blueyed/merge-master-into-features
Merge master into features
2019-03-26 09:01:46 -03:00
Daniel Hahler
e1ae469504 Merge master into features 2019-03-26 10:23:21 +01:00
Daniel Hahler
0d00be4f4f Do not swallow outcomes.Exit in assertrepr_compare 2019-03-26 10:20:00 +01:00
Daniel Hahler
23146e7527 Fix usages of "verbose" option
With `-qq` `bool(config.getoption("verbose"))` is True; it needs to be
checked for `> 0`.
2019-03-26 10:11:25 +01:00
Daniel Hahler
b18df936ea changelog 2019-03-26 10:06:53 +01:00
Daniel Hahler
4148663706 Merge pull request #4979 from blueyed/minor
Minor: whitespace, typo, docs
2019-03-26 10:01:13 +01:00
Bruno Oliveira
3e1971eb16 Merge pull request #4994 from blueyed/test_as_errors-subprocess
test_as_errors: use subprocess with `-W`
2019-03-25 20:50:15 -03:00
Bruno Oliveira
bcdb86ee7e Merge pull request #4991 from blueyed/fix-tests
Fix pytest's own tests with `-W error::ResourceWarning`
2019-03-25 20:47:51 -03:00
Bruno Oliveira
2d77018d1b Improve coverage for _report_unserialization_failure 2019-03-25 20:16:59 -03:00
Bruno Oliveira
ceef0af1ae Improve coverage for to_json() with paths in reports 2019-03-25 20:16:59 -03:00
Bruno Oliveira
e4eec3416a Note that tests from xdist reference the correct xdist issues 2019-03-25 20:16:59 -03:00
Bruno Oliveira
645774295f Add CHANGELOG 2019-03-25 20:16:59 -03:00
Bruno Oliveira
f2e0c740d3 Code review suggestions 2019-03-25 20:16:59 -03:00
Bruno Oliveira
d856f4e51f Make sure TestReports are not collected as test classes 2019-03-25 20:16:59 -03:00
Bruno Oliveira
7b9a414524 Add pytest_report_serialize and pytest_report_unserialize hooks
These hooks will be used by pytest-xdist and pytest-subtests to
serialize and customize reports.
2019-03-25 20:16:59 -03:00
Bruno Oliveira
0c63f99016 Add experimental _to_json and _from_json to TestReport and CollectReport
This methods were moved from xdist (ca03269).

Our intention is to keep this code closer to the core, given that it
might break easily due to refactorings.

Having it in the core might also allow to improve the code by moving
some responsibility to the "code" objects (ReprEntry, etc) which
are often found in the reports.

Finally pytest-xdist and pytest-subtests can use those functions
instead of coding it themselves.
2019-03-25 20:16:59 -03:00
Daniel Hahler
3bc9cbea63 Merge pull request #4989 from blueyed/test_collect_capturing
test_collect_capturing: cover captured stderr
2019-03-25 23:41:53 +01:00
Daniel Hahler
6eff3069da Merge pull request #4851 from blueyed/addopts-vv
ci: PYTEST_ADDOPTS=-vv
2019-03-25 23:41:33 +01:00
Daniel Hahler
58a14b6b99 Merge pull request #4986 from blueyed/fnmatch_lines-list
tests: fnmatch_lines: use list
2019-03-25 23:31:04 +01:00
Daniel Hahler
b53bf44139 Merge pull request #4985 from blueyed/assert-from_current
ExceptionInfo.from_current: assert current exception
2019-03-25 23:28:58 +01:00
Daniel Hahler
d8758443bd Merge pull request #4983 from blueyed/coveragerc
.coveragerc: use "src" only from current dir
2019-03-25 23:28:23 +01:00
Daniel Hahler
51f64c2920 Merge pull request #4980 from blueyed/fixup_namespace_packages
monkeypatch.syspath_prepend: call fixup_namespace_packages
2019-03-25 23:10:00 +01:00
Bruno Oliveira
cea42ff9e4 Docs: modules implementing pytest_cmdline_parse can be early-loaded
Related to #4974
2019-03-25 18:43:06 -03:00
Daniel Hahler
2df9d05981 Merge pull request #4982 from blueyed/cover
Revisit coverage in some tests
2019-03-25 10:28:55 +01:00
Daniel Hahler
4142c41ffc Merge pull request #4990 from blueyed/session-repr
Session repr
2019-03-24 19:54:18 +01:00
Daniel Hahler
de44293d59 CollectError.repr_failure: honor explicit tbstyle option 2019-03-24 11:24:19 +01:00
Daniel Hahler
5efe6ab93c test_log_cli_auto_enable: get stdout once 2019-03-24 11:22:07 +01:00
Daniel Hahler
ce59f42ce1 revisit test_root_logger_affected 2019-03-24 11:21:13 +01:00
Daniel Hahler
7da7b9610c minor: whitespace 2019-03-24 11:20:24 +01:00
Daniel Hahler
d44e42ec15 doc: improve warning_record_to_str 2019-03-24 11:20:01 +01:00
Daniel Hahler
0ea1889265 test_as_errors: use subprocess with -W
Ref: https://github.com/pytest-dev/pytest/pull/4981
2019-03-24 11:17:55 +01:00
Daniel Hahler
6352cf2374 test_implicit_bad_repr1: harden/cleanup 2019-03-24 11:15:40 +01:00
Daniel Hahler
3127ec737b Fix pytest's own tests with -W error::ResourceWarning 2019-03-24 11:05:00 +01:00
Daniel Hahler
aa0b657e58 Add Session.__repr__ 2019-03-24 11:02:58 +01:00
Daniel Hahler
d0f3f26fff test_collect_capturing: cover captured stderr 2019-03-23 23:17:07 +01:00
Daniel Hahler
08f3b02dfc tests: fnmatch_lines: use list
For strings fnmatch_lines converts it into a Source objects, splitted on
newlines.  This is not necessary here, and it is more consistent to use
lists here in the first place.
2019-03-23 11:36:18 +01:00
Daniel Hahler
2d690b83bf ExceptionInfo.from_current: assert current exception 2019-03-23 00:29:36 +01:00
Daniel Hahler
0642da0145 .coveragerc: use "src" only from current dir
This avoids including generated test files with "src" in their path.
2019-03-22 17:48:14 +01:00
Daniel Hahler
afa985c135 Revisit coverage in some tests 2019-03-22 17:26:16 +01:00
Daniel Hahler
fd64fa1863 Revisit test_importplugin_error_message
Should be more helpful in case of errors than before:

    >       assert re.match(expected_message, str(excinfo.value))
    E       _pytest.warning_types.PytestWarning: asserting the value None, please use "assert is None"

    https://travis-ci.org/pytest-dev/pytest/jobs/509970576#L208
2019-03-22 17:02:26 +01:00
Daniel Hahler
56dc01ffe0 minor: revisit _possibly_invalidate_import_caches 2019-03-22 17:02:26 +01:00
Daniel Hahler
5df45f5b27 Use fixup_namespace_packages also with pytester.syspathinsert 2019-03-22 17:02:26 +01:00
Daniel Hahler
05d55b86df tests: minor sys.path cleanup 2019-03-22 16:20:55 +01:00
Daniel Hahler
475119988c monkeypatch.syspath_prepend: call fixup_namespace_packages
Without the patch the test fails as follows:

            # Prepending should call fixup_namespace_packages.
            monkeypatch.syspath_prepend("world")
    >       import ns_pkg.world
    E       ModuleNotFoundError: No module named 'ns_pkg.world'
2019-03-22 15:29:08 +01:00
Daniel Hahler
7a6bcc3639 Add reference to test_cmdline_python_namespace_package 2019-03-22 13:23:44 +01:00
Daniel Hahler
8e125c9759 doc/en/reference.rst: whitespace/alignment 2019-03-22 13:23:44 +01:00
Daniel Hahler
ade773390a minor: rename inner test 2019-03-22 13:23:44 +01:00
Daniel Hahler
5c26ba9cb1 minor: wrap_session: s/Spurious/unexpected/ 2019-03-22 13:23:44 +01:00
Daniel Hahler
5a544d4fac tox.ini: usedevelop implies skipsdist 2019-03-22 13:23:44 +01:00
Daniel Hahler
2e7d6a6202 Fix test_assertrewrite in verbose mode
Fixes https://github.com/pytest-dev/pytest/issues/4879.
2019-03-22 13:00:35 +01:00
Daniel Hahler
ea7357bc58 ci: PYTEST_ADDOPTS=-vv in general
This is useful when viewing logs, especially with hanging tests.

Uses non-verbose mode with a single job for full coverage.
2019-03-22 07:56:15 +01:00
Bruno Oliveira
b3319a6074 Merge pull request #4944 from henrykironde/k-EXPRESSION
Add example for k flag
2019-03-21 19:33:53 -03:00
Bruno Oliveira
c9628c52d6 Merge pull request #4971 from bskinn/patch-1
Fix pytestmark syntax in reference.rst
2019-03-21 18:59:25 -03:00
Brian Skinn
dcbdcc729b Fix pytestmark syntax in reference.rst
pytest 4.3.1 throws an error if `pytestmark` is set to a tuple of marks; it appears to insist on a list.



With `pytestmark = [pytest.mark.api, pytest.mark.good]`:

```
============================== test session starts ==============================
platform win32 -- Python 3.6.3, pytest-4.3.1, py-1.8.0, pluggy-0.9.0
rootdir: C:\Temp\git\sphobjinv, inifile: tox.ini
plugins: timeout-1.3.3
collected 48 items / 41 deselected / 7 selected

tests\test_api_good.py .......                                             [100%]

==================== 7 passed, 41 deselected in 0.15 seconds ====================
```


With `pytestmark = (pytest.mark.api, pytest.mark.good)`:
```
==================================== ERRORS =====================================
____________________ ERROR collecting tests/test_api_good.py ____________________
env\lib\site-packages\_pytest\runner.py:226: in from_call
    result = func()
env\lib\site-packages\_pytest\runner.py:289: in <lambda>
    call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
env\lib\site-packages\_pytest\python.py:435: in collect
    self._inject_setup_module_fixture()
env\lib\site-packages\_pytest\python.py:447: in _inject_setup_module_fixture
    setup_module = _get_non_fixture_func(self.obj, "setUpModule")
env\lib\site-packages\_pytest\python.py:255: in obj
    self.own_markers.extend(get_unpacked_marks(self.obj))
env\lib\site-packages\_pytest\mark\structures.py:244: in get_unpacked_marks
    return normalize_mark_list(mark_list)
env\lib\site-packages\_pytest\mark\structures.py:259: in normalize_mark_list
    raise TypeError("got {!r} instead of Mark".format(mark))
E   TypeError: got (MarkDecorator(mark=Mark(name='api', args=(), kwargs={})), MarkDecorator(mark=Mark(name='good', args=(), kwargs={}))) instead of Mark
!!!!!!!!!!!!!!!!!!!! Interrupted: 1 errors during collection !!!!!!!!!!!!!!!!!!!!
==================== 19 deselected, 1 error in 0.27 seconds =====================
```
2019-03-21 15:14:28 -04:00
Daniel Hahler
15d608867d Merge pull request #4966 from blueyed/fix-preparse
config: fix consider_preparse with missing argument to -p
2019-03-21 19:12:57 +01:00
Daniel Hahler
ea2c6b8a88 config: fix consider_preparse with missing argument to -p
This is only required after/with 415899d4 - otherwise argparse ensures
there is an argument already.
2019-03-21 17:05:22 +01:00
Daniel Hahler
0e6cf0ff28 Merge pull request #4967 from blueyed/p-no-default
Fix some issues related to "-p no:X" with default_plugins
2019-03-21 17:02:34 +01:00
Daniel Hahler
553951c443 Fix some issues related to "-p no:X" with default_plugins 2019-03-21 17:01:50 +01:00
Bruno Oliveira
15ef168821 Merge pull request #4962 from blueyed/test_report_collect_after_half_a_second
tests: add test_report_collect_after_half_a_second
2019-03-20 19:35:08 -03:00
Daniel Hahler
cc6e5ec345 tests: add test_report_collect_after_half_a_second
This is meant for stable coverage with "collecting X item(s)".
2019-03-20 22:13:11 +01:00
Daniel Hahler
77643122a8 Merge pull request #4963 from blueyed/twisted
ci: rename "trial" tox factor to "twisted"
2019-03-20 21:58:48 +01:00
Daniel Hahler
832cef953b Merge pull request #4964 from blueyed/mkdir
Revisit mkdir/_ensure_supporting_files in cacheprovider
2019-03-20 21:58:24 +01:00
Daniel Hahler
bcdbb6b677 Revisit mkdir/_ensure_supporting_files in cacheprovider
- cacheprovider: move call to _ensure_supporting_files

  This makes it less likely to have a race here (which is not critical),
  but happened previously probably with xdist, causing flaky coverage with
  `if not readme_path.is_file():` etc checks in
  `_ensure_supporting_files`, which has been removed in the `features`
  branch already.
2019-03-20 19:00:11 +01:00
Daniel Hahler
543779fc43 tox: generic twisted factor 2019-03-20 18:41:48 +01:00
Daniel Hahler
2ade3d5c89 ci: rename "trial" tox factor to "twisted"
Ref: https://github.com/pytest-dev/pytest/pull/4848#issuecomment-467909204
2019-03-20 18:38:50 +01:00
Daniel Hahler
7939e5327c Merge pull request #4957 from blueyed/config-handle-pno-with-default-plugins
config: handle `-p no:plugin` with default plugins
2019-03-20 03:32:03 +01:00
Daniel Hahler
f7171034f9 terminal: remove unnecessary check in _get_progress_information_message
All calls to _get_progress_information_message are only done for
`_show_progress_info`, which is `False` with `capture=no`.
2019-03-20 03:04:41 +01:00
Daniel Hahler
c7c120fba6 terminal: handle "capture" option not being available
This is the case with `-p no:capture` now.
2019-03-20 03:01:26 +01:00
Daniel Hahler
415899d428 config: handle -p no:plugin with default plugins
`-p no:capture` should not load its fixtures in the first place.
2019-03-20 02:47:13 +01:00
Daniel Hahler
8dda5613ef Merge pull request #4956 from blueyed/home2
pytester: set HOME only with inline_run/popen
2019-03-20 02:29:29 +01:00
Daniel Hahler
714f2113bb Merge pull request #4929 from blueyed/fix-_factorytraceback-offset
Fix line offsets with `ScopeMismatch` errors
2019-03-20 01:30:58 +01:00
Daniel Hahler
a50b92ea67 pytester: set HOME only with inline_run/popen
Ref: https://github.com/pytest-dev/pytest/issues/4955
2019-03-20 01:15:51 +01:00
Daniel Hahler
da81c1e49a Merge pull request #4950 from blueyed/capture
Revisit capturing module: repr, doc fixes, minor
2019-03-20 01:12:22 +01:00
Bruno Oliveira
23ab43233e Merge pull request #4920 from nicoddemus/subtests-pytest-1367
Internal refactorings required for *external* pytest-subtests plugin
2019-03-19 18:57:11 -03:00
Bruno Oliveira
1a119a22d1 Internal refactorings in order to support the new pytest-subtests plugin
Related to #1367
2019-03-19 18:20:41 -03:00
Daniel Hahler
77c5191ad7 Merge pull request #4953 from blueyed/merge-master-into-features
Merge master into features
2019-03-19 04:13:31 +01:00
Daniel Hahler
7395501d1d Easier read with _colorama_workaround/_readline_workaround 2019-03-19 01:17:21 +01:00
Daniel Hahler
920bffbfbb Revisit _pytest.capture: repr, doc fixes, minor 2019-03-19 01:17:21 +01:00
Daniel Hahler
751c061d9a Merge master into features 2019-03-19 01:07:10 +01:00
Daniel Hahler
a624b84097 Merge pull request #4941 from blueyed/testdir-home
pytester: testdir: set $HOME to tmpdir
2019-03-18 23:02:15 +01:00
Daniel Hahler
c75dd10671 pytester: testdir: set $HOME to tmpdir
This avoids loading user configuration, which might interfere with test
results, e.g. a `~/.pdbrc.py` with pdb++.

Also sets USERPROFILE, which will be required with Python 3.8 [1].

1: https://bugs.python.org/issue36264
2019-03-18 20:55:39 +01:00
Bruno Oliveira
b696666f5a Merge pull request #4945 from blueyed/FDCapture-repr-None-targetfd_save
capture: fix FDCapture.__repr__ without targetfd_save
2019-03-18 13:09:30 -03:00
Bruno Oliveira
f4f6cb7532 Merge pull request #4946 from blueyed/fix-bench
Fix bench/bench.py without args
2019-03-18 13:04:12 -03:00
henrykironde
1e3d5a0412 Add example for k flag
Includes an example of multiple expressions
2019-03-17 22:33:16 -04:00
Daniel Hahler
98981276a0 capture: fix FDCapture.__repr__ without targetfd_save 2019-03-18 02:33:03 +01:00
Daniel Hahler
8c96b65082 Fix bench/bench.py without args
Fixes:

>   File "…/Vcs/pytest/src/_pytest/config/__init__.py", line 60, in main
>     config = _prepareconfig(args, plugins)
>   File "…/Vcs/pytest/src/_pytest/config/__init__.py", line 179, in _prepareconfig
>     raise TypeError(msg.format(args, type(args)))
> TypeError: `args` parameter expected to be a list or tuple of strings, got: 'empty.py' (type: <class 'str'>)
2019-03-18 02:28:41 +01:00
Bruno Oliveira
c926999cfb Merge pull request #4939 from smheidrich/mark_xfail_language
Minor grammar fixes in pytest.mark.xfail docs
2019-03-17 11:33:20 -03:00
smheidrich
519157cfcf Minor grammar fixes in pytest.mark.xfail docs 2019-03-17 10:14:40 +01:00
Bruno Oliveira
5d14362a75 Merge pull request #4936 from blueyed/use-blocked-plugin
Handle `-p plug` after `-p no:plug`
2019-03-16 12:21:33 -03:00
Daniel Hahler
15fe8c6e90 Handle -p plug after -p no:plug.
This can be used to override a blocked plugin (e.g. in "addopts") from the
command line etc.
2019-03-16 15:58:00 +01:00
Daniel Hahler
c1e01c2992 Merge pull request #4931 from blueyed/linematcher-list
pytester: LineMatcher: assert lines
2019-03-15 23:43:23 +01:00
Daniel Hahler
5e27ea5528 pytester: LineMatcher: assert Sequence when matching in order
This can be helpful when passing a set accidentally.
2019-03-15 23:07:08 +01:00
Bruno Oliveira
33d4c96aa2 Merge pull request #4830 from nicoddemus/warn-on-coroutine
Emit a warning when a coroutine test function is encountered
2019-03-15 09:51:34 -03:00
Bruno Oliveira
b3eb5d1eb7 Merge pull request #4932 from hroncok/sphinx2
Pin sphinx-removed-in to >= 0.2.0 to support Sphinx 2.0
2019-03-15 08:18:41 -03:00
Miro Hrončok
2af0a023c9 Pin sphinx-removed-in to >= 0.2.0 to support Sphinx 2.0
Fixes https://github.com/pytest-dev/pytest/issues/4912
2019-03-15 10:56:13 +01:00
Anthony Sottile
5f52d5ee17 Merge pull request #4927 from tkf/skip-doctest
Make pytest.skip work in doctest
2019-03-15 00:14:09 -07:00
Daniel Hahler
95701566f3 Update src/_pytest/outcomes.py
Co-Authored-By: tkf <takafumi.a@gmail.com>
2019-03-15 12:21:48 +09:00
Daniel Hahler
57be1d60dd Apply suggestions from code review
Co-Authored-By: tkf <takafumi.a@gmail.com>
2019-03-15 11:29:16 +09:00
Takafumi Arakaki
62f96eea6b Include documentation 2019-03-15 11:14:50 +09:00
Takafumi Arakaki
fa3cca51e1 Test pytest.skip in doctest 2019-03-15 11:06:57 +09:00
Daniel Hahler
d441fa66fe Fix line offsets with ScopeMismatch errors
Fixes https://github.com/pytest-dev/pytest/issues/4928.
2019-03-15 02:47:33 +01:00
Takafumi Arakaki
43aee15ba3 Make pytest.skip work in doctest 2019-03-15 10:20:46 +09:00
Bruno Oliveira
5c57d92978 Merge pull request #4926 from blueyed/merge-master-into-features
Merge master into features
2019-03-14 21:59:13 -03:00
Daniel Hahler
7afe17740f Merge master into features 2019-03-15 00:52:12 +01:00
Daniel Hahler
158432217c Merge pull request #4924 from blueyed/fix-pdbpp
Fix/harden some pdb tests
2019-03-15 00:51:12 +01:00
Daniel Hahler
437ff1c01a Merge pull request #4925 from blueyed/pm-super
pdb: post_mortem: use super()
2019-03-15 00:50:46 +01:00
Bruno Oliveira
40072b9511 Emit a warning when a async def function is not handled by a plugin
Fix #2224
2019-03-14 20:22:23 -03:00
Daniel Hahler
520af9d767 pdb: post_mortem: use super()
This is good practice in general, and I've seen it cause problems (MRO)
with pdb++.
2019-03-14 22:29:57 +01:00
Daniel Hahler
bdac9d3dd0 tests: improve test_pdb_interaction_doctest
- ignore pdbrc (might be done in general, but this was the only affected
  test)
- fail faster in case of unexpected failure
2019-03-14 19:16:34 +01:00
Daniel Hahler
37158f5303 tests: fix test_pdb_interaction_continue_recursive with pdbpp 2019-03-14 19:16:34 +01:00
Bruno Oliveira
612c3784e5 Merge pull request #4881 from blueyed/travis-cache
ci: Travis: disable cache by default, only for pre-commit
2019-03-14 12:39:47 -03:00
Daniel Hahler
951e07d71d Merge pull request #4919 from blueyed/opt
Optimize TracebackEntry.ishidden
2019-03-14 16:37:57 +01:00
Bruno Oliveira
36f774a8fb Merge pull request #4922 from hroncok/add_object_type
Remove deprecated Sphinx directive add_description_unit()
2019-03-14 12:31:34 -03:00
Bruno Oliveira
a2b921f890 Merge pull request #4921 from nicoddemus/mtime-test-for-4903
Add test for mtime issue in #4903
2019-03-14 10:56:08 -03:00
Bruno Oliveira
bd70f5c148 Add test for mtime issue in #4903 2019-03-14 10:16:08 -03:00
Miro Hrončok
134b957bf4 Remove deprecated Sphinx directive add_description_unit()
Partial solution for https://github.com/pytest-dev/pytest/issues/4912
2019-03-14 12:16:59 +01:00
Daniel Hahler
4d21dc4f2d Optimize TracebackEntry.ishidden
The expected behavior is that there is no "__tracebackhide__" attribute,
so use `getattr` instead of multiple try/except.
2019-03-14 01:02:46 +01:00
Bruno Oliveira
74416525d2 Merge pull request #4903 from bmwiedemann/y2038
Allow tests to pass after 2038
2019-03-13 20:07:25 -03:00
Bruno Oliveira
44cb51010c Improve CHANGELOG and code comment 2019-03-13 18:52:30 -03:00
Bruno Oliveira
90597226eb Merge pull request #4829 from nicoddemus/yield-tests-dead-code
Remove dead-code related to yield tests
2019-03-13 14:31:11 -03:00
Bruno Oliveira
7fb5ad82d9 Merge pull request #4916 from blueyed/pin-shinx
docs: pin Sphinx to <2.0
2019-03-12 22:55:24 -03:00
Daniel Hahler
f4bcb44025 docs: pin Sphinx to <2.0
Ref: https://github.com/pytest-dev/pytest/issues/4912
2019-03-13 02:36:15 +01:00
Bruno Oliveira
b7ae7a654b Remove callspec related block of code
It seems this is no longer required now that we don't support
yield tests anymore. The param attribute was added here:

91b6f2bda8/_pytest/python.py (L888-L891)
2019-03-12 20:10:59 -03:00
Bruno Oliveira
148e6a30c8 Improve coverage 2019-03-12 20:10:59 -03:00
Bruno Oliveira
47bd1688ed Remove dead-code related to yield tests
Just noticed some code that no longer is needed when we removed yield-tests
2019-03-12 20:10:59 -03:00
Daniel Hahler
6630d96253 Merge pull request #4914 from hroncok/issue4913
Fix pytest tests invocation with custom PYTHONPATH
2019-03-12 18:58:54 +01:00
Miro Hrončok
d32ab6029f Fix pytest tests invocation with custom PYTHONPATH
Fixes https://github.com/pytest-dev/pytest/issues/4913

Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
2019-03-12 17:31:42 +01:00
Bruno Oliveira
76c00d1c09 Merge pull request #4909 from nicoddemus/release-4.3.1
Prepare release 4.3.1
2019-03-12 13:23:32 -03:00
Bruno Oliveira
492cc4219c Prepare release 4.3.1 2019-03-11 12:59:54 -03:00
Bruno Oliveira
51bf7c3aef Merge pull request #4897 from altendky/4896-altendky-add_missing_hooks_to_docs
Add missing plugin hooks to docs
2019-03-11 12:08:56 -03:00
Bernhard M. Wiedemann
489c61a22d Allow tests to pass after 2038
without this change, the python-apache-libcloud tests failed
in the year 2039 with

     fp.write(struct.pack("<ll", mtime, size))
 E   error: 'l' format requires -2147483648 <= number <= 2147483647
2019-03-10 05:22:21 +01:00
Bruno Oliveira
0f3d630634 Merge pull request #4898 from shoyer/patch-1
Fix broken error message in pytester
2019-03-08 22:53:01 -03:00
Bruno Oliveira
a0f652c559 Rename 4898.trivial.rst to 4898.bugfix.rst 2019-03-08 22:41:57 -03:00
Bruno Oliveira
877b57ae9b Add CHANGELOG entry 2019-03-08 22:33:07 -03:00
Stephan Hoyer
dc7ae41f33 Fix broken error message in pytester 2019-03-08 09:22:00 -08:00
Kyle Altendorf
2d43f42769 Add missing plugin hooks to docs
pytest-dev/pytest#4896
2019-03-08 09:24:01 -05:00
Bruno Oliveira
03ef546706 Merge pull request #4893 from nicoddemus/simplify-obj-property
Simplify 'obj' property definition in PyobjMixin
2019-03-08 02:04:28 -03:00
Daniel Hahler
de5aa3847e Apply suggestions from code review
Co-Authored-By: nicoddemus <nicoddemus@gmail.com>
2019-03-07 12:53:47 -03:00
Bruno Oliveira
0f4905a259 Simplify 'obj' property definition in PyobjMixin
This uses modern property definition syntax, declaring both getter
and setter as obj() functions
2019-03-07 08:15:04 -03:00
Bruno Oliveira
936f725b81 Merge pull request #4890 from blueyed/dead
pytester: remove unused anypython fixture
2019-03-06 14:35:23 -03:00
Daniel Hahler
c86d2daf81 pytester: remove unused anypython fixture
This became unused after ab9f6a75 (in 2009).
2019-03-06 17:42:21 +01:00
Daniel Hahler
a70c1ca100 ci: Travis: disable cache by default, only for pre-commit
For pip the usual http caching should be good enough.
This keeps the cache for pre-commit with the linting env for now.

Ref: https://github.com/pytest-dev/pytest/issues/3502
2019-03-05 23:12:11 +01:00
Daniel Hahler
4668ee03f6 Merge pull request #4887 from blueyed/merge-master-into-features
Merge master into features
2019-03-05 23:05:55 +01:00
Daniel Hahler
236bada755 Merge pull request #4768 from horta/avoid-pkg_resources
Avoid pkg_resources import at the top-level.
2019-03-05 23:02:02 +01:00
Daniel Hahler
76687030f0 Merge branch 'master' into merge-master-into-features
Conflicts:
	appveyor.yml
	setup.py
2019-03-05 19:07:36 +01:00
Daniel Hahler
2b3d69da2b Merge pull request #4865 from blueyed/revisit-cov-new
ci: revisit coverage reporting
2019-03-05 19:05:36 +01:00
Daniel Hahler
8481e438bd ci: revisit coverage reporting
This brings coverage back that got missing with 9dcd6f2.

Continuation of #4839 / #4846.
2019-03-05 13:34:46 +01:00
Daniel Hahler
bd2c9bedcf Merge pull request #4885 from blueyed/fix-master
Fix master: ci: Azure: remove pypy3
2019-03-04 19:32:38 +01:00
Daniel Hahler
2fe922608f Merge pull request #4883 from blueyed/fix-coverage-includes
.coveragerc: fix include for pypy
2019-03-04 19:31:14 +01:00
Daniel Hahler
07fa69335c ci: Azure: remove pypy3
It was enabled accidentally.
2019-03-04 18:16:20 +01:00
Daniel Hahler
ddb16a1ab1 Merge pull request #4874 from blueyed/pypy3-azure
ci: azure: fix coverage reporting with PyPy (not used currently)
2019-03-04 17:58:28 +01:00
Daniel Hahler
2e871f35f3 ci: Azure: clarify name for coverage job 2019-03-04 17:51:25 +01:00
Daniel Hahler
fa94e3c1b2 ci: Azure: fix coverage generation with PyPy
While this is not used currently, it is still good for having it covered
(it failed when running coverage with all jobs unconditionally).
2019-03-04 17:46:01 +01:00
Daniel Hahler
c6eb3413f3 .coveragerc: fix include for pypy
PyPy uses "site-packages" directly.
2019-03-04 17:40:05 +01:00
Daniel Hahler
54c70bc02c Merge pull request #4878 from blueyed/fix-test_crash_on_closing_tmpfile_py27
tests: make test_crash_on_closing_tmpfile_py27 more reliable
2019-03-04 16:22:14 +01:00
Daniel Hahler
83558a0ba3 tests: make test_crash_on_closing_tmpfile_py27 more reliable
It fails reliable for me now without the fix from 9517c3a2a.

Ref: #2370
2019-03-04 15:04:55 +01:00
Bruno Oliveira
23ea04f910 Merge pull request #4860 from nicoddemus/getfixturevalue-cleanup-1895
getfixturevalue does not correctly declare dependency with the calling fixture
2019-03-03 15:56:45 -03:00
Daniel Hahler
c334adc78f Apply suggestions from code review
Co-Authored-By: nicoddemus <nicoddemus@gmail.com>
2019-03-03 11:20:00 -03:00
Bruno Oliveira
f3f6cb2093 Merge pull request #4877 from blueyed/pluggymaster
tox: generic pluggymaster factor, remove env hack
2019-03-03 09:52:54 -03:00
Daniel Hahler
c4aa57bc4c tox/setup.py: remove _PYTEST_SETUP_SKIP_PLUGGY_DEP=1 hack 2019-03-03 13:15:13 +01:00
Daniel Hahler
2970c1df24 tox: generic pluggymaster factor 2019-03-03 13:12:44 +01:00
Bruno Oliveira
35c85f0db9 Merge pull request #4876 from nicoddemus/show-testpaths-in-header-4875
Show testpaths option in the header if it has been used for collection
2019-03-02 13:01:50 -03:00
Bruno Oliveira
0deb7b1696 Do not show "inifile:" string if there's no configuration file 2019-03-02 11:45:08 -03:00
Bruno Oliveira
53b8aa065c Show testpaths option in the header if it has been used for collection
Fix #4875
2019-03-02 11:35:32 -03:00
Bruno Oliveira
6a2d122a50 Remove code debugging leftovers 2019-03-02 09:56:15 -03:00
Bruno Oliveira
d97473e551 Add test and CHANGELOG for #1895 2019-03-02 09:39:30 -03:00
Bruno Oliveira
525639eaa0 Rename fixtures testing file to be consistent with the module name 2019-03-02 09:37:30 -03:00
Bruno Oliveira
7dceabfcb2 Ensure fixtures obtained with getfixturevalue() are finalized in the correct order
Fix #1895
2019-03-02 09:36:16 -03:00
Daniel Hahler
e1f97e41e3 Merge pull request #4872 from blueyed/_ensure_supporting_files
cacheprovider: _ensure_supporting_files: remove unused branches
2019-03-02 02:35:07 +01:00
Daniel Hahler
2d2f6cd4fd cacheprovider: _ensure_supporting_files: remove unused branches
It is only called with empty/new dirs since 0385c273.
2019-03-01 22:51:45 +01:00
Bruno Oliveira
44c940765b Merge pull request #4868 from blueyed/pytester-unset-PYTEST_ADDOPTS
pytester: unset PYTEST_ADDOPTS
2019-03-01 18:48:36 -03:00
Daniel Hahler
ed68fcf665 Merge pull request #4867 from blueyed/fix-cov
.coveragerc: use globs with includes
2019-03-01 21:35:20 +01:00
Bruno Oliveira
907e9495a2 Merge pull request #4869 from nicoddemus/tmppath-docs
Add missing ref docs to tmp_path and tmp_path_factory
2019-03-01 17:22:14 -03:00
Bruno Oliveira
dac164cc99 Add missing ref docs to tmp_path and tmp_path_factory 2019-03-01 17:09:07 -03:00
Daniel Hahler
4290cacb86 Merge pull request #4864 from blueyed/isort-cfg
Add config for isort
2019-03-01 18:44:56 +01:00
Daniel Hahler
db5cc35b44 pytester: unset PYTEST_ADDOPTS 2019-03-01 18:43:17 +01:00
Daniel Hahler
90031edde8 Merge pull request #4847 from blueyed/tox-posargs-lsof
tox: split default posargs, use --lsof with single job only
2019-03-01 18:41:29 +01:00
Daniel Hahler
a96907a9db .coveragerc: use globs with includes
Apparently this caused missing coverage with pdb/pexpect tests.
2019-03-01 18:26:30 +01:00
Daniel Hahler
f8160f7bc5 ci: Travis: run py37-lsof-numpy-xdist with coverage 2019-03-01 16:59:56 +01:00
Daniel Hahler
f0d7773ffa tox: split default posargs in multiple env vars
This is required for combining.

Use it for new lsof facor also.
2019-03-01 16:49:51 +01:00
Bruno Oliveira
84555c89de Merge pull request #4855 from blueyed/pdbcls-attr
--pdbcls: improve validation, and allow for "mod:attr.class"
2019-03-01 12:20:29 -03:00
Daniel Hahler
f7a3e001f7 pdb: allow for --pdbclass=mod:attr.class 2019-03-01 15:20:04 +01:00
Daniel Hahler
42561db1ae Merge pull request #4863 from blueyed/remove-import
Move import of  _format_explanation in _pytest.assertion.rewrite
2019-03-01 15:13:26 +01:00
Daniel Hahler
0d31e852b1 Run isort 2019-03-01 14:24:18 +01:00
Daniel Hahler
75e1fde668 tox.ini: add config for isort 2019-03-01 14:24:18 +01:00
Daniel Hahler
9cb71af9e5 _pytest.assertion.rewrite: move _format_explanation import 2019-03-01 14:13:28 +01:00
Bruno Oliveira
0dd4cb0f8f Merge pull request #4862 from blueyed/encodedfile-write-typerror
Validate type with writing to captured output like without
2019-03-01 10:12:58 -03:00
Bruno Oliveira
33db5e081d Tweak changelog 2019-03-01 10:09:29 -03:00
Daniel Hahler
a51dc0c7ce Validate type with writing to captured output like without
Fixes https://github.com/pytest-dev/pytest/issues/4861.
2019-03-01 14:03:51 +01:00
Bruno Oliveira
276ffa81f6 Merge pull request #4856 from blueyed/travis-no-cov-with-cron
ci: Travis: skip coverage with cron runs
2019-03-01 09:03:56 -03:00
Bruno Oliveira
50610311a7 Merge pull request #4859 from blueyed/tox-py27-nobyte-xdist
tox: add generic nobyte and numpy factors
2019-03-01 08:48:50 -03:00
Daniel Hahler
c30ab1014e tox: add generic nobyte and numpy factors
Remove `py27-nobyte` from tox.ini, which was using xdist already.
Therefore this also removes `py27-xdist` from Travis.

"nobyte" was added in 036557ac to test that test_assertrewrite.py works
with a global PYTHONDONTWRITEBYTECODE=1 setting.

"numpy" is only a special dependency, and can be run together with
nobyte/xdist.
2019-03-01 09:12:16 +01:00
Bruno Oliveira
df8869cf1a Merge pull request #4858 from blueyed/codecov-remove-flags
ci: codecov: remove flags completely for now
2019-02-28 20:11:09 -03:00
Daniel Hahler
8b447878dc ci: codecov: remove flags completely for now
This appears to be one of the reasons for timeouts on their backend.
2019-02-28 21:24:26 +01:00
Daniel Hahler
9c590fa474 ci: Travis: skip coverage with cron runs
The current commit on features has 50+ uploads already:
c7bbb2a788/build
2019-02-28 19:40:24 +01:00
Daniel Hahler
a868a9ac13 pdb: validate --pdbcls option 2019-02-28 18:11:58 +01:00
Bruno Oliveira
55b78ff780 Merge pull request #4848 from blueyed/fix-test_argcomplete
Fix test_argcomplete: use python -m pytest
2019-02-27 14:11:53 -03:00
Daniel Hahler
ccab469a0c Fix test_argcomplete: use python -m pytest
Previously it was not run with a) xdist ("-c"), and b) "python -m
pytest" ("…/pytest.py", not executable).
2019-02-27 16:52:46 +01:00
Daniel Hahler
e711a6c275 Merge pull request #4841 from blueyed/coverage-source
coverage: use run.include, remove --ignore-errors, send TOXENV as name to codecov
2019-02-27 13:16:31 +01:00
Daniel Hahler
c1e3128b3f Merge pull request #4844 from blueyed/tox-pexpect
tox: generic pexpect factor
2019-02-27 12:33:51 +01:00
Daniel Hahler
05bb5ffb65 Merge pull request #4842 from nicoddemus/disable-bytecode-writing
Document how to disable caching rewritten .pyc files to disk
2019-02-27 12:11:20 +01:00
Daniel Hahler
ee95d666f8 coverage: run.include, drop --ignore-errors, codecov name 2019-02-27 12:07:19 +01:00
Daniel Hahler
1e2810e07d tox: generic pexpect factor 2019-02-27 11:48:22 +01:00
Daniel Hahler
fec656b3b1 Apply suggestions from code review
Co-Authored-By: nicoddemus <nicoddemus@gmail.com>
2019-02-27 07:46:03 -03:00
Daniel Hahler
31174f3f83 .coveragerc: fix/tighten paths 2019-02-27 10:02:11 +01:00
Daniel Hahler
5a0f379289 ci: codecov: set name for uploads/builds 2019-02-27 10:02:11 +01:00
Daniel Hahler
0138e9cbb0 ci: coverage: remove --ignore-errors
This should not be necessary (anymore).
2019-02-27 09:57:28 +01:00
Daniel Hahler
b5cf61312b coverage: use source=. and report.include
This appears to improve performance - ~4s with `tox -e py37-coverage --
testing/test_collection.py`.
2019-02-27 09:57:28 +01:00
Bruno Oliveira
16cbb3196c Document how to disable caching rewritten .pyc files to disk
Also changed how the section is presented: instead of "Note" blocks, use proper
sections as those contain enough information to exist on their own.

Fix #1680
2019-02-26 20:42:59 -03:00
Bruno Oliveira
f1254c4461 Merge pull request #4840 from nicoddemus/drop-appveyor
Drop AppVeyor
2019-02-26 20:42:47 -03:00
Bruno Oliveira
cd9415baf2 Configure azure to enable coverage on PYTEST_COVERAGE=1, similar to Travis 2019-02-26 19:31:52 -03:00
Bruno Oliveira
6bd77c0abd Remove appveyor.yml and related scripts 2019-02-26 19:03:53 -03:00
Bruno Oliveira
fb7ee7f42c Add badge for azure pipelines 2019-02-26 19:01:59 -03:00
Bruno Oliveira
9dcd6f2a87 Merge pull request #4839 from blueyed/less-cov
Less coverage reporting
2019-02-26 18:57:30 -03:00
Daniel Hahler
e3eb26f91a ci: Travis: no coverage by default [skip appveyor] 2019-02-26 21:56:01 +01:00
Daniel Hahler
86070f0b7d ci: AppVeyor: no coverage reporting
Should be covered by Azure.
2019-02-26 21:16:04 +01:00
Daniel Hahler
3fbe100a02 ci: Travis: remove coverage from some jobs 2019-02-26 21:14:54 +01:00
Bruno Oliveira
ee62674322 Merge pull request #4831 from nicoddemus/codocov-azure
Upload code coverage from azure
2019-02-26 14:42:14 -03:00
Daniel Hahler
a4192160ce Merge pull request #4837 from blueyed/travis-install
ci: Travis: use single install section
2019-02-26 17:20:05 +01:00
Daniel Hahler
4d9296c71f ci: Travis: use single install section 2019-02-26 17:06:03 +01:00
Bruno Oliveira
d5d190335c Upload code coverage from azure 2019-02-26 16:56:25 +01:00
Bruno Oliveira
7428064f79 Merge pull request #4836 from blueyed/travis-pluggymaster-xdist
ci: xdist with pluggymaster
2019-02-26 12:52:59 -03:00
Bruno Oliveira
af706edd59 Merge pull request #4835 from blueyed/travis-macos
Travis: improve macos setup (py37)
2019-02-26 12:42:51 -03:00
Daniel Hahler
4eb40ef283 ci: azure: use xdist with pluggymaster 2019-02-26 14:03:15 +01:00
Daniel Hahler
f85f36ed03 AppVeyor: drop pluggymaster 2019-02-26 14:01:37 +01:00
Daniel Hahler
904f1ca1ce ci: Travis: macos: use existing py37
`brew update` and `brew upgrade python` is very slow.

This uses the existing `/usr/local/bin/python3` (3.7.0).
2019-02-26 13:55:52 +01:00
Daniel Hahler
32b85e4ccc ci: Travis: use xdist with pluggymaster jobs 2019-02-26 12:57:27 +01:00
Daniel Hahler
c7bbb2a788 Merge pull request #4833 from blueyed/merge-master
Merge master into features
2019-02-26 12:40:15 +01:00
Daniel Hahler
29112d7e0b Merge master into features 2019-02-26 11:48:10 +01:00
Bruno Oliveira
3fd2f43fb6 Merge pull request #4828 from Zac-HD/mark-docs
Clarify docs on --strict marks
2019-02-25 10:07:40 -03:00
Bruno Oliveira
2cf1de3f2d Merge pull request #4727 from nicoddemus/early-load-4718
Change -p so it is possible to early load setuptools plugins
2019-02-25 10:05:51 -03:00
Zac-HD
d9bdf5cfca Clarify docs on --strict marks 2019-02-25 22:30:49 +11:00
Anthony Sottile
f494eefcae Merge pull request #4823 from nicoddemus/unittest2env
Add unittest2 testing to trial environment
2019-02-24 10:33:26 -08:00
Anthony Sottile
c9e69438b1 Merge pull request #4822 from nicoddemus/funcsigs
Require funcsigs>=1.0 on Python 2.7
2019-02-24 09:14:09 -08:00
Bruno Oliveira
2e89812fad Add unittest2 testing to trial environment
Just noticed that `test_usefixtures_marker_on_unittest` is parametrized
for unittest2, but no environment ever installed that library.
2019-02-24 13:21:00 -03:00
Bruno Oliveira
a0207274f4 -p option now can be used to early-load plugins by entry-point name
Fixes #4718
2019-02-24 13:20:17 -03:00
Bruno Oliveira
759d7fde5d Merge pull request #4786 from blueyed/av
AppVeyor: use xdist with pypy, drop pluggymaster
2019-02-24 12:17:59 -03:00
Bruno Oliveira
a0f5c4c8f5 Merge pull request #4799 from blueyed/codecov
codecov: use only linux/windows/osx flags
2019-02-24 12:16:51 -03:00
Bruno Oliveira
2e210acd00 Merge pull request #4819 from Handsome2734/doc-fix
add notice to use [tool:pytest] in setup.cfg
2019-02-24 12:16:30 -03:00
Bruno Oliveira
ede6387caa Require funcsigs>=1.0 on Python 2.7
Fix #4815
2019-02-24 12:11:08 -03:00
Anthony Sottile
ff25b52110 Merge pull request #4820 from nicoddemus/fix-test-prefix
Fix docs about pytest conventions for test functions
2019-02-23 12:02:04 -08:00
Daniel Hahler
dc8c27037a AppVeyor: drop pluggymaster 2019-02-23 20:15:11 +01:00
Bruno Oliveira
3e11bd0d6e Update wording about [tool:pytest] in setup.cfg files 2019-02-23 15:32:42 -03:00
Bruno Oliveira
6a4c7063fd Fix docs about pytest conventions for test functions
Fix #4818
2019-02-23 15:22:27 -03:00
songbowen
15fe60aa25 add notice to use [tool:pytest] in setup.cfg 2019-02-24 00:24:56 +08:00
Daniel Hahler
1ec7f60484 codecov: use only linux/windows flags
Using many flags are a reason for timeouts on Codecov's backend.
2019-02-23 08:28:08 +01:00
Daniel Hahler
63e7f8e340 Merge pull request #4817 from nicoddemus/merge-master-into-features
Merge master into features (including fix from 4816)
2019-02-23 08:24:21 +01:00
Bruno Oliveira
1cf9c2e76f Merge pull request #4816 from nicoddemus/fix-new-pluggy
Fix test failures after pluggy 1.8 release
2019-02-22 22:42:40 -03:00
Bruno Oliveira
0ca1f6e0f4 Merge branch 'fix-new-pluggy' into merge-master-into-features 2019-02-22 18:59:51 -03:00
Bruno Oliveira
a68f4fd2b9 Fix test failures after pluggy 1.8 release
pluggy now calls iter_entry_points with different arguments, and tests
which mocked that call need to be updated accordingly.
2019-02-22 18:58:54 -03:00
Bruno Oliveira
5b35241470 Merge pull request #4812 from mitzkia/logging_from_runtest_logreport
Logging: Make pytest_runtest_logreport() hook available for logging
2019-02-22 18:47:06 -03:00
Andras Mitzki
b26b731498 Logging: Make pytest_runtest_logreport() available for logging
Signed-off-by: Andras Mitzki <andras.mitzki@balabit.com>
2019-02-22 05:14:44 +01:00
Anthony Sottile
da305966d2 Merge pull request #4793 from nicoddemus/azure-include-more-envs
Add same environments to Azure, except py37-freeze
2019-02-21 09:15:44 -08:00
Bruno Oliveira
4ee10d2266 Merge pull request #4813 from discdiver/patch-1
Doc update - recommend use venv instead of virtualenv
2019-02-21 14:06:36 -03:00
Bruno Oliveira
e1aeb6915e Fix linting 2019-02-21 13:27:12 -03:00
Bruno Oliveira
e75915bb73 Revert recommending virtualenv for Python 3.4
`venv` is already available in Python 3.4, my mistake
2019-02-21 13:03:19 -03:00
Bruno Oliveira
ba2a43266a Mention that virtualenv should be used for Python 3.4 as well 2019-02-21 12:52:09 -03:00
Jeff Hale
cfaa8bbee8 recommend venv or virtualenv, depending on python version
venv has been installed with Python since 3.3.  https://docs.python.org/3/library/venv.html
2019-02-21 10:50:49 -05:00
Jeff Hale
6b661795cf update links.inc to include both venv and virtualenv 2019-02-21 10:41:59 -05:00
Jeff Hale
fa65b71c98 Merge pull request #1 from discdiver/discdiver-patch-1
update links for virtualenv -> venv
2019-02-20 15:41:39 -05:00
Jeff Hale
da5dec83f6 update links for virtualenv -> venv 2019-02-20 15:36:34 -05:00
Jeff Hale
2ef3cb2510 Recommend use venv instead of virtualenv
From the Python docs:
"Changed in version 3.5: The use of venv is now recommended for creating virtual environments."
-https://docs.python.org/3/library/venv.html
2019-02-20 15:33:48 -05:00
Zac Hatfield-Dodds
c8a87e48ab Merge pull request #4783 from gyermolenko/fix_syntax_highlighting_for_two_rst_docs
Fix sphinx code-block types (syntax highlighting) in two docs
2019-02-20 06:31:10 +11:00
Bruno Oliveira
b9561e29ff Merge pull request #4808 from nicoddemus/merge-master-into-features
Merge master into features
2019-02-19 08:08:06 -03:00
Bruno Oliveira
bf6dcd64dc Merge pull request #4757 from blueyed/pypy
Travis: use pypy from Xenial, add pypy3
2019-02-18 19:55:43 -03:00
Daniel Hahler
214c331236 Travis: use pypy from Xenial, add pypy3 2019-02-18 22:32:31 +01:00
Bruno Oliveira
9cb504ca9a Add same environments to Azure as have in AppVeyor, except py37-freeze
py37-freeze will be tackled in https://github.com/pytest-dev/pytest/issues/4807
2019-02-18 17:23:39 -03:00
Bruno Oliveira
f0a9f9042f Merge pull request #4805 from nicoddemus/release-4.3.0
Release 4.3.0
2019-02-18 17:18:50 -03:00
Bruno Oliveira
eeac28f4ab Merge pull request #4804 from asottile/fix_py38
Fix python3.8 / pypy failures
2019-02-18 12:59:21 -03:00
Anthony Sottile
5505826db9 Fix python3.8 / pypy failures 2019-02-16 11:23:23 -08:00
Grygorii Iermolenko
6e1ee0802f Fix sphinx code-block types for remaining rst docs 2019-02-15 15:10:37 +02:00
Grygorii Iermolenko
5cf58a9ae9 Revert '$' changes to not trigger regendoc 2019-02-15 14:09:37 +02:00
Grygorii Iermolenko
799bcccd1b Fix sphinx code-block types (syntax highlighting) in two docs 2019-02-13 17:08:49 +02:00
Danilo Horta
821b6ef2a6 Avoid pkg_resources import at the top-level. 2019-02-11 23:26:35 +00:00
205 changed files with 9788 additions and 5567 deletions

View File

@@ -1,9 +1,18 @@
[run]
source = pytest,_pytest,testing/
include =
src/*
testing/*
*/lib/python*/site-packages/_pytest/*
*/lib/python*/site-packages/pytest.py
*/pypy*/site-packages/_pytest/*
*/pypy*/site-packages/pytest.py
*\Lib\site-packages\_pytest\*
*\Lib\site-packages\pytest.py
parallel = 1
branch = 1
[paths]
source = src/
.tox/*/lib/python*/site-packages/
.tox\*\Lib\site-packages\
*/lib/python*/site-packages/
*/pypy*/site-packages/
*\Lib\site-packages\

5
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,5 @@
# info:
# * https://help.github.com/en/articles/displaying-a-sponsor-button-in-your-repository
# * https://tidelift.com/subscription/how-to-connect-tidelift-with-github
tidelift: pypi/pytest
open_collective: pytest

View File

@@ -1,8 +1,10 @@
<!--
Thanks for submitting an issue!
Here's a quick checklist in what to include:
Here's a quick checklist for what to provide:
-->
- [ ] Include a detailed description of the bug or suggestion
- [ ] `pip list` of the virtual environment you are using
- [ ] a detailed description of the bug or suggestion
- [ ] output of `pip list` from the virtual environment you are using
- [ ] pytest and operating system versions
- [ ] Minimal example if possible
- [ ] minimal example if possible

View File

@@ -1,9 +1,10 @@
<!--
Thanks for submitting a PR, your contribution is really appreciated!
Here's a quick checklist that should be present in PRs (you can delete this text from the final description, this is
just a guideline):
Here is a quick checklist that should be present in PRs.
(please delete this text from the final description, this is just a guideline)
-->
- [ ] Create a new changelog file in the `changelog` folder, with a name like `<ISSUE NUMBER>.<TYPE>.rst`. See [changelog/README.rst](https://github.com/pytest-dev/pytest/blob/master/changelog/README.rst) for details.
- [ ] Target the `master` branch for bug fixes, documentation updates and trivial changes.
- [ ] Target the `features` branch for new features and removals/deprecations.
- [ ] Include documentation when adding new features.
@@ -11,4 +12,5 @@ just a guideline):
Unless your change is trivial or a small documentation fix (e.g., a typo or reword of a small section) please:
- [ ] Create a new changelog file in the `changelog` folder, with a name like `<ISSUE NUMBER>.<TYPE>.rst`. See [changelog/README.rst](https://github.com/pytest-dev/pytest/blob/master/changelog/README.rst) for details.
- [ ] Add yourself to `AUTHORS` in alphabetical order;

View File

@@ -1,43 +1,45 @@
exclude: doc/en/example/py2py3/test_py2.py
repos:
- repo: https://github.com/ambv/black
rev: 18.9b0
- repo: https://github.com/python/black
rev: 19.3b0
hooks:
- id: black
args: [--safe, --quiet]
language_version: python3
- repo: https://github.com/asottile/blacken-docs
rev: v0.3.0
rev: v1.0.0
hooks:
- id: blacken-docs
additional_dependencies: [black==18.9b0]
additional_dependencies: [black==19.3b0]
language_version: python3
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.1.0
rev: v2.2.3
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: fix-encoding-pragma
args: [--remove]
- id: check-yaml
- id: debug-statements
exclude: _pytest/debugging.py
language_version: python3
- repo: https://gitlab.com/pycqa/flake8
rev: 3.7.0
rev: 3.7.7
hooks:
- id: flake8
language_version: python3
- repo: https://github.com/asottile/reorder_python_imports
rev: v1.3.5
rev: v1.4.0
hooks:
- id: reorder-python-imports
args: ['--application-directories=.:src']
args: ['--application-directories=.:src', --py3-plus]
- repo: https://github.com/asottile/pyupgrade
rev: v1.11.1
rev: v1.18.0
hooks:
- id: pyupgrade
args: [--keep-percent-format]
args: [--py3-plus]
- repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.2.0
rev: v1.4.0
hooks:
- id: rst-backticks
- repo: local
@@ -45,7 +47,7 @@ repos:
- id: rst
name: rst
entry: rst-lint --encoding utf-8
files: ^(CHANGELOG.rst|HOWTORELEASE.rst|README.rst|changelog/.*)$
files: ^(CHANGELOG.rst|HOWTORELEASE.rst|README.rst|TIDELIFT.rst|changelog/.*)$
language: python
additional_dependencies: [pygments, restructuredtext_lint]
- id: changelogs-rst

View File

@@ -6,55 +6,54 @@ stages:
if: repo = pytest-dev/pytest AND tag IS NOT present
- name: deploy
if: repo = pytest-dev/pytest AND tag IS present
python:
- '3.7'
install:
- pip install --upgrade --pre tox
env:
matrix:
- TOXENV=py27
# Specialized factors for py27.
- TOXENV=py27-nobyte
- TOXENV=py27-xdist
- TOXENV=py27-pluggymaster
# Specialized factors for py37.
- TOXENV=py37-pexpect,py37-trial,py37-numpy
- TOXENV=py37-pluggymaster
- TOXENV=py37-freeze PYTEST_NO_COVERAGE=1
python: '3.7'
cache: false
matrix:
allow_failures:
- python: '3.8-dev'
env: TOXENV=py38-xdist
env:
global:
- PYTEST_ADDOPTS=-vv
install:
- python -m pip install --upgrade --pre tox
jobs:
include:
# OSX tests - first (in test stage), since they are the slower ones.
- os: osx
osx_image: xcode10.1
language: generic
env: TOXENV=py37-xdist PYTEST_COVERAGE=1
before_install:
- which python3
- python3 -V
- ln -sfn "$(which python3)" /usr/local/bin/python
- python -V
- test $(python -c 'import sys; print("%d%d" % sys.version_info[0:2])') = 37
# Full run of latest supported version, without xdist.
- env: TOXENV=py37
python: '3.7'
# Coverage tracking is slow with pypy, skip it.
- env: TOXENV=pypy PYTEST_NO_COVERAGE=1
python: 'pypy-5.4'
dist: trusty
- env: TOXENV=py34-xdist
python: '3.4'
- env: TOXENV=pypy3-xdist
python: 'pypy3'
- env: TOXENV=py35-xdist
python: '3.5'
- env: TOXENV=py36-xdist
python: '3.6'
- env: TOXENV=py37
- &test-macos
language: generic
os: osx
osx_image: xcode9.4
sudo: required
install:
- python -m pip install --pre tox
env: TOXENV=py27-xdist
- <<: *test-macos
env: TOXENV=py37-xdist
before_install:
- brew update
- brew upgrade python
- brew unlink python
- brew link python
# Coverage for:
# - pytester's LsofFdLeakChecker
# - TestArgComplete (linux only)
# - numpy
# Empty PYTEST_ADDOPTS to run this non-verbose.
- env: TOXENV=py37-lsof-numpy-twisted-xdist PYTEST_COVERAGE=1 PYTEST_ADDOPTS=
# Specialized factors for py37.
# Coverage for:
# - test_sys_breakpoint_interception (via pexpect).
- env: TOXENV=py37-pexpect PYTEST_COVERAGE=1
- env: TOXENV=py37-pluggymaster-xdist
- env: TOXENV=py37-freeze
# Jobs only run via Travis cron jobs (currently daily).
- env: TOXENV=py38-xdist
@@ -62,14 +61,15 @@ jobs:
if: type = cron
- stage: baseline
env: TOXENV=py27-pexpect,py27-trial,py27-numpy
- env: TOXENV=py37-xdist
- env: TOXENV=linting,docs,doctesting
python: '3.7'
env: TOXENV=py36-xdist
python: '3.6'
- env: TOXENV=linting,docs,doctesting PYTEST_COVERAGE=1
cache:
directories:
- $HOME/.cache/pre-commit
- stage: deploy
python: '3.6'
env: PYTEST_NO_COVERAGE=1
install: pip install -U setuptools setuptools_scm
script: skip
deploy:
@@ -83,27 +83,31 @@ jobs:
tags: true
repo: pytest-dev/pytest
matrix:
allow_failures:
- python: '3.8-dev'
env: TOXENV=py38-xdist
before_script:
- |
if [[ "$PYTEST_NO_COVERAGE" != 1 ]]; then
# Do not (re-)upload coverage with cron runs.
if [[ "$TRAVIS_EVENT_TYPE" = cron ]]; then
PYTEST_COVERAGE=0
fi
- |
if [[ "$PYTEST_COVERAGE" = 1 ]]; then
export COVERAGE_FILE="$PWD/.coverage"
export COVERAGE_PROCESS_START="$PWD/.coveragerc"
export _PYTEST_TOX_COVERAGE_RUN="coverage run -m"
export _PYTEST_TOX_EXTRA_DEP=coverage-enable-subprocess
fi
script: tox --recreate
script: tox
after_success:
- |
if [[ "$PYTEST_NO_COVERAGE" != 1 ]]; then
set -e
# Add last TOXENV to $PATH.
PATH="$PWD/.tox/${TOXENV##*,}/bin:$PATH"
coverage combine
coverage xml --ignore-errors
coverage report -m --ignore-errors
bash <(curl -s https://codecov.io/bash) -Z -X gcov -X coveragepy -X search -X xcode -X gcovout -X fix -f coverage.xml -F "${TOXENV//-/,},linux"
if [[ "$PYTEST_COVERAGE" = 1 ]]; then
env CODECOV_NAME="$TOXENV-$TRAVIS_OS_NAME" scripts/report-coverage.sh
fi
notifications:
@@ -115,7 +119,3 @@ notifications:
skip_join: true
email:
- pytest-commit@python.org
cache:
directories:
- $HOME/.cache/pip
- $HOME/.cache/pre-commit

11
AUTHORS
View File

@@ -9,6 +9,7 @@ Abhijeet Kasurde
Adam Johnson
Adam Uhlir
Ahn Ki-Wook
Akiomi Kamakura
Alan Velasco
Alexander Johnson
Alexei Kozlenok
@@ -59,6 +60,7 @@ Christopher Gilling
Christopher Dignam
CrazyMerlyn
Cyrus Maden
Damian Skrzypczak
Dhiren Serai
Daniel Grana
Daniel Hahler
@@ -85,6 +87,7 @@ Endre Galaczi
Eric Hunsberger
Eric Siegerman
Erik M. Bray
Evan Kepner
Fabien Zarifian
Fabio Zadrozny
Feng Ma
@@ -105,6 +108,7 @@ Hugo van Kemenade
Hui Wang (coldnight)
Ian Bicking
Ian Lesperance
Ilya Konstantinov
Ionuț Turturică
Iwan Briquemont
Jaap Broekhuizen
@@ -131,6 +135,7 @@ Kale Kundert
Katarzyna Jachim
Katerina Koukiou
Kevin Cox
Kevin J. Foley
Kodi B. Arfer
Kostis Anagnostopoulos
Kristoffer Nordström
@@ -179,6 +184,7 @@ Nicholas Devenish
Nicholas Murphy
Niclas Olofsson
Nicolas Delaby
Nikolay Kondratyev
Oleg Pidsadnyi
Oleg Sushchenko
Oliver Bestwalter
@@ -191,9 +197,11 @@ Paweł Adamczak
Pedro Algarvio
Pieter Mulder
Piotr Banaszkiewicz
Pulkit Goyal
Punyashloka Biswal
Quentin Pradet
Ralf Schmitt
Ralph Giles
Ran Benita
Raphael Castaneda
Raphael Pierzina
@@ -208,6 +216,7 @@ Ross Lawley
Russel Winder
Ryan Wooden
Samuel Dion-Girardeau
Samuel Searles-Bryant
Samuele Pedroni
Sankt Petersbug
Segev Finer
@@ -222,6 +231,7 @@ Steffen Allner
Stephan Obermann
Sven-Hendrik Haase
Tadek Teleżyński
Takafumi Arakaki
Tarcisio Fischer
Tareq Alayan
Ted Xiao
@@ -241,6 +251,7 @@ Vidar T. Fauske
Virgil Dupras
Vitaly Lashmanov
Vlad Dragos
Volodymyr Piskun
Wil Cooley
William Lee
Wim Glenn

View File

@@ -18,6 +18,676 @@ with advance notice in the **Deprecations** section of releases.
.. towncrier release notes start
pytest 5.0.0 (2019-06-28)
=========================
Important
---------
This release is a Python3.5+ only release.
For more details, see our `Python 2.7 and 3.4 support plan <https://docs.pytest.org/en/latest/py27-py34-deprecation.html>`__.
Removals
--------
- `#1149 <https://github.com/pytest-dev/pytest/issues/1149>`_: Pytest no longer accepts prefixes of command-line arguments, for example
typing ``pytest --doctest-mod`` inplace of ``--doctest-modules``.
This was previously allowed where the ``ArgumentParser`` thought it was unambiguous,
but this could be incorrect due to delayed parsing of options for plugins.
See for example issues `#1149 <https://github.com/pytest-dev/pytest/issues/1149>`__,
`#3413 <https://github.com/pytest-dev/pytest/issues/3413>`__, and
`#4009 <https://github.com/pytest-dev/pytest/issues/4009>`__.
- `#5402 <https://github.com/pytest-dev/pytest/issues/5402>`_: **PytestDeprecationWarning are now errors by default.**
Following our plan to remove deprecated features with as little disruption as
possible, all warnings of type ``PytestDeprecationWarning`` now generate errors
instead of warning messages.
**The affected features will be effectively removed in pytest 5.1**, so please consult the
`Deprecations and Removals <https://docs.pytest.org/en/latest/deprecations.html>`__
section in the docs for directions on how to update existing code.
In the pytest ``5.0.X`` series, it is possible to change the errors back into warnings as a stop
gap measure by adding this to your ``pytest.ini`` file:
.. code-block:: ini
[pytest]
filterwarnings =
ignore::pytest.PytestDeprecationWarning
But this will stop working when pytest ``5.1`` is released.
**If you have concerns** about the removal of a specific feature, please add a
comment to `#5402 <https://github.com/pytest-dev/pytest/issues/5402>`__.
- `#5412 <https://github.com/pytest-dev/pytest/issues/5412>`_: ``ExceptionInfo`` objects (returned by ``pytest.raises``) now have the same ``str`` representation as ``repr``, which
avoids some confusion when users use ``print(e)`` to inspect the object.
Deprecations
------------
- `#4488 <https://github.com/pytest-dev/pytest/issues/4488>`_: The removal of the ``--result-log`` option and module has been postponed to (tentatively) pytest 6.0 as
the team has not yet got around to implement a good alternative for it.
- `#466 <https://github.com/pytest-dev/pytest/issues/466>`_: The ``funcargnames`` attribute has been an alias for ``fixturenames`` since
pytest 2.3, and is now deprecated in code too.
Features
--------
- `#3457 <https://github.com/pytest-dev/pytest/issues/3457>`_: New `pytest_assertion_pass <https://docs.pytest.org/en/latest/reference.html#_pytest.hookspec.pytest_assertion_pass>`__
hook, called with context information when an assertion *passes*.
This hook is still **experimental** so use it with caution.
- `#5440 <https://github.com/pytest-dev/pytest/issues/5440>`_: The `faulthandler <https://docs.python.org/3/library/faulthandler.html>`__ standard library
module is now enabled by default to help users diagnose crashes in C modules.
This functionality was provided by integrating the external
`pytest-faulthandler <https://github.com/pytest-dev/pytest-faulthandler>`__ plugin into the core,
so users should remove that plugin from their requirements if used.
For more information see the docs: https://docs.pytest.org/en/latest/usage.html#fault-handler
- `#5452 <https://github.com/pytest-dev/pytest/issues/5452>`_: When warnings are configured as errors, pytest warnings now appear as originating from ``pytest.`` instead of the internal ``_pytest.warning_types.`` module.
- `#5125 <https://github.com/pytest-dev/pytest/issues/5125>`_: ``Session.exitcode`` values are now coded in ``pytest.ExitCode``, an ``IntEnum``. This makes the exit code available for consumer code and are more explicit other than just documentation. User defined exit codes are still valid, but should be used with caution.
The team doesn't expect this change to break test suites or plugins in general, except in esoteric/specific scenarios.
**pytest-xdist** users should upgrade to ``1.29.0`` or later, as ``pytest-xdist`` required a compatibility fix because of this change.
Bug Fixes
---------
- `#1403 <https://github.com/pytest-dev/pytest/issues/1403>`_: Switch from ``imp`` to ``importlib``.
- `#1671 <https://github.com/pytest-dev/pytest/issues/1671>`_: The name of the ``.pyc`` files cached by the assertion writer now includes the pytest version
to avoid stale caches.
- `#2761 <https://github.com/pytest-dev/pytest/issues/2761>`_: Honor PEP 235 on case-insensitive file systems.
- `#5078 <https://github.com/pytest-dev/pytest/issues/5078>`_: Test module is no longer double-imported when using ``--pyargs``.
- `#5260 <https://github.com/pytest-dev/pytest/issues/5260>`_: Improved comparison of byte strings.
When comparing bytes, the assertion message used to show the byte numeric value when showing the differences::
def test():
> assert b'spam' == b'eggs'
E AssertionError: assert b'spam' == b'eggs'
E At index 0 diff: 115 != 101
E Use -v to get the full diff
It now shows the actual ascii representation instead, which is often more useful::
def test():
> assert b'spam' == b'eggs'
E AssertionError: assert b'spam' == b'eggs'
E At index 0 diff: b's' != b'e'
E Use -v to get the full diff
- `#5335 <https://github.com/pytest-dev/pytest/issues/5335>`_: Colorize level names when the level in the logging format is formatted using
'%(levelname).Xs' (truncated fixed width alignment), where X is an integer.
- `#5354 <https://github.com/pytest-dev/pytest/issues/5354>`_: Fix ``pytest.mark.parametrize`` when the argvalues is an iterator.
- `#5370 <https://github.com/pytest-dev/pytest/issues/5370>`_: Revert unrolling of ``all()`` to fix ``NameError`` on nested comprehensions.
- `#5371 <https://github.com/pytest-dev/pytest/issues/5371>`_: Revert unrolling of ``all()`` to fix incorrect handling of generators with ``if``.
- `#5372 <https://github.com/pytest-dev/pytest/issues/5372>`_: Revert unrolling of ``all()`` to fix incorrect assertion when using ``all()`` in an expression.
- `#5383 <https://github.com/pytest-dev/pytest/issues/5383>`_: ``-q`` has again an impact on the style of the collected items
(``--collect-only``) when ``--log-cli-level`` is used.
- `#5389 <https://github.com/pytest-dev/pytest/issues/5389>`_: Fix regressions of `#5063 <https://github.com/pytest-dev/pytest/pull/5063>`__ for ``importlib_metadata.PathDistribution`` which have their ``files`` attribute being ``None``.
- `#5390 <https://github.com/pytest-dev/pytest/issues/5390>`_: Fix regression where the ``obj`` attribute of ``TestCase`` items was no longer bound to methods.
- `#5404 <https://github.com/pytest-dev/pytest/issues/5404>`_: Emit a warning when attempting to unwrap a broken object raises an exception,
for easier debugging (`#5080 <https://github.com/pytest-dev/pytest/issues/5080>`__).
- `#5432 <https://github.com/pytest-dev/pytest/issues/5432>`_: Prevent "already imported" warnings from assertion rewriter when invoking pytest in-process multiple times.
- `#5433 <https://github.com/pytest-dev/pytest/issues/5433>`_: Fix assertion rewriting in packages (``__init__.py``).
- `#5444 <https://github.com/pytest-dev/pytest/issues/5444>`_: Fix ``--stepwise`` mode when the first file passed on the command-line fails to collect.
- `#5482 <https://github.com/pytest-dev/pytest/issues/5482>`_: Fix bug introduced in 4.6.0 causing collection errors when passing
more than 2 positional arguments to ``pytest.mark.parametrize``.
- `#5505 <https://github.com/pytest-dev/pytest/issues/5505>`_: Fix crash when discovery fails while using ``-p no:terminal``.
Improved Documentation
----------------------
- `#5315 <https://github.com/pytest-dev/pytest/issues/5315>`_: Expand docs on mocking classes and dictionaries with ``monkeypatch``.
- `#5416 <https://github.com/pytest-dev/pytest/issues/5416>`_: Fix PytestUnknownMarkWarning in run/skip example.
pytest 4.6.3 (2019-06-11)
=========================
Bug Fixes
---------
- `#5383 <https://github.com/pytest-dev/pytest/issues/5383>`_: ``-q`` has again an impact on the style of the collected items
(``--collect-only``) when ``--log-cli-level`` is used.
- `#5389 <https://github.com/pytest-dev/pytest/issues/5389>`_: Fix regressions of `#5063 <https://github.com/pytest-dev/pytest/pull/5063>`__ for ``importlib_metadata.PathDistribution`` which have their ``files`` attribute being ``None``.
- `#5390 <https://github.com/pytest-dev/pytest/issues/5390>`_: Fix regression where the ``obj`` attribute of ``TestCase`` items was no longer bound to methods.
pytest 4.6.2 (2019-06-03)
=========================
Bug Fixes
---------
- `#5370 <https://github.com/pytest-dev/pytest/issues/5370>`_: Revert unrolling of ``all()`` to fix ``NameError`` on nested comprehensions.
- `#5371 <https://github.com/pytest-dev/pytest/issues/5371>`_: Revert unrolling of ``all()`` to fix incorrect handling of generators with ``if``.
- `#5372 <https://github.com/pytest-dev/pytest/issues/5372>`_: Revert unrolling of ``all()`` to fix incorrect assertion when using ``all()`` in an expression.
pytest 4.6.1 (2019-06-02)
=========================
Bug Fixes
---------
- `#5354 <https://github.com/pytest-dev/pytest/issues/5354>`_: Fix ``pytest.mark.parametrize`` when the argvalues is an iterator.
- `#5358 <https://github.com/pytest-dev/pytest/issues/5358>`_: Fix assertion rewriting of ``all()`` calls to deal with non-generators.
pytest 4.6.0 (2019-05-31)
=========================
Important
---------
The ``4.6.X`` series will be the last series to support **Python 2 and Python 3.4**.
For more details, see our `Python 2.7 and 3.4 support plan <https://docs.pytest.org/en/latest/py27-py34-deprecation.html>`__.
Features
--------
- `#4559 <https://github.com/pytest-dev/pytest/issues/4559>`_: Added the ``junit_log_passing_tests`` ini value which can be used to enable or disable logging of passing test output in the Junit XML file.
- `#4956 <https://github.com/pytest-dev/pytest/issues/4956>`_: pytester's ``testdir.spawn`` uses ``tmpdir`` as HOME/USERPROFILE directory.
- `#5062 <https://github.com/pytest-dev/pytest/issues/5062>`_: Unroll calls to ``all`` to full for-loops with assertion rewriting for better failure messages, especially when using Generator Expressions.
- `#5063 <https://github.com/pytest-dev/pytest/issues/5063>`_: Switch from ``pkg_resources`` to ``importlib-metadata`` for entrypoint detection for improved performance and import time.
- `#5091 <https://github.com/pytest-dev/pytest/issues/5091>`_: The output for ini options in ``--help`` has been improved.
- `#5269 <https://github.com/pytest-dev/pytest/issues/5269>`_: ``pytest.importorskip`` includes the ``ImportError`` now in the default ``reason``.
- `#5311 <https://github.com/pytest-dev/pytest/issues/5311>`_: Captured logs that are output for each failing test are formatted using the
ColoredLevelFormatter.
- `#5312 <https://github.com/pytest-dev/pytest/issues/5312>`_: Improved formatting of multiline log messages in Python 3.
Bug Fixes
---------
- `#2064 <https://github.com/pytest-dev/pytest/issues/2064>`_: The debugging plugin imports the wrapped ``Pdb`` class (``--pdbcls``) on-demand now.
- `#4908 <https://github.com/pytest-dev/pytest/issues/4908>`_: The ``pytest_enter_pdb`` hook gets called with post-mortem (``--pdb``).
- `#5036 <https://github.com/pytest-dev/pytest/issues/5036>`_: Fix issue where fixtures dependent on other parametrized fixtures would be erroneously parametrized.
- `#5256 <https://github.com/pytest-dev/pytest/issues/5256>`_: Handle internal error due to a lone surrogate unicode character not being representable in Jython.
- `#5257 <https://github.com/pytest-dev/pytest/issues/5257>`_: Ensure that ``sys.stdout.mode`` does not include ``'b'`` as it is a text stream.
- `#5278 <https://github.com/pytest-dev/pytest/issues/5278>`_: Pytest's internal python plugin can be disabled using ``-p no:python`` again.
- `#5286 <https://github.com/pytest-dev/pytest/issues/5286>`_: Fix issue with ``disable_test_id_escaping_and_forfeit_all_rights_to_community_support`` option not working when using a list of test IDs in parametrized tests.
- `#5330 <https://github.com/pytest-dev/pytest/issues/5330>`_: Show the test module being collected when emitting ``PytestCollectionWarning`` messages for
test classes with ``__init__`` and ``__new__`` methods to make it easier to pin down the problem.
- `#5333 <https://github.com/pytest-dev/pytest/issues/5333>`_: Fix regression in 4.5.0 with ``--lf`` not re-running all tests with known failures from non-selected tests.
Improved Documentation
----------------------
- `#5250 <https://github.com/pytest-dev/pytest/issues/5250>`_: Expand docs on use of ``setenv`` and ``delenv`` with ``monkeypatch``.
pytest 4.5.0 (2019-05-11)
=========================
Features
--------
- `#4826 <https://github.com/pytest-dev/pytest/issues/4826>`_: A warning is now emitted when unknown marks are used as a decorator.
This is often due to a typo, which can lead to silently broken tests.
- `#4907 <https://github.com/pytest-dev/pytest/issues/4907>`_: Show XFail reason as part of JUnitXML message field.
- `#5013 <https://github.com/pytest-dev/pytest/issues/5013>`_: Messages from crash reports are displayed within test summaries now, truncated to the terminal width.
- `#5023 <https://github.com/pytest-dev/pytest/issues/5023>`_: New flag ``--strict-markers`` that triggers an error when unknown markers (e.g. those not registered using the `markers option`_ in the configuration file) are used in the test suite.
The existing ``--strict`` option has the same behavior currently, but can be augmented in the future for additional checks.
.. _`markers option`: https://docs.pytest.org/en/latest/reference.html#confval-markers
- `#5026 <https://github.com/pytest-dev/pytest/issues/5026>`_: Assertion failure messages for sequences and dicts contain the number of different items now.
- `#5034 <https://github.com/pytest-dev/pytest/issues/5034>`_: Improve reporting with ``--lf`` and ``--ff`` (run-last-failure).
- `#5035 <https://github.com/pytest-dev/pytest/issues/5035>`_: The ``--cache-show`` option/action accepts an optional glob to show only matching cache entries.
- `#5059 <https://github.com/pytest-dev/pytest/issues/5059>`_: Standard input (stdin) can be given to pytester's ``Testdir.run()`` and ``Testdir.popen()``.
- `#5068 <https://github.com/pytest-dev/pytest/issues/5068>`_: The ``-r`` option learnt about ``A`` to display all reports (including passed ones) in the short test summary.
- `#5108 <https://github.com/pytest-dev/pytest/issues/5108>`_: The short test summary is displayed after passes with output (``-rP``).
- `#5172 <https://github.com/pytest-dev/pytest/issues/5172>`_: The ``--last-failed`` (``--lf``) option got smarter and will now skip entire files if all tests
of that test file have passed in previous runs, greatly speeding up collection.
- `#5177 <https://github.com/pytest-dev/pytest/issues/5177>`_: Introduce new specific warning ``PytestWarning`` subclasses to make it easier to filter warnings based on the class, rather than on the message. The new subclasses are:
* ``PytestAssertRewriteWarning``
* ``PytestCacheWarning``
* ``PytestCollectionWarning``
* ``PytestConfigWarning``
* ``PytestUnhandledCoroutineWarning``
* ``PytestUnknownMarkWarning``
- `#5202 <https://github.com/pytest-dev/pytest/issues/5202>`_: New ``record_testsuite_property`` session-scoped fixture allows users to log ``<property>`` tags at the ``testsuite``
level with the ``junitxml`` plugin.
The generated XML is compatible with the latest xunit standard, contrary to
the properties recorded by ``record_property`` and ``record_xml_attribute``.
- `#5214 <https://github.com/pytest-dev/pytest/issues/5214>`_: The default logging format has been changed to improve readability. Here is an
example of a previous logging message::
test_log_cli_enabled_disabled.py 3 CRITICAL critical message logged by test
This has now become::
CRITICAL root:test_log_cli_enabled_disabled.py:3 critical message logged by test
The formatting can be changed through the `log_format <https://docs.pytest.org/en/latest/reference.html#confval-log_format>`__ configuration option.
- `#5220 <https://github.com/pytest-dev/pytest/issues/5220>`_: ``--fixtures`` now also shows fixture scope for scopes other than ``"function"``.
Bug Fixes
---------
- `#5113 <https://github.com/pytest-dev/pytest/issues/5113>`_: Deselected items from plugins using ``pytest_collect_modifyitems`` as a hookwrapper are correctly reported now.
- `#5144 <https://github.com/pytest-dev/pytest/issues/5144>`_: With usage errors ``exitstatus`` is set to ``EXIT_USAGEERROR`` in the ``pytest_sessionfinish`` hook now as expected.
- `#5235 <https://github.com/pytest-dev/pytest/issues/5235>`_: ``outcome.exit`` is not used with ``EOF`` in the pdb wrapper anymore, but only with ``quit``.
Improved Documentation
----------------------
- `#4935 <https://github.com/pytest-dev/pytest/issues/4935>`_: Expand docs on registering marks and the effect of ``--strict``.
Trivial/Internal Changes
------------------------
- `#4942 <https://github.com/pytest-dev/pytest/issues/4942>`_: ``logging.raiseExceptions`` is not set to ``False`` anymore.
- `#5013 <https://github.com/pytest-dev/pytest/issues/5013>`_: pytest now depends on `wcwidth <https://pypi.org/project/wcwidth>`__ to properly track unicode character sizes for more precise terminal output.
- `#5059 <https://github.com/pytest-dev/pytest/issues/5059>`_: pytester's ``Testdir.popen()`` uses ``stdout`` and ``stderr`` via keyword arguments with defaults now (``subprocess.PIPE``).
- `#5069 <https://github.com/pytest-dev/pytest/issues/5069>`_: The code for the short test summary in the terminal was moved to the terminal plugin.
- `#5082 <https://github.com/pytest-dev/pytest/issues/5082>`_: Improved validation of kwargs for various methods in the pytester plugin.
- `#5202 <https://github.com/pytest-dev/pytest/issues/5202>`_: ``record_property`` now emits a ``PytestWarning`` when used with ``junit_family=xunit2``: the fixture generates
``property`` tags as children of ``testcase``, which is not permitted according to the most
`recent schema <https://github.com/jenkinsci/xunit-plugin/blob/master/
src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd>`__.
- `#5239 <https://github.com/pytest-dev/pytest/issues/5239>`_: Pin ``pluggy`` to ``< 1.0`` so we don't update to ``1.0`` automatically when
it gets released: there are planned breaking changes, and we want to ensure
pytest properly supports ``pluggy 1.0``.
pytest 4.4.2 (2019-05-08)
=========================
Bug Fixes
---------
- `#5089 <https://github.com/pytest-dev/pytest/issues/5089>`_: Fix crash caused by error in ``__repr__`` function with both ``showlocals`` and verbose output enabled.
- `#5139 <https://github.com/pytest-dev/pytest/issues/5139>`_: Eliminate core dependency on 'terminal' plugin.
- `#5229 <https://github.com/pytest-dev/pytest/issues/5229>`_: Require ``pluggy>=0.11.0`` which reverts a dependency to ``importlib-metadata`` added in ``0.10.0``.
The ``importlib-metadata`` package cannot be imported when installed as an egg and causes issues when relying on ``setup.py`` to install test dependencies.
Improved Documentation
----------------------
- `#5171 <https://github.com/pytest-dev/pytest/issues/5171>`_: Doc: ``pytest_ignore_collect``, ``pytest_collect_directory``, ``pytest_collect_file`` and ``pytest_pycollect_makemodule`` hooks's 'path' parameter documented type is now ``py.path.local``
- `#5188 <https://github.com/pytest-dev/pytest/issues/5188>`_: Improve help for ``--runxfail`` flag.
Trivial/Internal Changes
------------------------
- `#5182 <https://github.com/pytest-dev/pytest/issues/5182>`_: Removed internal and unused ``_pytest.deprecated.MARK_INFO_ATTRIBUTE``.
pytest 4.4.1 (2019-04-15)
=========================
Bug Fixes
---------
- `#5031 <https://github.com/pytest-dev/pytest/issues/5031>`_: Environment variables are properly restored when using pytester's ``testdir`` fixture.
- `#5039 <https://github.com/pytest-dev/pytest/issues/5039>`_: Fix regression with ``--pdbcls``, which stopped working with local modules in 4.0.0.
- `#5092 <https://github.com/pytest-dev/pytest/issues/5092>`_: Produce a warning when unknown keywords are passed to ``pytest.param(...)``.
- `#5098 <https://github.com/pytest-dev/pytest/issues/5098>`_: Invalidate import caches with ``monkeypatch.syspath_prepend``, which is required with namespace packages being used.
pytest 4.4.0 (2019-03-29)
=========================
Features
--------
- `#2224 <https://github.com/pytest-dev/pytest/issues/2224>`_: ``async`` test functions are skipped and a warning is emitted when a suitable
async plugin is not installed (such as ``pytest-asyncio`` or ``pytest-trio``).
Previously ``async`` functions would not execute at all but still be marked as "passed".
- `#2482 <https://github.com/pytest-dev/pytest/issues/2482>`_: Include new ``disable_test_id_escaping_and_forfeit_all_rights_to_community_support`` option to disable ascii-escaping in parametrized values. This may cause a series of problems and as the name makes clear, use at your own risk.
- `#4718 <https://github.com/pytest-dev/pytest/issues/4718>`_: The ``-p`` option can now be used to early-load plugins also by entry-point name, instead of just
by module name.
This makes it possible to early load external plugins like ``pytest-cov`` in the command-line::
pytest -p pytest_cov
- `#4855 <https://github.com/pytest-dev/pytest/issues/4855>`_: The ``--pdbcls`` option handles classes via module attributes now (e.g.
``pdb:pdb.Pdb`` with `pdb++`_), and its validation was improved.
.. _pdb++: https://pypi.org/project/pdbpp/
- `#4875 <https://github.com/pytest-dev/pytest/issues/4875>`_: The `testpaths <https://docs.pytest.org/en/latest/reference.html#confval-testpaths>`__ configuration option is now displayed next
to the ``rootdir`` and ``inifile`` lines in the pytest header if the option is in effect, i.e., directories or file names were
not explicitly passed in the command line.
Also, ``inifile`` is only displayed if there's a configuration file, instead of an empty ``inifile:`` string.
- `#4911 <https://github.com/pytest-dev/pytest/issues/4911>`_: Doctests can be skipped now dynamically using ``pytest.skip()``.
- `#4920 <https://github.com/pytest-dev/pytest/issues/4920>`_: Internal refactorings have been made in order to make the implementation of the
`pytest-subtests <https://github.com/pytest-dev/pytest-subtests>`__ plugin
possible, which adds unittest sub-test support and a new ``subtests`` fixture as discussed in
`#1367 <https://github.com/pytest-dev/pytest/issues/1367>`__.
For details on the internal refactorings, please see the details on the related PR.
- `#4931 <https://github.com/pytest-dev/pytest/issues/4931>`_: pytester's ``LineMatcher`` asserts that the passed lines are a sequence.
- `#4936 <https://github.com/pytest-dev/pytest/issues/4936>`_: Handle ``-p plug`` after ``-p no:plug``.
This can be used to override a blocked plugin (e.g. in "addopts") from the
command line etc.
- `#4951 <https://github.com/pytest-dev/pytest/issues/4951>`_: Output capturing is handled correctly when only capturing via fixtures (capsys, capfs) with ``pdb.set_trace()``.
- `#4956 <https://github.com/pytest-dev/pytest/issues/4956>`_: ``pytester`` sets ``$HOME`` and ``$USERPROFILE`` to the temporary directory during test runs.
This ensures to not load configuration files from the real user's home directory.
- `#4980 <https://github.com/pytest-dev/pytest/issues/4980>`_: Namespace packages are handled better with ``monkeypatch.syspath_prepend`` and ``testdir.syspathinsert`` (via ``pkg_resources.fixup_namespace_packages``).
- `#4993 <https://github.com/pytest-dev/pytest/issues/4993>`_: The stepwise plugin reports status information now.
- `#5008 <https://github.com/pytest-dev/pytest/issues/5008>`_: If a ``setup.cfg`` file contains ``[tool:pytest]`` and also the no longer supported ``[pytest]`` section, pytest will use ``[tool:pytest]`` ignoring ``[pytest]``. Previously it would unconditionally error out.
This makes it simpler for plugins to support old pytest versions.
Bug Fixes
---------
- `#1895 <https://github.com/pytest-dev/pytest/issues/1895>`_: Fix bug where fixtures requested dynamically via ``request.getfixturevalue()`` might be teardown
before the requesting fixture.
- `#4851 <https://github.com/pytest-dev/pytest/issues/4851>`_: pytester unsets ``PYTEST_ADDOPTS`` now to not use outer options with ``testdir.runpytest()``.
- `#4903 <https://github.com/pytest-dev/pytest/issues/4903>`_: Use the correct modified time for years after 2038 in rewritten ``.pyc`` files.
- `#4928 <https://github.com/pytest-dev/pytest/issues/4928>`_: Fix line offsets with ``ScopeMismatch`` errors.
- `#4957 <https://github.com/pytest-dev/pytest/issues/4957>`_: ``-p no:plugin`` is handled correctly for default (internal) plugins now, e.g. with ``-p no:capture``.
Previously they were loaded (imported) always, making e.g. the ``capfd`` fixture available.
- `#4968 <https://github.com/pytest-dev/pytest/issues/4968>`_: The pdb ``quit`` command is handled properly when used after the ``debug`` command with `pdb++`_.
.. _pdb++: https://pypi.org/project/pdbpp/
- `#4975 <https://github.com/pytest-dev/pytest/issues/4975>`_: Fix the interpretation of ``-qq`` option where it was being considered as ``-v`` instead.
- `#4978 <https://github.com/pytest-dev/pytest/issues/4978>`_: ``outcomes.Exit`` is not swallowed in ``assertrepr_compare`` anymore.
- `#4988 <https://github.com/pytest-dev/pytest/issues/4988>`_: Close logging's file handler explicitly when the session finishes.
- `#5003 <https://github.com/pytest-dev/pytest/issues/5003>`_: Fix line offset with mark collection error (off by one).
Improved Documentation
----------------------
- `#4974 <https://github.com/pytest-dev/pytest/issues/4974>`_: Update docs for ``pytest_cmdline_parse`` hook to note availability liminations
Trivial/Internal Changes
------------------------
- `#4718 <https://github.com/pytest-dev/pytest/issues/4718>`_: ``pluggy>=0.9`` is now required.
- `#4815 <https://github.com/pytest-dev/pytest/issues/4815>`_: ``funcsigs>=1.0`` is now required for Python 2.7.
- `#4829 <https://github.com/pytest-dev/pytest/issues/4829>`_: Some left-over internal code related to ``yield`` tests has been removed.
- `#4890 <https://github.com/pytest-dev/pytest/issues/4890>`_: Remove internally unused ``anypython`` fixture from the pytester plugin.
- `#4912 <https://github.com/pytest-dev/pytest/issues/4912>`_: Remove deprecated Sphinx directive, ``add_description_unit()``,
pin sphinx-removed-in to >= 0.2.0 to support Sphinx 2.0.
- `#4913 <https://github.com/pytest-dev/pytest/issues/4913>`_: Fix pytest tests invocation with custom ``PYTHONPATH``.
- `#4965 <https://github.com/pytest-dev/pytest/issues/4965>`_: New ``pytest_report_to_serializable`` and ``pytest_report_from_serializable`` **experimental** hooks.
These hooks will be used by ``pytest-xdist``, ``pytest-subtests``, and the replacement for
resultlog to serialize and customize reports.
They are experimental, meaning that their details might change or even be removed
completely in future patch releases without warning.
Feedback is welcome from plugin authors and users alike.
- `#4987 <https://github.com/pytest-dev/pytest/issues/4987>`_: ``Collector.repr_failure`` respects the ``--tb`` option, but only defaults to ``short`` now (with ``auto``).
pytest 4.3.1 (2019-03-11)
=========================
Bug Fixes
---------
- `#4810 <https://github.com/pytest-dev/pytest/issues/4810>`_: Logging messages inside ``pytest_runtest_logreport()`` are now properly captured and displayed.
- `#4861 <https://github.com/pytest-dev/pytest/issues/4861>`_: Improve validation of contents written to captured output so it behaves the same as when capture is disabled.
- `#4898 <https://github.com/pytest-dev/pytest/issues/4898>`_: Fix ``AttributeError: FixtureRequest has no 'confg' attribute`` bug in ``testdir.copy_example``.
Trivial/Internal Changes
------------------------
- `#4768 <https://github.com/pytest-dev/pytest/issues/4768>`_: Avoid pkg_resources import at the top-level.
pytest 4.3.0 (2019-02-16)
=========================
@@ -263,7 +933,7 @@ Removals
See our `docs <https://docs.pytest.org/en/latest/deprecations.html#passing-command-line-string-to-pytest-main>`__ on information on how to update your code.
- `#3086 <https://github.com/pytest-dev/pytest/issues/3086>`_: ``[pytest]`` section in **setup.cfg** files is not longer supported, use ``[tool:pytest]`` instead. ``setup.cfg`` files
- `#3086 <https://github.com/pytest-dev/pytest/issues/3086>`_: ``[pytest]`` section in **setup.cfg** files is no longer supported, use ``[tool:pytest]`` instead. ``setup.cfg`` files
are meant for use with ``distutils``, and a section named ``pytest`` has notoriously been a source of conflicts and bugs.
Note that for **pytest.ini** and **tox.ini** files the section remains ``[pytest]``.
@@ -1266,7 +1936,7 @@ Bug Fixes
- `#2220 <https://github.com/pytest-dev/pytest/issues/2220>`_: Fix a bug where fixtures overridden by direct parameters (for example parametrization) were being instantiated even if they were not being used by a test.
- `#3695 <https://github.com/pytest-dev/pytest/issues/3695>`_: Fix ``ApproxNumpy`` initialisation argument mixup, ``abs`` and ``rel`` tolerances were flipped causing strange comparsion results.
- `#3695 <https://github.com/pytest-dev/pytest/issues/3695>`_: Fix ``ApproxNumpy`` initialisation argument mixup, ``abs`` and ``rel`` tolerances were flipped causing strange comparison results.
Add tests to check ``abs`` and ``rel`` tolerances for ``np.array`` and test for expecting ``nan`` with ``np.array()``
@@ -1485,7 +2155,7 @@ Features
exits the debugger. On python 3.2 and higher, use CTRL+D. (`#3299
<https://github.com/pytest-dev/pytest/issues/3299>`_)
- pytest not longer changes the log level of the root logger when the
- pytest no longer changes the log level of the root logger when the
``log-level`` parameter has greater numeric value than that of the level of
the root logger, which makes it play better with custom logging configuration
in user code. (`#3307 <https://github.com/pytest-dev/pytest/issues/3307>`_)

View File

@@ -166,14 +166,14 @@ Short version
#. Enable and install `pre-commit <https://pre-commit.com>`_ to ensure style-guides and code checks are followed.
#. Target ``master`` for bugfixes and doc changes.
#. Target ``features`` for new features or functionality changes.
#. Follow **PEP-8** for naming and `black <https://github.com/ambv/black>`_ for formatting.
#. Follow **PEP-8** for naming and `black <https://github.com/python/black>`_ for formatting.
#. Tests are run using ``tox``::
tox -e linting,py27,py37
The test environments above are usually enough to cover most cases locally.
#. Write a ``changelog`` entry: ``changelog/2574.bugfix``, use issue id number
#. Write a ``changelog`` entry: ``changelog/2574.bugfix.rst``, use issue id number
and one of ``bugfix``, ``removal``, ``feature``, ``vendor``, ``doc`` or
``trivial`` for the issue type.
#. Unless your change is a trivial or a documentation fix (e.g., a typo or reword of a small section) please
@@ -264,7 +264,7 @@ Here is a simple overview, with pytest-specific bits:
$ git commit -a -m "<commit message>"
$ git push -u
#. Create a new changelog entry in ``changelog``. The file should be named ``<issueid>.<type>``,
#. Create a new changelog entry in ``changelog``. The file should be named ``<issueid>.<type>.rst``,
where *issueid* is the number of the issue related to the change and *type* is one of
``bugfix``, ``removal``, ``feature``, ``vendor``, ``doc`` or ``trivial``.

View File

@@ -12,6 +12,8 @@ taking a lot of time to make a new one.
#. Create a branch ``release-X.Y.Z`` with the version for the release.
* **maintenance releases**: from ``4.6-maintenance``;
* **patch releases**: from the latest ``master``;
* **minor releases**: from the latest ``features``; then merge with the latest ``master``;
@@ -24,7 +26,8 @@ taking a lot of time to make a new one.
This will generate a commit with all the changes ready for pushing.
#. Open a PR for this branch targeting ``master``.
#. Open a PR for this branch targeting ``master`` (or ``4.6-maintenance`` for
maintenance releases).
#. After all tests pass and the PR has been approved, publish to PyPI by pushing the tag::
@@ -33,7 +36,16 @@ taking a lot of time to make a new one.
Wait for the deploy to complete, then make sure it is `available on PyPI <https://pypi.org/project/pytest>`_.
#. Merge the PR into ``master``.
#. Merge the PR.
#. If this is a maintenance release, cherry-pick the CHANGELOG / announce
files to the ``master`` branch::
git fetch --all --prune
git checkout origin/master -b cherry-pick-maintenance-release
git cherry-pick --no-commit -m1 origin/4.6-maintenance
git checkout origin/master -- changelog
git commit # no arguments
#. Send an email announcement with the contents from::

View File

@@ -22,11 +22,11 @@
.. image:: https://travis-ci.org/pytest-dev/pytest.svg?branch=master
:target: https://travis-ci.org/pytest-dev/pytest
.. image:: https://ci.appveyor.com/api/projects/status/mrgbjaua7t33pg6b?svg=true
:target: https://ci.appveyor.com/project/pytestbot/pytest
.. image:: https://dev.azure.com/pytest-dev/pytest/_apis/build/status/pytest-CI?branchName=master
:target: https://dev.azure.com/pytest-dev/pytest
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/ambv/black
:target: https://github.com/python/black
.. image:: https://www.codetriage.com/pytest-dev/pytest/badges/users.svg
:target: https://www.codetriage.com/pytest-dev/pytest
@@ -85,7 +85,7 @@ Features
- Can run `unittest <https://docs.pytest.org/en/latest/unittest.html>`_ (or trial),
`nose <https://docs.pytest.org/en/latest/nose.html>`_ test suites out of the box;
- Python 2.7, Python 3.4+, PyPy 2.3, Jython 2.5 (untested);
- Python 3.5+ and PyPy3;
- Rich plugin architecture, with over 315+ `external plugins <http://plugincompat.herokuapp.com>`_ and thriving community;
@@ -108,6 +108,26 @@ Changelog
Consult the `Changelog <https://docs.pytest.org/en/latest/changelog.html>`__ page for fixes and enhancements of each version.
Support pytest
--------------
You can support pytest by obtaining a `Tideflift subscription`_.
Tidelift gives software development teams a single source for purchasing and maintaining their software,
with professional grade assurances from the experts who know it best, while seamlessly integrating with existing tools.
.. _`Tideflift subscription`: https://tidelift.com/subscription/pkg/pypi-pytest?utm_source=pypi-pytest&utm_medium=referral&utm_campaign=readme
Security
^^^^^^^^
pytest has never been associated with a security vunerability, but in any case, to report a
security vulnerability please use the `Tidelift security contact <https://tidelift.com/security>`_.
Tidelift will coordinate the fix and disclosure.
License
-------

57
TIDELIFT.rst Normal file
View File

@@ -0,0 +1,57 @@
========
Tidelift
========
pytest is a member of `Tidelift`_. This document describes how the core team manages
Tidelift-related activities.
What is it
==========
Tidelift aims to make Open Source sustainable by offering subscriptions to companies which rely
on Open Source packages. This subscription allows it to pay maintainers of those Open Source
packages to aid sustainability of the work.
Funds
=====
It was decided in the `mailing list`_ that the Tidelift contribution will be split evenly between
members of the `contributors team`_ interested in receiving funding.
The current list of contributors receiving funding are:
* `@asottile`_
* `@blueyed`_
* `@nicoddemus`_
Contributors interested in receiving a part of the funds just need to submit a PR adding their
name to the list. Contributors that want to stop receiving the funds should also submit a PR
in the same way.
The PR should mention `@pytest-dev/tidelift-admins`_ so appropriate changes
can be made in the Tidelift platform.
After the PR has been accepted and merged, the contributor should register in the `Tidelift`_
platform and follow the instructions there, including signing an `agreement`_.
Admins
======
A few people have admin access to the Tidelift dashboard to make changes. Those people
are part of the `@pytest-dev/tidelift-admins`_ team.
`Core contributors`_ interested in helping out with Tidelift maintenance are welcome! We don't
expect much work here other than the occasional adding/removal of a contributor from receiving
funds. Just drop a line to one of the `@pytest-dev/tidelift-admins`_ or use the mailing list.
.. _`Tidelift`: https://tidelift.com
.. _`mailing list`: https://mail.python.org/pipermail/pytest-dev/2019-May/004716.html
.. _`contributors team`: https://github.com/orgs/pytest-dev/teams/contributors
.. _`core contributors`: https://github.com/orgs/pytest-dev/teams/core/members
.. _`@pytest-dev/tidelift-admins`: https://github.com/orgs/pytest-dev/teams/tidelift-admins/members
.. _`agreement`: https://tidelift.com/docs/lifting/agreement
.. _`@asottile`: https://github.com/asottile
.. _`@blueyed`: https://github.com/blueyed
.. _`@nicoddemus`: https://github.com/nicoddemus

View File

@@ -1,49 +0,0 @@
environment:
matrix:
- TOXENV: "py37-xdist"
- TOXENV: "py27-xdist"
- TOXENV: "linting,docs,doctesting"
- TOXENV: "py34-xdist"
- TOXENV: "py35-xdist"
- TOXENV: "py36-xdist"
- TOXENV: "pypy"
PYTEST_NO_COVERAGE: "1"
# Specialized factors for py27.
- TOXENV: "py27-trial,py27-numpy,py27-nobyte"
- TOXENV: "py27-pluggymaster"
# Specialized factors for py37.
- TOXENV: "py37-trial,py37-numpy"
- TOXENV: "py37-pluggymaster"
- TOXENV: "py37-freeze"
PYTEST_NO_COVERAGE: "1"
matrix:
fast_finish: true
install:
- echo Installed Pythons
- dir c:\Python*
- if "%TOXENV%" == "pypy" call scripts\install-pypy.bat
- C:\Python36\python -m pip install --upgrade pip
- C:\Python36\python -m pip install --upgrade --pre tox
build: false # Not a C# project, build stuff at the test step instead.
before_test:
- call scripts\prepare-coverage.bat
test_script:
- C:\Python36\python -m tox
on_success:
- call scripts\upload-coverage.bat
cache:
- '%LOCALAPPDATA%\pip\cache'
- '%USERPROFILE%\.cache\pre-commit'
# We don't deploy anything on tags with AppVeyor, we use Travis instead, so we
# might as well save resources
skip_tags: true

View File

@@ -3,7 +3,8 @@ trigger:
- features
variables:
PYTEST_ADDOPTS: "--junitxml=build/test-results/$(tox.env).xml"
PYTEST_ADDOPTS: "--junitxml=build/test-results/$(tox.env).xml -vv"
PYTEST_COVERAGE: '0'
jobs:
@@ -12,19 +13,33 @@ jobs:
vmImage: "vs2017-win2016"
strategy:
matrix:
py27:
python.version: '2.7'
tox.env: 'py27'
py35:
# -- pypy3 disabled for now: #5279 --
# pypy3:
# python.version: 'pypy3'
# tox.env: 'pypy3'
py35-xdist:
python.version: '3.5'
tox.env: 'py35'
py36:
tox.env: 'py35-xdist'
# Coverage for:
# - test_supports_breakpoint_module_global
PYTEST_COVERAGE: '1'
py36-xdist:
python.version: '3.6'
tox.env: 'py36'
tox.env: 'py36-xdist'
py37:
python.version: '3.7'
tox.env: 'py37'
maxParallel: 4
tox.env: 'py37-twisted-numpy'
# Coverage for:
# - _py36_windowsconsoleio_workaround (with py36+)
# - test_request_garbage (no xdist)
PYTEST_COVERAGE: '1'
py37-linting/docs/doctesting:
python.version: '3.7'
tox.env: 'linting,docs,doctesting'
py37-pluggymaster-xdist:
python.version: '3.7'
tox.env: 'py37-pluggymaster-xdist'
maxParallel: 10
steps:
- task: UsePythonVersion@0
@@ -32,10 +47,17 @@ jobs:
versionSpec: '$(python.version)'
architecture: 'x64'
- script: python -m pip install --upgrade pip && pip install tox
- script: python -m pip install --upgrade pip && python -m pip install tox
displayName: 'Install tox'
- script: python -m tox -e $(tox.env)
- bash: |
if [[ "$PYTEST_COVERAGE" == "1" ]]; then
export _PYTEST_TOX_COVERAGE_RUN="coverage run -m"
export _PYTEST_TOX_EXTRA_DEP=coverage-enable-subprocess
export COVERAGE_FILE="$PWD/.coverage"
export COVERAGE_PROCESS_START="$PWD/.coveragerc"
fi
python -m tox -e $(tox.env)
displayName: 'Run tests'
- task: PublishTestResults@2
@@ -43,3 +65,13 @@ jobs:
testResultsFiles: 'build/test-results/$(tox.env).xml'
testRunTitle: '$(tox.env)'
condition: succeededOrFailed()
- bash: |
if [[ "$PYTEST_COVERAGE" == 1 ]]; then
scripts/report-coverage.sh
fi
env:
CODECOV_NAME: $(tox.env)
CODECOV_TOKEN: $(CODECOV_TOKEN)
displayName: Report and upload coverage
condition: eq(variables['PYTEST_COVERAGE'], '1')

View File

@@ -5,7 +5,7 @@ if __name__ == "__main__":
import pytest # NOQA
import pstats
script = sys.argv[1:] if len(sys.argv) > 1 else "empty.py"
script = sys.argv[1:] if len(sys.argv) > 1 else ["empty.py"]
stats = cProfile.run("pytest.cmdline.main(%r)" % script, "prof")
p = pstats.Stats("prof")
p.strip_dirs()

View File

@@ -16,4 +16,4 @@ run = 'fc("/d")'
if __name__ == "__main__":
print(timeit.timeit(run, setup=setup % imports[0], number=count))
print((timeit.timeit(run, setup=setup % imports[1], number=count)))
print(timeit.timeit(run, setup=setup % imports[1], number=count))

View File

@@ -1,4 +1,2 @@
import six
for i in range(1000):
six.exec_("def test_func_%d(): pass" % i)
exec("def test_func_%d(): pass" % i)

View File

@@ -1,8 +1,5 @@
from six.moves import range
import pytest
SKIP = True

View File

@@ -13,6 +13,9 @@ PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
REGENDOC_ARGS := \
--normalize "/[ \t]+\n/\n/" \
--normalize "~\$$REGENDOC_TMPDIR~/home/sweet/project~" \
--normalize "~/path/to/example~/home/sweet/project~" \
--normalize "/in \d+.\d+ seconds/in 0.12 seconds/" \
--normalize "@/tmp/pytest-of-.*/pytest-\d+@PYTEST_TMPDIR@" \
--normalize "@pytest-(\d+)\\.[^ ,]+@pytest-\1.x.y@" \
@@ -38,8 +41,9 @@ help:
clean:
-rm -rf $(BUILDDIR)/*
regen: REGENDOC_FILES:=*.rst */*.rst
regen:
PYTHONDONTWRITEBYTECODE=1 PYTEST_ADDOPTS=-pno:hypothesis COLUMNS=76 regendoc --update *.rst */*.rst ${REGENDOC_ARGS}
PYTHONDONTWRITEBYTECODE=1 PYTEST_ADDOPTS="-pno:hypothesis -Wignore::pytest.PytestUnknownMarkWarning" COLUMNS=76 regendoc --update ${REGENDOC_FILES} ${REGENDOC_ARGS}
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html

View File

@@ -10,6 +10,7 @@
<li><a href="{{ pathto('changelog') }}">Changelog</a></li>
<li><a href="{{ pathto('contributing') }}">Contributing</a></li>
<li><a href="{{ pathto('backwards-compatibility') }}">Backwards Compatibility</a></li>
<li><a href="{{ pathto('py27-py34-deprecation') }}">Python 2.7 and 3.4 Support</a></li>
<li><a href="{{ pathto('license') }}">License</a></li>
<li><a href="{{ pathto('contact') }}">Contact Channels</a></li>
</ul>

View File

@@ -6,6 +6,16 @@ Release announcements
:maxdepth: 2
release-5.0.0
release-4.6.3
release-4.6.2
release-4.6.1
release-4.6.0
release-4.5.0
release-4.4.2
release-4.4.1
release-4.4.0
release-4.3.1
release-4.3.0
release-4.2.1
release-4.2.0

View File

@@ -20,7 +20,7 @@ Thanks to all who contributed to this release, among them:
* Jeffrey Rackauckas
* Jose Carlos Menezes
* Ronny Pfannschmidt
* Zac-HD
* Zac Hatfield-Dodds
* iwanb

View File

@@ -0,0 +1,28 @@
pytest-4.3.1
=======================================
pytest 4.3.1 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
Thanks to all who contributed to this release, among them:
* Andras Mitzki
* Anthony Sottile
* Bruno Oliveira
* Daniel Hahler
* Danilo Horta
* Grygorii Iermolenko
* Jeff Hale
* Kyle Altendorf
* Stephan Hoyer
* Zac Hatfield-Dodds
* songbowen
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,39 @@
pytest-4.4.0
=======================================
The pytest team is proud to announce the 4.4.0 release!
pytest is a mature Python testing tool with more than a 2000 tests
against itself, passing on many different interpreters and platforms.
This release contains a number of bugs fixes and improvements, so users are encouraged
to take a look at the CHANGELOG:
https://docs.pytest.org/en/latest/changelog.html
For complete documentation, please visit:
https://docs.pytest.org/en/latest/
As usual, you can upgrade from pypi via:
pip install -U pytest
Thanks to all who contributed to this release, among them:
* Anthony Sottile
* ApaDoctor
* Bernhard M. Wiedemann
* Brian Skinn
* Bruno Oliveira
* Daniel Hahler
* Gary Tyler
* Jeong YunWon
* Miro Hrončok
* Takafumi Arakaki
* henrykironde
* smheidrich
Happy testing,
The Pytest Development Team

View File

@@ -0,0 +1,20 @@
pytest-4.4.1
=======================================
pytest 4.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/latest/changelog.html.
Thanks to all who contributed to this release, among them:
* Anthony Sottile
* Bruno Oliveira
* Daniel Hahler
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,33 @@
pytest-4.4.2
=======================================
pytest 4.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/latest/changelog.html.
Thanks to all who contributed to this release, among them:
* Allan Lewis
* Anthony Sottile
* Bruno Oliveira
* DamianSkrzypczak
* Daniel Hahler
* Don Kirkby
* Douglas Thor
* Hugo
* Ilya Konstantinov
* Jon Dufresne
* Matt Cooper
* Nikolay Kondratyev
* Ondřej Súkup
* Peter Schutt
* Romain Chossart
* Sitaktif
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,34 @@
pytest-4.5.0
=======================================
The pytest team is proud to announce the 4.5.0 release!
pytest is a mature Python testing tool with more than a 2000 tests
against itself, passing on many different interpreters and platforms.
This release contains a number of bugs fixes and improvements, so users are encouraged
to take a look at the CHANGELOG:
https://docs.pytest.org/en/latest/changelog.html
For complete documentation, please visit:
https://docs.pytest.org/en/latest/
As usual, you can upgrade from pypi via:
pip install -U pytest
Thanks to all who contributed to this release, among them:
* Anthony Sottile
* Bruno Oliveira
* Daniel Hahler
* Floris Bruynooghe
* Pulkit Goyal
* Samuel Searles-Bryant
* Zac Hatfield-Dodds
Happy testing,
The Pytest Development Team

View File

@@ -0,0 +1,43 @@
pytest-4.6.0
=======================================
The pytest team is proud to announce the 4.6.0 release!
pytest is a mature Python testing tool with more than a 2000 tests
against itself, passing on many different interpreters and platforms.
This release contains a number of bugs fixes and improvements, so users are encouraged
to take a look at the CHANGELOG:
https://docs.pytest.org/en/latest/changelog.html
For complete documentation, please visit:
https://docs.pytest.org/en/latest/
As usual, you can upgrade from pypi via:
pip install -U pytest
Thanks to all who contributed to this release, among them:
* Akiomi Kamakura
* Anthony Sottile
* Bruno Oliveira
* Daniel Hahler
* David Röthlisberger
* Evan Kepner
* Jeffrey Rackauckas
* MyComputer
* Nikita Krokosh
* Raul Tambre
* Thomas Hisch
* Tim Hoffmann
* Tomer Keren
* Victor Maryama
* danielx123
* oleg-yegorov
Happy testing,
The Pytest Development Team

View File

@@ -0,0 +1,19 @@
pytest-4.6.1
=======================================
pytest 4.6.1 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
Thanks to all who contributed to this release, among them:
* Anthony Sottile
* Bruno Oliveira
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,18 @@
pytest-4.6.2
=======================================
pytest 4.6.2 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
Thanks to all who contributed to this release, among them:
* Anthony Sottile
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,21 @@
pytest-4.6.3
=======================================
pytest 4.6.3 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
Thanks to all who contributed to this release, among them:
* Anthony Sottile
* Bruno Oliveira
* Daniel Hahler
* Dirk Thomas
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,46 @@
pytest-5.0.0
=======================================
The pytest team is proud to announce the 5.0.0 release!
pytest is a mature Python testing tool with more than a 2000 tests
against itself, passing on many different interpreters and platforms.
This release contains a number of bugs fixes and improvements, so users are encouraged
to take a look at the CHANGELOG:
https://docs.pytest.org/en/latest/changelog.html
For complete documentation, please visit:
https://docs.pytest.org/en/latest/
As usual, you can upgrade from pypi via:
pip install -U pytest
Thanks to all who contributed to this release, among them:
* Anthony Sottile
* Bruno Oliveira
* Daniel Hahler
* Dirk Thomas
* Evan Kepner
* Florian Bruhin
* Hugo
* Kevin J. Foley
* Pulkit Goyal
* Ralph Giles
* Ronny Pfannschmidt
* Thomas Grainger
* Thomas Hisch
* Tim Gates
* Victor Maryama
* Yuri Apollov
* Zac Hatfield-Dodds
* curiousjazz77
* patriksevallius
Happy testing,
The Pytest Development Team

View File

@@ -12,12 +12,15 @@ Asserting with the ``assert`` statement
``pytest`` allows you to use the standard python ``assert`` for verifying
expectations and values in Python tests. For example, you can write the
following::
following:
.. code-block:: python
# content of test_assert1.py
def f():
return 3
def test_function():
assert f() == 4
@@ -30,7 +33,7 @@ you will see the return value of the function call:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 1 item
test_assert1.py F [100%]
@@ -43,7 +46,7 @@ you will see the return value of the function call:
E assert 3 == 4
E + where 3 = f()
test_assert1.py:5: AssertionError
test_assert1.py:6: AssertionError
========================= 1 failed in 0.12 seconds =========================
``pytest`` has support for showing the values of the most common subexpressions
@@ -52,7 +55,9 @@ 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::
However, if you specify a message with the assertion like this:
.. code-block:: python
assert a % 2 == 0, "value was odd, should be even"
@@ -67,22 +72,29 @@ Assertions about expected exceptions
------------------------------------------
In order to write assertions about raised exceptions, you can use
``pytest.raises`` as a context manager like this::
``pytest.raises`` as a context manager like this:
.. code-block:: python
import pytest
def test_zero_division():
with pytest.raises(ZeroDivisionError):
1 / 0
and if you need to have access to the actual exception info you may use::
and if you need to have access to the actual exception info you may use:
.. code-block:: python
def test_recursion_depth():
with pytest.raises(RuntimeError) as excinfo:
def f():
f()
f()
assert 'maximum recursion' in str(excinfo.value)
assert "maximum recursion" in str(excinfo.value)
``excinfo`` is a ``ExceptionInfo`` instance, which is a wrapper around
the actual exception raised. The main attributes of interest are
@@ -90,15 +102,19 @@ the actual exception raised. The main attributes of interest are
You can pass a ``match`` keyword parameter to the context-manager to test
that a regular expression matches on the string representation of an exception
(similar to the ``TestCase.assertRaisesRegexp`` method from ``unittest``)::
(similar to the ``TestCase.assertRaisesRegexp`` method from ``unittest``):
.. code-block:: python
import pytest
def myfunc():
raise ValueError("Exception 123 raised")
def test_match():
with pytest.raises(ValueError, match=r'.* 123 .*'):
with pytest.raises(ValueError, match=r".* 123 .*"):
myfunc()
The regexp parameter of the ``match`` method is matched with the ``re.search``
@@ -107,7 +123,9 @@ well.
There's an alternate form of the ``pytest.raises`` function where you pass
a function that will be executed with the given ``*args`` and ``**kwargs`` and
assert that the given exception is raised::
assert that the given exception is raised:
.. code-block:: python
pytest.raises(ExpectedException, func, *args, **kwargs)
@@ -116,7 +134,9 @@ exception* or *wrong exception*.
Note that it is also possible to specify a "raises" argument to
``pytest.mark.xfail``, which checks that the test is failing in a more
specific way than just having any exception raised::
specific way than just having any exception raised:
.. code-block:: python
@pytest.mark.xfail(raises=IndexError)
def test_f():
@@ -134,7 +154,7 @@ or bugs in dependencies.
Assertions about expected warnings
-----------------------------------------
.. versionadded:: 2.8
You can check that code raises a particular warning using
:ref:`pytest.warns <warns>`.
@@ -145,13 +165,16 @@ You can check that code raises a particular warning using
Making use of context-sensitive comparisons
-------------------------------------------------
.. versionadded:: 2.0
``pytest`` has rich support for providing context-sensitive information
when it encounters comparisons. For example::
when it encounters comparisons. For example:
.. code-block:: python
# content of test_assert2.py
def test_set_comparison():
set1 = set("1308")
set2 = set("8035")
@@ -165,7 +188,7 @@ if you run this module:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 1 item
test_assert2.py F [100%]
@@ -184,7 +207,7 @@ if you run this module:
E '5'
E Use -v to get the full diff
test_assert2.py:5: AssertionError
test_assert2.py:6: AssertionError
========================= 1 failed in 0.12 seconds =========================
Special comparisons are done for a number of cases:
@@ -205,16 +228,21 @@ the ``pytest_assertrepr_compare`` hook.
:noindex:
As an example consider adding the following hook in a :ref:`conftest.py <conftest.py>`
file which provides an alternative explanation for ``Foo`` objects::
file which provides an alternative explanation for ``Foo`` objects:
.. code-block:: python
# content of conftest.py
from test_foocompare import Foo
def pytest_assertrepr_compare(op, left, right):
if isinstance(left, Foo) and isinstance(right, Foo) and op == "==":
return ['Comparing Foo instances:',
' vals: %s != %s' % (left.val, right.val)]
return ["Comparing Foo instances:", " vals: %s != %s" % (left.val, right.val)]
now, given this test module::
now, given this test module:
.. code-block:: python
# content of test_foocompare.py
class Foo(object):
@@ -224,6 +252,7 @@ now, given this test module::
def __eq__(self, other):
return self.val == other.val
def test_compare():
f1 = Foo(1)
f2 = Foo(2)
@@ -246,16 +275,16 @@ the conftest file:
E assert Comparing Foo instances:
E vals: 1 != 2
test_foocompare.py:11: AssertionError
test_foocompare.py:12: AssertionError
1 failed in 0.12 seconds
.. _assert-details:
.. _`assert introspection`:
Advanced assertion introspection
----------------------------------
Assertion introspection details
-------------------------------
.. versionadded:: 2.1
Reporting details about a failing assertion is achieved by rewriting assert
@@ -266,35 +295,53 @@ supporting modules which are not themselves test modules will not be rewritten**
You can manually enable assertion rewriting for an imported module by calling
`register_assert_rewrite <https://docs.pytest.org/en/latest/writing_plugins.html#assertion-rewriting>`_
before you import it (a good place to do that is in ``conftest.py``).
.. note::
``pytest`` rewrites test modules on import by using an import
hook to write new ``pyc`` files. Most of the time this works transparently.
However, if you are messing with import yourself, the import hook may
interfere.
If this is the case you have two options:
* Disable rewriting for a specific module by adding the string
``PYTEST_DONT_REWRITE`` to its docstring.
* Disable rewriting for all modules by using ``--assert=plain``.
Additionally, rewriting will fail silently if it cannot write new ``.pyc`` files,
i.e. in a read-only filesystem or a zipfile.
before you import it (a good place to do that is in your root ``conftest.py``).
For further information, Benjamin Peterson wrote up `Behind the scenes of pytest's new assertion rewriting <http://pybites.blogspot.com/2011/07/behind-scenes-of-pytests-new-assertion.html>`_.
.. versionadded:: 2.1
Assertion rewriting caches files on disk
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``pytest`` will write back the rewritten modules to disk for caching. You can disable
this behavior (for example to avoid leaving stale ``.pyc`` files around in projects that
move files around a lot) by adding this to the top of your ``conftest.py`` file:
.. code-block:: python
import sys
sys.dont_write_bytecode = True
Note that you still get the benefits of assertion introspection, the only change is that
the ``.pyc`` files won't be cached on disk.
Additionally, rewriting will silently skip caching if it cannot write new ``.pyc`` files,
i.e. in a read-only filesystem or a zipfile.
Disabling assert rewriting
~~~~~~~~~~~~~~~~~~~~~~~~~~
``pytest`` rewrites test modules on import by using an import
hook to write new ``pyc`` files. Most of the time this works transparently.
However, if you are working with the import machinery yourself, the import hook may
interfere.
If this is the case you have two options:
* Disable rewriting for a specific module by adding the string
``PYTEST_DONT_REWRITE`` to its docstring.
* Disable rewriting for all modules by using ``--assert=plain``.
Add assert rewriting as an alternate introspection technique.
.. versionchanged:: 2.1
Introduce the ``--assert`` option. Deprecate ``--no-assert`` and
``--nomagic``.
.. versionchanged:: 3.0
Removes the ``--no-assert`` and ``--nomagic`` options.
Removes the ``--assert=reinterp`` option.

View File

@@ -8,18 +8,26 @@ When using bash as your shell, ``pytest`` can use argcomplete
(https://argcomplete.readthedocs.io/) for auto-completion.
For this ``argcomplete`` needs to be installed **and** enabled.
Install argcomplete using::
Install argcomplete using:
sudo pip install 'argcomplete>=0.5.7'
.. code-block:: bash
For global activation of all argcomplete enabled python applications run::
sudo pip install 'argcomplete>=0.5.7'
For global activation of all argcomplete enabled python applications run:
.. code-block:: bash
sudo activate-global-python-argcomplete
For permanent (but not global) ``pytest`` activation, use::
For permanent (but not global) ``pytest`` activation, use:
register-python-argcomplete pytest >> ~/.bashrc
.. code-block:: bash
For one-time activation of argcomplete for ``pytest`` only, use::
register-python-argcomplete pytest >> ~/.bashrc
eval "$(register-python-argcomplete pytest)"
For one-time activation of argcomplete for ``pytest`` only, use:
.. code-block:: bash
eval "$(register-python-argcomplete pytest)"

View File

@@ -27,36 +27,47 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
name of your plugin or application to avoid clashes with other cache users.
Values can be any object handled by the json stdlib module.
capsys
Enable capturing of writes to ``sys.stdout`` and ``sys.stderr`` and make
captured output available via ``capsys.readouterr()`` method calls
which return a ``(out, err)`` namedtuple. ``out`` and ``err`` will be ``text``
objects.
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.
capsysbinary
Enable capturing of writes to ``sys.stdout`` and ``sys.stderr`` and make
captured output available via ``capsys.readouterr()`` method calls
which return a ``(out, err)`` tuple. ``out`` and ``err`` will be ``bytes``
objects.
Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``.
The captured output is made available via ``capsysbinary.readouterr()``
method calls, which return a ``(out, err)`` namedtuple.
``out`` and ``err`` will be ``bytes`` objects.
capfd
Enable capturing of writes to file descriptors ``1`` and ``2`` and make
captured output available via ``capfd.readouterr()`` method calls
which return a ``(out, err)`` tuple. ``out`` and ``err`` will be ``text``
objects.
Enable text capturing of writes to file descriptors ``1`` and ``2``.
The captured output is made available via ``capfd.readouterr()`` method
calls, which return a ``(out, err)`` namedtuple.
``out`` and ``err`` will be ``text`` objects.
capfdbinary
Enable capturing of write to file descriptors 1 and 2 and make
captured output available via ``capfdbinary.readouterr`` method calls
which return a ``(out, err)`` tuple. ``out`` and ``err`` will be
``bytes`` objects.
doctest_namespace
Enable bytes capturing of writes to file descriptors ``1`` and ``2``.
The captured output is made available via ``capfd.readouterr()`` method
calls, which return a ``(out, err)`` namedtuple.
``out`` and ``err`` will be ``byte`` objects.
doctest_namespace [session scope]
Fixture that returns a :py:class:`dict` that will be injected into the namespace of doctests.
pytestconfig
pytestconfig [session scope]
Session-scoped fixture that returns the :class:`_pytest.config.Config` object.
Example::
def test_foo(pytestconfig):
if pytestconfig.getoption("verbose"):
if pytestconfig.getoption("verbose") > 0:
...
record_property
Add an extra properties the calling test.
User properties become part of the test report and are available to the
@@ -68,10 +79,26 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
def test_function(record_property):
record_property("example_key", 1)
record_xml_attribute
Add extra xml attributes to the tag for the calling test.
The fixture is callable with ``(name, value)``, with value being
automatically xml-encoded
record_testsuite_property [session scope]
Records a new ``<property>`` tag as child of the root ``<testsuite>``. This is suitable to
writing global information regarding the entire test suite, and is compatible with ``xunit2`` JUnit family.
This is a ``session``-scoped fixture which is called with ``(name, value)``. Example:
.. code-block:: python
def test_foo(record_testsuite_property):
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.
caplog
Access and control log capturing.
@@ -81,6 +108,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
* caplog.records -> list of logging.LogRecord instances
* caplog.record_tuples -> list of (logger_name, level, message) tuples
* caplog.clear() -> clear captured records and formatted log output string
monkeypatch
The returned ``monkeypatch`` fixture provides these
helper methods to modify objects, dictionaries or os.environ::
@@ -98,15 +126,19 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
test function or fixture has finished. The ``raising``
parameter determines if a KeyError or AttributeError
will be raised if the set/deletion operation has no target.
recwarn
Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions.
See http://docs.python.org/library/warnings.html for information
on warning categories.
tmpdir_factory
tmpdir_factory [session scope]
Return a :class:`_pytest.tmpdir.TempdirFactory` instance for the test session.
tmp_path_factory
tmp_path_factory [session scope]
Return a :class:`_pytest.tmpdir.TempPathFactory` instance for the test session.
tmpdir
Return a temporary directory path object
which is unique to each test function invocation,
@@ -115,6 +147,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
path object.
.. _`py.path.local`: https://py.readthedocs.io/en/latest/path.html
tmp_path
Return a temporary directory path object
which is unique to each test function invocation,
@@ -126,6 +159,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
in python < 3.6 this is a pathlib2.Path
no tests ran in 0.12 seconds
You can also interactively ask for help, e.g. by typing on the Python interactive prompt something like::

View File

@@ -5,7 +5,7 @@
Cache: working with cross-testrun state
=======================================
.. versionadded:: 2.8
Usage
---------
@@ -82,7 +82,7 @@ If you then run it with ``--lf``:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 50 items / 48 deselected / 2 selected
run-last-failure: rerun previous 2 failures
@@ -113,8 +113,8 @@ If you then run it with ``--lf``:
test_50.py:6: Failed
================= 2 failed, 48 deselected in 0.12 seconds ==================
You have run only the two failing test from the last run, while 48 tests have
not been run ("deselected").
You have run only the two failing tests from the last run, while the 48 passing
tests have not been run ("deselected").
Now, if you run with the ``--ff`` option, all tests will be run but the first
previous failures will be executed first (as can be seen from the series
@@ -126,7 +126,7 @@ of ``FF`` and dots):
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 50 items
run-last-failure: rerun previous 2 failures first
@@ -168,7 +168,9 @@ 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::
using the ``--last-failed-no-failures`` option, which takes one of the following values:
.. 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
@@ -216,15 +218,13 @@ If you run this command for the first time, you can see the print statement:
def test_function(mydata):
> assert mydata == 23
E assert 42 == 23
E -42
E +23
test_caching.py:17: AssertionError
-------------------------- Captured stdout setup ---------------------------
running expensive computation...
1 failed in 0.12 seconds
If you run it a second time the value will be retrieved from
If you run it a second time, the value will be retrieved from
the cache and nothing will be printed:
.. code-block:: pytest
@@ -239,8 +239,6 @@ the cache and nothing will be printed:
def test_function(mydata):
> assert mydata == 23
E assert 42 == 23
E -42
E +23
test_caching.py:17: AssertionError
1 failed in 0.12 seconds
@@ -249,7 +247,7 @@ See the :ref:`cache-api` for more details.
Inspecting Cache content
-------------------------------
------------------------
You can always peek at the content of the cache using the
``--cache-show`` command line option:
@@ -260,9 +258,9 @@ You can always peek at the content of the cache using the
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
cachedir: $PYTHON_PREFIX/.pytest_cache
------------------------------- cache values -------------------------------
--------------------------- cache values for '*' ---------------------------
cache/lastfailed contains:
{'test_50.py::test_num[17]': True,
'test_50.py::test_num[25]': True,
@@ -279,11 +277,30 @@ You can always peek at the content of the cache using the
======================= no tests ran in 0.12 seconds =======================
``--cache-show`` takes an optional argument to specify a glob pattern for
filtering:
.. code-block:: pytest
$ pytest --cache-show example/*
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
cachedir: $PYTHON_PREFIX/.pytest_cache
----------------------- cache values for 'example/*' -----------------------
example/value contains:
42
======================= no tests ran in 0.12 seconds =======================
Clearing Cache content
-------------------------------
----------------------
You can instruct pytest to clear all cache files and values
by adding the ``--cache-clear`` option like this::
by adding the ``--cache-clear`` option like this:
.. code-block:: bash
pytest --cache-clear

View File

@@ -35,7 +35,9 @@ There are two ways in which ``pytest`` can perform capturing:
.. _`disable capturing`:
You can influence output capturing mechanisms from the command line::
You can influence output capturing mechanisms from the command line:
.. code-block:: bash
pytest -s # disable all capturing
pytest --capture=sys # replace sys.stdout/stderr with in-mem files
@@ -69,7 +71,7 @@ of the failing function and hide the other one:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 2 items
test_module.py .F [100%]
@@ -119,11 +121,11 @@ same interface but allows to also capture output from
libraries or subprocesses that directly write to operating
system level output streams (FD1 and FD2).
.. versionadded:: 3.3
The return value from ``readouterr`` changed to a ``namedtuple`` with two attributes, ``out`` and ``err``.
.. versionadded:: 3.3
If the code under test writes non-textual data, you can capture this using
the ``capsysbinary`` fixture which instead returns ``bytes`` from
@@ -131,7 +133,7 @@ the ``readouterr`` method. The ``capfsysbinary`` fixture is currently only
available in python 3.
.. versionadded:: 3.3
If the code under test writes non-textual data, you can capture this using
the ``capfdbinary`` fixture which instead returns ``bytes`` from
@@ -139,7 +141,7 @@ the ``readouterr`` method. The ``capfdbinary`` fixture operates on the
filedescriptor level.
.. versionadded:: 3.0
To temporarily disable capture within a test, both ``capsys``
and ``capfd`` have a ``disabled()`` method that can be used

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# pytest documentation build configuration file, created by
# sphinx-quickstart on Fri Oct 8 17:54:28 2010.
@@ -63,9 +62,9 @@ source_suffix = ".rst"
master_doc = "contents"
# General information about the project.
project = u"pytest"
project = "pytest"
year = datetime.datetime.utcnow().year
copyright = u"20152019 , holger krekel and pytest-dev team"
copyright = "20152019 , holger krekel and pytest-dev team"
# The language for content autogenerated by Sphinx. Refer to documentation
@@ -233,8 +232,8 @@ latex_documents = [
(
"contents",
"pytest.tex",
u"pytest Documentation",
u"holger krekel, trainer and consultant, http://merlinux.eu",
"pytest Documentation",
"holger krekel, trainer and consultant, http://merlinux.eu",
"manual",
)
]
@@ -266,16 +265,16 @@ latex_domain_indices = False
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [("usage", "pytest", u"pytest usage", [u"holger krekel at merlinux eu"], 1)]
man_pages = [("usage", "pytest", "pytest usage", ["holger krekel at merlinux eu"], 1)]
# -- Options for Epub output ---------------------------------------------------
# Bibliographic Dublin Core info.
epub_title = u"pytest"
epub_author = u"holger krekel at merlinux eu"
epub_publisher = u"holger krekel at merlinux eu"
epub_copyright = u"2013, holger krekel et alii"
epub_title = "pytest"
epub_author = "holger krekel at merlinux eu"
epub_publisher = "holger krekel at merlinux eu"
epub_copyright = "2013, holger krekel et alii"
# The language of the text. It defaults to the language option
# or en if the language is not set.
@@ -335,7 +334,7 @@ intersphinx_mapping = {"python": ("https://docs.python.org/3", None)}
def setup(app):
# from sphinx.ext.autodoc import cut_lines
# app.connect('autodoc-process-docstring', cut_lines(4, what=['module']))
app.add_description_unit(
app.add_object_type(
"confval",
"confval",
objname="configuration value",

View File

@@ -50,6 +50,7 @@ Full pytest documentation
projects
faq
contact
tidelift
.. only:: html

View File

@@ -5,7 +5,9 @@ Command line options and configuration file settings
-----------------------------------------------------------------
You can get help on command line options and values in INI-style
configurations files by using the general help option::
configurations files by using the general help option:
.. code-block:: bash
pytest -h # prints options _and_ config file settings
@@ -18,7 +20,7 @@ which were registered by installed plugins.
Initialization: determining rootdir and inifile
-----------------------------------------------
.. versionadded:: 2.7
pytest determines a ``rootdir`` for each test run which depends on
the command line arguments (specified test files, paths) and on
@@ -88,16 +90,20 @@ The ``config`` object will subsequently carry these attributes:
- ``config.inifile``: the determined ini-file, may be ``None``.
The rootdir is used a reference directory for constructing test
The rootdir is used as a reference directory for constructing test
addresses ("nodeids") and can be used also by plugins for storing
per-testrun information.
Example::
Example:
.. code-block:: bash
pytest path/to/testdir path/other/
will determine the common ancestor as ``path`` and then
check for ini-files as follows::
check for ini-files as follows:
.. code-block:: text
# first look for pytest.ini files
path/pytest.ini
@@ -127,25 +133,33 @@ progress output, you can write it into a configuration file:
.. code-block:: ini
# content of pytest.ini
# (or tox.ini or setup.cfg)
# content of pytest.ini or tox.ini
# setup.cfg files should use [tool:pytest] section instead
[pytest]
addopts = -ra -q
Alternatively, you can set a ``PYTEST_ADDOPTS`` environment variable to add command
line options while the environment is in use::
line options while the environment is in use:
.. code-block:: bash
export PYTEST_ADDOPTS="-v"
Here's how the command-line is built in the presence of ``addopts`` or the environment variable::
Here's how the command-line is built in the presence of ``addopts`` or the environment variable:
.. code-block:: text
<pytest.ini:addopts> $PYTEST_ADDOPTS <extra command-line arguments>
So if the user executes in the command-line::
So if the user executes in the command-line:
.. code-block:: bash
pytest -m slow
The actual command line executed is::
The actual command line executed is:
.. code-block:: bash
pytest -ra -q -v -m slow

View File

@@ -19,6 +19,23 @@ Below is a complete list of all pytest features which are considered deprecated.
:class:`_pytest.warning_types.PytestWarning` or subclasses, which can be filtered using
:ref:`standard warning filters <warnings>`.
Removal of ``funcargnames`` alias for ``fixturenames``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 5.0
The ``FixtureRequest``, ``Metafunc``, and ``Function`` classes track the names of
their associated fixtures, with the aptly-named ``fixturenames`` attribute.
Prior to pytest 2.3, this attribute was named ``funcargnames``, and we have kept
that as an alias since. It is finally due for removal, as it is often confusing
in places where we or plugin authors must distinguish between fixture names and
names supplied by non-fixture things such as ``pytest.mark.parametrize``.
.. _`raises message deprecated`:
``"message"`` parameter of ``pytest.raises``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -60,7 +77,8 @@ If you still have concerns about this deprecation and future removal, please com
The ``pytest.config`` global object is deprecated. Instead use
``request.config`` (via the ``request`` fixture) or if you are a plugin author
use the ``pytest_configure(config)`` hook.
use the ``pytest_configure(config)`` hook. Note that many hooks can also access
the ``config`` object indirectly, through ``session.config`` or ``item.config`` for example.
.. _raises-warns-exec:
@@ -98,20 +116,21 @@ Becomes:
Result log (``--result-log``)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.0
.. deprecated:: 4.0
The ``--resultlog`` command line option has been deprecated: it is little used
and there are more modern and better alternatives, for example `pytest-tap <https://tappy.readthedocs.io/en/latest/>`_.
The ``--result-log`` option produces a stream of test reports which can be
analysed at runtime. It uses a custom format which requires users to implement their own
parser, but the team believes using a line-based format that can be parsed using standard
tools would provide a suitable and better alternative.
This feature will be effectively removed in pytest 4.0 as the team intends to include a better alternative in the core.
The current plan is to provide an alternative in the pytest 5.0 series and remove the ``--result-log``
option in pytest 6.0 after the new implementation proves satisfactory to all users and is deemed
stable.
If you have any concerns, please don't hesitate to `open an issue <https://github.com/pytest-dev/pytest/issues>`__.
The actual alternative is still being discussed in issue `#4488 <https://github.com/pytest-dev/pytest/issues/4488>`__.
Removed Features
----------------

View File

@@ -4,31 +4,69 @@ Doctest integration for modules and test files
By default all files matching the ``test*.txt`` pattern will
be run through the python standard ``doctest`` module. You
can change the pattern by issuing::
can change the pattern by issuing:
.. code-block:: bash
pytest --doctest-glob='*.rst'
on the command line. Since version ``2.9``, ``--doctest-glob``
can be given multiple times in the command-line.
on the command line. ``--doctest-glob`` can be given multiple times in the command-line.
.. versionadded:: 3.1
If you then have a text file like this:
You can specify the encoding that will be used for those doctest files
using the ``doctest_encoding`` ini option:
.. code-block:: text
.. code-block:: ini
# content of test_example.txt
# content of pytest.ini
[pytest]
doctest_encoding = latin1
hello this is a doctest
>>> x = 3
>>> x
3
The default encoding is UTF-8.
then you can just invoke ``pytest`` directly:
You can also trigger running of doctests
from docstrings in all python modules (including regular
python test modules)::
.. code-block:: pytest
pytest --doctest-modules
$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 1 item
test_example.txt . [100%]
========================= 1 passed in 0.12 seconds =========================
By default, pytest will collect ``test*.txt`` files looking for doctest directives, but you
can pass additional globs using the ``--doctest-glob`` option (multi-allowed).
In addition to text files, you can also execute doctests directly from docstrings of your classes
and functions, including from test modules:
.. code-block:: python
# content of mymodule.py
def something():
""" a doctest in a docstring
>>> something()
42
"""
return 42
.. code-block:: bash
$ pytest --doctest-modules
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 2 items
mymodule.py . [ 50%]
test_example.txt . [100%]
========================= 2 passed in 0.12 seconds =========================
You can make these changes permanent in your project by
putting them into a pytest.ini file like this:
@@ -39,53 +77,37 @@ putting them into a pytest.ini file like this:
[pytest]
addopts = --doctest-modules
If you then have a text file like this::
.. note::
# content of example.rst
The builtin pytest doctest supports only ``doctest`` blocks, but if you are looking
for more advanced checking over *all* your documentation,
including doctests, ``.. codeblock:: python`` Sphinx directive support,
and any other examples your documentation may include, you may wish to
consider `Sybil <https://sybil.readthedocs.io/en/latest/index.html>`__.
It provides pytest integration out of the box.
hello this is a doctest
>>> x = 3
>>> x
3
and another like this::
Encoding
--------
# content of mymodule.py
def something():
""" a doctest in a docstring
>>> something()
42
"""
return 42
The default encoding is **UTF-8**, but you can specify the encoding
that will be used for those doctest files using the
``doctest_encoding`` ini option:
then you can just invoke ``pytest`` without command line options:
.. code-block:: ini
.. code-block:: pytest
# content of pytest.ini
[pytest]
doctest_encoding = latin1
$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
collected 1 item
Using 'doctest' options
-----------------------
mymodule.py . [100%]
The standard ``doctest`` module provides some `options <https://docs.python.org/3/library/doctest.html#option-flags>`__
to configure the strictness of doctest tests. In pytest, you can enable those flags using the
configuration file.
========================= 1 passed in 0.12 seconds =========================
It is possible to use fixtures using the ``getfixture`` helper::
# content of example.rst
>>> tmp = getfixture('tmpdir')
>>> ...
>>>
Also, :ref:`usefixtures` and :ref:`autouse` fixtures are supported
when executing text doctest files.
The standard ``doctest`` module provides some setting flags to configure the
strictness of doctest tests. In pytest, you can enable those flags using the
configuration file. To make pytest ignore trailing whitespaces and ignore
For example, to make pytest ignore trailing whitespaces and ignore
lengthy exception stack traces you can just write:
.. code-block:: ini
@@ -102,34 +124,67 @@ Python 3 unchanged:
* ``ALLOW_BYTES``: when enabled, the ``b`` prefix is stripped from byte strings
in expected doctest output.
As with any other option flag, these flags can be enabled in ``pytest.ini`` using
the ``doctest_optionflags`` ini option:
Alternatively, options can be enabled by an inline comment in the doc test
itself:
.. code-block:: ini
[pytest]
doctest_optionflags = ALLOW_UNICODE ALLOW_BYTES
Alternatively, it can be enabled by an inline comment in the doc test
itself::
.. code-block:: rst
# content of example.rst
>>> get_unicode_greeting() # doctest: +ALLOW_UNICODE
'Hello'
By default, pytest would report only the first failure for a given doctest. If
you want to continue the test even when you have failures, do::
By default, pytest would report only the first failure for a given doctest. If
you want to continue the test even when you have failures, do:
.. code-block:: bash
pytest --doctest-modules --doctest-continue-on-failure
Output format
-------------
You can change the diff output format on failure for your doctests
by using one of standard doctest modules format in options
(see :data:`python:doctest.REPORT_UDIFF`, :data:`python:doctest.REPORT_CDIFF`,
:data:`python:doctest.REPORT_NDIFF`, :data:`python:doctest.REPORT_ONLY_FIRST_FAILURE`):
.. code-block:: bash
pytest --doctest-modules --doctest-report none
pytest --doctest-modules --doctest-report udiff
pytest --doctest-modules --doctest-report cdiff
pytest --doctest-modules --doctest-report ndiff
pytest --doctest-modules --doctest-report only_first_failure
pytest-specific features
------------------------
Some features are provided to make writing doctests easier or with better integration with
your existing test suite. Keep in mind however that by using those features you will make
your doctests incompatible with the standard ``doctests`` module.
Using fixtures
^^^^^^^^^^^^^^
It is possible to use fixtures using the ``getfixture`` helper:
.. code-block:: text
# content of example.rst
>>> tmp = getfixture('tmpdir')
>>> ...
>>>
Also, :ref:`usefixtures` and :ref:`autouse` fixtures are supported
when executing text doctest files.
.. _`doctest_namespace`:
The 'doctest_namespace' fixture
-------------------------------
.. versionadded:: 3.0
'doctest_namespace' fixture
^^^^^^^^^^^^^^^^^^^^^^^^^^^
The ``doctest_namespace`` fixture can be used to inject items into the
namespace in which your doctests run. It is intended to be used within
@@ -159,18 +214,14 @@ Note that like the normal ``conftest.py``, the fixtures are discovered in the di
Meaning that if you put your doctest with your source code, the relevant conftest.py needs to be in the same directory tree.
Fixtures will not be discovered in a sibling directory tree!
Output format
-------------
Skipping tests dynamically
^^^^^^^^^^^^^^^^^^^^^^^^^^
.. versionadded:: 3.0
.. versionadded:: 4.4
You can change the diff output format on failure for your doctests
by using one of standard doctest modules format in options
(see :data:`python:doctest.REPORT_UDIFF`, :data:`python:doctest.REPORT_CDIFF`,
:data:`python:doctest.REPORT_NDIFF`, :data:`python:doctest.REPORT_ONLY_FIRST_FAILURE`)::
You can use ``pytest.skip`` to dynamically skip doctests. For example::
pytest --doctest-modules --doctest-report none
pytest --doctest-modules --doctest-report udiff
pytest --doctest-modules --doctest-report cdiff
pytest --doctest-modules --doctest-report ndiff
pytest --doctest-modules --doctest-report only_first_failure
>>> import sys, pytest
>>> if sys.platform.startswith('win'):
... pytest.skip('this doctest does not work on Windows')
...

View File

@@ -1,5 +1,3 @@
import six
import _pytest._code
import pytest
from pytest import raises
@@ -22,7 +20,7 @@ def test_generative(param1, param2):
assert param1 * 2 < param2
class TestFailing(object):
class TestFailing:
def test_simple(self):
def f():
return 42
@@ -42,7 +40,7 @@ class TestFailing(object):
assert not f()
class TestSpecialisedExplanations(object):
class TestSpecialisedExplanations:
def test_eq_text(self):
assert "spam" == "eggs"
@@ -102,7 +100,7 @@ class TestSpecialisedExplanations(object):
from dataclasses import dataclass
@dataclass
class Foo(object):
class Foo:
a: int
b: str
@@ -114,7 +112,7 @@ class TestSpecialisedExplanations(object):
import attr
@attr.s
class Foo(object):
class Foo:
a = attr.ib()
b = attr.ib()
@@ -124,7 +122,7 @@ class TestSpecialisedExplanations(object):
def test_attribute():
class Foo(object):
class Foo:
b = 1
i = Foo()
@@ -132,14 +130,14 @@ def test_attribute():
def test_attribute_instance():
class Foo(object):
class Foo:
b = 1
assert Foo().b == 2
def test_attribute_failure():
class Foo(object):
class Foo:
def _get_b(self):
raise Exception("Failed to get attrib")
@@ -150,10 +148,10 @@ def test_attribute_failure():
def test_attribute_multiple():
class Foo(object):
class Foo:
b = 1
class Bar(object):
class Bar:
b = 2
assert Foo().b == Bar().b
@@ -163,7 +161,7 @@ def globf(x):
return x + 1
class TestRaises(object):
class TestRaises:
def test_raises(self):
s = "qwe"
raises(TypeError, int, s)
@@ -192,19 +190,20 @@ class TestRaises(object):
# thanks to Matthew Scott for this test
def test_dynamic_compile_shows_nicely():
import imp
import importlib.util
import sys
src = "def foo():\n assert 1 == 0\n"
name = "abc-123"
module = imp.new_module(name)
spec = importlib.util.spec_from_loader(name, loader=None)
module = importlib.util.module_from_spec(spec)
code = _pytest._code.compile(src, name, "exec")
six.exec_(code, module.__dict__)
exec(code, module.__dict__)
sys.modules[name] = module
module.foo()
class TestMoreErrors(object):
class TestMoreErrors:
def test_complex_error(self):
def f():
return 44
@@ -254,16 +253,16 @@ class TestMoreErrors(object):
x = 0
class TestCustomAssertMsg(object):
class TestCustomAssertMsg:
def test_single_line(self):
class A(object):
class A:
a = 1
b = 2
assert A.a == b, "A.a appears not to be b"
def test_multiline(self):
class A(object):
class A:
a = 1
b = 2
@@ -272,7 +271,7 @@ class TestCustomAssertMsg(object):
), "A.a appears not to be b\nor does not appear to be b\none of those"
def test_custom_repr(self):
class JSON(object):
class JSON:
a = 1
def __repr__(self):

View File

@@ -2,7 +2,7 @@ def setup_module(module):
module.TestStateFullThing.classcount = 0
class TestStateFullThing(object):
class TestStateFullThing:
def setup_class(cls):
cls.classcount += 1

View File

@@ -8,7 +8,7 @@ def setup(request):
setup.finalize()
class CostlySetup(object):
class CostlySetup:
def __init__(self):
import time

View File

@@ -4,28 +4,40 @@
Working with custom markers
=================================================
Here are some example using the :ref:`mark` mechanism.
Here are some examples using the :ref:`mark` mechanism.
.. _`mark run`:
Marking test functions and selecting them for a run
----------------------------------------------------
You can "mark" a test function with custom metadata like this::
You can "mark" a test function with custom metadata like this:
.. code-block:: python
# content of test_server.py
import pytest
@pytest.mark.webtest
def test_send_http():
pass # perform some webtest test for your app
pass # perform some webtest test for your app
def test_something_quick():
pass
def test_another():
pass
class TestClass(object):
def test_method(self):
pass
.. versionadded:: 2.2
You can then restrict a test run to only run tests marked with ``webtest``:
@@ -35,7 +47,7 @@ You can then restrict a test run to only run tests marked with ``webtest``:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collecting ... collected 4 items / 3 deselected / 1 selected
test_server.py::test_send_http PASSED [100%]
@@ -50,7 +62,7 @@ Or the inverse, running all tests except the webtest ones:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collecting ... collected 4 items / 1 deselected / 3 selected
test_server.py::test_something_quick PASSED [ 33%]
@@ -72,7 +84,7 @@ tests based on their module, class, method, or function name:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collecting ... collected 1 item
test_server.py::TestClass::test_method PASSED [100%]
@@ -87,7 +99,7 @@ You can also select on the class:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collecting ... collected 1 item
test_server.py::TestClass::test_method PASSED [100%]
@@ -102,7 +114,7 @@ Or select multiple nodes:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collecting ... collected 2 items
test_server.py::TestClass::test_method PASSED [ 50%]
@@ -142,7 +154,7 @@ select tests based on their names:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collecting ... collected 4 items / 3 deselected / 1 selected
test_server.py::test_send_http PASSED [100%]
@@ -157,7 +169,7 @@ And you can also run all tests except the ones that match the keyword:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collecting ... collected 4 items / 1 deselected / 3 selected
test_server.py::test_something_quick PASSED [ 33%]
@@ -174,7 +186,7 @@ Or to select "http" and "quick" tests:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collecting ... collected 4 items / 2 deselected / 2 selected
test_server.py::test_send_http PASSED [ 50%]
@@ -200,18 +212,22 @@ Or to select "http" and "quick" tests:
Registering markers
-------------------------------------
.. versionadded:: 2.2
.. ini-syntax for custom markers:
Registering markers for your test suite is simple::
Registering markers for your test suite is simple:
.. code-block:: ini
# content of pytest.ini
[pytest]
markers =
webtest: mark a test as a webtest.
You can ask which markers exist for your test suite - the list includes our just defined ``webtest`` markers::
You can ask which markers exist for your test suite - the list includes our just defined ``webtest`` markers:
.. code-block:: pytest
$ pytest --markers
@pytest.mark.webtest: mark a test as a webtest.
@@ -245,7 +261,7 @@ For an example on how to add and work with markers from a plugin, see
* Asking for existing markers via ``pytest --markers`` gives good output
* Typos in function markers are treated as an error if you use
the ``--strict`` option.
the ``--strict-markers`` option.
.. _`scoped-marking`:
@@ -253,32 +269,42 @@ Marking whole classes or modules
----------------------------------------------------
You may use ``pytest.mark`` decorators with classes to apply markers to all of
its test methods::
its test methods:
.. code-block:: python
# content of test_mark_classlevel.py
import pytest
@pytest.mark.webtest
class TestClass(object):
def test_startup(self):
pass
def test_startup_and_more(self):
pass
This is equivalent to directly applying the decorator to the
two test functions.
To remain backward-compatible with Python 2.4 you can also set a
``pytestmark`` attribute on a TestClass like this::
Due to legacy reasons, it is possible to set the ``pytestmark`` attribute on a TestClass like this:
.. code-block:: python
import pytest
class TestClass(object):
pytestmark = pytest.mark.webtest
or if you need to use multiple markers you can use a list::
or if you need to use multiple markers you can use a list:
.. code-block:: python
import pytest
class TestClass(object):
pytestmark = [pytest.mark.webtest, pytest.mark.slowtest]
@@ -301,18 +327,19 @@ Marking individual tests when using parametrize
When using parametrize, applying a mark will make it apply
to each individual test. However it is also possible to
apply a marker to an individual test instance::
apply a marker to an individual test instance:
.. code-block:: python
import pytest
@pytest.mark.foo
@pytest.mark.parametrize(("n", "expected"), [
(1, 2),
pytest.param((1, 3), marks=pytest.mark.bar),
(2, 3),
])
@pytest.mark.parametrize(
("n", "expected"), [(1, 2), pytest.param((1, 3), marks=pytest.mark.bar), (2, 3)]
)
def test_increment(n, expected):
assert n + 1 == expected
assert n + 1 == expected
In this example the mark "foo" will apply to each of the three
tests, whereas the "bar" mark is only applied to the second test.
@@ -328,31 +355,46 @@ Custom marker and command line option to control test runs
Plugins can provide custom markers and implement specific behaviour
based on it. This is a self-contained example which adds a command
line option and a parametrized test function marker to run tests
specifies via named environments::
specifies via named environments:
.. code-block:: python
# content of conftest.py
import pytest
def pytest_addoption(parser):
parser.addoption("-E", action="store", metavar="NAME",
help="only run tests matching the environment NAME.")
parser.addoption(
"-E",
action="store",
metavar="NAME",
help="only run tests matching the environment NAME.",
)
def pytest_configure(config):
# register an additional marker
config.addinivalue_line("markers",
"env(name): mark test to run only on named environment")
config.addinivalue_line(
"markers", "env(name): mark test to run only on named environment"
)
def pytest_runtest_setup(item):
envnames = [mark.args[0] for mark in item.iter_markers(name='env')]
envnames = [mark.args[0] for mark in item.iter_markers(name="env")]
if envnames:
if item.config.getoption("-E") not in envnames:
pytest.skip("test requires env in %r" % envnames)
A test file using this local plugin::
A test file using this local plugin:
.. code-block:: python
# content of test_someenv.py
import pytest
@pytest.mark.env("stage1")
def test_basic_db_operation():
pass
@@ -366,7 +408,7 @@ the test needs:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 1 item
test_someenv.py s [100%]
@@ -381,14 +423,16 @@ and here is one that specifies exactly the environment needed:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 1 item
test_someenv.py . [100%]
========================= 1 passed in 0.12 seconds =========================
The ``--markers`` option always gives you a list of available markers::
The ``--markers`` option always gives you a list of available markers:
.. code-block:: pytest
$ pytest --markers
@pytest.mark.env(name): mark test to run only on named environment
@@ -417,25 +461,32 @@ Passing a callable to custom markers
.. regendoc:wipe
Below is the config file that will be used in the next examples::
Below is the config file that will be used in the next examples:
.. code-block:: python
# content of conftest.py
import sys
def pytest_runtest_setup(item):
for marker in item.iter_markers(name='my_marker'):
for marker in item.iter_markers(name="my_marker"):
print(marker)
sys.stdout.flush()
A custom marker can have its argument set, i.e. ``args`` and ``kwargs`` properties, defined by either invoking it as a callable or using ``pytest.mark.MARKER_NAME.with_args``. These two methods achieve the same effect most of the time.
However, if there is a callable as the single positional argument with no keyword arguments, using the ``pytest.mark.MARKER_NAME(c)`` will not pass ``c`` as a positional argument but decorate ``c`` with the custom marker (see :ref:`MarkDecorator <mark>`). Fortunately, ``pytest.mark.MARKER_NAME.with_args`` comes to the rescue::
However, if there is a callable as the single positional argument with no keyword arguments, using the ``pytest.mark.MARKER_NAME(c)`` will not pass ``c`` as a positional argument but decorate ``c`` with the custom marker (see :ref:`MarkDecorator <mark>`). Fortunately, ``pytest.mark.MARKER_NAME.with_args`` comes to the rescue:
.. code-block:: python
# content of test_custom_marker.py
import pytest
def hello_world(*args, **kwargs):
return 'Hello World'
return "Hello World"
@pytest.mark.my_marker.with_args(hello_world)
def test_with_args():
@@ -461,12 +512,16 @@ Reading markers which were set from multiple places
.. regendoc:wipe
If you are heavily using markers in your test suite you may encounter the case where a marker is applied several times to a test function. From plugin
code you can read over all such settings. Example::
code you can read over all such settings. Example:
.. code-block:: python
# content of test_mark_three_times.py
import pytest
pytestmark = pytest.mark.glob("module", x=1)
@pytest.mark.glob("class", x=2)
class TestClass(object):
@pytest.mark.glob("function", x=3)
@@ -474,13 +529,16 @@ code you can read over all such settings. Example::
pass
Here we have the marker "glob" applied three times to the same
test function. From a conftest file we can read it like this::
test function. From a conftest file we can read it like this:
.. code-block:: python
# content of conftest.py
import sys
def pytest_runtest_setup(item):
for mark in item.iter_markers(name='glob'):
for mark in item.iter_markers(name="glob"):
print("glob args=%s kwargs=%s" % (mark.args, mark.kwargs))
sys.stdout.flush()
@@ -504,7 +562,9 @@ Consider you have a test suite which marks tests for particular platforms,
namely ``pytest.mark.darwin``, ``pytest.mark.win32`` etc. and you
also have tests that run on all platforms and have no specific
marker. If you now want to have a way to only run the tests
for your particular platform, you could use the following plugin::
for your particular platform, you could use the following plugin:
.. code-block:: python
# content of conftest.py
#
@@ -513,6 +573,7 @@ for your particular platform, you could use the following plugin::
ALL = set("darwin linux win32".split())
def pytest_runtest_setup(item):
supported_platforms = ALL.intersection(mark.name for mark in item.iter_markers())
plat = sys.platform
@@ -520,24 +581,30 @@ for your particular platform, you could use the following plugin::
pytest.skip("cannot run on platform %s" % (plat))
then tests will be skipped if they were specified for a different platform.
Let's do a little test file to show how this looks like::
Let's do a little test file to show how this looks like:
.. code-block:: python
# content of test_plat.py
import pytest
@pytest.mark.darwin
def test_if_apple_is_evil():
pass
@pytest.mark.linux
def test_if_linux_works():
pass
@pytest.mark.win32
def test_if_win32_crashes():
pass
def test_runs_everywhere():
pass
@@ -549,13 +616,13 @@ then you will see two tests skipped and two executed tests as expected:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 4 items
test_plat.py s.s. [100%]
========================= short test summary info ==========================
SKIPPED [2] $REGENDOC_TMPDIR/conftest.py:12: cannot run on platform linux
========================= short test summary info ==========================
SKIPPED [2] $REGENDOC_TMPDIR/conftest.py:13: cannot run on platform linux
=================== 2 passed, 2 skipped in 0.12 seconds ====================
Note that if you specify a platform via the marker-command line option like this:
@@ -566,7 +633,7 @@ Note that if you specify a platform via the marker-command line option like this
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 4 items / 3 deselected / 1 selected
test_plat.py . [100%]
@@ -583,28 +650,38 @@ Automatically adding markers based on test names
If you a test suite where test function names indicate a certain
type of test, you can implement a hook that automatically defines
markers so that you can use the ``-m`` option with it. Let's look
at this test module::
at this test module:
.. code-block:: python
# content of test_module.py
def test_interface_simple():
assert 0
def test_interface_complex():
assert 0
def test_event_simple():
assert 0
def test_something_else():
assert 0
We want to dynamically define two markers and can do it in a
``conftest.py`` plugin::
``conftest.py`` plugin:
.. code-block:: python
# content of conftest.py
import pytest
def pytest_collection_modifyitems(items):
for item in items:
if "interface" in item.nodeid:
@@ -620,18 +697,18 @@ We can now use the ``-m option`` to select one set:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 4 items / 2 deselected / 2 selected
test_module.py FF [100%]
================================= FAILURES =================================
__________________________ test_interface_simple ___________________________
test_module.py:3: in test_interface_simple
test_module.py:4: in test_interface_simple
assert 0
E assert 0
__________________________ test_interface_complex __________________________
test_module.py:6: in test_interface_complex
test_module.py:8: in test_interface_complex
assert 0
E assert 0
================== 2 failed, 2 deselected in 0.12 seconds ==================
@@ -644,22 +721,22 @@ or to select both "event" and "interface" tests:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 4 items / 1 deselected / 3 selected
test_module.py FFF [100%]
================================= FAILURES =================================
__________________________ test_interface_simple ___________________________
test_module.py:3: in test_interface_simple
test_module.py:4: in test_interface_simple
assert 0
E assert 0
__________________________ test_interface_complex __________________________
test_module.py:6: in test_interface_complex
test_module.py:8: in test_interface_complex
assert 0
E assert 0
____________________________ test_event_simple _____________________________
test_module.py:9: in test_event_simple
test_module.py:12: in test_event_simple
assert 0
E assert 0
================== 3 failed, 1 deselected in 0.12 seconds ==================

View File

@@ -2,13 +2,13 @@
module containing a parametrized tests testing cross-python
serialization via the pickle module.
"""
import distutils.spawn
import shutil
import subprocess
import textwrap
import pytest
pythonlist = ["python2.7", "python3.4", "python3.5"]
pythonlist = ["python3.5", "python3.6", "python3.7"]
@pytest.fixture(params=pythonlist)
@@ -22,9 +22,9 @@ def python2(request, python1):
return Python(request.param, python1.picklefile)
class Python(object):
class Python:
def __init__(self, version, picklefile):
self.pythonpath = distutils.spawn.find_executable(version)
self.pythonpath = shutil.which(version)
if not self.pythonpath:
pytest.skip("{!r} not found".format(version))
self.picklefile = picklefile

View File

@@ -31,7 +31,7 @@ now execute the test specification:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
rootdir: $REGENDOC_TMPDIR/nonpython
collected 2 items
test_simple.yml F. [100%]
@@ -66,7 +66,7 @@ consulted when reporting in ``verbose`` mode:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
rootdir: $REGENDOC_TMPDIR/nonpython
collecting ... collected 2 items
test_simple.yml::hello FAILED [ 50%]
@@ -90,7 +90,7 @@ interesting to just look at the collection tree:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
rootdir: $REGENDOC_TMPDIR/nonpython
collected 2 items
<Package $REGENDOC_TMPDIR/nonpython>
<YamlFile test_simple.yml>

View File

@@ -18,7 +18,7 @@ class YamlFile(pytest.File):
class YamlItem(pytest.Item):
def __init__(self, name, parent, spec):
super(YamlItem, self).__init__(name, parent)
super().__init__(name, parent)
self.spec = spec
def runtest(self):

View File

@@ -146,7 +146,7 @@ objects, they are still using the default pytest representation:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 8 items
<Module test_time.py>
<Function test_timedistance_v0[a0-b0-expected0]>
@@ -205,7 +205,7 @@ this is a fully self-contained example which you can run with:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 4 items
test_scenarios.py .... [100%]
@@ -220,7 +220,7 @@ If you just collect tests you'll also nicely see 'advanced' and 'basic' as varia
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 4 items
<Module test_scenarios.py>
<Class TestSampleWithScenarios>
@@ -287,7 +287,7 @@ Let's first see how it looks like at collection time:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 2 items
<Module test_backends.py>
<Function test_db_initialized[d1]>
@@ -353,7 +353,7 @@ The result of this test will be successful:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 1 item
<Module test_indirect_list.py>
<Function test_indirect[a-b]>
@@ -411,8 +411,6 @@ argument sets to use for each test function. Let's run it:
def test_equals(self, a, b):
> assert a == b
E assert 1 == 2
E -1
E +2
test_parametrize.py:18: AssertionError
1 failed, 2 passed in 0.12 seconds
@@ -431,13 +429,15 @@ is to be run with different sets of arguments for its three arguments:
.. literalinclude:: multipython.py
Running it results in some skips if we don't have all the python interpreters installed and otherwise runs all combinations (5 interpreters times 5 interpreters times 3 objects to serialize/deserialize):
Running it results in some skips if we don't have all the python interpreters installed and otherwise runs all combinations (3 interpreters times 3 interpreters times 3 objects to serialize/deserialize):
.. code-block:: pytest
. $ pytest -rs -q multipython.py
........................... [100%]
27 passed in 0.12 seconds
ssssssssssss......sss...... [100%]
========================= short test summary info ==========================
SKIPPED [15] $REGENDOC_TMPDIR/CWD/multipython.py:30: 'python3.5' not found
12 passed, 15 skipped in 0.12 seconds
Indirect parametrization of optional implementations/imports
--------------------------------------------------------------------
@@ -488,13 +488,13 @@ If you run this with reporting for skips enabled:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 2 items
test_module.py .s [100%]
========================= short test summary info ==========================
SKIPPED [1] $REGENDOC_TMPDIR/conftest.py:11: could not import 'opt2'
========================= short test summary info ==========================
SKIPPED [1] $REGENDOC_TMPDIR/conftest.py:11: could not import 'opt2': No module named 'opt2'
=================== 1 passed, 1 skipped in 0.12 seconds ====================
You'll see that we don't have an ``opt2`` module and thus the second test run
@@ -515,21 +515,25 @@ Set marks or test ID for individual parametrized test
--------------------------------------------------------------------
Use ``pytest.param`` to apply marks or set test ID to individual parametrized test.
For example::
For example:
.. code-block:: python
# content of test_pytest_param_example.py
import pytest
@pytest.mark.parametrize('test_input,expected', [
('3+5', 8),
pytest.param('1+7', 8,
marks=pytest.mark.basic),
pytest.param('2+4', 6,
marks=pytest.mark.basic,
id='basic_2+4'),
pytest.param('6*9', 42,
marks=[pytest.mark.basic, pytest.mark.xfail],
id='basic_6*9'),
])
@pytest.mark.parametrize(
"test_input,expected",
[
("3+5", 8),
pytest.param("1+7", 8, marks=pytest.mark.basic),
pytest.param("2+4", 6, marks=pytest.mark.basic, id="basic_2+4"),
pytest.param(
"6*9", 42, marks=[pytest.mark.basic, pytest.mark.xfail], id="basic_6*9"
),
],
)
def test_eval(test_input, expected):
assert eval(test_input) == expected
@@ -546,7 +550,7 @@ Then run ``pytest`` with verbose mode and with only the ``basic`` marker:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collecting ... collected 17 items / 14 deselected / 3 selected
test_pytest_param_example.py::test_eval[1+7-8] PASSED [ 33%]

View File

@@ -6,7 +6,7 @@ def test_function():
pass
class TestClass(object):
class TestClass:
def test_method(self):
pass

View File

@@ -6,7 +6,9 @@ Ignore paths during test collection
You can easily ignore certain test directories and modules during collection
by passing the ``--ignore=path`` option on the cli. ``pytest`` allows multiple
``--ignore`` options. Example::
``--ignore`` options. Example:
.. code-block:: text
tests/
|-- example
@@ -57,7 +59,9 @@ Keeping duplicate paths specified from command line
----------------------------------------------------
Default behavior of ``pytest`` is to ignore duplicate paths specified from the command line.
Example::
Example:
.. code-block:: pytest
pytest path_a path_a
@@ -68,7 +72,9 @@ Example::
Just collect tests once.
To collect duplicate tests, use the ``--keep-duplicates`` option on the cli.
Example::
Example:
.. code-block:: pytest
pytest --keep-duplicates path_a path_a
@@ -78,7 +84,9 @@ Example::
As the collector just works on directories, if you specify twice a single test file, ``pytest`` will
still collect it twice, no matter if the ``--keep-duplicates`` is not specified.
Example::
Example:
.. code-block:: pytest
pytest test_a.py test_a.py
@@ -90,7 +98,9 @@ Example::
Changing directory recursion
-----------------------------------------------------
You can set the :confval:`norecursedirs` option in an ini-file, for example your ``pytest.ini`` in the project root directory::
You can set the :confval:`norecursedirs` option in an ini-file, for example your ``pytest.ini`` in the project root directory:
.. code-block:: ini
# content of pytest.ini
[pytest]
@@ -106,7 +116,9 @@ Changing naming conventions
You can configure different naming conventions by setting
the :confval:`python_files`, :confval:`python_classes` and
:confval:`python_functions` configuration options.
Here is an example::
Here is an example:
.. code-block:: ini
# content of pytest.ini
# Example 1: have pytest look for "check" instead of "test"
@@ -145,7 +157,9 @@ The test collection would look like this:
======================= no tests ran in 0.12 seconds =======================
You can check for multiple glob patterns by adding a space between the patterns::
You can check for multiple glob patterns by adding a space between the patterns:
.. code-block:: ini
# Example 2: have pytest look for files with "test" and "example"
# content of pytest.ini, tox.ini, or setup.cfg file (replace "pytest"
@@ -165,13 +179,17 @@ Interpreting cmdline arguments as Python packages
You can use the ``--pyargs`` option to make ``pytest`` try
interpreting arguments as python package names, deriving
their file system path and then running the test. For
example if you have unittest2 installed you can type::
example if you have unittest2 installed you can type:
.. code-block:: bash
pytest --pyargs unittest2.test.test_skipping -q
which would run the respective test module. Like with
other options, through an ini-file and the :confval:`addopts` option you
can make this change more permanently::
can make this change more permanently:
.. code-block:: ini
# content of pytest.ini
[pytest]
@@ -209,7 +227,9 @@ Customizing test collection
.. regendoc:wipe
You can easily instruct ``pytest`` to discover tests from every Python file::
You can easily instruct ``pytest`` to discover tests from every Python file:
.. code-block:: ini
# content of pytest.ini
[pytest]

View File

@@ -1,13 +1,9 @@
.. _`tbreportdemo`:
Demo of Python failure reports with pytest
==================================================
==========================================
Here is a nice run of several tens of failures
and how ``pytest`` presents things (unfortunately
not showing the nice colors here in the HTML that you
get on the terminal - we are working on that):
Here is a nice run of several failures and how ``pytest`` presents things:
.. code-block:: pytest
@@ -15,7 +11,7 @@ get on the terminal - we are working on that):
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR/assertion, inifile:
rootdir: $REGENDOC_TMPDIR/assertion
collected 44 items
failure_demo.py FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF [100%]
@@ -30,7 +26,7 @@ get on the terminal - we are working on that):
> assert param1 * 2 < param2
E assert (3 * 2) < 6
failure_demo.py:22: AssertionError
failure_demo.py:20: AssertionError
_________________________ TestFailing.test_simple __________________________
self = <failure_demo.TestFailing object at 0xdeadbeef>
@@ -47,7 +43,7 @@ get on the terminal - we are working on that):
E + where 42 = <function TestFailing.test_simple.<locals>.f at 0xdeadbeef>()
E + and 43 = <function TestFailing.test_simple.<locals>.g at 0xdeadbeef>()
failure_demo.py:33: AssertionError
failure_demo.py:31: AssertionError
____________________ TestFailing.test_simple_multiline _____________________
self = <failure_demo.TestFailing object at 0xdeadbeef>
@@ -55,7 +51,7 @@ get on the terminal - we are working on that):
def test_simple_multiline(self):
> otherfunc_multi(42, 6 * 9)
failure_demo.py:36:
failure_demo.py:34:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
a = 42, b = 54
@@ -64,7 +60,7 @@ get on the terminal - we are working on that):
> assert a == b
E assert 42 == 54
failure_demo.py:17: AssertionError
failure_demo.py:15: AssertionError
___________________________ TestFailing.test_not ___________________________
self = <failure_demo.TestFailing object at 0xdeadbeef>
@@ -77,7 +73,7 @@ get on the terminal - we are working on that):
E assert not 42
E + where 42 = <function TestFailing.test_not.<locals>.f at 0xdeadbeef>()
failure_demo.py:42: AssertionError
failure_demo.py:40: AssertionError
_________________ TestSpecialisedExplanations.test_eq_text _________________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -88,7 +84,7 @@ get on the terminal - we are working on that):
E - spam
E + eggs
failure_demo.py:47: AssertionError
failure_demo.py:45: AssertionError
_____________ TestSpecialisedExplanations.test_eq_similar_text _____________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -101,7 +97,7 @@ get on the terminal - we are working on that):
E + foo 2 bar
E ? ^
failure_demo.py:50: AssertionError
failure_demo.py:48: AssertionError
____________ TestSpecialisedExplanations.test_eq_multiline_text ____________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -114,7 +110,7 @@ get on the terminal - we are working on that):
E + eggs
E bar
failure_demo.py:53: AssertionError
failure_demo.py:51: AssertionError
______________ TestSpecialisedExplanations.test_eq_long_text _______________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -131,7 +127,7 @@ get on the terminal - we are working on that):
E + 1111111111b222222222
E ? ^
failure_demo.py:58: AssertionError
failure_demo.py:56: AssertionError
_________ TestSpecialisedExplanations.test_eq_long_text_multiline __________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -151,7 +147,7 @@ get on the terminal - we are working on that):
E
E ...Full output truncated (7 lines hidden), use '-vv' to show
failure_demo.py:63: AssertionError
failure_demo.py:61: AssertionError
_________________ TestSpecialisedExplanations.test_eq_list _________________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -162,7 +158,7 @@ get on the terminal - we are working on that):
E At index 2 diff: 2 != 3
E Use -v to get the full diff
failure_demo.py:66: AssertionError
failure_demo.py:64: AssertionError
______________ TestSpecialisedExplanations.test_eq_list_long _______________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -175,7 +171,7 @@ get on the terminal - we are working on that):
E At index 100 diff: 1 != 2
E Use -v to get the full diff
failure_demo.py:71: AssertionError
failure_demo.py:69: AssertionError
_________________ TestSpecialisedExplanations.test_eq_dict _________________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -186,14 +182,14 @@ get on the terminal - we are working on that):
E Omitting 1 identical items, use -vv to show
E Differing items:
E {'b': 1} != {'b': 2}
E Left contains more items:
E Left contains 1 more item:
E {'c': 0}
E Right contains more items:
E Right contains 1 more item:
E {'d': 0}...
E
E ...Full output truncated (2 lines hidden), use '-vv' to show
failure_demo.py:74: AssertionError
failure_demo.py:72: AssertionError
_________________ TestSpecialisedExplanations.test_eq_set __________________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -211,7 +207,7 @@ get on the terminal - we are working on that):
E
E ...Full output truncated (2 lines hidden), use '-vv' to show
failure_demo.py:77: AssertionError
failure_demo.py:75: AssertionError
_____________ TestSpecialisedExplanations.test_eq_longer_list ______________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -219,10 +215,10 @@ get on the terminal - we are working on that):
def test_eq_longer_list(self):
> assert [1, 2] == [1, 2, 3]
E assert [1, 2] == [1, 2, 3]
E Right contains more items, first extra item: 3
E Right contains one more item: 3
E Use -v to get the full diff
failure_demo.py:80: AssertionError
failure_demo.py:78: AssertionError
_________________ TestSpecialisedExplanations.test_in_list _________________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -231,7 +227,7 @@ get on the terminal - we are working on that):
> assert 1 in [0, 2, 3, 4, 5]
E assert 1 in [0, 2, 3, 4, 5]
failure_demo.py:83: AssertionError
failure_demo.py:81: AssertionError
__________ TestSpecialisedExplanations.test_not_in_text_multiline __________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -250,7 +246,7 @@ get on the terminal - we are working on that):
E
E ...Full output truncated (2 lines hidden), use '-vv' to show
failure_demo.py:87: AssertionError
failure_demo.py:85: AssertionError
___________ TestSpecialisedExplanations.test_not_in_text_single ____________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -263,7 +259,7 @@ get on the terminal - we are working on that):
E single foo line
E ? +++
failure_demo.py:91: AssertionError
failure_demo.py:89: AssertionError
_________ TestSpecialisedExplanations.test_not_in_text_single_long _________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -276,7 +272,7 @@ get on the terminal - we are working on that):
E head head foo tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail
E ? +++
failure_demo.py:95: AssertionError
failure_demo.py:93: AssertionError
______ TestSpecialisedExplanations.test_not_in_text_single_long_term _______
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -289,7 +285,7 @@ get on the terminal - we are working on that):
E head head fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffftail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail
E ? ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
failure_demo.py:99: AssertionError
failure_demo.py:97: AssertionError
______________ TestSpecialisedExplanations.test_eq_dataclass _______________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -298,7 +294,7 @@ get on the terminal - we are working on that):
from dataclasses import dataclass
@dataclass
class Foo(object):
class Foo:
a: int
b: str
@@ -310,7 +306,7 @@ get on the terminal - we are working on that):
E Differing attributes:
E b: 'b' != 'c'
failure_demo.py:111: AssertionError
failure_demo.py:109: AssertionError
________________ TestSpecialisedExplanations.test_eq_attrs _________________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -319,7 +315,7 @@ get on the terminal - we are working on that):
import attr
@attr.s
class Foo(object):
class Foo:
a = attr.ib()
b = attr.ib()
@@ -331,11 +327,11 @@ get on the terminal - we are working on that):
E Differing attributes:
E b: 'b' != 'c'
failure_demo.py:123: AssertionError
failure_demo.py:121: AssertionError
______________________________ test_attribute ______________________________
def test_attribute():
class Foo(object):
class Foo:
b = 1
i = Foo()
@@ -343,11 +339,11 @@ get on the terminal - we are working on that):
E assert 1 == 2
E + where 1 = <failure_demo.test_attribute.<locals>.Foo object at 0xdeadbeef>.b
failure_demo.py:131: AssertionError
failure_demo.py:129: AssertionError
_________________________ test_attribute_instance __________________________
def test_attribute_instance():
class Foo(object):
class Foo:
b = 1
> assert Foo().b == 2
@@ -355,11 +351,11 @@ get on the terminal - we are working on that):
E + where 1 = <failure_demo.test_attribute_instance.<locals>.Foo object at 0xdeadbeef>.b
E + where <failure_demo.test_attribute_instance.<locals>.Foo object at 0xdeadbeef> = <class 'failure_demo.test_attribute_instance.<locals>.Foo'>()
failure_demo.py:138: AssertionError
failure_demo.py:136: AssertionError
__________________________ test_attribute_failure __________________________
def test_attribute_failure():
class Foo(object):
class Foo:
def _get_b(self):
raise Exception("Failed to get attrib")
@@ -368,7 +364,7 @@ get on the terminal - we are working on that):
i = Foo()
> assert i.b == 2
failure_demo.py:149:
failure_demo.py:147:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <failure_demo.test_attribute_failure.<locals>.Foo object at 0xdeadbeef>
@@ -377,14 +373,14 @@ get on the terminal - we are working on that):
> raise Exception("Failed to get attrib")
E Exception: Failed to get attrib
failure_demo.py:144: Exception
failure_demo.py:142: Exception
_________________________ test_attribute_multiple __________________________
def test_attribute_multiple():
class Foo(object):
class Foo:
b = 1
class Bar(object):
class Bar:
b = 2
> assert Foo().b == Bar().b
@@ -394,7 +390,7 @@ get on the terminal - we are working on that):
E + and 2 = <failure_demo.test_attribute_multiple.<locals>.Bar object at 0xdeadbeef>.b
E + where <failure_demo.test_attribute_multiple.<locals>.Bar object at 0xdeadbeef> = <class 'failure_demo.test_attribute_multiple.<locals>.Bar'>()
failure_demo.py:159: AssertionError
failure_demo.py:157: AssertionError
__________________________ TestRaises.test_raises __________________________
self = <failure_demo.TestRaises object at 0xdeadbeef>
@@ -404,7 +400,7 @@ get on the terminal - we are working on that):
> raises(TypeError, int, s)
E ValueError: invalid literal for int() with base 10: 'qwe'
failure_demo.py:169: ValueError
failure_demo.py:167: ValueError
______________________ TestRaises.test_raises_doesnt _______________________
self = <failure_demo.TestRaises object at 0xdeadbeef>
@@ -413,7 +409,7 @@ get on the terminal - we are working on that):
> raises(IOError, int, "3")
E Failed: DID NOT RAISE <class 'OSError'>
failure_demo.py:172: Failed
failure_demo.py:170: Failed
__________________________ TestRaises.test_raise ___________________________
self = <failure_demo.TestRaises object at 0xdeadbeef>
@@ -422,7 +418,7 @@ get on the terminal - we are working on that):
> raise ValueError("demo error")
E ValueError: demo error
failure_demo.py:175: ValueError
failure_demo.py:173: ValueError
________________________ TestRaises.test_tupleerror ________________________
self = <failure_demo.TestRaises object at 0xdeadbeef>
@@ -431,7 +427,7 @@ get on the terminal - we are working on that):
> a, b = [1] # NOQA
E ValueError: not enough values to unpack (expected 2, got 1)
failure_demo.py:178: ValueError
failure_demo.py:176: ValueError
______ TestRaises.test_reinterpret_fails_with_print_for_the_fun_of_it ______
self = <failure_demo.TestRaises object at 0xdeadbeef>
@@ -442,7 +438,7 @@ get on the terminal - we are working on that):
> a, b = items.pop()
E TypeError: 'int' object is not iterable
failure_demo.py:183: TypeError
failure_demo.py:181: TypeError
--------------------------- Captured stdout call ---------------------------
items is [1, 2, 3]
________________________ TestRaises.test_some_error ________________________
@@ -453,29 +449,30 @@ get on the terminal - we are working on that):
> if namenotexi: # NOQA
E NameError: name 'namenotexi' is not defined
failure_demo.py:186: NameError
failure_demo.py:184: NameError
____________________ test_dynamic_compile_shows_nicely _____________________
def test_dynamic_compile_shows_nicely():
import imp
import importlib.util
import sys
src = "def foo():\n assert 1 == 0\n"
name = "abc-123"
module = imp.new_module(name)
spec = importlib.util.spec_from_loader(name, loader=None)
module = importlib.util.module_from_spec(spec)
code = _pytest._code.compile(src, name, "exec")
six.exec_(code, module.__dict__)
exec(code, module.__dict__)
sys.modules[name] = module
> module.foo()
failure_demo.py:204:
failure_demo.py:203:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
def foo():
> assert 1 == 0
E AssertionError
<0-codegen 'abc-123' $REGENDOC_TMPDIR/assertion/failure_demo.py:201>:2: AssertionError
<0-codegen 'abc-123' $REGENDOC_TMPDIR/assertion/failure_demo.py:200>:2: AssertionError
____________________ TestMoreErrors.test_complex_error _____________________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
@@ -489,9 +486,9 @@ get on the terminal - we are working on that):
> somefunc(f(), g())
failure_demo.py:215:
failure_demo.py:214:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
failure_demo.py:13: in somefunc
failure_demo.py:11: in somefunc
otherfunc(x, y)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
@@ -501,7 +498,7 @@ get on the terminal - we are working on that):
> assert a == b
E assert 44 == 43
failure_demo.py:9: AssertionError
failure_demo.py:7: AssertionError
___________________ TestMoreErrors.test_z1_unpack_error ____________________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
@@ -511,7 +508,7 @@ get on the terminal - we are working on that):
> a, b = items
E ValueError: not enough values to unpack (expected 2, got 0)
failure_demo.py:219: ValueError
failure_demo.py:218: ValueError
____________________ TestMoreErrors.test_z2_type_error _____________________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
@@ -521,7 +518,7 @@ get on the terminal - we are working on that):
> a, b = items
E TypeError: 'int' object is not iterable
failure_demo.py:223: TypeError
failure_demo.py:222: TypeError
______________________ TestMoreErrors.test_startswith ______________________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
@@ -534,7 +531,7 @@ get on the terminal - we are working on that):
E + where False = <built-in method startswith of str object at 0xdeadbeef>('456')
E + where <built-in method startswith of str object at 0xdeadbeef> = '123'.startswith
failure_demo.py:228: AssertionError
failure_demo.py:227: AssertionError
__________________ TestMoreErrors.test_startswith_nested ___________________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
@@ -553,7 +550,7 @@ get on the terminal - we are working on that):
E + where '123' = <function TestMoreErrors.test_startswith_nested.<locals>.f at 0xdeadbeef>()
E + and '456' = <function TestMoreErrors.test_startswith_nested.<locals>.g at 0xdeadbeef>()
failure_demo.py:237: AssertionError
failure_demo.py:236: AssertionError
_____________________ TestMoreErrors.test_global_func ______________________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
@@ -564,7 +561,7 @@ get on the terminal - we are working on that):
E + where False = isinstance(43, float)
E + where 43 = globf(42)
failure_demo.py:240: AssertionError
failure_demo.py:239: AssertionError
_______________________ TestMoreErrors.test_instance _______________________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
@@ -575,7 +572,7 @@ get on the terminal - we are working on that):
E assert 42 != 42
E + where 42 = <failure_demo.TestMoreErrors object at 0xdeadbeef>.x
failure_demo.py:244: AssertionError
failure_demo.py:243: AssertionError
_______________________ TestMoreErrors.test_compare ________________________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
@@ -585,7 +582,7 @@ get on the terminal - we are working on that):
E assert 11 < 5
E + where 11 = globf(10)
failure_demo.py:247: AssertionError
failure_demo.py:246: AssertionError
_____________________ TestMoreErrors.test_try_finally ______________________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
@@ -596,13 +593,13 @@ get on the terminal - we are working on that):
> assert x == 0
E assert 1 == 0
failure_demo.py:252: AssertionError
failure_demo.py:251: AssertionError
___________________ TestCustomAssertMsg.test_single_line ___________________
self = <failure_demo.TestCustomAssertMsg object at 0xdeadbeef>
def test_single_line(self):
class A(object):
class A:
a = 1
b = 2
@@ -611,13 +608,13 @@ get on the terminal - we are working on that):
E assert 1 == 2
E + where 1 = <class 'failure_demo.TestCustomAssertMsg.test_single_line.<locals>.A'>.a
failure_demo.py:263: AssertionError
failure_demo.py:262: AssertionError
____________________ TestCustomAssertMsg.test_multiline ____________________
self = <failure_demo.TestCustomAssertMsg object at 0xdeadbeef>
def test_multiline(self):
class A(object):
class A:
a = 1
b = 2
@@ -630,13 +627,13 @@ get on the terminal - we are working on that):
E assert 1 == 2
E + where 1 = <class 'failure_demo.TestCustomAssertMsg.test_multiline.<locals>.A'>.a
failure_demo.py:270: AssertionError
failure_demo.py:269: AssertionError
___________________ TestCustomAssertMsg.test_custom_repr ___________________
self = <failure_demo.TestCustomAssertMsg object at 0xdeadbeef>
def test_custom_repr(self):
class JSON(object):
class JSON:
a = 1
def __repr__(self):
@@ -652,5 +649,5 @@ get on the terminal - we are working on that):
E assert 1 == 2
E + where 1 = This is JSON\n{\n 'foo': 'bar'\n}.a
failure_demo.py:283: AssertionError
failure_demo.py:282: AssertionError
======================== 44 failed in 0.12 seconds =========================

View File

@@ -107,7 +107,7 @@ the command line arguments before they get processed:
.. code-block:: python
# content of conftest.py
# setuptools plugin
import sys
@@ -129,7 +129,7 @@ directory with the above conftest.py:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 0 items
======================= no tests ran in 0.12 seconds =======================
@@ -157,6 +157,10 @@ line option to control skipping of ``pytest.mark.slow`` marked tests:
)
def pytest_configure(config):
config.addinivalue_line("markers", "slow: mark test as slow to run")
def pytest_collection_modifyitems(config, items):
if config.getoption("--runslow"):
# --runslow given in cli: do not skip slow tests
@@ -190,13 +194,13 @@ and when running it will see a skipped "slow" test:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 2 items
test_module.py .s [100%]
========================= short test summary info ==========================
SKIPPED [1] test_module.py:8: need --runslow option to run
=================== 1 passed, 1 skipped in 0.12 seconds ====================
Or run it including the ``slow`` marked test:
@@ -207,7 +211,7 @@ Or run it including the ``slow`` marked test:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 2 items
test_module.py .. [100%]
@@ -351,7 +355,7 @@ which will add the string to the test header accordingly:
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
project deps: mylib-1.1
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 0 items
======================= no tests ran in 0.12 seconds =======================
@@ -381,7 +385,7 @@ which will add info only when run with "--v":
cachedir: $PYTHON_PREFIX/.pytest_cache
info1: did you know that ...
did you?
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collecting ... collected 0 items
======================= no tests ran in 0.12 seconds =======================
@@ -394,7 +398,7 @@ and nothing when run plainly:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 0 items
======================= no tests ran in 0.12 seconds =======================
@@ -434,7 +438,7 @@ Now we can profile which test functions execute the slowest:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 3 items
test_some_are_slow.py ... [100%]
@@ -509,7 +513,7 @@ If we run this:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 4 items
test_step.py .Fx. [100%]
@@ -593,7 +597,7 @@ We can run this:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 7 items
test_step.py .Fx. [ 57%]
@@ -606,7 +610,7 @@ We can run this:
file $REGENDOC_TMPDIR/b/test_error.py, line 1
def test_root(db): # no db here, will error out
E fixture 'db' not found
> available fixtures: cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, monkeypatch, pytestconfig, record_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory
> available fixtures: cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory
> use 'pytest --fixtures [testpath]' for help on them.
$REGENDOC_TMPDIR/b/test_error.py:1
@@ -707,7 +711,7 @@ and run them:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 2 items
test_module.py FF [100%]
@@ -731,7 +735,9 @@ and run them:
test_module.py:6: AssertionError
========================= 2 failed in 0.12 seconds =========================
you will have a "failures" file which contains the failing test ids::
you will have a "failures" file which contains the failing test ids:
.. code-block:: bash
$ cat failures
test_module.py::test_fail1 (PYTEST_TMPDIR/test_fail10)
@@ -809,7 +815,7 @@ and run it:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 3 items
test_module.py Esetting up a test failed! test_module.py::test_setup_fails
@@ -852,7 +858,7 @@ information.
``PYTEST_CURRENT_TEST`` environment variable
--------------------------------------------
.. versionadded:: 3.2
Sometimes a test session might get stuck and there might be no easy way to figure out
which test got stuck, for example if pytest was run in quiet mode (``-q``) or you don't have access to the console
@@ -935,6 +941,8 @@ like ``pytest-timeout`` they must be imported explicitly and passed on to pytest
This allows you to execute tests using the frozen
application with standard ``pytest`` command-line options::
application with standard ``pytest`` command-line options:
.. code-block:: bash
./app_main --pytest --verbose --tb=long --junitxml=results.xml test-suite/

View File

@@ -7,7 +7,7 @@ pytest fixtures: explicit, modular, scalable
.. currentmodule:: _pytest.python
.. versionadded:: 2.0/2.3/2.4
.. _`xUnit`: http://en.wikipedia.org/wiki/XUnit
.. _`purpose of test fixtures`: http://en.wikipedia.org/wiki/Test_fixture#Software
@@ -74,7 +74,7 @@ marked ``smtp_connection`` fixture function. Running the test looks like this:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 1 item
test_smtpsimple.py F [100%]
@@ -114,7 +114,9 @@ with a list of available function arguments.
.. note::
You can always issue ::
You can always issue:
.. code-block:: bash
pytest --fixtures test_simplefactory.py
@@ -215,7 +217,7 @@ inspect what is going on and can now run the tests:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 2 items
test_module.py FF [100%]
@@ -274,7 +276,7 @@ Finally, the ``class`` scope will invoke the fixture once per test *class*.
``package`` scope (experimental)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. versionadded:: 3.7
In pytest 3.7 the ``package`` scope has been introduced. Package-scoped fixtures
are finalized when the last test of a *package* finishes.
@@ -290,7 +292,7 @@ are finalized when the last test of a *package* finishes.
Higher-scoped fixtures are instantiated first
---------------------------------------------
.. versionadded:: 3.5
Within a function request for features, fixture of higher-scopes (such as ``session``) are instantiated first than
lower-scoped fixtures (such as ``function`` or ``class``). The relative order of fixtures of same scope follows
@@ -362,7 +364,9 @@ The ``print`` and ``smtp.close()`` statements will execute when the last test in
the module has finished execution, regardless of the exception status of the
tests.
Let's execute it::
Let's execute it:
.. code-block:: pytest
$ pytest -s -q --tb=no
FFteardown smtp
@@ -471,7 +475,9 @@ read an optional server URL from the test module which uses our fixture::
We use the ``request.module`` attribute to optionally obtain an
``smtpserver`` attribute from the test module. If we just execute
again, nothing much has changed::
again, nothing much has changed:
.. code-block:: pytest
$ pytest -s -q --tb=no
FFfinalizing <smtplib.SMTP object at 0xdeadbeef> (smtp.gmail.com)
@@ -704,7 +710,7 @@ Running the above tests results in the following test IDs being used:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 10 items
<Module test_anothersmtp.py>
<Function test_showhelo[smtp.gmail.com]>
@@ -749,7 +755,7 @@ Running this test will *skip* the invocation of ``data_set`` with value ``2``:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collecting ... collected 3 items
test_fixture_marks.py::test_data[0] PASSED [ 33%]
@@ -794,7 +800,7 @@ Here we declare an ``app`` fixture which receives the previously defined
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collecting ... collected 2 items
test_appsetup.py::test_smtp_connection_exists[smtp.gmail.com] PASSED [ 50%]
@@ -865,7 +871,7 @@ Let's run the tests in verbose mode and with looking at the print-output:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collecting ... collected 8 items
test_module.py::test_0[1] SETUP otherarg 1
@@ -1173,6 +1179,8 @@ Given the tests file structure is:
conftest.py
# content of tests/conftest.py
import pytest
@pytest.fixture
def username():
return 'username'

View File

@@ -1,9 +1,9 @@
Installation and Getting Started
===================================
**Pythons**: Python 2.7, 3.4, 3.5, 3.6, 3.7, Jython, PyPy-2.3
**Pythons**: Python 3.5, 3.6, 3.7, PyPy3
**Platforms**: Unix/Posix and Windows
**Platforms**: Linux and Windows
**PyPI package name**: `pytest <https://pypi.org/project/pytest/>`_
@@ -17,11 +17,15 @@ Installation and Getting Started
Install ``pytest``
----------------------------------------
1. Run the following command in your command line::
1. Run the following command in your command line:
.. code-block:: bash
pip install -U pytest
2. Check that you installed the correct version::
2. Check that you installed the correct version:
.. code-block:: bash
$ pytest --version
This is pytest version 4.x.y, imported from $PYTHON_PREFIX/lib/python3.6/site-packages/pytest.py
@@ -48,7 +52,7 @@ Thats it. You can now execute the test function:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 1 item
test_sample.py F [100%]
@@ -79,7 +83,7 @@ Run multiple tests
Assert that a certain exception is raised
--------------------------------------------------------------
Use the ``raises`` helper to assert that some code raises an exception::
Use the :ref:`raises <assertraises>` helper to assert that some code raises an exception::
# content of test_sysexit.py
import pytest
@@ -168,7 +172,9 @@ List the name ``tmpdir`` in the test function signature and ``pytest`` will look
More info on tmpdir handling is available at :ref:`Temporary directories and files <tmpdir handling>`.
Find out what kind of builtin :ref:`pytest fixtures <fixtures>` exist with the command::
Find out what kind of builtin :ref:`pytest fixtures <fixtures>` exist with the command:
.. code-block:: bash
pytest --fixtures # shows builtin and custom fixtures

View File

@@ -7,12 +7,12 @@ Good Integration Practices
Install package with pip
-------------------------------------------------
For development, we recommend to use virtualenv_ environments and pip_
for installing your application and any dependencies
as well as the ``pytest`` package itself. This ensures your code and
dependencies are isolated from the system Python installation.
For development, we recommend you use venv_ for virtual environments and
pip_ for installing your application and any dependencies,
as well as the ``pytest`` package itself.
This ensures your code and dependencies are isolated from your system Python installation.
First you need to place a ``setup.py`` file in the root of your package with the following minimum content::
Next, place a ``setup.py`` file in the root of your package with the following minimum content::
from setuptools import setup, find_packages
@@ -41,8 +41,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)
For examples of how to customize your test discovery :doc:`example/pythoncollection`.

View File

@@ -4,10 +4,121 @@ Historical Notes
This page lists features or behavior from previous versions of pytest which have changed over the years. They are
kept here as a historical note so users looking at old code can find documentation related to them.
.. _marker-revamp:
Marker revamp and iteration
---------------------------
.. versionchanged:: 3.6
pytest's marker implementation traditionally worked by simply updating the ``__dict__`` attribute of functions to cumulatively add markers. As a result, markers would unintentionally be passed along class hierarchies in surprising ways. Further, the API for retrieving them was inconsistent, as markers from parameterization would be stored differently than markers applied using the ``@pytest.mark`` decorator and markers added via ``node.add_marker``.
This state of things made it technically next to impossible to use data from markers correctly without having a deep understanding of the internals, leading to subtle and hard to understand bugs in more advanced usages.
Depending on how a marker got declared/changed one would get either a ``MarkerInfo`` which might contain markers from sibling classes,
``MarkDecorators`` when marks came from parameterization or from a ``node.add_marker`` call, discarding prior marks. Also ``MarkerInfo`` acts like a single mark, when it in fact represents a merged view on multiple marks with the same name.
On top of that markers were not accessible in the same way for modules, classes, and functions/methods.
In fact, markers were only accessible in functions, even if they were declared on classes/modules.
A new API to access markers has been introduced in pytest 3.6 in order to solve the problems with
the initial design, providing the :func:`_pytest.nodes.Node.iter_markers` method to iterate over
markers in a consistent manner and reworking the internals, which solved a great deal of problems
with the initial design.
.. _update marker code:
Updating code
~~~~~~~~~~~~~
The old ``Node.get_marker(name)`` function is considered deprecated because it returns an internal ``MarkerInfo`` object
which contains the merged name, ``*args`` and ``**kwargs`` of all the markers which apply to that node.
In general there are two scenarios on how markers should be handled:
1. Marks overwrite each other. Order matters but you only want to think of your mark as a single item. E.g.
``log_level('info')`` at a module level can be overwritten by ``log_level('debug')`` for a specific test.
In this case, use ``Node.get_closest_marker(name)``:
.. code-block:: python
# replace this:
marker = item.get_marker("log_level")
if marker:
level = marker.args[0]
# by this:
marker = item.get_closest_marker("log_level")
if marker:
level = marker.args[0]
2. Marks compose in an additive manner. E.g. ``skipif(condition)`` marks mean you just want to evaluate all of them,
order doesn't even matter. You probably want to think of your marks as a set here.
In this case iterate over each mark and handle their ``*args`` and ``**kwargs`` individually.
.. code-block:: python
# replace this
skipif = item.get_marker("skipif")
if skipif:
for condition in skipif.args:
# eval condition
...
# by this:
for skipif in item.iter_markers("skipif"):
condition = skipif.args[0]
# eval condition
If you are unsure or have any questions, please consider opening
`an issue <https://github.com/pytest-dev/pytest/issues>`_.
Related issues
~~~~~~~~~~~~~~
Here is a non-exhaustive list of issues fixed by the new implementation:
* Marks don't pick up nested classes (`#199 <https://github.com/pytest-dev/pytest/issues/199>`_).
* Markers stain on all related classes (`#568 <https://github.com/pytest-dev/pytest/issues/568>`_).
* Combining marks - args and kwargs calculation (`#2897 <https://github.com/pytest-dev/pytest/issues/2897>`_).
* ``request.node.get_marker('name')`` returns ``None`` for markers applied in classes (`#902 <https://github.com/pytest-dev/pytest/issues/902>`_).
* Marks applied in parametrize are stored as markdecorator (`#2400 <https://github.com/pytest-dev/pytest/issues/2400>`_).
* Fix marker interaction in a backward incompatible way (`#1670 <https://github.com/pytest-dev/pytest/issues/1670>`_).
* Refactor marks to get rid of the current "marks transfer" mechanism (`#2363 <https://github.com/pytest-dev/pytest/issues/2363>`_).
* Introduce FunctionDefinition node, use it in generate_tests (`#2522 <https://github.com/pytest-dev/pytest/issues/2522>`_).
* Remove named marker attributes and collect markers in items (`#891 <https://github.com/pytest-dev/pytest/issues/891>`_).
* skipif mark from parametrize hides module level skipif mark (`#1540 <https://github.com/pytest-dev/pytest/issues/1540>`_).
* skipif + parametrize not skipping tests (`#1296 <https://github.com/pytest-dev/pytest/issues/1296>`_).
* Marker transfer incompatible with inheritance (`#535 <https://github.com/pytest-dev/pytest/issues/535>`_).
More details can be found in the `original PR <https://github.com/pytest-dev/pytest/pull/3317>`_.
.. note::
in a future major relase of pytest we will introduce class based markers,
at which point markers will no longer be limited to instances of :py:class:`Mark`.
cache plugin integrated into the core
-------------------------------------
.. versionadded:: 2.8
The functionality of the :ref:`core cache <cache>` plugin was previously distributed
as a third party plugin named ``pytest-cache``. The core plugin
@@ -18,7 +129,7 @@ can only store/receive data between test runs that is json-serializable.
funcargs and ``pytest_funcarg__``
---------------------------------
.. versionchanged:: 2.3
In versions prior to 2.3 there was no ``@pytest.fixture`` marker
and you had to use a magic ``pytest_funcarg__NAME`` prefix
@@ -30,7 +141,7 @@ functions.
``@pytest.yield_fixture`` decorator
-----------------------------------
.. versionchanged:: 2.10
Prior to version 2.10, in order to use a ``yield`` statement to execute teardown code one
had to mark a fixture using the ``yield_fixture`` marker. From 2.10 onward, normal
@@ -41,7 +152,7 @@ and considered deprecated.
``[pytest]`` header in ``setup.cfg``
------------------------------------
.. versionchanged:: 3.0
Prior to 3.0, the supported section name was ``[pytest]``. Due to how
this may collide with some distutils commands, the recommended
@@ -54,17 +165,19 @@ name is ``[pytest]``.
Applying marks to ``@pytest.mark.parametrize`` parameters
---------------------------------------------------------
.. versionchanged:: 3.1
Prior to version 3.1 the supported mechanism for marking values
used the syntax::
used the syntax:
.. code-block:: python
import pytest
@pytest.mark.parametrize("test_input,expected", [
("3+5", 8),
("2+4", 6),
pytest.mark.xfail(("6*9", 42),),
])
@pytest.mark.parametrize(
"test_input,expected", [("3+5", 8), ("2+4", 6), pytest.mark.xfail(("6*9", 42))]
)
def test_eval(test_input, expected):
assert eval(test_input) == expected
@@ -78,7 +191,7 @@ The old syntax is planned to be removed in pytest-4.0.
``@pytest.mark.parametrize`` argument names as a tuple
------------------------------------------------------
.. versionchanged:: 2.4
In versions prior to 2.4 one needed to specify the argument
names as a tuple. This remains valid but the simpler ``"name1,name2,..."``
@@ -89,7 +202,7 @@ it's easier to write and produces less line noise.
setup: is now an "autouse fixture"
----------------------------------
.. versionchanged:: 2.3
During development prior to the pytest-2.3 release the name
``pytest.setup`` was used but before the release it was renamed
@@ -102,12 +215,16 @@ namely :ref:`autouse fixtures`
Conditions as strings instead of booleans
-----------------------------------------
.. versionchanged:: 2.4
Prior to pytest-2.4 the only way to specify skipif/xfail conditions was
to use strings::
to use strings:
.. code-block:: python
import sys
@pytest.mark.skipif("sys.version_info >= (3,3)")
def test_function():
...
@@ -139,17 +256,20 @@ dictionary which is constructed as follows:
expression is applied.
The pytest ``config`` object allows you to skip based on a test
configuration value which you might have added::
configuration value which you might have added:
.. code-block:: python
@pytest.mark.skipif("not config.getvalue('db')")
def test_function(...):
def test_function():
...
The equivalent with "boolean conditions" is::
The equivalent with "boolean conditions" is:
@pytest.mark.skipif(not pytest.config.getvalue("db"),
reason="--db was not specified")
def test_function(...):
.. code-block:: python
@pytest.mark.skipif(not pytest.config.getvalue("db"), reason="--db was not specified")
def test_function():
pass
.. note::
@@ -162,14 +282,18 @@ The equivalent with "boolean conditions" is::
``pytest.set_trace()``
----------------------
.. versionchanged:: 2.4
Previous to version 2.4 to set a break point in code one needed to use ``pytest.set_trace()``::
Previous to version 2.4 to set a break point in code one needed to use ``pytest.set_trace()``:
.. code-block:: python
import pytest
def test_function():
...
pytest.set_trace() # invoke PDB debugger and tracing
pytest.set_trace() # invoke PDB debugger and tracing
This is no longer needed and one can use the native ``import pdb;pdb.set_trace()`` call directly.
@@ -179,7 +303,7 @@ For more details see :ref:`breakpoints`.
"compat" properties
-------------------
.. deprecated:: 3.9
Access of ``Module``, ``Function``, ``Class``, ``Instance``, ``File`` and ``Item`` through ``Node`` instances have long
been documented as deprecated, but started to emit warnings from pytest ``3.9`` and onward.

View File

@@ -30,7 +30,7 @@ To execute it:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 1 item
test_sample.py F [100%]
@@ -61,7 +61,7 @@ Features
- Can run :ref:`unittest <unittest>` (including trial) and :ref:`nose <noseintegration>` test suites out of the box;
- Python 2.7, Python 3.4+, PyPy 2.3, Jython 2.5 (untested);
- Python Python 3.5+ and PyPy 3;
- Rich plugin architecture, with over 315+ `external plugins <http://plugincompat.herokuapp.com>`_ and thriving community;

View File

@@ -5,7 +5,7 @@ License
Distributed under the terms of the `MIT`_ license, pytest is free and open source software.
::
.. code-block:: text
The MIT License (MIT)

View File

@@ -14,6 +14,7 @@
.. _`distribute docs`:
.. _`distribute`: https://pypi.org/project/distribute/
.. _`pip`: https://pypi.org/project/pip/
.. _`venv`: https://docs.python.org/3/library/venv.html/
.. _`virtualenv`: https://pypi.org/project/virtualenv/
.. _hudson: http://hudson-ci.org/
.. _jenkins: http://jenkins-ci.org/

View File

@@ -3,17 +3,21 @@
Logging
-------
.. versionadded:: 3.3
.. versionchanged:: 3.4
pytest captures log messages of level ``WARNING`` or above automatically and displays them in their own section
for each failed test in the same manner as captured stdout and stderr.
Running without options::
Running without options:
.. code-block:: bash
pytest
Shows failed tests like so::
Shows failed tests like so:
.. code-block:: pytest
----------------------- Captured stdlog call ----------------------
test_reporting.py 26 WARNING text going to logger
@@ -27,12 +31,16 @@ By default each captured log message shows the module, line number, log level
and message.
If desired the log and date format can be specified to
anything that the logging module supports by passing specific formatting options::
anything that the logging module supports by passing specific formatting options:
.. code-block:: bash
pytest --log-format="%(asctime)s %(levelname)s %(message)s" \
--log-date-format="%Y-%m-%d %H:%M:%S"
Shows failed tests like so::
Shows failed tests like so:
.. code-block:: pytest
----------------------- Captured stdlog call ----------------------
2010-04-10 14:48:44 WARNING text going to logger
@@ -51,7 +59,9 @@ These options can also be customized through ``pytest.ini`` file:
log_date_format = %Y-%m-%d %H:%M:%S
Further it is possible to disable reporting of captured content (stdout,
stderr and logs) on failed tests completely with::
stderr and logs) on failed tests completely with:
.. code-block:: bash
pytest --show-capture=no
@@ -133,7 +143,6 @@ the records for the ``setup`` and ``call`` stages during teardown like so:
.. code-block:: python
@pytest.fixture
def window(caplog):
window = create_window()

View File

@@ -1,9 +1,7 @@
.. _mark:
Marking test functions with attributes
=================================================================
======================================
By using the ``pytest.mark`` helper you can easily set
metadata on your test functions. There are
@@ -17,8 +15,10 @@ some builtin markers, for example:
to the same test function.
It's easy to create custom markers or to apply markers
to whole test classes or modules. See :ref:`mark examples` for examples
which also serve as documentation.
to whole test classes or modules. Those markers can be used by plugins, and also
are commonly used to :ref:`select tests <mark run>` on the command-line with the ``-m`` option.
See :ref:`mark examples` for examples which also serve as documentation.
.. note::
@@ -26,134 +26,53 @@ which also serve as documentation.
:ref:`fixtures <fixtures>`.
Raising errors on unknown marks: --strict
-----------------------------------------
Registering marks
-----------------
When the ``--strict`` command-line flag is passed, any marks not registered in the ``pytest.ini`` file will trigger an error.
Marks can be registered like this:
You can register custom marks in your ``pytest.ini`` file like this:
.. code-block:: ini
[pytest]
markers =
slow
slow: marks tests as slow (deselect with '-m "not slow"')
serial
This can be used to prevent users mistyping mark names by accident. Test suites that want to enforce this
should add ``--strict`` to ``addopts``:
Note that everything after the ``:`` is an optional description.
Alternatively, you can register new markers programatically in a
:ref:`pytest_configure <initialization-hooks>` hook:
.. code-block:: python
def pytest_configure(config):
config.addinivalue_line(
"markers", "env(name): mark test to run only on named environment"
)
Registered marks appear in pytest's help text and do not emit warnings (see the next section). It
is recommended that third-party plugins always :ref:`register their markers <registering-markers>`.
.. _unknown-marks:
Raising errors on unknown marks
-------------------------------
Unregistered marks applied with the ``@pytest.mark.name_of_the_mark`` decorator
will always emit a warning in order to avoid silently doing something
surprising due to mis-typed names. As described in the previous section, you can disable
the warning for custom marks by registering them in your ``pytest.ini`` file or
using a custom ``pytest_configure`` hook.
When the ``--strict-markers`` command-line flag is passed, any unknown marks applied
with the ``@pytest.mark.name_of_the_mark`` decorator will trigger an error. You can
enforce this validation in your project by adding ``--strict-markers`` to ``addopts``:
.. code-block:: ini
[pytest]
addopts = --strict
addopts = --strict-markers
markers =
slow
slow: marks tests as slow (deselect with '-m "not slow"')
serial
.. _marker-revamp:
Marker revamp and iteration
---------------------------
.. versionadded:: 3.6
pytest's marker implementation traditionally worked by simply updating the ``__dict__`` attribute of functions to cumulatively add markers. As a result, markers would unintentionally be passed along class hierarchies in surprising ways. Further, the API for retrieving them was inconsistent, as markers from parameterization would be stored differently than markers applied using the ``@pytest.mark`` decorator and markers added via ``node.add_marker``.
This state of things made it technically next to impossible to use data from markers correctly without having a deep understanding of the internals, leading to subtle and hard to understand bugs in more advanced usages.
Depending on how a marker got declared/changed one would get either a ``MarkerInfo`` which might contain markers from sibling classes,
``MarkDecorators`` when marks came from parameterization or from a ``node.add_marker`` call, discarding prior marks. Also ``MarkerInfo`` acts like a single mark, when it in fact represents a merged view on multiple marks with the same name.
On top of that markers were not accessible the same way for modules, classes, and functions/methods.
In fact, markers were only accessible in functions, even if they were declared on classes/modules.
A new API to access markers has been introduced in pytest 3.6 in order to solve the problems with the initial design, providing :func:`_pytest.nodes.Node.iter_markers` method to iterate over markers in a consistent manner and reworking the internals, which solved great deal of problems with the initial design.
.. _update marker code:
Updating code
~~~~~~~~~~~~~
The old ``Node.get_marker(name)`` function is considered deprecated because it returns an internal ``MarkerInfo`` object
which contains the merged name, ``*args`` and ``**kwargs`` of all the markers which apply to that node.
In general there are two scenarios on how markers should be handled:
1. Marks overwrite each other. Order matters but you only want to think of your mark as a single item. E.g.
``log_level('info')`` at a module level can be overwritten by ``log_level('debug')`` for a specific test.
In this case, use ``Node.get_closest_marker(name)``:
.. code-block:: python
# replace this:
marker = item.get_marker("log_level")
if marker:
level = marker.args[0]
# by this:
marker = item.get_closest_marker("log_level")
if marker:
level = marker.args[0]
2. Marks compose in an additive manner. E.g. ``skipif(condition)`` marks mean you just want to evaluate all of them,
order doesn't even matter. You probably want to think of your marks as a set here.
In this case iterate over each mark and handle their ``*args`` and ``**kwargs`` individually.
.. code-block:: python
# replace this
skipif = item.get_marker("skipif")
if skipif:
for condition in skipif.args:
# eval condition
...
# by this:
for skipif in item.iter_markers("skipif"):
condition = skipif.args[0]
# eval condition
If you are unsure or have any questions, please consider opening
`an issue <https://github.com/pytest-dev/pytest/issues>`_.
Related issues
~~~~~~~~~~~~~~
Here is a non-exhaustive list of issues fixed by the new implementation:
* Marks don't pick up nested classes (`#199 <https://github.com/pytest-dev/pytest/issues/199>`_).
* Markers stain on all related classes (`#568 <https://github.com/pytest-dev/pytest/issues/568>`_).
* Combining marks - args and kwargs calculation (`#2897 <https://github.com/pytest-dev/pytest/issues/2897>`_).
* ``request.node.get_marker('name')`` returns ``None`` for markers applied in classes (`#902 <https://github.com/pytest-dev/pytest/issues/902>`_).
* Marks applied in parametrize are stored as markdecorator (`#2400 <https://github.com/pytest-dev/pytest/issues/2400>`_).
* Fix marker interaction in a backward incompatible way (`#1670 <https://github.com/pytest-dev/pytest/issues/1670>`_).
* Refactor marks to get rid of the current "marks transfer" mechanism (`#2363 <https://github.com/pytest-dev/pytest/issues/2363>`_).
* Introduce FunctionDefinition node, use it in generate_tests (`#2522 <https://github.com/pytest-dev/pytest/issues/2522>`_).
* Remove named marker attributes and collect markers in items (`#891 <https://github.com/pytest-dev/pytest/issues/891>`_).
* skipif mark from parametrize hides module level skipif mark (`#1540 <https://github.com/pytest-dev/pytest/issues/1540>`_).
* skipif + parametrize not skipping tests (`#1296 <https://github.com/pytest-dev/pytest/issues/1296>`_).
* Marker transfer incompatible with inheritance (`#535 <https://github.com/pytest-dev/pytest/issues/535>`_).
More details can be found in the `original PR <https://github.com/pytest-dev/pytest/pull/3317>`_.
.. note::
in a future major relase of pytest we will introduce class based markers,
at which point markers will no longer be limited to instances of :py:class:`Mark`

View File

@@ -8,46 +8,215 @@ Sometimes tests need to invoke functionality which depends
on global settings or which invokes code which cannot be easily
tested such as network access. The ``monkeypatch`` fixture
helps you to safely set/delete an attribute, dictionary item or
environment variable or to modify ``sys.path`` for importing.
environment variable, or to modify ``sys.path`` for importing.
The ``monkeypatch`` fixture provides these helper methods for safely patching and mocking
functionality in tests:
.. code-block:: python
monkeypatch.setattr(obj, name, value, raising=True)
monkeypatch.delattr(obj, name, raising=True)
monkeypatch.setitem(mapping, name, value)
monkeypatch.delitem(obj, name, raising=True)
monkeypatch.setenv(name, value, prepend=False)
monkeypatch.delenv(name, raising=True)
monkeypatch.syspath_prepend(path)
monkeypatch.chdir(path)
All modifications will be undone after the requesting
test function or fixture has finished. The ``raising``
parameter determines if a ``KeyError`` or ``AttributeError``
will be raised if the target of the set/deletion operation does not exist.
Consider the following scenarios:
1. Modifying the behavior of a function or the property of a class for a test e.g.
there is an API call or database connection you will not make for a test but you know
what the expected output should be. Use :py:meth:`monkeypatch.setattr` to patch the
function or property with your desired testing behavior. This can include your own functions.
Use :py:meth:`monkeypatch.delattr` to remove the function or property for the test.
2. Modifying the values of dictionaries e.g. you have a global configuration that
you want to modify for certain test cases. Use :py:meth:`monkeypatch.setitem` to patch the
dictionary for the test. :py:meth:`monkeypatch.delitem` can be used to remove items.
3. Modifying environment variables for a test e.g. to test program behavior if an
environment variable is missing, or to set multiple values to a known variable.
:py:meth:`monkeypatch.setenv` and :py:meth:`monkeypatch.delenv` can be used for
these patches.
4. Use :py:meth:`monkeypatch.syspath_prepend` to modify the system ``$PATH`` safely, and
:py:meth:`monkeypatch.chdir` to change the context of the current working directory
during a test.
See the `monkeypatch blog post`_ for some introduction material
and a discussion of its motivation.
.. _`monkeypatch blog post`: http://tetamap.wordpress.com/2009/03/03/monkeypatching-in-unit-tests-done-right/
Simple example: monkeypatching functions
---------------------------------------------------
----------------------------------------
If you want to pretend that ``os.expanduser`` returns a certain
directory, you can use the :py:meth:`monkeypatch.setattr` method to
patch this function before calling into a function which uses it::
Consider a scenario where you are working with user directories. In the context of
testing, you do not want your test to depend on the running user. ``monkeypatch``
can be used to patch functions dependent on the user to always return a
specific value.
# content of test_module.py
import os.path
def getssh(): # pseudo application code
return os.path.join(os.path.expanduser("~admin"), '.ssh')
In this example, :py:meth:`monkeypatch.setattr` is used to patch ``Path.home``
so that the known testing path ``Path("/abc")`` is always used when the test is run.
This removes any dependency on the running user for testing purposes.
:py:meth:`monkeypatch.setattr` must be called before the function which will use
the patched function is called.
After the test function finishes the ``Path.home`` modification will be undone.
def test_mytest(monkeypatch):
def mockreturn(path):
return '/abc'
monkeypatch.setattr(os.path, 'expanduser', mockreturn)
.. code-block:: python
# contents of test_module.py with source code and the test
from pathlib import Path
def getssh():
"""Simple function to return expanded homedir ssh path."""
return Path.home() / ".ssh"
def test_getssh(monkeypatch):
# mocked return function to replace Path.home
# always return '/abc'
def mockreturn():
return Path("/abc")
# Application of the monkeypatch to replace Path.home
# with the behavior of mockreturn defined above.
monkeypatch.setattr(Path, "home", mockreturn)
# Calling getssh() will use mockreturn in place of Path.home
# for this test with the monkeypatch.
x = getssh()
assert x == '/abc/.ssh'
assert x == Path("/abc/.ssh")
Here our test function monkeypatches ``os.path.expanduser`` and
then calls into a function that calls it. After the test function
finishes the ``os.path.expanduser`` modification will be undone.
example: preventing "requests" from remote operations
Monkeypatching returned objects: building mock classes
------------------------------------------------------
If you want to prevent the "requests" library from performing http
requests in all your tests, you can do::
:py:meth:`monkeypatch.setattr` can be used in conjunction with classes to mock returned
objects from functions instead of values.
Imagine a simple function to take an API url and return the json response.
# content of conftest.py
.. code-block:: python
# contents of app.py, a simple API retrieval example
import requests
def get_json(url):
"""Takes a URL, and returns the JSON."""
r = requests.get(url)
return r.json()
We need to mock ``r``, the returned response object for testing purposes.
The mock of ``r`` needs a ``.json()`` method which returns a dictionary.
This can be done in our test file by defining a class to represent ``r``.
.. code-block:: python
# contents of test_app.py, a simple test for our API retrieval
# import requests for the purposes of monkeypatching
import requests
# our app.py that includes the get_json() function
# 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():
return {"mock_key": "mock_response"}
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):
return MockResponse()
# apply the monkeypatch for requests.get to mock_get
monkeypatch.setattr(requests, "get", mock_get)
# app.get_json, which contains requests.get, uses the monkeypatch
result = app.get_json("https://fakeurl")
assert result["mock_key"] == "mock_response"
``monkeypatch`` applies the mock for ``requests.get`` with our ``mock_get`` function.
The ``mock_get`` function returns an instance of the ``MockResponse`` class, which
has a ``json()`` method defined to return a known testing dictionary and does not
require any outside API connection.
You can build the ``MockResponse`` class with the appropriate degree of complexity for
the scenario you are testing. For instance, it could include an ``ok`` property that
always returns ``True``, or return different values from the ``json()`` mocked method
based on input strings.
This mock can be shared across tests using a ``fixture``:
.. code-block:: python
# contents of test_app.py, a simple test for our API retrieval
import pytest
import requests
# app.py that includes the get_json() function
import app
# custom class to be the mock return value of requests.get()
class MockResponse:
@staticmethod
def json():
return {"mock_key": "mock_response"}
# monkeypatched requests.get moved to a fixture
@pytest.fixture
def mock_response(monkeypatch):
"""Requests.get() mocked to return {'mock_key':'mock_response'}."""
def mock_get(*args, **kwargs):
return MockResponse()
monkeypatch.setattr(requests, "get", mock_get)
# notice our test uses the custom fixture instead of monkeypatch directly
def test_get_json(mock_response):
result = app.get_json("https://fakeurl")
assert result["mock_key"] == "mock_response"
Furthermore, if the mock was designed to be applied to all tests, the ``fixture`` could
be moved to a ``conftest.py`` file and use the with ``autouse=True`` option.
Global patch example: preventing "requests" from remote operations
------------------------------------------------------------------
If you want to prevent the "requests" library from performing http
requests in all your tests, you can do:
.. code-block:: python
# contents of conftest.py
import pytest
@pytest.fixture(autouse=True)
def no_requests(monkeypatch):
"""Remove requests.sessions.Session.request for all tests."""
monkeypatch.delattr("requests.sessions.Session.request")
This autouse fixture will be executed for each test function and it
@@ -81,6 +250,187 @@ so that any attempts within tests to create http requests will fail.
See issue `#3290 <https://github.com/pytest-dev/pytest/issues/3290>`_ for details.
Monkeypatching environment variables
------------------------------------
If you are working with environment variables you often need to safely change the values
or delete them from the system for testing purposes. ``monkeypatch`` provides a mechanism
to do this using the ``setenv`` and ``delenv`` method. Our example code to test:
.. code-block:: python
# contents of our original code file e.g. code.py
import os
def get_os_user_lower():
"""Simple retrieval function.
Returns lowercase USER or raises EnvironmentError."""
username = os.getenv("USER")
if username is None:
raise EnvironmentError("USER environment is not set.")
return username.lower()
There are two potential paths. First, the ``USER`` environment variable is set to a
value. Second, the ``USER`` environment variable does not exist. Using ``monkeypatch``
both paths can be safely tested without impacting the running environment:
.. code-block:: python
# contents of our test file e.g. test_code.py
import pytest
def test_upper_to_lower(monkeypatch):
"""Set the USER env var to assert the behavior."""
monkeypatch.setenv("USER", "TestingUser")
assert get_os_user_lower() == "testinguser"
def test_raise_exception(monkeypatch):
"""Remove the USER env var and assert EnvironmentError is raised."""
monkeypatch.delenv("USER", raising=False)
with pytest.raises(EnvironmentError):
_ = get_os_user_lower()
This behavior can be moved into ``fixture`` structures and shared across tests:
.. code-block:: python
# contents of our test file e.g. test_code.py
import pytest
@pytest.fixture
def mock_env_user(monkeypatch):
monkeypatch.setenv("USER", "TestingUser")
@pytest.fixture
def mock_env_missing(monkeypatch):
monkeypatch.delenv("USER", raising=False)
# notice the tests reference the fixtures for mocks
def test_upper_to_lower(mock_env_user):
assert get_os_user_lower() == "testinguser"
def test_raise_exception(mock_env_missing):
with pytest.raises(EnvironmentError):
_ = get_os_user_lower()
Monkeypatching dictionaries
---------------------------
:py:meth:`monkeypatch.setitem` can be used to safely set the values of dictionaries
to specific values during tests. Take this simplified connection string example:
.. code-block:: python
# contents of app.py to generate a simple connection string
DEFAULT_CONFIG = {"user": "user1", "database": "db1"}
def create_connection_string(config=None):
"""Creates a connection string from input or defaults."""
config = config or DEFAULT_CONFIG
return f"User Id={config['user']}; Location={config['database']};"
For testing purposes we can patch the ``DEFAULT_CONFIG`` dictionary to specific values.
.. code-block:: python
# contents of test_app.py
# app.py with the connection string function (prior code block)
import app
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")
monkeypatch.setitem(app.DEFAULT_CONFIG, "database", "test_db")
# expected result based on the mocks
expected = "User Id=test_user; Location=test_db;"
# the test uses the monkeypatched dictionary settings
result = app.create_connection_string()
assert result == expected
You can use the :py:meth:`monkeypatch.delitem` to remove values.
.. code-block:: python
# contents of test_app.py
import pytest
# app.py with the connection string function
import app
def test_missing_user(monkeypatch):
# patch the DEFAULT_CONFIG t be missing the 'user' key
monkeypatch.delitem(app.DEFAULT_CONFIG, "user", raising=False)
# Key error expected because a config is not passed, and the
# default is now missing the 'user' entry.
with pytest.raises(KeyError):
_ = app.create_connection_string()
The modularity of fixtures gives you the flexibility to define
separate fixtures for each potential mock and reference them in the needed tests.
.. code-block:: python
# contents of test_app.py
import pytest
# 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):
"""Set the DEFAULT_CONFIG user to test_user."""
monkeypatch.setitem(app.DEFAULT_CONFIG, "user", "test_user")
@pytest.fixture
def mock_test_database(monkeypatch):
"""Set the DEFAULT_CONFIG database to test_db."""
monkeypatch.setitem(app.DEFAULT_CONFIG, "database", "test_db")
@pytest.fixture
def mock_missing_default_user(monkeypatch):
"""Remove the user key from DEFAULT_CONFIG"""
monkeypatch.delitem(app.DEFAULT_CONFIG, "user", raising=False)
# 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()
assert result == expected
def test_missing_user(mock_missing_default_user):
with pytest.raises(KeyError):
_ = app.create_connection_string()
.. currentmodule:: _pytest.monkeypatch
API Reference

View File

@@ -12,7 +12,9 @@ Running tests written for nose
Usage
-------------
After :ref:`installation` type::
After :ref:`installation` type:
.. code-block:: bash
python setup.py develop # make sure tests can import our package
pytest # instead of 'nosetests'
@@ -44,7 +46,7 @@ Unsupported idioms / known issues
<https://github.com/pytest-dev/pytest/issues/377/>`_.
- nose imports test modules with the same import path (e.g.
``tests.test_mod``) but different file system paths
``tests.test_mode``) but different file system paths
(e.g. ``tests/test_mode.py`` and ``other/tests/test_mode.py``)
by extending sys.path/import semantics. pytest does not do that
but there is discussion in `#268 <https://github.com/pytest-dev/pytest/issues/268>`_ for adding some support. Note that

View File

@@ -29,22 +29,22 @@ pytest enables test parametrization at several levels:
.. regendoc: wipe
.. versionadded:: 2.2
.. versionchanged:: 2.4
Several improvements.
The builtin :ref:`pytest.mark.parametrize ref` decorator enables
parametrization of arguments for a test function. Here is a typical example
of a test function that implements checking that a certain input leads
to an expected output::
to an expected output:
.. code-block:: python
# content of test_expectation.py
import pytest
@pytest.mark.parametrize("test_input,expected", [
("3+5", 8),
("2+4", 6),
("6*9", 42),
])
@pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+4", 6), ("6*9", 42)])
def test_eval(test_input, expected):
assert eval(test_input) == expected
@@ -58,7 +58,7 @@ them in turn:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 3 items
test_expectation.py ..F [100%]
@@ -68,19 +68,30 @@ them in turn:
test_input = '6*9', expected = 42
@pytest.mark.parametrize("test_input,expected", [
("3+5", 8),
("2+4", 6),
("6*9", 42),
])
@pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+4", 6), ("6*9", 42)])
def test_eval(test_input, expected):
> assert eval(test_input) == expected
E AssertionError: assert 54 == 42
E + where 54 = eval('6*9')
test_expectation.py:8: AssertionError
test_expectation.py:6: AssertionError
==================== 1 failed, 2 passed in 0.12 seconds ====================
.. note::
pytest by default escapes any non-ascii characters used in unicode strings
for the parametrization because it has several downsides.
If however you would like to use unicode strings in parametrization and see them in the terminal as is (non-escaped), use this option in your ``pytest.ini``:
.. code-block:: ini
[pytest]
disable_test_id_escaping_and_forfeit_all_rights_to_community_support = True
Keep in mind however that this might cause unwanted side effects and
even bugs depending on the OS used and plugins currently installed, so use it at your own risk.
As designed in this example, only one pair of input/output values fails
the simple test function. And as usual with test function arguments,
you can see the ``input`` and ``output`` values in the traceback.
@@ -89,16 +100,18 @@ Note that you could also use the parametrize marker on a class or a module
(see :ref:`mark`) which would invoke several functions with the argument sets.
It is also possible to mark individual test instances within parametrize,
for example with the builtin ``mark.xfail``::
for example with the builtin ``mark.xfail``:
.. code-block:: python
# content of test_expectation.py
import pytest
@pytest.mark.parametrize("test_input,expected", [
("3+5", 8),
("2+4", 6),
pytest.param("6*9", 42,
marks=pytest.mark.xfail),
])
@pytest.mark.parametrize(
"test_input,expected",
[("3+5", 8), ("2+4", 6), pytest.param("6*9", 42, marks=pytest.mark.xfail)],
)
def test_eval(test_input, expected):
assert eval(test_input) == expected
@@ -110,7 +123,7 @@ Let's run this:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 3 items
test_expectation.py ..x [100%]
@@ -125,9 +138,13 @@ example, if they're dynamically generated by some function - the behaviour of
pytest is defined by the :confval:`empty_parameter_set_mark` option.
To get all combinations of multiple parametrized arguments you can stack
``parametrize`` decorators::
``parametrize`` decorators:
.. code-block:: python
import pytest
@pytest.mark.parametrize("x", [0, 1])
@pytest.mark.parametrize("y", [2, 3])
def test_foo(x, y):
@@ -151,28 +168,40 @@ parametrization.
For example, let's say we want to run a test taking string inputs which
we want to set via a new ``pytest`` command line option. Let's first write
a simple test accepting a ``stringinput`` fixture function argument::
a simple test accepting a ``stringinput`` fixture function argument:
.. code-block:: python
# content of test_strings.py
def test_valid_string(stringinput):
assert stringinput.isalpha()
Now we add a ``conftest.py`` file containing the addition of a
command line option and the parametrization of our test function::
command line option and the parametrization of our test function:
.. code-block:: python
# content of conftest.py
def pytest_addoption(parser):
parser.addoption("--stringinput", action="append", default=[],
help="list of stringinputs to pass to test functions")
parser.addoption(
"--stringinput",
action="append",
default=[],
help="list of stringinputs to pass to test functions",
)
def pytest_generate_tests(metafunc):
if 'stringinput' in metafunc.fixturenames:
metafunc.parametrize("stringinput",
metafunc.config.getoption('stringinput'))
if "stringinput" in metafunc.fixturenames:
metafunc.parametrize("stringinput", metafunc.config.getoption("stringinput"))
If we now pass two stringinput values, our test will run twice::
If we now pass two stringinput values, our test will run twice:
.. code-block:: pytest
$ pytest -q --stringinput="hello" --stringinput="world" test_strings.py
.. [100%]
@@ -195,7 +224,7 @@ Let's also run with a stringinput that will lead to a failing test:
E + where False = <built-in method isalpha of str object at 0xdeadbeef>()
E + where <built-in method isalpha of str object at 0xdeadbeef> = '!'.isalpha
test_strings.py:3: AssertionError
test_strings.py:4: AssertionError
1 failed in 0.12 seconds
As expected our test function fails.
@@ -209,7 +238,7 @@ list:
$ pytest -q -rs test_strings.py
s [100%]
========================= short test summary info ==========================
SKIPPED [1] test_strings.py: got empty parameter set ['stringinput'], function test_valid_string at $REGENDOC_TMPDIR/test_strings.py:1
SKIPPED [1] test_strings.py: got empty parameter set ['stringinput'], function test_valid_string at $REGENDOC_TMPDIR/test_strings.py:2
1 skipped in 0.12 seconds
Note that when calling ``metafunc.parametrize`` multiple times with different parameter sets, all parameter names across

View File

@@ -27,7 +27,7 @@ Here is a little annotated list for some popular plugins:
for `twisted <http://twistedmatrix.com>`_ apps, starting a reactor and
processing deferreds from test functions.
* `pytest-cov <https://pypi.org/project/pytest-cov/>`_:
* `pytest-cov <https://pypi.org/project/pytest-cov/>`__:
coverage reporting, compatible with distributed testing
* `pytest-xdist <https://pypi.org/project/pytest-xdist/>`_:

View File

@@ -7,16 +7,20 @@ Python 3.4's last release is scheduled for
`March 2019 <https://www.python.org/dev/peps/pep-0429/#release-schedule>`__. pytest is one of
the participating projects of the https://python3statement.org.
We plan to drop support for Python 2.7 and 3.4 at the same time with the release of **pytest 5.0**,
scheduled to be released by **mid-2019**. Thanks to the `python_requires <https://packaging.python.org/guides/distributing-packages-using-setuptools/#python-requires>`__ ``setuptools`` option,
The **pytest 4.6** series will be the last to support Python 2.7 and 3.4, and is scheduled
to be released by **mid-2019**. **pytest 5.0** and onwards will support only Python 3.5+.
Thanks to the `python_requires`_ ``setuptools`` option,
Python 2.7 and Python 3.4 users using a modern ``pip`` version
will install the last compatible pytest ``4.X`` version automatically even if ``5.0`` or later
will install the last pytest ``4.6`` version automatically even if ``5.0`` or later
are available on PyPI.
During the period **from mid-2019 and 2020**, the pytest core team plans to make
bug-fix releases of the pytest ``4.X`` series by back-porting patches to the ``4.x-maintenance``
branch.
While pytest ``5.0`` will be the new mainstream and development version, until **January 2020**
the pytest core team plans to make bug-fix releases of the pytest ``4.6`` series by
back-porting patches to the ``4.6-maintenance`` branch that affect Python 2 users.
**After 2020**, the core team will no longer actively back port-patches, but the ``4.x-maintenance``
branch will continue to exist so the community itself can contribute patches. The
core team will be happy to accept those patches and make new ``4.X`` releases **until mid-2020**.
**After 2020**, the core team will no longer actively backport patches, but the ``4.6-maintenance``
branch will continue to exist so the community itself can contribute patches. The core team will
be happy to accept those patches and make new ``4.6`` releases **until mid-2020**.
.. _`python_requires`: https://packaging.python.org/guides/distributing-packages-using-setuptools/#python-requires

View File

@@ -1,4 +1,3 @@
Reference
=========
@@ -49,7 +48,7 @@ pytest.main
.. autofunction:: _pytest.config.main
pytest.param
~~~~~~~~~~~~~
~~~~~~~~~~~~
.. autofunction:: pytest.param(*values, [id], [marks])
@@ -199,16 +198,18 @@ Marks a test function as *expected to fail*.
.. py:function:: pytest.mark.xfail(condition=None, *, reason=None, raises=None, run=True, strict=False)
:type condition: bool or str
:param condition: ``True/False`` if the condition should be marked as xfail or a :ref:`condition string <string conditions>`.
:param condition:
Condition for marking the test function as xfail (``True/False`` or a
:ref:`condition string <string conditions>`).
:keyword str reason: Reason why the test function is marked as xfail.
:keyword Exception raises: Exception subclass expected to be raised by the test function; other exceptions will fail the test.
:keyword bool run:
If the test function should actually be executed. If ``False``, the function will always xfail and will
not be executed (useful a function is segfaulting).
not be executed (useful if a function is segfaulting).
:keyword bool strict:
* If ``False`` (the default) the function will be shown in the terminal output as ``xfailed`` if it fails
and as ``xpass`` if it passes. In both cases this will not cause the test suite to fail as a whole. This
is particularly useful to mark *flaky* tests (tests that random at fail) to be tackled later.
is particularly useful to mark *flaky* tests (tests that fail at random) to be tackled later.
* If ``True``, the function will be shown in the terminal output as ``xfailed`` if it fails, but if it
unexpectedly passes then it will **fail** the test suite. This is particularly useful to mark functions
that are always failing and there should be a clear indication if they unexpectedly start to pass (for example
@@ -423,6 +424,14 @@ record_property
.. autofunction:: _pytest.junitxml.record_property()
record_testsuite_property
~~~~~~~~~~~~~~~~~~~~~~~~~
**Tutorial**: :ref:`record_testsuite_property example`.
.. autofunction:: _pytest.junitxml.record_testsuite_property()
caplog
~~~~~~
@@ -499,6 +508,32 @@ Each recorded warning is an instance of :class:`warnings.WarningMessage`.
differently; see :ref:`ensuring_function_triggers`.
tmp_path
~~~~~~~~
**Tutorial**: :doc:`tmpdir`
.. currentmodule:: _pytest.tmpdir
.. autofunction:: tmp_path()
:no-auto-options:
tmp_path_factory
~~~~~~~~~~~~~~~~
**Tutorial**: :ref:`tmp_path_factory example`
.. _`tmp_path_factory factory api`:
``tmp_path_factory`` instances have the following methods:
.. currentmodule:: _pytest.tmpdir
.. automethod:: TempPathFactory.mktemp
.. automethod:: TempPathFactory.getbasetemp
tmpdir
~~~~~~
@@ -546,6 +581,8 @@ Bootstrapping hooks called for plugins registered early enough (internal and set
.. autofunction:: pytest_cmdline_parse
.. autofunction:: pytest_cmdline_main
.. _`initialization-hooks`:
Initialization hooks
~~~~~~~~~~~~~~~~~~~~
@@ -558,6 +595,8 @@ Initialization hooks called for plugins and ``conftest.py`` files.
.. autofunction:: pytest_sessionstart
.. autofunction:: pytest_sessionfinish
.. autofunction:: pytest_plugin_registered
Test running hooks
~~~~~~~~~~~~~~~~~~
@@ -581,6 +620,8 @@ into interactive debugging when a test failure occurs.
The :py:mod:`_pytest.terminal` reported specifically uses
the reporting hook to print information about a test run.
.. autofunction:: pytest_pyfunc_call
Collection hooks
~~~~~~~~~~~~~~~~
@@ -590,6 +631,7 @@ Collection hooks
.. autofunction:: pytest_ignore_collect
.. autofunction:: pytest_collect_directory
.. autofunction:: pytest_collect_file
.. autofunction:: pytest_pycollect_makemodule
For influencing the collection of objects in Python modules
you can use the following hook:
@@ -603,12 +645,15 @@ items, delete or otherwise amend the test items:
.. autofunction:: pytest_collection_modifyitems
.. autofunction:: pytest_collection_finish
Reporting hooks
~~~~~~~~~~~~~~~
Session related reporting hooks:
.. autofunction:: pytest_collectstart
.. autofunction:: pytest_make_collect_report
.. autofunction:: pytest_itemcollected
.. autofunction:: pytest_collectreport
.. autofunction:: pytest_deselected
@@ -620,15 +665,14 @@ Session related reporting hooks:
.. autofunction:: pytest_fixture_post_finalizer
.. autofunction:: pytest_warning_captured
And here is the central hook for reporting about
test execution:
Central hook for reporting about test execution:
.. autofunction:: pytest_runtest_logreport
You can also use this hook to customize assertion representation for some
types:
Assertion related hooks:
.. autofunction:: pytest_assertrepr_compare
.. autofunction:: pytest_assertion_pass
Debugging/Interaction hooks
@@ -682,6 +726,14 @@ ExceptionInfo
.. autoclass:: _pytest._code.ExceptionInfo
:members:
pytest.ExitCode
~~~~~~~~~~~~~~~
.. autoclass:: _pytest.main.ExitCode
:members:
FixtureDef
~~~~~~~~~~
@@ -847,7 +899,7 @@ pytest_mark
**Tutorial**: :ref:`scoped-marking`
Can be declared at the **global** level in *test modules* to apply one or more :ref:`marks <marks ref>` to all
test functions and methods. Can be either a single mark or a sequence of marks.
test functions and methods. Can be either a single mark or a list of marks.
.. code-block:: python
@@ -860,7 +912,7 @@ test functions and methods. Can be either a single mark or a sequence of marks.
import pytest
pytestmark = (pytest.mark.integration, pytest.mark.slow)
pytestmark = [pytest.mark.integration, pytest.mark.slow]
PYTEST_DONT_REWRITE (module docstring)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -906,6 +958,14 @@ PYTEST_CURRENT_TEST
This is not meant to be set by users, but is set by pytest internally with the name of the current test so other
processes can inspect it, see :ref:`pytest current test env` for more information.
Exceptions
----------
UsageError
~~~~~~~~~~
.. autoclass:: _pytest.config.UsageError()
.. _`ini options ref`:
@@ -948,7 +1008,7 @@ passed multiple times. The expected format is ``name=value``. For example::
.. confval:: cache_dir
.. versionadded:: 3.2
Sets a directory where stores content of cache plugin. Default directory is
``.pytest_cache`` which is created in :ref:`rootdir <rootdir>`. Directory may be
@@ -968,7 +1028,7 @@ passed multiple times. The expected format is ``name=value``. For example::
.. confval:: console_output_style
.. versionadded:: 3.3
Sets the console output style while running tests:
@@ -988,7 +1048,7 @@ passed multiple times. The expected format is ``name=value``. For example::
.. confval:: doctest_encoding
.. versionadded:: 3.1
Default encoding to use to decode text files with docstrings.
:doc:`See how pytest handles doctests <doctest>`.
@@ -1002,7 +1062,7 @@ passed multiple times. The expected format is ``name=value``. For example::
.. confval:: empty_parameter_set_mark
.. versionadded:: 3.4
Allows to pick the action for empty parametersets in parameterization
@@ -1023,9 +1083,26 @@ passed multiple times. The expected format is ``name=value``. For example::
for more details.
.. confval:: faulthandler_timeout
Dumps the tracebacks of all threads if a test takes longer than ``X`` seconds to run (including
fixture setup and teardown). Implemented using the `faulthandler.dump_traceback_later`_ function,
so all caveats there apply.
.. code-block:: ini
# content of pytest.ini
[pytest]
faulthandler_timeout=5
For more information please refer to :ref:`faulthandler`.
.. _`faulthandler.dump_traceback_later`: https://docs.python.org/3/library/faulthandler.html#faulthandler.dump_traceback_later
.. confval:: filterwarnings
.. versionadded:: 3.1
Sets a list of filters and actions that should be taken for matched
warnings. By default all warnings emitted during the test session
@@ -1042,6 +1119,22 @@ passed multiple times. The expected format is ``name=value``. For example::
This tells pytest to ignore deprecation warnings and turn all other warnings
into errors. For more information please refer to :ref:`warnings`.
.. confval:: junit_duration_report
.. versionadded:: 4.1
Configures how durations are recorded into the JUnit XML report:
* ``total`` (the default): duration times reported include setup, call, and teardown times.
* ``call``: duration times reported include only call times, excluding setup and teardown.
.. code-block:: ini
[pytest]
junit_duration_report = call
.. confval:: junit_family
.. versionadded:: 4.2
@@ -1057,9 +1150,34 @@ passed multiple times. The expected format is ``name=value``. For example::
[pytest]
junit_family = xunit2
.. confval:: junit_suite_name
.. versionadded:: 3.1
.. confval:: junit_logging
.. versionadded:: 3.5
Configures if stdout/stderr should be written to the JUnit XML file. Valid values are
``system-out``, ``system-err``, and ``no`` (the default).
.. code-block:: ini
[pytest]
junit_logging = system-out
.. confval:: junit_log_passing_tests
.. versionadded:: 4.6
If ``junit_logging != "no"``, configures if the captured output should be written
to the JUnit XML file for **passing** tests. Default is ``True``.
.. code-block:: ini
[pytest]
junit_log_passing_tests = False
.. confval:: junit_suite_name
To set the name of the root test suite xml item, you can configure the ``junit_suite_name`` option in your config file:
@@ -1071,7 +1189,7 @@ passed multiple times. The expected format is ``name=value``. For example::
.. confval:: log_cli_date_format
.. versionadded:: 3.3
Sets a :py:func:`time.strftime`-compatible string that will be used when formatting dates for live logging.
@@ -1084,7 +1202,7 @@ passed multiple times. The expected format is ``name=value``. For example::
.. confval:: log_cli_format
.. versionadded:: 3.3
Sets a :py:mod:`logging`-compatible string used to format live logging messages.
@@ -1098,7 +1216,7 @@ passed multiple times. The expected format is ``name=value``. For example::
.. confval:: log_cli_level
.. versionadded:: 3.3
Sets the minimum log message level that should be captured for live logging. The integer value or
the names of the levels can be used.
@@ -1113,7 +1231,7 @@ passed multiple times. The expected format is ``name=value``. For example::
.. confval:: log_date_format
.. versionadded:: 3.3
Sets a :py:func:`time.strftime`-compatible string that will be used when formatting dates for logging capture.
@@ -1127,7 +1245,7 @@ passed multiple times. The expected format is ``name=value``. For example::
.. confval:: log_file
.. versionadded:: 3.3
Sets a file name relative to the ``pytest.ini`` file where log messages should be written to, in addition
to the other logging facilities that are active.
@@ -1142,7 +1260,7 @@ passed multiple times. The expected format is ``name=value``. For example::
.. confval:: log_file_date_format
.. versionadded:: 3.3
Sets a :py:func:`time.strftime`-compatible string that will be used when formatting dates for the logging file.
@@ -1155,7 +1273,7 @@ passed multiple times. The expected format is ``name=value``. For example::
.. confval:: log_file_format
.. versionadded:: 3.3
Sets a :py:mod:`logging`-compatible string used to format logging messages redirected to the logging file.
@@ -1168,7 +1286,7 @@ passed multiple times. The expected format is ``name=value``. For example::
.. confval:: log_file_level
.. versionadded:: 3.3
Sets the minimum log message level that should be captured for the logging file. The integer value or
the names of the levels can be used.
@@ -1183,7 +1301,7 @@ passed multiple times. The expected format is ``name=value``. For example::
.. confval:: log_format
.. versionadded:: 3.3
Sets a :py:mod:`logging`-compatible string used to format captured logging messages.
@@ -1197,7 +1315,7 @@ passed multiple times. The expected format is ``name=value``. For example::
.. confval:: log_level
.. versionadded:: 3.3
Sets the minimum log message level that should be captured for logging capture. The integer value or
the names of the levels can be used.
@@ -1212,7 +1330,7 @@ passed multiple times. The expected format is ``name=value``. For example::
.. confval:: log_print
.. versionadded:: 3.3
If set to ``False``, will disable displaying captured logging messages for failed tests.
@@ -1226,12 +1344,17 @@ passed multiple times. The expected format is ``name=value``. For example::
.. confval:: markers
List of markers that are allowed in test functions, enforced when ``--strict`` command-line argument is used.
You can use a marker name per line, indented from the option name.
When the ``--strict-markers`` or ``--strict`` command-line arguments are used,
only known markers - defined in code by core pytest or some plugin - are allowed.
You can list additional markers in this setting to add them to the whitelist,
in which case you probably want to add ``--strict-markers`` to ``addopts``
to avoid future regressions:
.. code-block:: ini
[pytest]
addopts = --strict-markers
markers =
slow
serial
@@ -1346,7 +1469,7 @@ passed multiple times. The expected format is ``name=value``. For example::
.. confval:: testpaths
.. versionadded:: 2.8
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

View File

@@ -1,4 +1,4 @@
pygments-pytest>=1.1.0
sphinx>=1.8.2
sphinx>=1.8.2,<2.1
sphinxcontrib-trio
sphinx-removed-in>=0.1.3
sphinx-removed-in>=0.2.0

View File

@@ -22,7 +22,9 @@ it's an **xpass** and will be reported in the test summary.
``pytest`` counts and lists *skip* and *xfail* tests separately. Detailed
information about skipped/xfailed tests is not shown by default to avoid
cluttering the output. You can use the ``-r`` option to see details
corresponding to the "short" letters shown in the test progress::
corresponding to the "short" letters shown in the test progress:
.. code-block:: bash
pytest -rxXs # show extra info on xfailed, xpassed, and skipped tests
@@ -37,7 +39,7 @@ More details on the ``-r`` option can be found by running ``pytest -h``.
Skipping test functions
-----------------------
.. versionadded:: 2.9
The simplest way to skip a test function is to mark it with the ``skip`` decorator
which may be passed an optional ``reason``:
@@ -78,36 +80,48 @@ It is also possible to skip the whole module using
``skipif``
~~~~~~~~~~
.. versionadded:: 2.0
If you wish to skip something conditionally then you can use ``skipif`` instead.
Here is an example of marking a test function to be skipped
when run on an interpreter earlier than Python3.6 ::
when run on an interpreter earlier than Python3.6:
.. code-block:: python
import sys
@pytest.mark.skipif(sys.version_info < (3,6),
reason="requires python3.6 or higher")
@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher")
def test_function():
...
If the condition evaluates to ``True`` during collection, the test function will be skipped,
with the specified reason appearing in the summary when using ``-rs``.
You can share ``skipif`` markers between modules. Consider this test module::
You can share ``skipif`` markers between modules. Consider this test module:
.. code-block:: python
# content of test_mymodule.py
import mymodule
minversion = pytest.mark.skipif(mymodule.__versioninfo__ < (1,1),
reason="at least mymodule-1.1 required")
minversion = pytest.mark.skipif(
mymodule.__versioninfo__ < (1, 1), reason="at least mymodule-1.1 required"
)
@minversion
def test_function():
...
You can import the marker and reuse it in another test module::
You can import the marker and reuse it in another test module:
.. code-block:: python
# test_myothermodule.py
from test_mymodule import minversion
@minversion
def test_anotherfunction():
...
@@ -126,12 +140,12 @@ so they are supported mainly for backward compatibility reasons.
Skip all test functions of a class or module
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can use the ``skipif`` marker (as any other marker) on classes::
You can use the ``skipif`` marker (as any other marker) on classes:
@pytest.mark.skipif(sys.platform == 'win32',
reason="does not run on windows")
.. code-block:: python
@pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows")
class TestPosixCalls(object):
def test_function(self):
"will not be setup or run under 'win32' platform"
@@ -194,7 +208,7 @@ Here's a quick guide on how to skip tests in a module in different situations:
.. code-block:: python
pytestmark = pytest.mark.skipif(sys.platform == "win32", "tests for linux only")
pytestmark = pytest.mark.skipif(sys.platform == "win32", reason="tests for linux only")
3. Skip all tests in a module if some import is missing:
@@ -240,7 +254,7 @@ internally by raising a known exception.
``strict`` parameter
~~~~~~~~~~~~~~~~~~~~
.. versionadded:: 2.9
Both ``XFAIL`` and ``XPASS`` don't fail the test suite, unless the ``strict`` keyword-only
parameter is passed as ``True``:
@@ -267,10 +281,11 @@ You can change the default value of the ``strict`` parameter using the
~~~~~~~~~~~~~~~~~~~~
As with skipif_ you can also mark your expectation of a failure
on a particular platform::
on a particular platform:
@pytest.mark.xfail(sys.version_info >= (3,6),
reason="python3.6 api changes")
.. code-block:: python
@pytest.mark.xfail(sys.version_info >= (3, 6), reason="python3.6 api changes")
def test_function():
...
@@ -309,7 +324,9 @@ investigated later.
Ignoring xfail
~~~~~~~~~~~~~~
By specifying on the commandline::
By specifying on the commandline:
.. code-block:: bash
pytest --runxfail
@@ -331,10 +348,11 @@ Running it with the report-on-xfail option gives this output:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR/example, inifile:
rootdir: $REGENDOC_TMPDIR/example
collected 7 items
xfail_demo.py xxxxxxx [100%]
========================= short test summary info ==========================
XFAIL xfail_demo.py::test_hello
XFAIL xfail_demo.py::test_hello2
@@ -348,7 +366,6 @@ Running it with the report-on-xfail option gives this output:
XFAIL xfail_demo.py::test_hello6
reason: reason
XFAIL xfail_demo.py::test_hello7
======================== 7 xfailed in 0.12 seconds =========================
.. _`skip/xfail with parametrize`:

4
doc/en/tidelift.rst Normal file
View File

@@ -0,0 +1,4 @@
.. include:: ../../TIDELIFT.rst

View File

@@ -8,7 +8,7 @@ Temporary directories and files
The ``tmp_path`` fixture
------------------------
.. versionadded:: 3.9
You can use the ``tmp_path`` fixture which will
@@ -22,7 +22,7 @@ created in the `base temporary directory`_.
# content of test_tmp_path.py
import os
CONTENT = u"content"
CONTENT = "content"
def test_create_file(tmp_path):
@@ -43,7 +43,7 @@ Running this would result in a passed test except for the last
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 1 item
test_tmp_path.py F [100%]
@@ -66,10 +66,12 @@ Running this would result in a passed test except for the last
test_tmp_path.py:13: AssertionError
========================= 1 failed in 0.12 seconds =========================
.. _`tmp_path_factory example`:
The ``tmp_path_factory`` fixture
--------------------------------
.. versionadded:: 3.9
The ``tmp_path_factory`` is a session-scoped fixture which can be used
@@ -77,6 +79,8 @@ to create arbitrary temporary directories from any other fixture or test.
It is intended to replace ``tmpdir_factory``, and returns :class:`pathlib.Path` instances.
See :ref:`tmp_path_factory API <tmp_path_factory factory api>` for details.
The 'tmpdir' fixture
--------------------
@@ -106,7 +110,7 @@ Running this would result in a passed test except for the last
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 1 item
test_tmpdir.py F [100%]
@@ -132,7 +136,7 @@ Running this would result in a passed test except for the last
The 'tmpdir_factory' fixture
----------------------------
.. versionadded:: 2.8
The ``tmpdir_factory`` is a session-scoped fixture which can be used
to create arbitrary temporary directories from any other fixture or test.
@@ -174,7 +178,9 @@ 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.
You can override the default temporary directory setting like this::
You can override the default temporary directory setting like this:
.. code-block:: bash
pytest --basetemp=mydir

View File

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

View File

@@ -10,9 +10,11 @@ Usage and Invocations
Calling pytest through ``python -m pytest``
-----------------------------------------------------
.. versionadded:: 2.0
You can invoke testing through the Python interpreter from the command line::
You can invoke testing through the Python interpreter from the command line:
.. code-block:: text
python -m pytest [...]
@@ -31,10 +33,12 @@ Running ``pytest`` can result in six different exit codes:
:Exit code 4: pytest command line usage error
:Exit code 5: No tests were collected
They are represented by the :class:`_pytest.main.ExitCode` enum.
Getting help on version, option names, environment variables
--------------------------------------------------------------
::
.. code-block:: bash
pytest --version # shows where pytest was imported from
pytest --fixtures # show available builtin function arguments
@@ -46,7 +50,9 @@ Getting help on version, option names, environment variables
Stopping after the first (or N) failures
---------------------------------------------------
To stop the testing process after the first (N) failures::
To stop the testing process after the first (N) failures:
.. code-block:: bash
pytest -x # stop after first failure
pytest --maxfail=2 # stop after two failures
@@ -60,19 +66,19 @@ Pytest supports several ways to run and select tests from the command-line.
**Run tests in a module**
::
.. code-block:: bash
pytest test_mod.py
**Run tests in a directory**
::
.. code-block:: bash
pytest testing/
**Run tests by keyword expressions**
::
.. code-block:: bash
pytest -k "MyClass and not method"
@@ -87,18 +93,22 @@ The example above will run ``TestMyClass.test_something`` but not ``TestMyClass
Each collected test is assigned a unique ``nodeid`` which consist of the module filename followed
by specifiers like class names, function names and parameters from parametrization, separated by ``::`` characters.
To run a specific test within a module::
To run a specific test within a module:
.. code-block:: bash
pytest test_mod.py::test_func
Another example specifying a test method in the command line::
Another example specifying a test method in the command line:
.. code-block:: bash
pytest test_mod.py::TestClass::test_method
**Run tests by marker expressions**
::
.. code-block:: bash
pytest -m slow
@@ -108,7 +118,7 @@ For more information see :ref:`marks <mark>`.
**Run tests from packages**
::
.. code-block:: bash
pytest --pyargs pkg.testing
@@ -118,7 +128,9 @@ This will import ``pkg.testing`` and use its filesystem location to find and run
Modifying Python traceback printing
----------------------------------------------
Examples for modifying traceback printing::
Examples for modifying traceback printing:
.. code-block:: bash
pytest --showlocals # show local variables in tracebacks
pytest -l # show local variables (shortcut)
@@ -145,7 +157,7 @@ option you make sure a trace is shown.
Detailed summary report
-----------------------
.. versionadded:: 2.9
The ``-r`` flag can be used to display a "short test summary info" at the end of the test session,
making it easy in large test suites to get a clear picture of all failures, skips, xfails, etc.
@@ -194,7 +206,7 @@ Example:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 6 items
test_example.py .FEsxX [100%]
@@ -221,11 +233,12 @@ Example:
XFAIL test_example.py::test_xfail
reason: xfailing this test
XPASS test_example.py::test_xpass always xfail
ERROR test_example.py::test_error
FAILED test_example.py::test_fail
1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12 seconds
ERROR test_example.py::test_error - assert 0
FAILED test_example.py::test_fail - assert 0
= 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12 seconds =
The ``-r`` options accepts a number of characters after it, with ``a`` used above meaning "all except passes".
The ``-r`` options accepts a number of characters after it, with ``a`` used
above meaning "all except passes".
Here is the full list of available characters that can be used:
@@ -237,6 +250,7 @@ Here is the full list of available characters that can be used:
- ``p`` - passed
- ``P`` - passed with output
- ``a`` - all except ``pP``
- ``A`` - all
More than one character can be used, so for example to only see failed and skipped tests, you can execute:
@@ -246,7 +260,7 @@ More than one character can be used, so for example to only see failed and skipp
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 6 items
test_example.py .FEsxX [100%]
@@ -269,9 +283,9 @@ More than one character can be used, so for example to only see failed and skipp
test_example.py:14: AssertionError
========================= short test summary info ==========================
FAILED test_example.py::test_fail
FAILED test_example.py::test_fail - assert 0
SKIPPED [1] $REGENDOC_TMPDIR/test_example.py:23: skipping this test
1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12 seconds
= 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12 seconds =
Using ``p`` lists the passing tests, whilst ``P`` adds an extra section "PASSES" with those tests that passed but had
captured output:
@@ -282,7 +296,7 @@ captured output:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 6 items
test_example.py .FEsxX [100%]
@@ -304,13 +318,13 @@ captured output:
E assert 0
test_example.py:14: AssertionError
========================= short test summary info ==========================
PASSED test_example.py::test_ok
================================== PASSES ==================================
_________________________________ test_ok __________________________________
--------------------------- Captured stdout call ---------------------------
ok
1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12 seconds
========================= short test summary info ==========================
PASSED test_example.py::test_ok
= 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12 seconds =
.. _pdb-option:
@@ -320,13 +334,17 @@ Dropping to PDB_ (Python Debugger) on failures
.. _PDB: http://docs.python.org/library/pdb.html
Python comes with a builtin Python debugger called PDB_. ``pytest``
allows one to drop into the PDB_ prompt via a command line option::
allows one to drop into the PDB_ prompt via a command line option:
.. code-block:: bash
pytest --pdb
This will invoke the Python debugger on every failure (or KeyboardInterrupt).
Often you might only want to do this for the first failing test to understand
a certain failure situation::
a certain failure situation:
.. code-block:: bash
pytest -x --pdb # drop to PDB on first failure, then end test session
pytest --pdb --maxfail=3 # drop to PDB for first three failures
@@ -349,7 +367,9 @@ Dropping to PDB_ (Python Debugger) at the start of a test
----------------------------------------------------------
``pytest`` allows one to drop into the PDB_ prompt immediately at the start of each test via a command line option::
``pytest`` allows one to drop into the PDB_ prompt immediately at the start of each test via a command line option:
.. code-block:: bash
pytest --trace
@@ -368,10 +388,8 @@ in your code and pytest automatically disables its output capture for that test:
* Output capture in other tests is not affected.
* Any prior test output that has already been captured and will be processed as
such.
* Any later output produced within the same test will not be captured and will
instead get sent directly to ``sys.stdout``. Note that this holds true even
for test output occurring after you exit the interactive PDB_ tracing session
and continue with the regular test run.
* Output capture gets resumed when ending the debugger session (via the
``continue`` command).
.. _`breakpoint-builtin`:
@@ -392,25 +410,60 @@ Pytest supports the use of ``breakpoint()`` with the following behaviours:
Profiling test execution duration
-------------------------------------
.. versionadded: 2.2
To get a list of the slowest 10 test durations::
To get a list of the slowest 10 test durations:
.. code-block:: bash
pytest --durations=10
By default, pytest will not show test durations that are too small (<0.01s) unless ``-vv`` is passed on the command-line.
.. _faulthandler:
Fault Handler
-------------
.. versionadded:: 5.0
The `faulthandler <https://docs.python.org/3/library/faulthandler.html>`__ standard module
can be used to dump Python tracebacks on a segfault or after a timeout.
The module is automatically enabled for pytest runs, unless the ``-p no:faulthandler`` is given
on the command-line.
Also the :confval:`faulthandler_timeout=X<faulthandler_timeout>` configuration option can be used
to dump the traceback of all threads if a test takes longer than ``X``
seconds to finish (not available on Windows).
.. note::
This functionality has been integrated from the external
`pytest-faulthandler <https://github.com/pytest-dev/pytest-faulthandler>`__ plugin, with two
small differences:
* To disable it, use ``-p no:faulthandler`` instead of ``--no-faulthandler``: the former
can be used with any plugin, so it saves one option.
* The ``--faulthandler-timeout`` command-line option has become the
:confval:`faulthandler_timeout` configuration option. It can still be configured from
the command-line using ``-o faulthandler_timeout=X``.
Creating JUnitXML format files
----------------------------------------------------
To create result files which can be read by Jenkins_ or other Continuous
integration servers, use this invocation::
integration servers, use this invocation:
.. code-block:: bash
pytest --junitxml=path
to create an XML file at ``path``.
.. versionadded:: 3.1
To set the name of the root test suite xml item, you can configure the ``junit_suite_name`` option in your config file:
@@ -438,13 +491,6 @@ instead, configure the ``junit_duration_report`` option like this:
record_property
^^^^^^^^^^^^^^^
.. versionadded:: 2.8
.. versionchanged:: 3.5
Fixture renamed from ``record_xml_property`` to ``record_property`` as user
properties are now available to all reporters.
``record_xml_property`` is now deprecated.
If you want to log additional information for a test, you can use the
``record_property`` fixture:
@@ -502,15 +548,13 @@ Will result in:
.. warning::
``record_property`` is an experimental feature and may change in the future.
Also please note that using this feature will break any schema verification.
Please note that using this feature will break schema verifications for the latest JUnitXML schema.
This might be a problem when used with some CI servers.
record_xml_attribute
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. versionadded:: 3.4
To add an additional xml attribute to a testcase element, you can use
``record_xml_attribute`` fixture. This can also be used to override existing values:
@@ -567,43 +611,45 @@ Instead, this will add an attribute ``assertions="REQ-1234"`` inside the generat
</xs:complexType>
</xs:element>
LogXML: add_global_property
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. warning::
.. versionadded:: 3.0
Please note that using this feature will break schema verifications for the latest JUnitXML schema.
This might be a problem when used with some CI servers.
If you want to add a properties node in the testsuite level, which may contains properties that are relevant
to all testcases you can use ``LogXML.add_global_properties``
.. _record_testsuite_property example:
record_testsuite_property
^^^^^^^^^^^^^^^^^^^^^^^^^
.. versionadded:: 4.5
If you want to add a properties node at the test-suite level, which may contains properties
that are relevant to all tests, you can use the ``record_testsuite_property`` session-scoped fixture:
The ``record_testsuite_property`` session-scoped fixture can be used to add properties relevant
to all tests.
.. code-block:: python
import pytest
@pytest.fixture(scope="session")
def log_global_env_facts(f):
if pytest.config.pluginmanager.hasplugin("junitxml"):
my_junit = getattr(pytest.config, "_xml", None)
my_junit.add_global_property("ARCH", "PPC")
my_junit.add_global_property("STORAGE_TYPE", "CEPH")
@pytest.mark.usefixtures(log_global_env_facts.__name__)
def start_and_prepare_env():
pass
@pytest.fixture(scope="session", autouse=True)
def log_global_env_facts(record_testsuite_property):
record_testsuite_property("ARCH", "PPC")
record_testsuite_property("STORAGE_TYPE", "CEPH")
class TestMe(object):
def test_foo(self):
assert True
This will add a property node below the testsuite node to the generated xml:
The fixture is a callable which receives ``name`` and ``value`` of a ``<property>`` tag
added at the test-suite level of the generated xml:
.. code-block:: xml
<testsuite errors="0" failures="0" name="pytest" skips="0" tests="1" time="0.006">
<testsuite errors="0" failures="0" name="pytest" skipped="0" tests="1" time="0.006">
<properties>
<property name="ARCH" value="PPC"/>
<property name="STORAGE_TYPE" value="CEPH"/>
@@ -611,23 +657,25 @@ This will add a property node below the testsuite node to the generated xml:
<testcase classname="test_me.TestMe" file="test_me.py" line="16" name="test_foo" time="0.000243663787842"/>
</testsuite>
.. warning::
``name`` must be a string, ``value`` will be converted to a string and properly xml-escaped.
The generated XML is compatible with the latest ``xunit`` standard, contrary to `record_property`_
and `record_xml_attribute`_.
This is an experimental feature, and its interface might be replaced
by something more powerful and general in future versions. The
functionality per-se will be kept.
Creating resultlog format files
----------------------------------------------------
.. deprecated:: 3.0
This option is rarely used and is scheduled for removal in 5.0.
See `the deprecation docs <https://docs.pytest.org/en/latest/deprecations.html#result-log-result-log>`__
for more information.
To create plain-text machine-readable result files you can issue::
To create plain-text machine-readable result files you can issue:
.. code-block:: bash
pytest --resultlog=path
@@ -640,7 +688,9 @@ by the `PyPy-test`_ web page to show test results over several revisions.
Sending test report to online pastebin service
-----------------------------------------------------
**Creating a URL for each test failure**::
**Creating a URL for each test failure**:
.. code-block:: bash
pytest --pastebin=failed
@@ -648,12 +698,30 @@ This will submit test run information to a remote Paste service and
provide a URL for each failure. You may select tests as usual or add
for example ``-x`` if you only want to send one particular failure.
**Creating a URL for a whole test session log**::
**Creating a URL for a whole test session log**:
.. code-block:: bash
pytest --pastebin=all
Currently only pasting to the http://bpaste.net service is implemented.
Early loading plugins
---------------------
You can early-load plugins (internal and external) explicitly in the command-line with the ``-p`` option::
pytest -p mypluginmodule
The option receives a ``name`` parameter, which can be:
* A full module dotted name, for example ``myproject.plugins``. This dotted name must be importable.
* The entry-point name of a plugin. This is the name passed to ``setuptools`` when the plugin is
registered. For example to early-load the `pytest-cov <https://pypi.org/project/pytest-cov/>`__ plugin you can use::
pytest -p pytest_cov
Disabling plugins
-----------------
@@ -661,7 +729,9 @@ To disable loading specific plugins at invocation time, use the ``-p`` option
together with the prefix ``no:``.
Example: to disable loading the plugin ``doctest``, which is responsible for
executing doctest tests from text files, invoke pytest like this::
executing doctest tests from text files, invoke pytest like this:
.. code-block:: bash
pytest -p no:doctest
@@ -670,7 +740,7 @@ executing doctest tests from text files, invoke pytest like this::
Calling pytest from Python code
----------------------------------------------------
.. versionadded:: 2.0
You can invoke ``pytest`` from Python code directly::
@@ -693,7 +763,9 @@ You can specify additional plugins to ``pytest.main``::
pytest.main(["-qq"], plugins=[MyPlugin()])
Running it will show that ``MyPlugin`` was added and its
hook was invoked::
hook was invoked:
.. code-block:: pytest
$ python myinvoke.py
.FEsxX. [100%]*** test run reporting finishing

View File

@@ -3,18 +3,22 @@
Warnings Capture
================
.. versionadded:: 3.1
Starting from version ``3.1``, pytest now automatically catches warnings during test execution
and displays them at the end of the session::
and displays them at the end of the session:
.. code-block:: python
# content of test_show_warnings.py
import warnings
def api_v1():
warnings.warn(UserWarning("api v1, should use functions from v2"))
return 1
def test_one():
assert api_v1() == 1
@@ -26,14 +30,14 @@ Running pytest now produces this output:
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
rootdir: $REGENDOC_TMPDIR
collected 1 item
test_show_warnings.py . [100%]
============================= warnings summary =============================
test_show_warnings.py::test_one
$REGENDOC_TMPDIR/test_show_warnings.py:4: UserWarning: api v1, should use functions from v2
$REGENDOC_TMPDIR/test_show_warnings.py:5: UserWarning: api v1, should use functions from v2
warnings.warn(UserWarning("api v1, should use functions from v2"))
-- Docs: https://docs.pytest.org/en/latest/warnings.html
@@ -52,14 +56,14 @@ them into errors:
def test_one():
> assert api_v1() == 1
test_show_warnings.py:8:
test_show_warnings.py:10:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
def api_v1():
> warnings.warn(UserWarning("api v1, should use functions from v2"))
E UserWarning: api v1, should use functions from v2
test_show_warnings.py:4: UserWarning
test_show_warnings.py:5: UserWarning
1 failed in 0.12 seconds
The same option can be set in the ``pytest.ini`` file using the ``filterwarnings`` ini option.
@@ -86,7 +90,7 @@ documentation for other examples and advanced usage.
``@pytest.mark.filterwarnings``
-------------------------------
.. versionadded:: 3.2
You can use the ``@pytest.mark.filterwarnings`` to add warning filters to specific test items,
allowing you to have finer control of which warnings should be captured at test, class or
@@ -152,8 +156,8 @@ using an external system.
DeprecationWarning and PendingDeprecationWarning
------------------------------------------------
.. versionadded:: 3.8
.. versionchanged:: 3.9
By default pytest will display ``DeprecationWarning`` and ``PendingDeprecationWarning`` warnings from
user code and third-party libraries, as recommended by `PEP-0565 <https://www.python.org/dev/peps/pep-0565>`_.
@@ -195,28 +199,36 @@ Ensuring code triggers a deprecation warning
You can also call a global helper for checking
that a certain function call triggers a ``DeprecationWarning`` or
``PendingDeprecationWarning``::
``PendingDeprecationWarning``:
.. code-block:: python
import pytest
def test_global():
pytest.deprecated_call(myfunction, 17)
By default, ``DeprecationWarning`` and ``PendingDeprecationWarning`` will not be
caught when using ``pytest.warns`` or ``recwarn`` because default Python warnings filters hide
them. If you wish to record them in your own code, use the
command ``warnings.simplefilter('always')``::
command ``warnings.simplefilter('always')``:
.. code-block:: python
import warnings
import pytest
def test_deprecation(recwarn):
warnings.simplefilter('always')
warnings.simplefilter("always")
warnings.warn("deprecated", DeprecationWarning)
assert len(recwarn) == 1
assert recwarn.pop(DeprecationWarning)
You can also use it as a contextmanager::
You can also use it as a contextmanager:
.. code-block:: python
def test_global():
with pytest.deprecated_call():
@@ -235,14 +247,17 @@ You can also use it as a contextmanager::
Asserting warnings with the warns function
------------------------------------------
.. versionadded:: 2.8
You can check that code raises a particular warning using ``pytest.warns``,
which works in a similar manner to :ref:`raises <assertraises>`::
which works in a similar manner to :ref:`raises <assertraises>`:
.. code-block:: python
import warnings
import pytest
def test_warning():
with pytest.warns(UserWarning):
warnings.warn("my warning", UserWarning)
@@ -269,7 +284,9 @@ You can also call ``pytest.warns`` on a function or code string::
The function also returns a list of all raised warnings (as
``warnings.WarningMessage`` objects), which you can query for
additional information::
additional information:
.. code-block:: python
with pytest.warns(RuntimeWarning) as record:
warnings.warn("another warning", RuntimeWarning)
@@ -297,7 +314,9 @@ You can record raised warnings either using ``pytest.warns`` or with
the ``recwarn`` fixture.
To record with ``pytest.warns`` without asserting anything about the warnings,
pass ``None`` as the expected warning type::
pass ``None`` as the expected warning type:
.. code-block:: python
with pytest.warns(None) as record:
warnings.warn("user", UserWarning)
@@ -307,10 +326,13 @@ pass ``None`` as the expected warning type::
assert str(record[0].message) == "user"
assert str(record[1].message) == "runtime"
The ``recwarn`` fixture will record warnings for the whole function::
The ``recwarn`` fixture will record warnings for the whole function:
.. code-block:: python
import warnings
def test_hello(recwarn):
warnings.warn("hello", UserWarning)
assert len(recwarn) == 1
@@ -355,7 +377,7 @@ custom error message.
Internal pytest warnings
------------------------
.. versionadded:: 3.8
pytest may generate its own warnings in some situations, such as improper usage or deprecated features.
@@ -378,7 +400,7 @@ defines an ``__init__`` constructor, as this prevents the class from being insta
============================= warnings summary =============================
test_pytest_warnings.py:1
$REGENDOC_TMPDIR/test_pytest_warnings.py:1: PytestWarning: cannot collect test class 'Test' because it has a __init__ constructor
$REGENDOC_TMPDIR/test_pytest_warnings.py:1: PytestCollectionWarning: cannot collect test class 'Test' because it has a __init__ constructor (from: test_pytest_warnings.py)
class Test:
-- Docs: https://docs.pytest.org/en/latest/warnings.html
@@ -393,8 +415,20 @@ The following warning types ares used by pytest and are part of the public API:
.. autoclass:: pytest.PytestWarning
.. autoclass:: pytest.PytestAssertRewriteWarning
.. autoclass:: pytest.PytestCacheWarning
.. autoclass:: pytest.PytestCollectionWarning
.. autoclass:: pytest.PytestConfigWarning
.. autoclass:: pytest.PytestDeprecationWarning
.. autoclass:: pytest.RemovedInPytest4Warning
.. autoclass:: pytest.PytestExperimentalApiWarning
.. autoclass:: pytest.PytestUnhandledCoroutineWarning
.. autoclass:: pytest.PytestUnknownMarkWarning
.. autoclass:: pytest.RemovedInPytest4Warning

View File

@@ -223,7 +223,6 @@ import ``helper.py`` normally. The contents of
pytest.register_assert_rewrite("pytest_foo.helper")
Requiring/Loading plugins in a test module or conftest file
-----------------------------------------------------------
@@ -286,6 +285,26 @@ the plugin manager like this:
If you want to look at the names of existing plugins, use
the ``--trace-config`` option.
.. _registering-markers:
Registering custom markers
--------------------------
If your plugin uses any markers, you should register them so that they appear in
pytest's help text and do not :ref:`cause spurious warnings <unknown-marks>`.
For example, the following plugin would register ``cool_marker`` and
``mark_with`` for all users:
.. code-block:: python
def pytest_configure(config):
config.addinivalue_line("markers", "cool_marker: this one is for cool tests.")
config.addinivalue_line(
"markers", "mark_with(arg, arg2): this marker takes arguments."
)
Testing plugins
---------------
@@ -316,8 +335,6 @@ string value of ``Hello World!`` if we do not supply a value or ``Hello
.. code-block:: python
# -*- coding: utf-8 -*-
import pytest
@@ -496,7 +513,7 @@ hookwrapper: executing around other hooks
.. currentmodule:: _pytest.core
.. versionadded:: 2.7
pytest plugins can implement hook wrappers which wrap the execution
of other hook implementations. A hook wrapper is a generator function
@@ -509,10 +526,13 @@ a :py:class:`Result <pluggy._Result>` instance which encapsulates a result or
exception info. The yield point itself will thus typically not raise
exceptions (unless there are bugs).
Here is an example definition of a hook wrapper::
Here is an example definition of a hook wrapper:
.. code-block:: python
import pytest
@pytest.hookimpl(hookwrapper=True)
def pytest_pyfunc_call(pyfuncitem):
do_something_before_next_hook_executes()
@@ -617,10 +637,13 @@ if you depend on a plugin that is not installed, validation will fail and
the error message will not make much sense to your users.
One approach is to defer the hook implementation to a new plugin instead of
declaring the hook functions directly in your plugin module, for example::
declaring the hook functions directly in your plugin module, for example:
.. code-block:: python
# contents of myplugin.py
class DeferPlugin(object):
"""Simple plugin to defer pytest-xdist hook functions."""
@@ -628,8 +651,9 @@ declaring the hook functions directly in your plugin module, for example::
"""standard xdist hook function.
"""
def pytest_configure(config):
if config.pluginmanager.hasplugin('xdist'):
if config.pluginmanager.hasplugin("xdist"):
config.pluginmanager.register(DeferPlugin())
This has the added benefit of allowing you to conditionally install hooks

View File

@@ -5,9 +5,9 @@
"yield_fixture" functions
---------------------------------------------------------------
.. deprecated:: 3.0
.. versionadded:: 2.4
.. important::
Since pytest-3.0, fixtures using the normal ``fixture`` decorator can use a ``yield``

View File

@@ -1,6 +0,0 @@
REM install pypy using choco
REM redirect to a file because choco install python.pypy is too noisy. If the command fails, write output to console
choco install python.pypy > pypy-inst.log 2>&1 || (type pypy-inst.log & exit /b 1)
set PATH=C:\tools\pypy\pypy;%PATH% # so tox can find pypy
echo PyPy installed
pypy --version

View File

@@ -1,10 +0,0 @@
REM scripts called by AppVeyor to setup the environment variables to enable coverage
if not defined PYTEST_NO_COVERAGE (
set "COVERAGE_FILE=%CD%\.coverage"
set "COVERAGE_PROCESS_START=%CD%\.coveragerc"
set "_PYTEST_TOX_COVERAGE_RUN=coverage run -m"
set "_PYTEST_TOX_EXTRA_DEP=coverage-enable-subprocess"
echo Coverage setup completed
) else (
echo Skipping coverage setup, PYTEST_NO_COVERAGE is set
)

16
scripts/report-coverage.sh Executable file
View File

@@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -e
set -x
if [ -z "$TOXENV" ]; then
python -m pip install coverage
else
# Add last TOXENV to $PATH.
PATH="$PWD/.tox/${TOXENV##*,}/bin:$PATH"
fi
python -m coverage combine
python -m coverage xml
python -m coverage report -m
bash <(curl -s https://codecov.io/bash) -Z -X gcov -X coveragepy -X search -X xcode -X gcovout -X fix -f coverage.xml

View File

@@ -1,11 +0,0 @@
REM script called by AppVeyor to combine and upload coverage information to codecov
if not defined PYTEST_NO_COVERAGE (
echo Prepare to upload coverage information
C:\Python36\Scripts\pip install codecov
C:\Python36\Scripts\coverage combine
C:\Python36\Scripts\coverage xml --ignore-errors
C:\Python36\Scripts\coverage report -m --ignore-errors
scripts\appveyor-retry C:\Python36\Scripts\codecov --required -X gcov pycov search -f coverage.xml --flags %TOXENV:-= % windows
) else (
echo Skipping coverage upload, PYTEST_NO_COVERAGE is set
)

View File

@@ -1,5 +1,4 @@
[metadata]
name = pytest
description = pytest: simple powerful testing with Python
long_description = file: README.rst
@@ -23,13 +22,11 @@ classifiers =
Topic :: Software Development :: Testing
Topic :: Software Development :: Libraries
Topic :: Utilities
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.4
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
platforms = unix, linux, osx, cygwin, win32
[options]
@@ -43,8 +40,7 @@ packages =
_pytest.mark
py_modules = pytest
python_requires = >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
python_requires = >=3.5
[options.entry_points]
console_scripts =
@@ -59,13 +55,9 @@ all_files = 1
[upload_sphinx]
upload-dir = doc/en/build/html
[bdist_wheel]
universal = 1
[check-manifest]
ignore =
_pytest/_version.py
[devpi:upload]
formats = sdist.tgz,bdist_wheel

View File

@@ -1,30 +1,21 @@
import os
from setuptools import setup
# TODO: if py gets upgrade to >=1.6,
# remove _width_of_current_line in terminal.py
INSTALL_REQUIRES = [
"py>=1.5.0",
"six>=1.10.0",
"setuptools",
"packaging",
"attrs>=17.4.0",
'more-itertools>=4.0.0,<6.0.0;python_version<="2.7"',
'more-itertools>=4.0.0;python_version>"2.7"',
"more-itertools>=4.0.0",
"atomicwrites>=1.0",
'funcsigs;python_version<"3.0"',
'pathlib2>=2.2.0;python_version<"3.6"',
'colorama;sys_platform=="win32"',
"pluggy>=0.12,<1.0",
"importlib-metadata>=0.12",
"wcwidth",
]
# if _PYTEST_SETUP_SKIP_PLUGGY_DEP is set, skip installing pluggy;
# used by tox.ini to test with pluggy master
if "_PYTEST_SETUP_SKIP_PLUGGY_DEP" not in os.environ:
INSTALL_REQUIRES.append("pluggy>=0.7")
def main():
setup(
use_scm_version={"write_to": "src/_pytest/_version.py"},
@@ -33,10 +24,11 @@ def main():
# fmt: off
extras_require={
"testing": [
"argcomplete",
"hypothesis>=3.56",
"mock",
"nose",
"requests",
"mock;python_version=='2.7'",
],
},
# fmt: on

View File

@@ -53,16 +53,12 @@ If things do not work right away:
which should throw a KeyError: 'COMPLINE' (which is properly set by the
global argcomplete script).
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import os
import sys
from glob import glob
class FastFilesCompleter(object):
class FastFilesCompleter:
"Fast file completer class"
def __init__(self, directories=True):

View File

@@ -1,8 +1,4 @@
""" python inspection/code generation API """
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from .code import Code # noqa
from .code import ExceptionInfo # noqa
from .code import filter_traceback # noqa

View File

@@ -1,94 +0,0 @@
# copied from python-2.7.3's traceback.py
# CHANGES:
# - some_str is replaced, trying to create unicode strings
#
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import types
from six import text_type
def format_exception_only(etype, value):
"""Format the exception part of a traceback.
The arguments are the exception type and value such as given by
sys.last_type and sys.last_value. The return value is a list of
strings, each ending in a newline.
Normally, the list contains a single string; however, for
SyntaxError exceptions, it contains several lines that (when
printed) display detailed information about where the syntax
error occurred.
The message indicating which exception occurred is always the last
string in the list.
"""
# An instance should not have a meaningful value parameter, but
# sometimes does, particularly for string exceptions, such as
# >>> raise string1, string2 # deprecated
#
# Clear these out first because issubtype(string1, SyntaxError)
# would throw another exception and mask the original problem.
if (
isinstance(etype, BaseException)
or isinstance(etype, types.InstanceType)
or etype is None
or type(etype) is str
):
return [_format_final_exc_line(etype, value)]
stype = etype.__name__
if not issubclass(etype, SyntaxError):
return [_format_final_exc_line(stype, value)]
# It was a syntax error; show exactly where the problem was found.
lines = []
try:
msg, (filename, lineno, offset, badline) = value.args
except Exception:
pass
else:
filename = filename or "<string>"
lines.append(' File "{}", line {}\n'.format(filename, lineno))
if badline is not None:
if isinstance(badline, bytes): # python 2 only
badline = badline.decode("utf-8", "replace")
lines.append(" {}\n".format(badline.strip()))
if offset is not None:
caretspace = badline.rstrip("\n")[:offset].lstrip()
# non-space whitespace (likes tabs) must be kept for alignment
caretspace = ((c.isspace() and c or " ") for c in caretspace)
# only three spaces to account for offset1 == pos 0
lines.append(" {}^\n".format("".join(caretspace)))
value = msg
lines.append(_format_final_exc_line(stype, value))
return lines
def _format_final_exc_line(etype, value):
"""Return a list of a single line -- normal case for format_exception_only"""
valuestr = _some_str(value)
if value is None or not valuestr:
line = "{}\n".format(etype)
else:
line = "{}: {}\n".format(etype, valuestr)
return line
def _some_str(value):
try:
return text_type(value)
except Exception:
try:
return bytes(value).decode("UTF-8", "replace")
except Exception:
pass
return "<unprintable {} object>".format(type(value).__name__)

View File

@@ -1,36 +1,22 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import inspect
import pprint
import re
import sys
import traceback
from inspect import CO_VARARGS
from inspect import CO_VARKEYWORDS
from traceback import format_exception_only
from weakref import ref
import attr
import pluggy
import py
import six
from six import text_type
import _pytest
from _pytest._io.saferepr import safeformat
from _pytest._io.saferepr import saferepr
from _pytest.compat import _PY2
from _pytest.compat import _PY3
from _pytest.compat import PY35
from _pytest.compat import safe_str
if _PY3:
from traceback import format_exception_only
else:
from ._py2traceback import format_exception_only
class Code(object):
class Code:
""" wrapper around Python code objects """
def __init__(self, rawcode):
@@ -41,7 +27,7 @@ class Code(object):
self.firstlineno = rawcode.co_firstlineno - 1
self.name = rawcode.co_name
except AttributeError:
raise TypeError("not a code object: %r" % (rawcode,))
raise TypeError("not a code object: {!r}".format(rawcode))
self.raw = rawcode
def __eq__(self, other):
@@ -100,7 +86,7 @@ class Code(object):
return raw.co_varnames[:argcount]
class Frame(object):
class Frame:
"""Wrapper around a Python frame holding f_locals and f_globals
in which expressions can be evaluated."""
@@ -138,7 +124,7 @@ class Frame(object):
"""
f_locals = self.f_locals.copy()
f_locals.update(vars)
six.exec_(code, self.f_globals, f_locals)
exec(code, self.f_globals, f_locals)
def repr(self, object):
""" return a 'safe' (non-recursive, one-line) string repr for 'object'
@@ -163,7 +149,7 @@ class Frame(object):
return retval
class TracebackEntry(object):
class TracebackEntry:
""" a single entry in a traceback """
_repr_style = None
@@ -208,8 +194,7 @@ class TracebackEntry(object):
locals = property(getlocals, None, None, "locals of underlaying frame")
def getfirstlinesource(self):
# on Jython this firstlineno can be -1 apparently
return max(self.frame.code.firstlineno, 0)
return self.frame.code.firstlineno
def getsource(self, astcache=None):
""" return failing source code. """
@@ -241,25 +226,20 @@ class TracebackEntry(object):
def ishidden(self):
""" return True if the current frame has a var __tracebackhide__
resolving to True
resolving to True.
If __tracebackhide__ is a callable, it gets called with the
ExceptionInfo instance and can decide whether to hide the traceback.
mostly for internal use
"""
try:
tbh = self.frame.f_locals["__tracebackhide__"]
except KeyError:
try:
tbh = self.frame.f_globals["__tracebackhide__"]
except KeyError:
return False
if callable(tbh):
f = self.frame
tbh = f.f_locals.get(
"__tracebackhide__", f.f_globals.get("__tracebackhide__", False)
)
if tbh and callable(tbh):
return tbh(None if self._excinfo is None else self._excinfo())
else:
return tbh
return tbh
def __str__(self):
try:
@@ -329,7 +309,7 @@ class Traceback(list):
return self
def __getitem__(self, key):
val = super(Traceback, self).__getitem__(key)
val = super().__getitem__(key)
if isinstance(key, type(slice(0))):
val = self.__class__(val)
return val
@@ -391,14 +371,12 @@ co_equal = compile(
@attr.s(repr=False)
class ExceptionInfo(object):
class ExceptionInfo:
""" wraps sys.exc_info() objects and offers
help for navigating the traceback.
"""
_assert_start_repr = (
"AssertionError(u'assert " if _PY2 else "AssertionError('assert "
)
_assert_start_repr = "AssertionError('assert "
_excinfo = attr.ib()
_striptext = attr.ib(default="")
@@ -418,6 +396,7 @@ class ExceptionInfo(object):
to the exception message/``__str__()``
"""
tup = sys.exc_info()
assert tup[0] is not None, "no current exception"
_striptext = ""
if exprinfo is None and isinstance(tup[1], AssertionError):
exprinfo = getattr(tup[1], "msg", None)
@@ -555,24 +534,13 @@ class ExceptionInfo(object):
)
return fmt.repr_excinfo(self)
def __str__(self):
if self._excinfo is None:
return repr(self)
entry = self.traceback[-1]
loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly())
return str(loc)
def __unicode__(self):
entry = self.traceback[-1]
loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly())
return text_type(loc)
def match(self, regexp):
"""
Match the regular expression 'regexp' on the string representation of
the exception. If it matches then True is returned (so that it is
possible to write 'assert excinfo.match()'). If it doesn't match an
AssertionError is raised.
Check whether the regular expression 'regexp' is found in the string
representation of the exception using ``re.search``. If it matches
then True is returned (so that it is possible to write
``assert excinfo.match()``). If it doesn't match an AssertionError is
raised.
"""
__tracebackhide__ = True
if not re.search(regexp, str(self.value)):
@@ -581,7 +549,7 @@ class ExceptionInfo(object):
@attr.s
class FormattedExcinfo(object):
class FormattedExcinfo:
""" presenting information about failing Functions and Generators. """
# for traceback entries
@@ -618,14 +586,11 @@ class FormattedExcinfo(object):
source = source.deindent()
return source
def _saferepr(self, obj):
return saferepr(obj)
def repr_args(self, entry):
if self.funcargs:
args = []
for argname, argvalue in entry.frame.getargs(var=True):
args.append((argname, self._saferepr(argvalue)))
args.append((argname, saferepr(argvalue)))
return ReprFuncArgs(args)
def get_source(self, source, line_index=-1, excinfo=None, short=False):
@@ -678,12 +643,12 @@ class FormattedExcinfo(object):
# _repr() function, which is only reprlib.Repr in
# disguise, so is very configurable.
if self.truncate_locals:
str_repr = self._saferepr(value)
str_repr = saferepr(value)
else:
str_repr = pprint.pformat(value)
str_repr = safeformat(value)
# if len(str_repr) < 70 or not isinstance(value,
# (list, tuple, dict)):
lines.append("%-10s = %s" % (name, str_repr))
lines.append("{:<10} = {}".format(name, str_repr))
# else:
# self._line("%-10s =\\" % (name,))
# # XXX
@@ -698,8 +663,7 @@ class FormattedExcinfo(object):
source = _pytest._code.Source("???")
line_index = 0
else:
# entry.getfirstlinesource() can be -1, should be 0 on jython
line_index = entry.lineno - max(entry.getfirstlinesource(), 0)
line_index = entry.lineno - entry.getfirstlinesource()
lines = []
style = entry._repr_style
@@ -739,7 +703,7 @@ class FormattedExcinfo(object):
if self.tbfilter:
traceback = traceback.filter()
if is_recursion_error(excinfo):
if excinfo.errisinstance(RecursionError):
traceback, extraline = self._truncate_recursive_traceback(traceback)
else:
extraline = None
@@ -775,7 +739,7 @@ class FormattedExcinfo(object):
" Displaying first and last {max_frames} stack frames out of {total}."
).format(
exc_type=type(e).__name__,
exc_msg=safe_str(e),
exc_msg=str(e),
max_frames=max_frames,
total=len(traceback),
)
@@ -790,64 +754,51 @@ class FormattedExcinfo(object):
return traceback, extraline
def repr_excinfo(self, excinfo):
if _PY2:
reprtraceback = self.repr_traceback(excinfo)
reprcrash = excinfo._getreprcrash()
return ReprExceptionInfo(reprtraceback, reprcrash)
else:
repr_chain = []
e = excinfo.value
descr = None
seen = set()
while e is not None and id(e) not in seen:
seen.add(id(e))
if excinfo:
reprtraceback = self.repr_traceback(excinfo)
reprcrash = excinfo._getreprcrash()
else:
# fallback to native repr if the exception doesn't have a traceback:
# ExceptionInfo objects require a full traceback to work
reprtraceback = ReprTracebackNative(
traceback.format_exception(type(e), e, None)
)
reprcrash = None
repr_chain = []
e = excinfo.value
descr = None
seen = set()
while e is not None and id(e) not in seen:
seen.add(id(e))
if excinfo:
reprtraceback = self.repr_traceback(excinfo)
reprcrash = excinfo._getreprcrash()
else:
# fallback to native repr if the exception doesn't have a traceback:
# ExceptionInfo objects require a full traceback to work
reprtraceback = ReprTracebackNative(
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((type(e), e, e.__traceback__))
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((type(e), e, e.__traceback__))
if e.__traceback__
else None
)
descr = "During handling of the above exception, another exception occurred:"
else:
e = None
repr_chain.reverse()
return ExceptionChainRepr(repr_chain)
repr_chain += [(reprtraceback, reprcrash, descr)]
if e.__cause__ is not None and self.chain:
e = e.__cause__
excinfo = (
ExceptionInfo((type(e), e, e.__traceback__))
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((type(e), e, e.__traceback__))
if e.__traceback__
else None
)
descr = "During handling of the above exception, another exception occurred:"
else:
e = None
repr_chain.reverse()
return ExceptionChainRepr(repr_chain)
class TerminalRepr(object):
class TerminalRepr:
def __str__(self):
s = self.__unicode__()
if _PY2:
s = s.encode("utf-8")
return s
def __unicode__(self):
# FYI this is called from pytest-xdist's serialization of exception
# information.
io = py.io.TextIO()
@@ -856,7 +807,7 @@ class TerminalRepr(object):
return io.getvalue().strip()
def __repr__(self):
return "<%s instance at %0x>" % (self.__class__, id(self))
return "<{} instance at {:0x}>".format(self.__class__, id(self))
class ExceptionRepr(TerminalRepr):
@@ -874,7 +825,7 @@ class ExceptionRepr(TerminalRepr):
class ExceptionChainRepr(ExceptionRepr):
def __init__(self, chain):
super(ExceptionChainRepr, self).__init__()
super().__init__()
self.chain = chain
# reprcrash and reprtraceback of the outermost (the newest) exception
# in the chain
@@ -887,18 +838,18 @@ class ExceptionChainRepr(ExceptionRepr):
if element[2] is not None:
tw.line("")
tw.line(element[2], yellow=True)
super(ExceptionChainRepr, self).toterminal(tw)
super().toterminal(tw)
class ReprExceptionInfo(ExceptionRepr):
def __init__(self, reprtraceback, reprcrash):
super(ReprExceptionInfo, self).__init__()
super().__init__()
self.reprtraceback = reprtraceback
self.reprcrash = reprcrash
def toterminal(self, tw):
self.reprtraceback.toterminal(tw)
super(ReprExceptionInfo, self).toterminal(tw)
super().toterminal(tw)
class ReprTraceback(TerminalRepr):
@@ -959,7 +910,6 @@ class ReprEntry(TerminalRepr):
for line in self.lines:
red = line.startswith("E ")
tw.line(line, bold=True, red=red)
# tw.line("")
return
if self.reprfuncargs:
self.reprfuncargs.toterminal(tw)
@@ -975,7 +925,9 @@ class ReprEntry(TerminalRepr):
self.reprfileloc.toterminal(tw)
def __str__(self):
return "%s\n%s\n%s" % ("\n".join(self.lines), self.reprlocals, self.reprfileloc)
return "{}\n{}\n{}".format(
"\n".join(self.lines), self.reprlocals, self.reprfileloc
)
class ReprFileLocation(TerminalRepr):
@@ -992,7 +944,7 @@ class ReprFileLocation(TerminalRepr):
if i != -1:
msg = msg[:i]
tw.write(self.path, bold=True, red=True)
tw.line(":%s: %s" % (self.lineno, msg))
tw.line(":{}: {}".format(self.lineno, msg))
class ReprLocals(TerminalRepr):
@@ -1012,7 +964,7 @@ class ReprFuncArgs(TerminalRepr):
if self.args:
linesofar = ""
for name, value in self.args:
ns = "%s = %s" % (safe_str(name), safe_str(value))
ns = "{} = {}".format(name, value)
if len(ns) + len(linesofar) + 2 > tw.fullwidth:
if linesofar:
tw.line(linesofar)
@@ -1044,23 +996,6 @@ def getrawcode(obj, trycall=True):
return obj
if PY35: # RecursionError introduced in 3.5
def is_recursion_error(excinfo):
return excinfo.errisinstance(RecursionError) # noqa
else:
def is_recursion_error(excinfo):
if not excinfo.errisinstance(RuntimeError):
return False
try:
return "maximum recursion depth exceeded" in str(excinfo.value)
except UnicodeError:
return False
# relative paths that we use to filter traceback entries from appearing to the user;
# see filter_traceback
# note: if we need to add more paths than what we have now we should probably use a list

View File

@@ -1,7 +1,3 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import ast
import inspect
import linecache
@@ -13,10 +9,9 @@ from ast import PyCF_ONLY_AST as _AST_FLAG
from bisect import bisect_right
import py
import six
class Source(object):
class Source:
""" an immutable object holding a source code fragment,
possibly deindenting it.
"""
@@ -33,7 +28,7 @@ class Source(object):
partlines = part.lines
elif isinstance(part, (tuple, list)):
partlines = [x.rstrip("\n") for x in part]
elif isinstance(part, six.string_types):
elif isinstance(part, str):
partlines = part.split("\n")
else:
partlines = getsource(part, deindent=de).lines
@@ -203,7 +198,9 @@ def compile_(source, filename=None, mode="exec", flags=0, dont_inherit=0):
def getfslineno(obj):
""" Return source location (path, lineno) for the given object.
If the source cannot be determined return ("", -1)
If the source cannot be determined return ("", -1).
The line number is 0-based.
"""
from .code import Code

View File

@@ -1,6 +1,20 @@
import sys
import pprint
import reprlib
from six.moves import reprlib
def _call_and_format_exception(call, x, *args):
try:
# Try the vanilla repr and make sure that the result is a string
return call(x, *args)
except Exception as exc:
exc_name = type(exc).__name__
try:
exc_info = str(exc)
except Exception:
exc_info = "unknown"
return '<[{}("{}") raised in repr()] {} object at 0x{:x}>'.format(
exc_name, exc_info, x.__class__.__name__, id(x)
)
class SafeRepr(reprlib.Repr):
@@ -15,11 +29,11 @@ class SafeRepr(reprlib.Repr):
# Strictly speaking wrong on narrow builds
def repr(u):
if "'" not in u:
return u"'%s'" % u
return "'%s'" % u
elif '"' not in u:
return u'"%s"' % u
return '"%s"' % u
else:
return u"'%s'" % u.replace("'", r"\'")
return "'%s'" % u.replace("'", r"\'")
s = repr(x[: self.maxstring])
if len(s) > self.maxstring:
@@ -33,28 +47,20 @@ class SafeRepr(reprlib.Repr):
return self._callhelper(repr, x)
def _callhelper(self, call, x, *args):
try:
# Try the vanilla repr and make sure that the result is a string
s = call(x, *args)
except Exception:
cls, e, tb = sys.exc_info()
exc_name = getattr(cls, "__name__", "unknown")
try:
exc_info = str(e)
except Exception:
exc_info = "unknown"
return '<[%s("%s") raised in repr()] %s object at 0x%x>' % (
exc_name,
exc_info,
x.__class__.__name__,
id(x),
)
else:
if len(s) > self.maxsize:
i = max(0, (self.maxsize - 3) // 2)
j = max(0, self.maxsize - 3 - i)
s = s[:i] + "..." + s[len(s) - j :]
return s
s = _call_and_format_exception(call, x, *args)
if len(s) > self.maxsize:
i = max(0, (self.maxsize - 3) // 2)
j = max(0, self.maxsize - 3 - i)
s = s[:i] + "..." + s[len(s) - j :]
return s
def safeformat(obj):
"""return a pretty printed string for the given object.
Failing __repr__ functions of user instances will be represented
with a short exception info.
"""
return _call_and_format_exception(pprint.pformat, obj)
def saferepr(obj, maxsize=240):

View File

@@ -1,14 +1,8 @@
"""
support for presenting detailed information in failing assertions.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import sys
import six
from _pytest.assertion import rewrite
from _pytest.assertion import truncate
from _pytest.assertion import util
@@ -29,6 +23,13 @@ def pytest_addoption(parser):
test modules on import to provide assert
expression information.""",
)
parser.addini(
"enable_assertion_pass_hook",
type="bool",
default=False,
help="Enables the pytest_assertion_pass hook."
"Make sure to delete any previously generated pyc cache files.",
)
def register_assert_rewrite(*names):
@@ -55,14 +56,14 @@ def register_assert_rewrite(*names):
importhook.mark_rewrite(*names)
class DummyRewriteHook(object):
class DummyRewriteHook:
"""A no-op import hook for when rewriting is disabled."""
def mark_rewrite(self, *names):
pass
class AssertionState(object):
class AssertionState:
"""State for the assertion plugin."""
def __init__(self, config, mode):
@@ -73,10 +74,6 @@ class AssertionState(object):
def install_importhook(config):
"""Try to install the rewrite hook, raise SystemError if it fails."""
# Jython has an AST bug that make the assertion rewriting hook malfunction.
if sys.platform.startswith("java"):
raise SystemError("rewrite not supported")
config._assertstate = AssertionState(config, "rewrite")
config._assertstate.hook = hook = rewrite.AssertionRewritingHook(config)
sys.meta_path.insert(0, hook)
@@ -102,7 +99,7 @@ def pytest_collection(session):
def pytest_runtest_setup(item):
"""Setup the pytest_assertrepr_compare hook
"""Setup the pytest_assertrepr_compare and pytest_assertion_pass hooks
The newinterpret and rewrite modules will use util._reprcompare if
it exists to use custom reporting via the
@@ -132,16 +129,26 @@ def pytest_runtest_setup(item):
if new_expl:
new_expl = truncate.truncate_if_required(new_expl, item)
new_expl = [line.replace("\n", "\\n") for line in new_expl]
res = six.text_type("\n~").join(new_expl)
res = "\n~".join(new_expl)
if item.config.getvalue("assertmode") == "rewrite":
res = res.replace("%", "%%")
return res
util._reprcompare = callbinrepr
if item.ihook.pytest_assertion_pass.get_hookimpls():
def call_assertion_pass_hook(lineno, expl, orig):
item.ihook.pytest_assertion_pass(
item=item, lineno=lineno, orig=orig, expl=expl
)
util._assertion_pass = call_assertion_pass_hook
def pytest_runtest_teardown(item):
util._reprcompare = None
util._assertion_pass = None
def pytest_sessionfinish(session):

File diff suppressed because it is too large Load Diff

View File

@@ -4,15 +4,8 @@ Utilities for truncating assertion output.
Current default behaviour is to truncate assertion explanations at
~8 terminal lines, unless running in "-vv" mode or running on CI.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import os
import six
DEFAULT_MAX_LINES = 8
DEFAULT_MAX_CHARS = 8 * 80
USAGE_MSG = "use '-vv' to show"
@@ -76,7 +69,7 @@ def _truncate_explanation(input_lines, max_lines=None, max_chars=None):
else:
msg += " ({} lines hidden)".format(truncated_line_count)
msg += ", {}".format(USAGE_MSG)
truncated_explanation.extend([six.text_type(""), six.text_type(msg)])
truncated_explanation.extend(["", str(msg)])
return truncated_explanation

View File

@@ -1,14 +1,9 @@
"""Utilities for assertion debugging"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import pprint
import six
from collections.abc import Sequence
import _pytest._code
from ..compat import Sequence
from _pytest import outcomes
from _pytest._io.saferepr import saferepr
# The _reprcompare attribute on the util module is used by the new assertion
@@ -17,14 +12,9 @@ from _pytest._io.saferepr import saferepr
# DebugInterpreter.
_reprcompare = None
# the re-encoding is needed for python2 repr
# with non-ascii characters (see issue 877 and 1379)
def ecu(s):
if isinstance(s, bytes):
return s.decode("UTF-8", "replace")
else:
return s
# Works similarly as _reprcompare attribute. Is populated with the hook call
# when pytest_runtest_setup is called.
_assertion_pass = None
def format_explanation(explanation):
@@ -37,10 +27,10 @@ def format_explanation(explanation):
for when one explanation needs to span multiple lines, e.g. when
displaying diffs.
"""
explanation = ecu(explanation)
explanation = explanation
lines = _split_explanation(explanation)
result = _format_lines(lines)
return u"\n".join(result)
return "\n".join(result)
def _split_explanation(explanation):
@@ -50,7 +40,7 @@ def _split_explanation(explanation):
Any other newlines will be escaped and appear in the line as the
literal '\n' characters.
"""
raw_lines = (explanation or u"").split("\n")
raw_lines = (explanation or "").split("\n")
lines = [raw_lines[0]]
for values in raw_lines[1:]:
if values and values[0] in ["{", "}", "~", ">"]:
@@ -75,13 +65,13 @@ def _format_lines(lines):
for line in lines[1:]:
if line.startswith("{"):
if stackcnt[-1]:
s = u"and "
s = "and "
else:
s = u"where "
s = "where "
stack.append(len(result))
stackcnt[-1] += 1
stackcnt.append(0)
result.append(u" +" + u" " * (len(stack) - 1) + s + line[1:])
result.append(" +" + " " * (len(stack) - 1) + s + line[1:])
elif line.startswith("}"):
stack.pop()
stackcnt.pop()
@@ -90,16 +80,41 @@ def _format_lines(lines):
assert line[0] in ["~", ">"]
stack[-1] += 1
indent = len(stack) if line.startswith("~") else len(stack) - 1
result.append(u" " * indent + line[1:])
result.append(" " * indent + line[1:])
assert len(stack) == 1
return result
# Provide basestring in python3
try:
basestring = basestring
except NameError:
basestring = str
def issequence(x):
return isinstance(x, Sequence) and not isinstance(x, str)
def istext(x):
return isinstance(x, str)
def isdict(x):
return isinstance(x, dict)
def isset(x):
return isinstance(x, (set, frozenset))
def isdatacls(obj):
return getattr(obj, "__dataclass_fields__", None) is not None
def isattrs(obj):
return getattr(obj, "__attrs_attrs__", None) is not None
def isiterable(obj):
try:
iter(obj)
return not istext(obj)
except TypeError:
return False
def assertrepr_compare(config, op, left, right):
@@ -108,32 +123,7 @@ def assertrepr_compare(config, op, left, right):
left_repr = saferepr(left, maxsize=int(width // 2))
right_repr = saferepr(right, maxsize=width - len(left_repr))
summary = u"%s %s %s" % (ecu(left_repr), op, ecu(right_repr))
def issequence(x):
return isinstance(x, Sequence) and not isinstance(x, basestring)
def istext(x):
return isinstance(x, basestring)
def isdict(x):
return isinstance(x, dict)
def isset(x):
return isinstance(x, (set, frozenset))
def isdatacls(obj):
return getattr(obj, "__dataclass_fields__", None) is not None
def isattrs(obj):
return getattr(obj, "__attrs_attrs__", None) is not None
def isiterable(obj):
try:
iter(obj)
return not istext(obj)
except TypeError:
return False
summary = "{} {} {}".format(left_repr, op, right_repr)
verbose = config.getoption("verbose")
explanation = None
@@ -151,7 +141,7 @@ def assertrepr_compare(config, op, left, right):
elif type(left) == type(right) and (isdatacls(left) or isattrs(left)):
type_fn = (isdatacls, isattrs)
explanation = _compare_eq_cls(left, right, verbose, type_fn)
elif verbose:
elif verbose > 0:
explanation = _compare_eq_verbose(left, right)
if isiterable(left) and isiterable(right):
expl = _compare_eq_iterable(left, right, verbose)
@@ -162,11 +152,13 @@ def assertrepr_compare(config, op, left, right):
elif op == "not in":
if istext(left) and istext(right):
explanation = _notin_text(left, right, verbose)
except outcomes.Exit:
raise
except Exception:
explanation = [
u"(pytest_assertion plugin: representation of details failed. "
u"Probably an object has a faulty __repr__.)",
six.text_type(_pytest._code.ExceptionInfo.from_current()),
"(pytest_assertion plugin: representation of details failed. "
"Probably an object has a faulty __repr__.)",
str(_pytest._code.ExceptionInfo.from_current()),
]
if not explanation:
@@ -175,8 +167,8 @@ def assertrepr_compare(config, op, left, right):
return [summary] + explanation
def _diff_text(left, right, verbose=False):
"""Return the explanation for the diff between text or bytes
def _diff_text(left, right, verbose=0):
"""Return the explanation for the diff between text or bytes.
Unless --verbose is used this will skip leading and trailing
characters which are identical to keep the diff minimal.
@@ -193,7 +185,7 @@ def _diff_text(left, right, verbose=False):
This is done using repr() which then needs post-processing to fix the encompassing quotes and un-escape
newlines and carriage returns (#429).
"""
r = six.text_type(repr(binary_text)[1:-1])
r = str(repr(binary_text)[1:-1])
r = r.replace(r"\n", "\n")
r = r.replace(r"\r", "\r")
return r
@@ -202,7 +194,7 @@ def _diff_text(left, right, verbose=False):
left = escape_for_readable_diff(left)
if isinstance(right, bytes):
right = escape_for_readable_diff(right)
if not verbose:
if verbose < 1:
i = 0 # just in case left or right has zero length
for i in range(min(len(left), len(right))):
if left[i] != right[i]:
@@ -210,7 +202,7 @@ def _diff_text(left, right, verbose=False):
if i > 42:
i -= 10 # Provide some context
explanation = [
u"Skipping %s identical leading characters in diff, use -v to show" % i
"Skipping %s identical leading characters in diff, use -v to show" % i
]
left = left[i:]
right = right[i:]
@@ -221,8 +213,8 @@ def _diff_text(left, right, verbose=False):
if i > 42:
i -= 10 # Provide some context
explanation += [
u"Skipping {} identical trailing "
u"characters in diff, use -v to show".format(i)
"Skipping {} identical trailing "
"characters in diff, use -v to show".format(i)
]
left = left[:-i]
right = right[:-i]
@@ -230,7 +222,7 @@ def _diff_text(left, right, verbose=False):
if left.isspace() or right.isspace():
left = repr(str(left))
right = repr(str(right))
explanation += [u"Strings contain only whitespace, escaping them using repr()"]
explanation += ["Strings contain only whitespace, escaping them using repr()"]
explanation += [
line.strip("\n")
for line in ndiff(left.splitlines(keepends), right.splitlines(keepends))
@@ -244,92 +236,129 @@ def _compare_eq_verbose(left, right):
right_lines = repr(right).splitlines(keepends)
explanation = []
explanation += [u"-" + line for line in left_lines]
explanation += [u"+" + line for line in right_lines]
explanation += ["-" + line for line in left_lines]
explanation += ["+" + line for line in right_lines]
return explanation
def _compare_eq_iterable(left, right, verbose=False):
def _compare_eq_iterable(left, right, verbose=0):
if not verbose:
return [u"Use -v to get the full diff"]
return ["Use -v to get the full diff"]
# dynamic import to speedup pytest
import difflib
try:
left_formatting = pprint.pformat(left).splitlines()
right_formatting = pprint.pformat(right).splitlines()
explanation = [u"Full diff:"]
except Exception:
# hack: PrettyPrinter.pformat() in python 2 fails when formatting items that can't be sorted(), ie, calling
# sorted() on a list would raise. See issue #718.
# As a workaround, the full diff is generated by using the repr() string of each item of each container.
left_formatting = sorted(repr(x) for x in left)
right_formatting = sorted(repr(x) for x in right)
explanation = [u"Full diff (fallback to calling repr on each item):"]
left_formatting = pprint.pformat(left).splitlines()
right_formatting = pprint.pformat(right).splitlines()
explanation = ["Full diff:"]
explanation.extend(
line.strip() for line in difflib.ndiff(left_formatting, right_formatting)
)
return explanation
def _compare_eq_sequence(left, right, verbose=False):
def _compare_eq_sequence(left, right, verbose=0):
comparing_bytes = isinstance(left, bytes) and isinstance(right, bytes)
explanation = []
for i in range(min(len(left), len(right))):
len_left = len(left)
len_right = len(right)
for i in range(min(len_left, len_right)):
if left[i] != right[i]:
explanation += [u"At index %s diff: %r != %r" % (i, left[i], right[i])]
if comparing_bytes:
# when comparing bytes, we want to see their ascii representation
# instead of their numeric values (#5260)
# using a slice gives us the ascii representation:
# >>> s = b'foo'
# >>> s[0]
# 102
# >>> s[0:1]
# b'f'
left_value = left[i : i + 1]
right_value = right[i : i + 1]
else:
left_value = left[i]
right_value = right[i]
explanation += [
"At index {} diff: {!r} != {!r}".format(i, left_value, right_value)
]
break
if len(left) > len(right):
explanation += [
u"Left contains more items, first extra item: %s"
% saferepr(left[len(right)])
]
elif len(left) < len(right):
explanation += [
u"Right contains more items, first extra item: %s"
% saferepr(right[len(left)])
]
if comparing_bytes:
# when comparing bytes, it doesn't help to show the "sides contain one or more items"
# longer explanation, so skip it
return explanation
len_diff = len_left - len_right
if len_diff:
if len_diff > 0:
dir_with_more = "Left"
extra = saferepr(left[len_right])
else:
len_diff = 0 - len_diff
dir_with_more = "Right"
extra = saferepr(right[len_left])
if len_diff == 1:
explanation += [
"{} contains one more item: {}".format(dir_with_more, extra)
]
else:
explanation += [
"%s contains %d more items, first extra item: %s"
% (dir_with_more, len_diff, extra)
]
return explanation
def _compare_eq_set(left, right, verbose=False):
def _compare_eq_set(left, right, verbose=0):
explanation = []
diff_left = left - right
diff_right = right - left
if diff_left:
explanation.append(u"Extra items in the left set:")
explanation.append("Extra items in the left set:")
for item in diff_left:
explanation.append(saferepr(item))
if diff_right:
explanation.append(u"Extra items in the right set:")
explanation.append("Extra items in the right set:")
for item in diff_right:
explanation.append(saferepr(item))
return explanation
def _compare_eq_dict(left, right, verbose=False):
def _compare_eq_dict(left, right, verbose=0):
explanation = []
common = set(left).intersection(set(right))
set_left = set(left)
set_right = set(right)
common = set_left.intersection(set_right)
same = {k: left[k] for k in common if left[k] == right[k]}
if same and verbose < 2:
explanation += [u"Omitting %s identical items, use -vv to show" % len(same)]
explanation += ["Omitting %s identical items, use -vv to show" % len(same)]
elif same:
explanation += [u"Common items:"]
explanation += ["Common items:"]
explanation += pprint.pformat(same).splitlines()
diff = {k for k in common if left[k] != right[k]}
if diff:
explanation += [u"Differing items:"]
explanation += ["Differing items:"]
for k in diff:
explanation += [saferepr({k: left[k]}) + " != " + saferepr({k: right[k]})]
extra_left = set(left) - set(right)
if extra_left:
explanation.append(u"Left contains more items:")
extra_left = set_left - set_right
len_extra_left = len(extra_left)
if len_extra_left:
explanation.append(
"Left contains %d more item%s:"
% (len_extra_left, "" if len_extra_left == 1 else "s")
)
explanation.extend(
pprint.pformat({k: left[k] for k in extra_left}).splitlines()
)
extra_right = set(right) - set(left)
if extra_right:
explanation.append(u"Right contains more items:")
extra_right = set_right - set_left
len_extra_right = len(extra_right)
if len_extra_right:
explanation.append(
"Right contains %d more item%s:"
% (len_extra_right, "" if len_extra_right == 1 else "s")
)
explanation.extend(
pprint.pformat({k: right[k] for k in extra_right}).splitlines()
)
@@ -355,33 +384,33 @@ def _compare_eq_cls(left, right, verbose, type_fns):
explanation = []
if same and verbose < 2:
explanation.append(u"Omitting %s identical items, use -vv to show" % len(same))
explanation.append("Omitting %s identical items, use -vv to show" % len(same))
elif same:
explanation += [u"Matching attributes:"]
explanation += ["Matching attributes:"]
explanation += pprint.pformat(same).splitlines()
if diff:
explanation += [u"Differing attributes:"]
explanation += ["Differing attributes:"]
for field in diff:
explanation += [
(u"%s: %r != %r") % (field, getattr(left, field), getattr(right, field))
("%s: %r != %r") % (field, getattr(left, field), getattr(right, field))
]
return explanation
def _notin_text(term, text, verbose=False):
def _notin_text(term, text, verbose=0):
index = text.find(term)
head = text[:index]
tail = text[index + len(term) :]
correct_text = head + tail
diff = _diff_text(correct_text, text, verbose)
newdiff = [u"%s is contained here:" % saferepr(term, maxsize=42)]
newdiff = ["%s is contained here:" % saferepr(term, maxsize=42)]
for line in diff:
if line.startswith(u"Skipping"):
if line.startswith("Skipping"):
continue
if line.startswith(u"- "):
if line.startswith("- "):
continue
if line.startswith(u"+ "):
newdiff.append(u" " + line[2:])
if line.startswith("+ "):
newdiff.append(" " + line[2:])
else:
newdiff.append(line)
return newdiff

View File

@@ -4,25 +4,19 @@ merged implementation of the cache provider
the name cache was not chosen to ensure pluggy automatically
ignores the external pytest-cache
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import json
import os
from collections import OrderedDict
import attr
import py
import six
import pytest
from .compat import _PY2 as PY2
from .pathlib import Path
from .pathlib import resolve_from_str
from .pathlib import rmtree
README_CONTENT = u"""\
README_CONTENT = """\
# pytest cache directory #
This directory contains data from the pytest's cache plugin,
@@ -42,7 +36,7 @@ Signature: 8a477f597d28d172789f06886806bc55
@attr.s
class Cache(object):
class Cache:
_cachedir = attr.ib(repr=False)
_config = attr.ib(repr=False)
@@ -60,10 +54,10 @@ class Cache(object):
def warn(self, fmt, **args):
from _pytest.warnings import _issue_warning_captured
from _pytest.warning_types import PytestWarning
from _pytest.warning_types import PytestCacheWarning
_issue_warning_captured(
PytestWarning(fmt.format(**args) if args else fmt),
PytestCacheWarning(fmt.format(**args) if args else fmt),
self._config.hook,
stacklevel=3,
)
@@ -121,38 +115,34 @@ class Cache(object):
cache_dir_exists_already = True
else:
cache_dir_exists_already = self._cachedir.exists()
path.parent.mkdir(exist_ok=True, parents=True)
path.parent.mkdir(exist_ok=True, parents=True)
except (IOError, OSError):
self.warn("could not create cache path {path}", path=path)
return
if not cache_dir_exists_already:
self._ensure_supporting_files()
try:
f = path.open("wb" if PY2 else "w")
f = path.open("w")
except (IOError, OSError):
self.warn("cache could not write path {path}", path=path)
else:
with f:
json.dump(value, f, indent=2, sort_keys=True)
if not cache_dir_exists_already:
self._ensure_supporting_files()
def _ensure_supporting_files(self):
"""Create supporting files in the cache dir that are not really part of the cache."""
if self._cachedir.is_dir():
readme_path = self._cachedir / "README.md"
if not readme_path.is_file():
readme_path.write_text(README_CONTENT)
readme_path = self._cachedir / "README.md"
readme_path.write_text(README_CONTENT)
gitignore_path = self._cachedir.joinpath(".gitignore")
if not gitignore_path.is_file():
msg = u"# Created by pytest automatically.\n*"
gitignore_path.write_text(msg, encoding="UTF-8")
gitignore_path = self._cachedir.joinpath(".gitignore")
msg = "# Created by pytest automatically.\n*"
gitignore_path.write_text(msg, encoding="UTF-8")
cachedir_tag_path = self._cachedir.joinpath("CACHEDIR.TAG")
if not cachedir_tag_path.is_file():
cachedir_tag_path.write_bytes(CACHEDIR_TAG_CONTENT)
cachedir_tag_path = self._cachedir.joinpath("CACHEDIR.TAG")
cachedir_tag_path.write_bytes(CACHEDIR_TAG_CONTENT)
class LFPlugin(object):
class LFPlugin:
""" Plugin which implements the --lf (run last-failing) option """
def __init__(self, config):
@@ -161,18 +151,37 @@ class LFPlugin(object):
self.active = any(config.getoption(key) for key in active_keys)
self.lastfailed = config.cache.get("cache/lastfailed", {})
self._previously_failed_count = None
self._no_failures_behavior = self.config.getoption("last_failed_no_failures")
self._report_status = None
self._skipped_files = 0 # count skipped files during collection due to --lf
def last_failed_paths(self):
"""Returns a set with all Paths()s of the previously failed nodeids (cached).
"""
try:
return self._last_failed_paths
except AttributeError:
rootpath = Path(self.config.rootdir)
result = {rootpath / nodeid.split("::")[0] for nodeid in self.lastfailed}
result = {x for x in result if x.exists()}
self._last_failed_paths = result
return result
def pytest_ignore_collect(self, path):
"""
Ignore this file path if we are in --lf mode and it is not in the list of
previously failed files.
"""
if self.active and self.config.getoption("lf") and path.isfile():
last_failed_paths = self.last_failed_paths()
if last_failed_paths:
skip_it = Path(path) not in self.last_failed_paths()
if skip_it:
self._skipped_files += 1
return skip_it
def pytest_report_collectionfinish(self):
if self.active and self.config.getoption("verbose") >= 0:
if not self._previously_failed_count:
return None
noun = "failure" if self._previously_failed_count == 1 else "failures"
suffix = " first" if self.config.getoption("failedfirst") else ""
mode = "rerun previous {count} {noun}{suffix}".format(
count=self._previously_failed_count, suffix=suffix, noun=noun
)
return "run-last-failure: %s" % mode
return "run-last-failure: %s" % self._report_status
def pytest_runtest_logreport(self, report):
if (report.when == "call" and report.passed) or report.skipped:
@@ -190,28 +199,51 @@ class LFPlugin(object):
self.lastfailed[report.nodeid] = True
def pytest_collection_modifyitems(self, session, config, items):
if self.active:
if self.lastfailed:
previously_failed = []
previously_passed = []
for item in items:
if item.nodeid in self.lastfailed:
previously_failed.append(item)
else:
previously_passed.append(item)
self._previously_failed_count = len(previously_failed)
if not previously_failed:
# running a subset of all tests with recorded failures outside
# of the set of tests currently executing
return
if not self.active:
return
if self.lastfailed:
previously_failed = []
previously_passed = []
for item in items:
if item.nodeid in self.lastfailed:
previously_failed.append(item)
else:
previously_passed.append(item)
self._previously_failed_count = len(previously_failed)
if not previously_failed:
# Running a subset of all tests with recorded failures
# only outside of it.
self._report_status = "%d known failures not in selected tests" % (
len(self.lastfailed),
)
else:
if self.config.getoption("lf"):
items[:] = previously_failed
config.hook.pytest_deselected(items=previously_passed)
else:
else: # --failedfirst
items[:] = previously_failed + previously_passed
elif self._no_failures_behavior == "none":
noun = "failure" if self._previously_failed_count == 1 else "failures"
suffix = " first" if self.config.getoption("failedfirst") else ""
self._report_status = "rerun previous {count} {noun}{suffix}".format(
count=self._previously_failed_count, suffix=suffix, noun=noun
)
if self._skipped_files > 0:
files_noun = "file" if self._skipped_files == 1 else "files"
self._report_status += " (skipped {files} {files_noun})".format(
files=self._skipped_files, files_noun=files_noun
)
else:
self._report_status = "no previously failed tests, "
if self.config.getoption("last_failed_no_failures") == "none":
self._report_status += "deselecting all items."
config.hook.pytest_deselected(items=items)
items[:] = []
else:
self._report_status += "not deselecting items."
def pytest_sessionfinish(self, session):
config = self.config
@@ -223,7 +255,7 @@ class LFPlugin(object):
config.cache.set("cache/lastfailed", self.lastfailed)
class NFPlugin(object):
class NFPlugin:
""" Plugin which implements the --nf (run new-first) option """
def __init__(self, config):
@@ -242,8 +274,8 @@ class NFPlugin(object):
other_items[item.nodeid] = item
items[:] = self._get_increasing_order(
six.itervalues(new_items)
) + self._get_increasing_order(six.itervalues(other_items))
new_items.values()
) + self._get_increasing_order(other_items.values())
self.cached_nodeids = [x.nodeid for x in items if isinstance(x, pytest.Item)]
def _get_increasing_order(self, items):
@@ -286,9 +318,13 @@ def pytest_addoption(parser):
)
group.addoption(
"--cache-show",
action="store_true",
action="append",
nargs="?",
dest="cacheshow",
help="show cache contents, don't perform collection or tests",
help=(
"show cache contents, don't perform collection or tests. "
"Optional argument: glob (default: '*')."
),
)
group.addoption(
"--cache-clear",
@@ -307,8 +343,7 @@ def pytest_addoption(parser):
dest="last_failed_no_failures",
choices=("all", "none"),
default="all",
help="change the behavior when no test failed in the last run or no "
"information about the last failures was found in the cache",
help="which tests to run with no previously (known) failures.",
)
@@ -344,7 +379,7 @@ def cache(request):
def pytest_report_header(config):
"""Display cachedir with --cache-show and if non-default."""
if config.option.verbose or config.getini("cache_dir") != ".pytest_cache":
if config.option.verbose > 0 or config.getini("cache_dir") != ".pytest_cache":
cachedir = config.cache._cachedir
# TODO: evaluate generating upward relative paths
# starting with .., ../.. if sensible
@@ -364,11 +399,16 @@ def cacheshow(config, session):
if not config.cache._cachedir.is_dir():
tw.line("cache is empty")
return 0
glob = config.option.cacheshow[0]
if glob is None:
glob = "*"
dummy = object()
basedir = config.cache._cachedir
vdir = basedir / "v"
tw.sep("-", "cache values")
for valpath in sorted(x for x in vdir.rglob("*") if x.is_file()):
tw.sep("-", "cache values for %r" % glob)
for valpath in sorted(x for x in vdir.rglob(glob) if x.is_file()):
key = valpath.relative_to(vdir)
val = config.cache.get(key, dummy)
if val is dummy:
@@ -380,8 +420,8 @@ def cacheshow(config, session):
ddir = basedir / "d"
if ddir.is_dir():
contents = sorted(ddir.rglob("*"))
tw.sep("-", "cache directories")
contents = sorted(ddir.rglob(glob))
tw.sep("-", "cache directories for %r" % glob)
for p in contents:
# if p.check(dir=1):
# print("%s/" % p.relto(basedir))

View File

@@ -2,10 +2,6 @@
per-test stdout/stderr capturing mechanism.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import collections
import contextlib
import io
@@ -14,8 +10,6 @@ import sys
from io import UnsupportedOperation
from tempfile import TemporaryFile
import six
import pytest
from _pytest.compat import CaptureIO
@@ -55,13 +49,6 @@ def pytest_load_initial_conftests(early_config, parser, args):
# make sure that capturemanager is properly reset at final shutdown
early_config.add_cleanup(capman.stop_global_capturing)
# make sure logging does not raise exceptions at the end
def silence_logging_at_shutdown():
if "logging" in sys.modules:
sys.modules["logging"].raiseExceptions = False
early_config.add_cleanup(silence_logging_at_shutdown)
# finally trigger conftest loading but while capturing (issue93)
capman.start_global_capturing()
outcome = yield
@@ -72,7 +59,7 @@ def pytest_load_initial_conftests(early_config, parser, args):
sys.stderr.write(err)
class CaptureManager(object):
class CaptureManager:
"""
Capture plugin, manages that the appropriate capture method is enabled/disabled during collection and each
test phase (setup, call, teardown). After each of those points, the captured output is obtained and
@@ -90,6 +77,11 @@ class CaptureManager(object):
self._global_capturing = None
self._current_item = None
def __repr__(self):
return "<CaptureManager _method={!r} _global_capturing={!r} _current_item={!r}>".format(
self._method, self._global_capturing, self._current_item
)
def _getcapture(self, method):
if method == "fd":
return MultiCapture(out=True, err=True, Capture=FDCapture)
@@ -97,8 +89,17 @@ class CaptureManager(object):
return MultiCapture(out=True, err=True, Capture=SysCapture)
elif method == "no":
return MultiCapture(out=False, err=False, in_=False)
else:
raise ValueError("unknown capturing method: %r" % method)
raise ValueError("unknown capturing method: %r" % method) # pragma: no cover
def is_capturing(self):
if self.is_globally_capturing():
return "global"
capture_fixture = getattr(self._current_item, "_capture_fixture", None)
if capture_fixture is not None:
return (
"fixture %s" % self._current_item._capture_fixture.request.fixturename
)
return False
# Global capturing control
@@ -127,6 +128,15 @@ class CaptureManager(object):
if cap is not None:
cap.suspend_capturing(in_=in_)
def suspend(self, in_=False):
# Need to undo local capsys-et-al if it exists before disabling global capture.
self.suspend_fixture(self._current_item)
self.suspend_global_capture(in_)
def resume(self):
self.resume_global_capture()
self.resume_fixture(self._current_item)
def read_global_capture(self):
return self._global_capturing.readouterr()
@@ -160,15 +170,12 @@ class CaptureManager(object):
@contextlib.contextmanager
def global_and_fixture_disabled(self):
"""Context manager to temporarily disables global and current fixture capturing."""
# Need to undo local capsys-et-al if exists before disabling global capture
self.suspend_fixture(self._current_item)
self.suspend_global_capture(in_=False)
"""Context manager to temporarily disable global and current fixture capturing."""
self.suspend()
try:
yield
finally:
self.resume_global_capture()
self.resume_fixture(self._current_item)
self.resume()
@contextlib.contextmanager
def item_capture(self, when, item):
@@ -246,10 +253,11 @@ def _ensure_only_one_capture_fixture(request, name):
@pytest.fixture
def capsys(request):
"""Enable capturing of writes to ``sys.stdout`` and ``sys.stderr`` and make
captured output available via ``capsys.readouterr()`` method calls
which return a ``(out, err)`` namedtuple. ``out`` and ``err`` will be ``text``
objects.
"""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.
"""
_ensure_only_one_capture_fixture(request, "capsys")
with _install_capture_fixture_on_item(request, SysCapture) as fixture:
@@ -258,26 +266,24 @@ def capsys(request):
@pytest.fixture
def capsysbinary(request):
"""Enable capturing of writes to ``sys.stdout`` and ``sys.stderr`` and make
captured output available via ``capsys.readouterr()`` method calls
which return a ``(out, err)`` tuple. ``out`` and ``err`` will be ``bytes``
objects.
"""Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``.
The captured output is made available via ``capsysbinary.readouterr()``
method calls, which return a ``(out, err)`` namedtuple.
``out`` and ``err`` will be ``bytes`` objects.
"""
_ensure_only_one_capture_fixture(request, "capsysbinary")
# Currently, the implementation uses the python3 specific `.buffer`
# property of CaptureIO.
if sys.version_info < (3,):
raise request.raiseerror("capsysbinary is only supported on python 3")
with _install_capture_fixture_on_item(request, SysCaptureBinary) as fixture:
yield fixture
@pytest.fixture
def capfd(request):
"""Enable capturing of writes to file descriptors ``1`` and ``2`` and make
captured output available via ``capfd.readouterr()`` method calls
which return a ``(out, err)`` tuple. ``out`` and ``err`` will be ``text``
objects.
"""Enable text capturing of writes to file descriptors ``1`` and ``2``.
The captured output is made available via ``capfd.readouterr()`` method
calls, which return a ``(out, err)`` namedtuple.
``out`` and ``err`` will be ``text`` objects.
"""
_ensure_only_one_capture_fixture(request, "capfd")
if not hasattr(os, "dup"):
@@ -290,10 +296,11 @@ def capfd(request):
@pytest.fixture
def capfdbinary(request):
"""Enable capturing of write to file descriptors 1 and 2 and make
captured output available via ``capfdbinary.readouterr`` method calls
which return a ``(out, err)`` tuple. ``out`` and ``err`` will be
``bytes`` objects.
"""Enable bytes capturing of writes to file descriptors ``1`` and ``2``.
The captured output is made available via ``capfd.readouterr()`` method
calls, which return a ``(out, err)`` namedtuple.
``out`` and ``err`` will be ``byte`` objects.
"""
_ensure_only_one_capture_fixture(request, "capfdbinary")
if not hasattr(os, "dup"):
@@ -315,16 +322,16 @@ def _install_capture_fixture_on_item(request, capture_class):
"""
request.node._capture_fixture = fixture = CaptureFixture(capture_class, request)
capmanager = request.config.pluginmanager.getplugin("capturemanager")
# need to active this fixture right away in case it is being used by another fixture (setup phase)
# if this fixture is being used only by a test function (call phase), then we wouldn't need this
# activation, but it doesn't hurt
# Need to active this fixture right away in case it is being used by another fixture (setup phase).
# If this fixture is being used only by a test function (call phase), then we wouldn't need this
# activation, but it doesn't hurt.
capmanager.activate_fixture(request.node)
yield fixture
fixture.close()
del request.node._capture_fixture
class CaptureFixture(object):
class CaptureFixture:
"""
Object returned by :py:func:`capsys`, :py:func:`capsysbinary`, :py:func:`capfd` and :py:func:`capfdbinary`
fixtures.
@@ -338,8 +345,7 @@ class CaptureFixture(object):
self._captured_err = self.captureclass.EMPTY_BUFFER
def _start(self):
# Start if not started yet
if getattr(self, "_capture", None) is None:
if self._capture is None:
self._capture = MultiCapture(
out=True, err=True, in_=False, Capture=self.captureclass
)
@@ -356,7 +362,7 @@ class CaptureFixture(object):
def readouterr(self):
"""Read and return the captured output so far, resetting the internal buffer.
:return: captured content as a namedtuple with ``out`` and ``err`` string attributes
:return: captured content as a namedtuple with ``out`` and ``err`` string attributes
"""
captured_out, captured_err = self._captured_out, self._captured_err
if self._capture is not None:
@@ -369,11 +375,13 @@ class CaptureFixture(object):
def _suspend(self):
"""Suspends this fixture's own capturing temporarily."""
self._capture.suspend_capturing()
if self._capture is not None:
self._capture.suspend_capturing()
def _resume(self):
"""Resumes this fixture's own capturing temporarily."""
self._capture.resume_capturing()
if self._capture is not None:
self._capture.resume_capturing()
@contextlib.contextmanager
def disabled(self):
@@ -402,7 +410,7 @@ def safe_text_dupfile(f, mode, default_encoding="UTF8"):
return EncodedFile(f, encoding or default_encoding)
class EncodedFile(object):
class EncodedFile:
errors = "strict" # possibly needed by py3 code (issue555)
def __init__(self, buffer, encoding):
@@ -410,8 +418,12 @@ class EncodedFile(object):
self.encoding = encoding
def write(self, obj):
if isinstance(obj, six.text_type):
if isinstance(obj, str):
obj = obj.encode(self.encoding, "replace")
else:
raise TypeError(
"write() argument must be str, not {}".format(type(obj).__name__)
)
self.buffer.write(obj)
def writelines(self, linelist):
@@ -423,6 +435,10 @@ class EncodedFile(object):
"""Ensure that file.name is a string."""
return repr(self.buffer)
@property
def mode(self):
return self.buffer.mode.replace("b", "")
def __getattr__(self, name):
return getattr(object.__getattribute__(self, "buffer"), name)
@@ -430,8 +446,9 @@ class EncodedFile(object):
CaptureResult = collections.namedtuple("CaptureResult", ["out", "err"])
class MultiCapture(object):
class MultiCapture:
out = err = in_ = None
_state = None
def __init__(self, out=True, err=True, in_=True, Capture=None):
if in_:
@@ -441,7 +458,17 @@ class MultiCapture(object):
if err:
self.err = Capture(2)
def __repr__(self):
return "<MultiCapture out={!r} err={!r} in_={!r} _state={!r} _in_suspended={!r}>".format(
self.out,
self.err,
self.in_,
self._state,
getattr(self, "_in_suspended", "<UNSET>"),
)
def start_capturing(self):
self._state = "started"
if self.in_:
self.in_.start()
if self.out:
@@ -459,6 +486,7 @@ class MultiCapture(object):
return out, err
def suspend_capturing(self, in_=False):
self._state = "suspended"
if self.out:
self.out.suspend()
if self.err:
@@ -468,6 +496,7 @@ class MultiCapture(object):
self._in_suspended = True
def resume_capturing(self):
self._state = "resumed"
if self.out:
self.out.resume()
if self.err:
@@ -478,9 +507,9 @@ class MultiCapture(object):
def stop_capturing(self):
""" stop capturing and reset capturing streams """
if hasattr(self, "_reset"):
if self._state == "stopped":
raise ValueError("was already stopped")
self._reset = True
self._state = "stopped"
if self.out:
self.out.done()
if self.err:
@@ -496,18 +525,19 @@ class MultiCapture(object):
)
class NoCapture(object):
class NoCapture:
EMPTY_BUFFER = None
__init__ = start = done = suspend = resume = lambda *args: None
class FDCaptureBinary(object):
class FDCaptureBinary:
"""Capture IO to/from a given os-level filedescriptor.
snap() produces `bytes`
"""
EMPTY_BUFFER = b""
_state = None
def __init__(self, targetfd, tmpfile=None):
self.targetfd = targetfd
@@ -534,7 +564,9 @@ class FDCaptureBinary(object):
self.tmpfile_fd = tmpfile.fileno()
def __repr__(self):
return "<FDCapture %s oldfd=%s>" % (self.targetfd, self.targetfd_save)
return "<FDCapture {} oldfd={} _state={!r}>".format(
self.targetfd, getattr(self, "targetfd_save", None), self._state
)
def start(self):
""" Start capturing on targetfd using memorized tmpfile. """
@@ -544,6 +576,7 @@ class FDCaptureBinary(object):
raise ValueError("saved filedescriptor not valid anymore")
os.dup2(self.tmpfile_fd, self.targetfd)
self.syscapture.start()
self._state = "started"
def snap(self):
self.tmpfile.seek(0)
@@ -559,19 +592,22 @@ class FDCaptureBinary(object):
os.dup2(targetfd_save, self.targetfd)
os.close(targetfd_save)
self.syscapture.done()
_attempt_to_close_capture_file(self.tmpfile)
self.tmpfile.close()
self._state = "done"
def suspend(self):
self.syscapture.suspend()
os.dup2(self.targetfd_save, self.targetfd)
self._state = "suspended"
def resume(self):
self.syscapture.resume()
os.dup2(self.tmpfile_fd, self.targetfd)
self._state = "resumed"
def writeorg(self, data):
""" write to original file descriptor. """
if isinstance(data, six.text_type):
if isinstance(data, str):
data = data.encode("utf8") # XXX use encoding of original stream
os.write(self.targetfd_save, data)
@@ -585,16 +621,17 @@ class FDCapture(FDCaptureBinary):
EMPTY_BUFFER = str()
def snap(self):
res = FDCaptureBinary.snap(self)
res = super().snap()
enc = getattr(self.tmpfile, "encoding", None)
if enc and isinstance(res, bytes):
res = six.text_type(res, enc, "replace")
res = str(res, enc, "replace")
return res
class SysCapture(object):
class SysCapture:
EMPTY_BUFFER = str()
_state = None
def __init__(self, fd, tmpfile=None):
name = patchsysdict[fd]
@@ -607,8 +644,14 @@ class SysCapture(object):
tmpfile = CaptureIO()
self.tmpfile = tmpfile
def __repr__(self):
return "<SysCapture {} _old={!r}, tmpfile={!r} _state={!r}>".format(
self.name, self._old, self.tmpfile, self._state
)
def start(self):
setattr(sys, self.name, self.tmpfile)
self._state = "started"
def snap(self):
res = self.tmpfile.getvalue()
@@ -619,13 +662,16 @@ class SysCapture(object):
def done(self):
setattr(sys, self.name, self._old)
del self._old
_attempt_to_close_capture_file(self.tmpfile)
self.tmpfile.close()
self._state = "done"
def suspend(self):
setattr(sys, self.name, self._old)
self._state = "suspended"
def resume(self):
setattr(sys, self.name, self.tmpfile)
self._state = "resumed"
def writeorg(self, data):
self._old.write(data)
@@ -642,7 +688,7 @@ class SysCaptureBinary(SysCapture):
return res
class DontReadFromInput(six.Iterator):
class DontReadFromInput:
"""Temporary stub class. Ideally when stdin is accessed, the
capturing should be turned off, with possibly all data captured
so far sent to the screen. This should be configurable, though,
@@ -673,10 +719,7 @@ class DontReadFromInput(six.Iterator):
@property
def buffer(self):
if sys.version_info >= (3, 0):
return self
else:
raise AttributeError("redirected stdin has no attribute buffer")
return self
def _colorama_workaround():
@@ -688,13 +731,11 @@ def _colorama_workaround():
first import of colorama while I/O capture is active, colorama will
fail in various ways.
"""
if not sys.platform.startswith("win32"):
return
try:
import colorama # noqa
except ImportError:
pass
if sys.platform.startswith("win32"):
try:
import colorama # noqa: F401
except ImportError:
pass
def _readline_workaround():
@@ -715,13 +756,11 @@ def _readline_workaround():
See https://github.com/pytest-dev/pytest/pull/1281
"""
if not sys.platform.startswith("win32"):
return
try:
import readline # noqa
except ImportError:
pass
if sys.platform.startswith("win32"):
try:
import readline # noqa: F401
except ImportError:
pass
def _py36_windowsconsoleio_workaround(stream):
@@ -776,14 +815,3 @@ def _py36_windowsconsoleio_workaround(stream):
sys.stdin = _reopen_stdio(sys.stdin, "rb")
sys.stdout = _reopen_stdio(sys.stdout, "wb")
sys.stderr = _reopen_stdio(sys.stderr, "wb")
def _attempt_to_close_capture_file(f):
"""Suppress IOError when closing the temporary file used for capturing streams in py27 (#2370)"""
if six.PY2:
try:
f.close()
except IOError:
pass
else:
f.close()

View File

@@ -1,74 +1,35 @@
"""
python version compatibility code
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import codecs
import functools
import inspect
import io
import re
import sys
from contextlib import contextmanager
from inspect import Parameter
from inspect import signature
import attr
import py
import six
from six import text_type
import _pytest
from _pytest._io.saferepr import saferepr
from _pytest.outcomes import fail
from _pytest.outcomes import TEST_OUTCOME
try:
import enum
except ImportError: # pragma: no cover
# Only available in Python 3.4+ or as a backport
enum = None
_PY3 = sys.version_info > (3, 0)
_PY2 = not _PY3
if _PY3:
from inspect import signature, Parameter as Parameter
else:
from funcsigs import signature, Parameter as Parameter
NoneType = type(None)
NOTSET = object()
PY35 = sys.version_info[:2] >= (3, 5)
PY36 = sys.version_info[:2] >= (3, 6)
MODULE_NOT_FOUND_ERROR = "ModuleNotFoundError" if PY36 else "ImportError"
if _PY3:
from collections.abc import MutableMapping as MappingMixin
from collections.abc import Iterable, Mapping, Sequence, Sized
else:
# those raise DeprecationWarnings in Python >=3.7
from collections import MutableMapping as MappingMixin # noqa
from collections import Iterable, Mapping, Sequence, Sized # noqa
if sys.version_info >= (3, 4):
from importlib.util import spec_from_file_location
else:
def spec_from_file_location(*_, **__):
return None
MODULE_NOT_FOUND_ERROR = (
"ModuleNotFoundError" if sys.version_info[:2] >= (3, 6) else "ImportError"
)
def _format_args(func):
return str(signature(func))
isfunction = inspect.isfunction
isclass = inspect.isclass
# used to work around a python2 exception info leak
exc_clear = getattr(sys, "exc_clear", lambda: None)
# The type of re.compile objects is not exposed in Python.
REGEX_TYPE = type(re.compile(""))
@@ -165,11 +126,15 @@ def getfuncargnames(function, is_method=False, cls=None):
return arg_names
@contextmanager
def dummy_context_manager():
"""Context manager that does nothing, useful in situations where you might need an actual context manager or not
depending on some condition. Using this allow to keep the same code"""
yield
if sys.version_info < (3, 7):
@contextmanager
def nullcontext():
yield
else:
from contextlib import nullcontext # noqa
def get_default_arg_names(function):
@@ -184,10 +149,10 @@ def get_default_arg_names(function):
_non_printable_ascii_translate_table = {
i: u"\\x{:02x}".format(i) for i in range(128) if i not in range(32, 127)
i: "\\x{:02x}".format(i) for i in range(128) if i not in range(32, 127)
}
_non_printable_ascii_translate_table.update(
{ord("\t"): u"\\t", ord("\r"): u"\\r", ord("\n"): u"\\n"}
{ord("\t"): "\\t", ord("\r"): "\\r", ord("\n"): "\\n"}
)
@@ -195,75 +160,40 @@ def _translate_non_printable(s):
return s.translate(_non_printable_ascii_translate_table)
if _PY3:
STRING_TYPES = bytes, str
UNICODE_TYPES = six.text_type
STRING_TYPES = bytes, str
if PY35:
def _bytes_to_ascii(val):
return val.decode("ascii", "backslashreplace")
def _bytes_to_ascii(val):
return val.decode("ascii", "backslashreplace")
def ascii_escaped(val):
"""If val is pure ascii, returns it as a str(). Otherwise, escapes
bytes objects into a sequence of escaped bytes:
b'\xc3\xb4\xc5\xd6' -> '\\xc3\\xb4\\xc5\\xd6'
and escapes unicode objects into a sequence of escaped unicode
ids, e.g.:
'4\\nV\\U00043efa\\x0eMXWB\\x1e\\u3028\\u15fd\\xcd\\U0007d944'
note:
the obvious "v.decode('unicode-escape')" will return
valid utf-8 unicode if it finds them in bytes, but we
want to return escaped bytes for any byte, even if they match
a utf-8 string.
"""
if isinstance(val, bytes):
ret = _bytes_to_ascii(val)
else:
def _bytes_to_ascii(val):
if val:
# source: http://goo.gl/bGsnwC
encoded_bytes, _ = codecs.escape_encode(val)
return encoded_bytes.decode("ascii")
else:
# empty bytes crashes codecs.escape_encode (#1087)
return ""
def ascii_escaped(val):
"""If val is pure ascii, returns it as a str(). Otherwise, escapes
bytes objects into a sequence of escaped bytes:
b'\xc3\xb4\xc5\xd6' -> u'\\xc3\\xb4\\xc5\\xd6'
and escapes unicode objects into a sequence of escaped unicode
ids, e.g.:
'4\\nV\\U00043efa\\x0eMXWB\\x1e\\u3028\\u15fd\\xcd\\U0007d944'
note:
the obvious "v.decode('unicode-escape')" will return
valid utf-8 unicode if it finds them in bytes, but we
want to return escaped bytes for any byte, even if they match
a utf-8 string.
"""
if isinstance(val, bytes):
ret = _bytes_to_ascii(val)
else:
ret = val.encode("unicode_escape").decode("ascii")
return _translate_non_printable(ret)
ret = val.encode("unicode_escape").decode("ascii")
return _translate_non_printable(ret)
else:
STRING_TYPES = six.string_types
UNICODE_TYPES = six.text_type
def ascii_escaped(val):
"""In py2 bytes and str are the same type, so return if it's a bytes
object, return it unchanged if it is a full ascii string,
otherwise escape it into its binary form.
If it's a unicode string, change the unicode characters into
unicode escapes.
"""
if isinstance(val, bytes):
try:
ret = val.decode("ascii")
except UnicodeDecodeError:
ret = val.encode("string-escape").decode("ascii")
else:
ret = val.encode("unicode-escape").decode("ascii")
return _translate_non_printable(ret)
class _PytestWrapper(object):
@attr.s
class _PytestWrapper:
"""Dummy wrapper around a function object for internal use only.
Used to correctly unwrap the underlying function object
@@ -271,8 +201,7 @@ class _PytestWrapper(object):
to issue warnings when the fixture function is called directly.
"""
def __init__(self, obj):
self.obj = obj
obj = attr.ib()
def get_real_func(obj):
@@ -352,41 +281,11 @@ def safe_getattr(object, name, default):
def safe_isclass(obj):
"""Ignore any exception via isinstance on Python 3."""
try:
return isclass(obj)
return inspect.isclass(obj)
except Exception:
return False
def _is_unittest_unexpected_success_a_failure():
"""Return if the test suite should fail if an @expectedFailure unittest test PASSES.
From https://docs.python.org/3/library/unittest.html?highlight=unittest#unittest.TestResult.wasSuccessful:
Changed in version 3.4: Returns False if there were any
unexpectedSuccesses from tests marked with the expectedFailure() decorator.
"""
return sys.version_info >= (3, 4)
if _PY3:
def safe_str(v):
"""returns v as string"""
return str(v)
else:
def safe_str(v):
"""returns v as string, converting to ascii if necessary"""
try:
return str(v)
except UnicodeError:
if not isinstance(v, text_type):
v = text_type(v)
errors = "replace"
return v.encode("utf-8", errors)
COLLECT_FAKEMODULE_ATTRIBUTES = (
"Collector",
"Module",
@@ -406,34 +305,19 @@ def _setup_collect_fakemodule():
pytest.collect = ModuleType("pytest.collect")
pytest.collect.__all__ = [] # used for setns
for attr in COLLECT_FAKEMODULE_ATTRIBUTES:
setattr(pytest.collect, attr, getattr(pytest, attr))
for attr_name in COLLECT_FAKEMODULE_ATTRIBUTES:
setattr(pytest.collect, attr_name, getattr(pytest, attr_name))
if _PY2:
# Without this the test_dupfile_on_textio will fail, otherwise CaptureIO could directly inherit from StringIO.
from py.io import TextIO
class CaptureIO(io.TextIOWrapper):
def __init__(self):
super().__init__(io.BytesIO(), encoding="UTF-8", newline="", write_through=True)
class CaptureIO(TextIO):
@property
def encoding(self):
return getattr(self, "_encoding", "UTF-8")
def getvalue(self):
return self.buffer.getvalue().decode("UTF-8")
else:
import io
class CaptureIO(io.TextIOWrapper):
def __init__(self):
super(CaptureIO, self).__init__(
io.BytesIO(), encoding="UTF-8", newline="", write_through=True
)
def getvalue(self):
return self.buffer.getvalue().decode("UTF-8")
class FuncargnamesCompatAttr(object):
class FuncargnamesCompatAttr:
""" helper class so that Metafunc, Function and FixtureRequest
don't need to each define the "funcargnames" compatibility attribute.
"""
@@ -441,17 +325,8 @@ class FuncargnamesCompatAttr(object):
@property
def funcargnames(self):
""" alias attribute for ``fixturenames`` for pre-2.3 compatibility"""
import warnings
from _pytest.deprecated import FUNCARGNAMES
warnings.warn(FUNCARGNAMES, stacklevel=2)
return self.fixturenames
if six.PY2:
def lru_cache(*_, **__):
def dec(fn):
return fn
return dec
else:
from functools import lru_cache # noqa: F401

View File

@@ -1,8 +1,4 @@
""" command line options, ini-file and conftest.py processing. """
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import argparse
import copy
import inspect
@@ -11,10 +7,11 @@ import shlex
import sys
import types
import warnings
from functools import lru_cache
import importlib_metadata
import py
import six
from pkg_resources import parse_version
from packaging.version import Version
from pluggy import HookimplMarker
from pluggy import HookspecMarker
from pluggy import PluginManager
@@ -26,14 +23,11 @@ from .exceptions import PrintHelp
from .exceptions import UsageError
from .findpaths import determine_setup
from .findpaths import exists
from _pytest import deprecated
from _pytest._code import ExceptionInfo
from _pytest._code import filter_traceback
from _pytest.compat import lru_cache
from _pytest.compat import safe_str
from _pytest.outcomes import fail
from _pytest.outcomes import Skipped
from _pytest.warning_types import PytestWarning
from _pytest.warning_types import PytestConfigWarning
hookimpl = HookimplMarker("pytest")
hookspec = HookspecMarker("pytest")
@@ -54,7 +48,7 @@ def main(args=None, plugins=None):
:arg plugins: list of plugin objects to be auto-registered during
initialization.
"""
from _pytest.main import EXIT_USAGEERROR
from _pytest.main import ExitCode
try:
try:
@@ -71,7 +65,7 @@ def main(args=None, plugins=None):
if exc_info.traceback
else exc_info.exconly()
)
formatted_tb = safe_str(exc_repr)
formatted_tb = str(exc_repr)
for line in formatted_tb.splitlines():
tw.line(line.rstrip(), red=True)
return 4
@@ -84,10 +78,10 @@ def main(args=None, plugins=None):
tw = py.io.TerminalWriter(sys.stderr)
for msg in e.args:
tw.line("ERROR: {}\n".format(msg), red=True)
return EXIT_USAGEERROR
return ExitCode.USAGE_ERROR
class cmdline(object): # compatibility namespace
class cmdline: # compatibility namespace
main = staticmethod(main)
@@ -113,13 +107,18 @@ def directory_arg(path, optname):
return path
default_plugins = (
# Plugins that cannot be disabled via "-p no:X" currently.
essential_plugins = ( # fmt: off
"mark",
"main",
"terminal",
"runner",
"python",
"fixtures",
"helpconfig", # Provides -p.
) # fmt: on
default_plugins = essential_plugins + (
"python",
"terminal",
"debugging",
"unittest",
"capture",
@@ -128,7 +127,6 @@ default_plugins = (
"monkeypatch",
"recwarn",
"pastebin",
"helpconfig",
"nose",
"assertion",
"junitxml",
@@ -141,17 +139,23 @@ default_plugins = (
"stepwise",
"warnings",
"logging",
"reports",
"faulthandler",
)
builtin_plugins = set(default_plugins)
builtin_plugins.add("pytester")
def get_config():
def get_config(args=None):
# subsequent calls to main will create a fresh instance
pluginmanager = PytestPluginManager()
config = Config(pluginmanager)
if args is not None:
# Handle any "-p no:plugin" args.
pluginmanager.consider_preparse(args)
for spec in default_plugins:
pluginmanager.import_plugin(spec)
return config
@@ -179,12 +183,12 @@ def _prepareconfig(args=None, plugins=None):
msg = "`args` parameter expected to be a list or tuple of strings, got: {!r} (type: {})"
raise TypeError(msg.format(args, type(args)))
config = get_config()
config = get_config(args)
pluginmanager = config.pluginmanager
try:
if plugins:
for plugin in plugins:
if isinstance(plugin, six.string_types):
if isinstance(plugin, str):
pluginmanager.consider_pluginarg(plugin)
else:
pluginmanager.register(plugin)
@@ -211,7 +215,7 @@ class PytestPluginManager(PluginManager):
"""
def __init__(self):
super(PytestPluginManager, self).__init__("pytest")
super().__init__("pytest")
self._conftest_plugins = set()
# state related to local conftest plugins
@@ -238,16 +242,6 @@ class PytestPluginManager(PluginManager):
# Used to know when we are importing conftests after the pytest_configure stage
self._configured = False
def addhooks(self, module_or_class):
"""
.. deprecated:: 2.8
Use :py:meth:`pluggy.PluginManager.add_hookspecs <PluginManager.add_hookspecs>`
instead.
"""
warnings.warn(deprecated.PLUGIN_MANAGER_ADDHOOKS, stacklevel=2)
return self.add_hookspecs(module_or_class)
def parse_hookimpl_opts(self, plugin, name):
# pytest hooks are always prefixed with pytest_
# so we avoid accessing possibly non-readable attributes
@@ -259,7 +253,7 @@ class PytestPluginManager(PluginManager):
return
method = getattr(plugin, name)
opts = super(PytestPluginManager, self).parse_hookimpl_opts(plugin, name)
opts = super().parse_hookimpl_opts(plugin, name)
# consider only actual functions for hooks (#3775)
if not inspect.isroutine(method):
@@ -274,14 +268,11 @@ class PytestPluginManager(PluginManager):
known_marks = {m.name for m in getattr(method, "pytestmark", [])}
for name in ("tryfirst", "trylast", "optionalhook", "hookwrapper"):
opts.setdefault(name, hasattr(method, name) or name in known_marks)
return opts
def parse_hookspec_opts(self, module_or_class, name):
opts = super(PytestPluginManager, self).parse_hookspec_opts(
module_or_class, name
)
opts = super().parse_hookspec_opts(module_or_class, name)
if opts is None:
method = getattr(module_or_class, name)
@@ -298,9 +289,9 @@ class PytestPluginManager(PluginManager):
return opts
def register(self, plugin, name=None):
if name in ["pytest_catchlog", "pytest_capturelog"]:
if name in _pytest.deprecated.DEPRECATED_EXTERNAL_PLUGINS:
warnings.warn(
PytestWarning(
PytestConfigWarning(
"{} plugin has been merged into the core, "
"please remove it from your requirements.".format(
name.replace("_", "-")
@@ -308,7 +299,7 @@ class PytestPluginManager(PluginManager):
)
)
return
ret = super(PytestPluginManager, self).register(plugin, name)
ret = super().register(plugin, name)
if ret:
self.hook.pytest_plugin_registered.call_historic(
kwargs=dict(plugin=plugin, manager=self)
@@ -393,12 +384,6 @@ class PytestPluginManager(PluginManager):
else:
directory = path
if six.PY2: # py2 is not using lru_cache.
try:
return self._dirpath2confmods[directory]
except KeyError:
pass
# XXX these days we may rather want to use config.rootdir
# and allow users to opt into looking into the rootdir parent
# directories instead of requiring to specify confcutdir
@@ -475,9 +460,12 @@ class PytestPluginManager(PluginManager):
while i < n:
opt = args[i]
i += 1
if isinstance(opt, six.string_types):
if isinstance(opt, str):
if opt == "-p":
parg = args[i]
try:
parg = args[i]
except IndexError:
return
i += 1
elif opt.startswith("-p"):
parg = opt[2:]
@@ -488,6 +476,9 @@ class PytestPluginManager(PluginManager):
def consider_pluginarg(self, arg):
if arg.startswith("no:"):
name = arg[3:]
if name in essential_plugins:
raise UsageError("plugin %s cannot be disabled" % name)
# PR #4304 : remove stepwise if cacheprovider is blocked
if name == "cacheprovider":
self.set_blocked("stepwise")
@@ -497,7 +488,15 @@ class PytestPluginManager(PluginManager):
if not name.startswith("pytest_"):
self.set_blocked("pytest_" + name)
else:
self.import_plugin(arg)
name = arg
# Unblock the plugin. None indicates that it has been blocked.
# There is no interface with pluggy for this.
if self._name2plugin.get(name, -1) is None:
del self._name2plugin[name]
if not name.startswith("pytest_"):
if self._name2plugin.get("pytest_" + name, -1) is None:
del self._name2plugin["pytest_" + name]
self.import_plugin(arg, consider_entry_points=True)
def consider_conftest(self, conftestmodule):
self.register(conftestmodule, name=conftestmodule.__file__)
@@ -513,39 +512,46 @@ class PytestPluginManager(PluginManager):
for import_spec in plugins:
self.import_plugin(import_spec)
def import_plugin(self, modname):
def import_plugin(self, modname, consider_entry_points=False):
"""
Imports a plugin with ``modname``. If ``consider_entry_points`` is True, entry point
names are also considered to find a plugin.
"""
# most often modname refers to builtin modules, e.g. "pytester",
# "terminal" or "capture". Those plugins are registered under their
# basename for historic purposes but must be imported with the
# _pytest prefix.
assert isinstance(modname, six.string_types), (
assert isinstance(modname, str), (
"module name as text required, got %r" % modname
)
modname = str(modname)
if self.is_blocked(modname) or self.get_plugin(modname) is not None:
return
if modname in builtin_plugins:
importspec = "_pytest." + modname
else:
importspec = modname
importspec = "_pytest." + modname if modname in builtin_plugins else modname
self.rewrite_hook.mark_rewrite(importspec)
if consider_entry_points:
loaded = self.load_setuptools_entrypoints("pytest11", name=modname)
if loaded:
return
try:
__import__(importspec)
except ImportError as e:
new_exc_type = ImportError
new_exc_message = 'Error importing plugin "%s": %s' % (
modname,
safe_str(e.args[0]),
new_exc_message = 'Error importing plugin "{}": {}'.format(
modname, str(e.args[0])
)
new_exc = new_exc_type(new_exc_message)
new_exc = ImportError(new_exc_message)
tb = sys.exc_info()[2]
six.reraise(new_exc_type, new_exc, sys.exc_info()[2])
raise new_exc.with_traceback(tb)
except Skipped as e:
from _pytest.warnings import _issue_warning_captured
_issue_warning_captured(
PytestWarning("skipped plugin %r: %s" % (modname, e.msg)),
PytestConfigWarning("skipped plugin {!r}: {}".format(modname, e.msg)),
self.hook,
stacklevel=1,
)
@@ -563,7 +569,7 @@ def _get_plugin_specs_as_list(specs):
empty list is returned.
"""
if specs is not None and not isinstance(specs, types.ModuleType):
if isinstance(specs, six.string_types):
if isinstance(specs, str):
specs = specs.split(",") if specs else []
if not isinstance(specs, (list, tuple)):
raise UsageError(
@@ -581,7 +587,7 @@ def _ensure_removed_sysmodule(modname):
pass
class Notset(object):
class Notset:
def __repr__(self):
return "<NOTSET>"
@@ -601,7 +607,7 @@ def _iter_rewritable_modules(package_files):
yield package_name
class Config(object):
class Config:
""" access to configuration values, pluginmanager and plugin hooks. """
def __init__(self, pluginmanager):
@@ -612,7 +618,7 @@ class Config(object):
_a = FILE_OR_DIR
self._parser = Parser(
usage="%%(prog)s [options] [%s] [%s] [...]" % (_a, _a),
usage="%(prog)s [options] [{}] [{}] [...]".format(_a, _a),
processopt=self._processopt,
)
#: a pluginmanager instance
@@ -675,7 +681,7 @@ class Config(object):
return self
def notify_exception(self, excinfo, option=None):
if option and option.fulltrace:
if option and getattr(option, "fulltrace", False):
style = "long"
else:
style = "native"
@@ -698,7 +704,7 @@ class Config(object):
@classmethod
def fromdictargs(cls, option_dict, args):
""" constructor useable for subprocesses. """
config = get_config()
config = get_config(args)
config.option.__dict__.update(option_dict)
config.parse(args, addopts=False)
for x in config.option.plugins:
@@ -742,7 +748,7 @@ class Config(object):
by the importhook.
"""
ns, unknown_args = self._parser.parse_known_and_unknown_args(args)
mode = ns.assertmode
mode = getattr(ns, "assertmode", "plain")
if mode == "rewrite":
try:
hook = _pytest.assertion.install_importhook(self)
@@ -758,25 +764,17 @@ class Config(object):
modules or packages in the distribution package for
all pytest plugins.
"""
import pkg_resources
self.pluginmanager.rewrite_hook = hook
if os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"):
# We don't autoload from setuptools entry points, no need to continue.
return
# 'RECORD' available for plugins installed normally (pip install)
# 'SOURCES.txt' available for plugins installed in dev mode (pip install -e)
# for installed plugins 'SOURCES.txt' returns an empty list, and vice-versa
# so it shouldn't be an issue
metadata_files = "RECORD", "SOURCES.txt"
package_files = (
entry.split(",")[0]
for entrypoint in pkg_resources.iter_entry_points("pytest11")
for metadata in metadata_files
for entry in entrypoint.dist._get_metadata(metadata)
str(file)
for dist in importlib_metadata.distributions()
if any(ep.group == "pytest11" for ep in dist.entry_points)
for file in dist.files or []
)
for name in _iter_rewritable_modules(package_files):
@@ -834,7 +832,7 @@ class Config(object):
from _pytest.warnings import _issue_warning_captured
_issue_warning_captured(
PytestWarning(
PytestConfigWarning(
"could not load initial conftests: {}".format(e.path)
),
self.hook,
@@ -848,7 +846,7 @@ class Config(object):
minver = self.inicfg.get("minversion", None)
if minver:
if parse_version(minver) > parse_version(pytest.__version__):
if Version(minver) > Version(pytest.__version__):
raise pytest.UsageError(
"%s:%d: requires pytest-%s, actual pytest-%s'"
% (
@@ -908,7 +906,7 @@ class Config(object):
try:
description, type, default = self._parser._inidict[name]
except KeyError:
raise ValueError("unknown configuration value: %r" % (name,))
raise ValueError("unknown configuration value: {!r}".format(name))
value = self._get_override_ini_value(name)
if value is None:
try:
@@ -985,8 +983,8 @@ class Config(object):
if skip:
import pytest
pytest.skip("no %r option found" % (name,))
raise ValueError("no option named %r" % (name,))
pytest.skip("no {!r} option found".format(name))
raise ValueError("no option named {!r}".format(name))
def getvalue(self, name, path=None):
""" (deprecated, use getoption()) """
@@ -1074,4 +1072,4 @@ def _strtobool(val):
elif val in ("n", "no", "f", "false", "off", "0"):
return 0
else:
raise ValueError("invalid truth value %r" % (val,))
raise ValueError("invalid truth value {!r}".format(val))

View File

@@ -1,15 +1,16 @@
import argparse
import sys
import warnings
from gettext import gettext
import py
import six
from _pytest.config.exceptions import UsageError
FILE_OR_DIR = "file_or_dir"
class Parser(object):
class Parser:
""" Parser for command line arguments and ini-file values.
:ivar extra_info: dict of generic param -> value to display in case
@@ -144,12 +145,12 @@ class ArgumentError(Exception):
def __str__(self):
if self.option_id:
return "option %s: %s" % (self.option_id, self.msg)
return "option {}: {}".format(self.option_id, self.msg)
else:
return self.msg
class Argument(object):
class Argument:
"""class that mimics the necessary behaviour of optparse.Option
it's currently a least effort implementation
@@ -178,7 +179,7 @@ class Argument(object):
pass
else:
# this might raise a keyerror as well, don't want to catch that
if isinstance(typ, six.string_types):
if isinstance(typ, str):
if typ == "choice":
warnings.warn(
"`type` argument to addoption() is the string %r."
@@ -281,7 +282,7 @@ class Argument(object):
return "Argument({})".format(", ".join(args))
class OptionGroup(object):
class OptionGroup:
def __init__(self, name, description="", parser=None):
self.name = name
self.description = description
@@ -329,6 +330,7 @@ class MyOptionParser(argparse.ArgumentParser):
usage=parser._usage,
add_help=False,
formatter_class=DropShorterLongHelpFormatter,
allow_abbrev=False,
)
# extra_info is a dict of (param -> value) to display if there's
# an usage error to provide more contextual information to the user
@@ -336,10 +338,10 @@ class MyOptionParser(argparse.ArgumentParser):
def error(self, message):
"""Transform argparse error message into UsageError."""
msg = "%s: error: %s" % (self.prog, message)
msg = "{}: error: {}".format(self.prog, message)
if hasattr(self._parser, "_config_source_hint"):
msg = "%s (%s)" % (msg, self._parser._config_source_hint)
msg = "{} ({})".format(msg, self._parser._config_source_hint)
raise UsageError(self.format_usage() + msg)
@@ -351,11 +353,47 @@ class MyOptionParser(argparse.ArgumentParser):
if arg and arg[0] == "-":
lines = ["unrecognized arguments: %s" % (" ".join(argv))]
for k, v in sorted(self.extra_info.items()):
lines.append(" %s: %s" % (k, v))
lines.append(" {}: {}".format(k, v))
self.error("\n".join(lines))
getattr(args, FILE_OR_DIR).extend(argv)
return args
if sys.version_info[:2] < (3, 8): # pragma: no cover
# Backport of https://github.com/python/cpython/pull/14316 so we can
# disable long --argument abbreviations without breaking short flags.
def _parse_optional(self, arg_string):
if not arg_string:
return None
if not arg_string[0] in self.prefix_chars:
return None
if arg_string in self._option_string_actions:
action = self._option_string_actions[arg_string]
return action, arg_string, None
if len(arg_string) == 1:
return None
if "=" in arg_string:
option_string, explicit_arg = arg_string.split("=", 1)
if option_string in self._option_string_actions:
action = self._option_string_actions[option_string]
return action, option_string, explicit_arg
if self.allow_abbrev or not arg_string.startswith("--"):
option_tuples = self._get_option_tuples(arg_string)
if len(option_tuples) > 1:
msg = gettext(
"ambiguous option: %(option)s could match %(matches)s"
)
options = ", ".join(option for _, option, _ in option_tuples)
self.error(msg % {"option": arg_string, "matches": options})
elif len(option_tuples) == 1:
option_tuple, = option_tuples
return option_tuple
if self._negative_number_matcher.match(arg_string):
if not self._has_negative_number_optionals:
return None
if " " in arg_string:
return None
return None, arg_string, None
class DropShorterLongHelpFormatter(argparse.HelpFormatter):
"""shorten help for long options that differ only in extra hyphens

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