Compare commits

...

560 Commits
3.7.2 ... 3.9.0

Author SHA1 Message Date
Bruno Oliveira
2868c31495 Preparing release version 3.9.0 2018-10-15 20:23:30 +00:00
Bruno Oliveira
39a13d7064 Fix tmp_path example in docs 2018-10-15 20:19:15 +00:00
Bruno Oliveira
e4e4fd1e52 Merge pull request #4158 from nicoddemus/merge-master-into-features
Merge master into features (prepare for 3.9, pt2)
2018-10-15 17:12:08 -03:00
Bruno Oliveira
9646a1cd7a Merge remote-tracking branch 'upstream/master' into merge-master-into-features 2018-10-15 12:55:28 -03:00
Bruno Oliveira
9087ac4010 Merge pull request #4157 from nicoddemus/fix-pr-template-link
Use full link to changelog's README in PR template
2018-10-15 12:50:16 -03:00
Anthony Sottile
093e19a7d9 Merge pull request #4153 from asottile/syntax_warning_filename
Display the filename when encountering `SyntaxWarning`.
2018-10-15 08:40:51 -07:00
Bruno Oliveira
9e867ce864 Use full link to changelog's README in PR template
Fix #4156
2018-10-15 12:19:52 -03:00
Bruno Oliveira
8abf30ad71 Merge pull request #4155 from Tadaboody/Add_a_simple_example_on_how_to_use_pytester_to_the_CONTRIBUTING_guide_4151
Add testdir examples to CONTRIBUTING guide
2018-10-15 08:48:57 -03:00
Bruno Oliveira
ea25eb1ecc Fix linting 2018-10-15 08:15:40 -03:00
Bruno Oliveira
58b6e8616c Merge pull request #4149 from RonnyPfannschmidt/modern-build
modernize packaging for setuptools>30.3
2018-10-15 08:13:47 -03:00
Bruno Oliveira
f129ba617f Improve docs a bit 2018-10-15 08:00:16 -03:00
Tomer Keren
99d957bd3d Check off PR requirements 2018-10-15 11:36:31 +03:00
Tomer Keren
661013c3e9 Add testdir examples to CONTRIBUTING guide
Hopefully Closes: #4151
2018-10-15 11:13:24 +03:00
Ankit Goel
141c51f0cb Merge pull request #4145 from labcodes/4098
Add returncode argument to pytest.exit
2018-10-15 11:04:11 +05:30
Ronny Pfannschmidt
d65c7658d5 changelog 2018-10-15 07:32:38 +02:00
Ronny Pfannschmidt
7855284ef7 move most setuptools parameters over to setup.cfg 2018-10-15 07:30:07 +02:00
Ronny Pfannschmidt
5b0f88712b Merge pull request #4148 from RonnyPfannschmidt/pathlib-cleanup-symlink-is-fine
fix #4135 - handle symlinks in tmp path cleanup
2018-10-15 07:02:13 +02:00
Anthony Sottile
2e42d937dc Display the filename when encountering SyntaxWarning.
```console
$ cd t && rm -rf __pycache__ && pytest t.py -q -c /dev/null; cd ..
.                                                                        [100%]
=============================== warnings summary ===============================
<unknown>:2: DeprecationWarning: invalid escape sequence \.

-- Docs: https://docs.pytest.org/en/latest/warnings.html
1 passed, 1 warnings in 0.01 seconds

```

```console
$ cd t && rm -rf __pycache__ && pytest t.py -q -c /dev/null; cd ..
.                                                                        [100%]
=============================== warnings summary ===============================
/tmp/pytest/t/t.py:2: DeprecationWarning: invalid escape sequence \.
  '\.wat'

-- Docs: https://docs.pytest.org/en/latest/warnings.html
1 passed, 1 warnings in 0.01 seconds
```
2018-10-14 16:11:47 -07:00
Jose Carlos Menezes
27d932e882 Fix order of parameters when raising Exit exception 2018-10-14 18:48:32 -03:00
Jose Carlos Menezes
40091ec2c7 Update pytest.exit docstring 2018-10-14 18:44:53 -03:00
Jose Carlos Menezes
76fb9970c8 Check if returncode is not None before assigning test return code 2018-10-14 18:43:48 -03:00
Jose Carlos Menezes
d32f2c5c14 Change Exit.__init__ params order to keep backward compatibility 2018-10-14 18:42:55 -03:00
Bruno Oliveira
49defa2890 Merge pull request #4144 from nicoddemus/fix-flaky-durations-test
Fix flaky durations test
2018-10-14 18:40:14 -03:00
Bruno Oliveira
fe2dae4885 Merge pull request #4140 from blueyed/ci
Faster CI
2018-10-14 18:39:13 -03:00
Bruno Oliveira
ced62f30ba Attempt to create symlinks even on Windows, skipping when not possible 2018-10-14 18:21:04 -03:00
Jose Carlos Menezes
bbd1cbb0b3 Update changelog to better reading 2018-10-14 18:03:37 -03:00
Jose Carlos Menezes
d4dfd526c1 Update pytest.exit docstring 2018-10-14 18:01:47 -03:00
Ronny Pfannschmidt
d4351ac5a2 modernize packaging for setuptools>30.3 2018-10-14 21:44:32 +02:00
Jose Carlos Menezes
766d2daa06 Update returncode exit test to check exitstatus returrned from test session 2018-10-14 16:41:16 -03:00
Jose Carlos Menezes
836c9f82f1 Set test session exitstatus value from Exit excetion return code 2018-10-14 16:39:43 -03:00
Jose Carlos Menezes
46d6a3fc27 Pass returncode to Error exception when creating instance 2018-10-14 16:36:53 -03:00
Ronny Pfannschmidt
1dfa303b1e fix #4135 - handle symlinks in tmp path cleanup 2018-10-14 21:20:34 +02:00
Bruno Oliveira
6258248865 Merge pull request #4138 from blueyed/cov-pexpect
tox.ini: clean up changedir
2018-10-14 15:41:54 -03:00
Bruno Oliveira
4808145846 test_request_garbage is flaky when running with xdist
Example failure:

https://travis-ci.org/pytest-dev/pytest/jobs/441305926#L545
2018-10-14 15:17:08 -03:00
Jose Carlos Menezes
a0666354dd Update changelog 2018-10-14 12:29:19 -03:00
Jose Carlos Menezes
ce55dcf64c Add test for calling pytest.exit with statuscode
It checks that a SystemError was raised and the SystemError code
is the same as the returncode argument.
2018-10-14 12:29:19 -03:00
Jose Carlos Menezes
d7be039f1b Add returncode argument to pytest.exit
If the argument is not None, it'll raise a SystemExit exception to
cleanly exit pytest.
2018-10-14 12:26:31 -03:00
Bruno Oliveira
7e1fac5f91 Merge pull request #4139 from blueyed/passenv
tox.ini: passenv: COVERAGE_*
2018-10-14 12:24:28 -03:00
Bruno Oliveira
486ded3fca Fix flaky durations test
Unfortunately due to fluctuations in runtime "test_something"
might still appear in the final message.

Example failure:

https://ci.appveyor.com/project/pytestbot/pytest/builds/19494829/job/8lx847u0c78m63wf
2018-10-14 12:22:56 -03:00
Daniel Hahler
0be84cd68b Merge pull request #4141 from blueyed/testpaths-fix
tox.ini: fix testpaths
2018-10-14 17:21:10 +02:00
Daniel Hahler
323c846ce6 tox.ini: fix testpaths
Broken recently in 307fa7a4 (features branch).

[ci skip] (since it apparently is not tested/used there)
2018-10-14 17:05:28 +02:00
Daniel Hahler
3bd9f981a2 tox.ini: clean up changedir 2018-10-14 14:33:28 +02:00
Daniel Hahler
7ded937e19 AppVeyor: use fast_finish
This runs py27, py37 and linting first - simulating the baseline stage
used on Travis.
2018-10-14 14:27:34 +02:00
Daniel Hahler
6d0667f1db CI: run specialized factors in a single job
Given the setup time for jobs, it makes sense to run
*-pexpect,*-trial,*-numpy in a single build job.
2018-10-14 14:27:34 +02:00
Daniel Hahler
7c380b19f3 tox.ini: passenv: COVERAGE_*
This is required to pass through COVERAGE_PROCESS_START etc.
2018-10-14 12:34:17 +02:00
Daniel Hahler
5322f422e3 Merge pull request #4108 from blueyed/realpath
Resolve symlinks for args
2018-10-14 11:30:18 +02:00
Daniel Hahler
c6c326f076 Merge pull request #4137 from blueyed/toxini
tox.ini: pexpect: use posargs; cleanup posargs
2018-10-14 11:29:34 +02:00
Daniel Hahler
d6832a8b56 Merge pull request #4133 from blueyed/pdb-quit
pdb: handle quitting in post_mortem
2018-10-14 11:29:09 +02:00
Daniel Hahler
3bfaa8ab84 Merge pull request #4132 from blueyed/pdb-internal-dupe
Do not print (duplicate) INTERNALERROR with --pdb.
2018-10-14 11:25:35 +02:00
Bruno Oliveira
9fb305b17b Merge pull request #4086 from jeffreyrack/4063-exclude-0-durations
Exclude durations that are 0.00 seconds long.
2018-10-13 22:16:04 -03:00
Bruno Oliveira
e3bf9cede4 Fix linting 2018-10-13 22:13:25 -03:00
Daniel Hahler
4a49715614 tox.ini: pexpect: use posargs; cleanup posargs
- no need for {posargs:testing} really
- remove `-ra`, used with addopts already
2018-10-14 00:08:46 +02:00
Daniel Hahler
86c7dcff68 pdb: handle quitting in post_mortem
`help quit` in pdb says:

> Quit from the debugger. The program being executed is aborted.

But pytest would continue with the next tests, often making it necessary
to kill the pytest process when using `--pdb` and trying to cancel the
tests using `KeyboardInterrupt` / `Ctrl-C`.
2018-10-14 00:05:45 +02:00
Daniel Hahler
7268462b33 Resolve symlinks for args
This fixes running `pytest tests/test_foo.py::test_bar`, where `tests`
is a symlink to `project/app/tests`: previously
`project/app/conftest.py` would be ignored for fixtures then.
2018-10-13 23:59:05 +02:00
Daniel Hahler
448830e656 Do not print INTERNALERROR with --pdb
This gets printed by the terminal reporter already, and currently
results in the same error being displayed twice, e.g. when raising an
`Exception` manually from `pytest.debugging.pytest_exception_interact`.
2018-10-13 23:56:34 +02:00
Bruno Oliveira
3683d92c53 Adjust the 'durations hidden' message 2018-10-13 18:40:32 -03:00
Daniel Hahler
d3d8d53e41 tests: test_pdb: fix print statements 2018-10-13 23:33:52 +02:00
Jeffrey Rackauckas
7a271a91b0 Fix rounding error when displaying durations in non-verbose mode. 2018-10-13 12:55:17 -07:00
Jeffrey Rackauckas
47f5c29002 Update messaging for --durations when not in verbose mode. 2018-10-13 12:51:04 -07:00
Bruno Oliveira
27d2683a02 Merge pull request #4134 from blueyed/minor
Minor: code style / constant
2018-10-13 16:13:02 -03:00
Bruno Oliveira
792f365c14 Merge pull request #4136 from luziferius/patch-1
docs: deprecation.rst: Add missing arguments to example code
2018-10-13 16:12:49 -03:00
Thomas Hess
91b2797498 docs: deprecation.rst: Add missing arguments to code example
In the proposed new style using `@pytest.mark.parametrize`,
the example function signature missed the actual arguments.
Add the missing arguments
2018-10-13 20:33:31 +02:00
Daniel Hahler
c27c8f41a8 Merge pull request #4123 from blueyed/clarify
tests: clarify/document/harden acceptance tests
2018-10-13 18:26:10 +02:00
Daniel Hahler
ee54fb9a6b pytester: use EXIT_INTERRUPTED 2018-10-13 17:06:40 +02:00
Daniel Hahler
10ddc466bf minor: typo and code style 2018-10-13 17:06:40 +02:00
Bruno Oliveira
24c83d725a Merge pull request #4129 from nicoddemus/merge-master-into-features
Merge master into features (prepare for 3.9)
2018-10-13 12:05:54 -03:00
Daniel Hahler
6bf4692c7d acceptance_test: clarify/document/fix tests
Ref: e2e6e31711 (r30863971)
2018-10-13 14:41:17 +02:00
Daniel Hahler
81426c3d19 tests: harden test_cmdline_python_package_symlink 2018-10-13 14:41:12 +02:00
Bruno Oliveira
ed42ada373 Merge pull request #4124 from nicoddemus/traceback-import-error-3332
Improve tracebacks for ImportErrors in conftest
2018-10-13 09:25:10 -03:00
Daniel Hahler
e2667106a2 Merge pull request #4126 from blueyed/testpaths
tox.ini: use testpaths, cleanup other pytest options
2018-10-13 14:10:44 +02:00
Bruno Oliveira
29d5849519 Merge remote-tracking branch 'upstream/master' into merge-master-into-features 2018-10-13 08:50:32 -03:00
Bruno Oliveira
eabf15b626 Merge pull request #4125 from nicoddemus/docs-baseline
Run docs, doctesting and linting in the same job to save some CI time
2018-10-12 21:01:59 -03:00
Bruno Oliveira
2dc619cbf4 Run docs, doctesting and linting in the same environment to save some CI time 2018-10-12 13:35:27 -03:00
Daniel Hahler
307fa7a42a tox.ini: use testpaths, cleanup other pytest options 2018-10-12 17:07:45 +02:00
Bruno Oliveira
ef97121d42 Removed unused ConftestImportFailure.__str__ method 2018-10-12 10:57:13 -03:00
Ankit Goel
d46b6b2bc3 Merge pull request #4121 from labcodes/3713
Update usefixtures documentation
2018-10-12 19:08:40 +05:30
Bruno Oliveira
2cb3534679 Move filter_traceback to _pytest._code 2018-10-12 10:19:50 -03:00
Bruno Oliveira
8e11fe5304 Improve tracebacks for ImportErrors in conftest.py files
Fix #3332
2018-10-12 10:10:55 -03:00
Bruno Oliveira
36dc671843 New ExceptionInfo.getrepr 'chain' parameter to be able to suppress chained exceptions 2018-10-12 10:08:55 -03:00
Jose Carlos Menezes
dbaa9464ba Update usefixtures documentation
Clarifying that it can't be used with fixture functions
2018-10-12 09:45:49 -03:00
Bruno Oliveira
933de16fe4 Merge pull request #3988 from RonnyPfannschmidt/tmpdir-port-pathlib
Tmpdir port pathlib
2018-10-12 08:33:47 -03:00
Bruno Oliveira
e8348a1d12 Merge pull request #4077 from nicoddemus/short-usage-errors
Improve internal error messages
2018-10-12 08:18:07 -03:00
Bruno Oliveira
0f5263cdc3 Merge pull request #4109 from njonesu/master
Fix multiple string literals on a line #4093
2018-10-11 15:50:08 -03:00
Ronny Pfannschmidt
4736b2bdfb address review comments 2018-10-11 20:48:30 +02:00
Anthony Sottile
8ecdd4e9ff Merge pull request #4104 from asottile/deprecated_call_match
Implement pytest.deprecated_call with pytest.warns
2018-10-11 08:20:13 -07:00
Daniel Hahler
b3940666a7 Merge pull request #4103 from blueyed/conftest
conftest: optimize _getconftestmodules
2018-10-11 13:52:48 +02:00
Bruno Oliveira
e20987ce82 Merge pull request #4110 from blueyed/pdb
tests: fixes for pdbpp
2018-10-11 08:43:24 -03:00
Ronny Pfannschmidt
584051aa90 extend docs with basics about tmp_path and tmp_path_facotry 2018-10-11 10:33:59 +02:00
Ronny Pfannschmidt
16e2737da3 implement tmp_path_factory and deprecate pytest.ensuretemp as intended 2018-10-11 09:41:37 +02:00
Ronny Pfannschmidt
36c2a101cb add missing docstring 2018-10-11 07:15:09 +02:00
Ronny Pfannschmidt
ebd597b2fd use the constant for lock timeouts 2018-10-11 07:15:09 +02:00
Ronny Pfannschmidt
94829c391b make tmpdir env cleanup idempotent 2018-10-11 07:15:09 +02:00
Ronny Pfannschmidt
b82d6f7a0b pytester: use per test tmproot 2018-10-11 07:15:09 +02:00
Ronny Pfannschmidt
4a436b5470 resolve in code review commments 2018-10-11 07:15:09 +02:00
Ronny Pfannschmidt
ad6f63edda add changelog 2018-10-11 07:15:09 +02:00
Ronny Pfannschmidt
3036914097 sort out rmtree expectations 2018-10-11 07:15:09 +02:00
Ronny Pfannschmidt
2831cb9ab5 unify paths.py and pathlib.py 2018-10-11 07:15:09 +02:00
Ronny Pfannschmidt
00716177b4 fix missed Path import 2018-10-11 07:15:09 +02:00
Ronny Pfannschmidt
85cc9b8f12 move all the things into _pytest.pathlib 2018-10-11 07:15:08 +02:00
Ronny Pfannschmidt
fed4f73a61 ignore rmtree errors 2018-10-11 07:15:08 +02:00
Ronny Pfannschmidt
d76fa59b35 fix lock timeouts for good this time 2018-10-11 07:15:08 +02:00
Ronny Pfannschmidt
2532dc1dbb fix up lock consideration argument 2018-10-11 07:15:08 +02:00
Ronny Pfannschmidt
642cd86dd1 shape up removal and lock destruction 2018-10-11 07:15:08 +02:00
Ronny Pfannschmidt
b3a5b0ebe1 remove path from exposure 2018-10-11 07:15:08 +02:00
Ronny Pfannschmidt
8b4a29357e fix typo 2018-10-11 07:15:08 +02:00
Ronny Pfannschmidt
ab3637d486 implement cleanup for unlocked folders 2018-10-11 07:15:08 +02:00
Ronny Pfannschmidt
66a690928c bring in purepath and fix an assertion 2018-10-11 07:15:08 +02:00
Ronny Pfannschmidt
8e00280fc1 fix linting 2018-10-11 07:15:08 +02:00
Ronny Pfannschmidt
d053cdfbbb factor out max and iterate on locks and cleanups 2018-10-11 07:15:08 +02:00
Ronny Pfannschmidt
2e39fd89d1 add python27 support by using reduce instead of max 2018-10-11 07:15:08 +02:00
Ronny Pfannschmidt
b48e23d54c port interals of tmpdir to a basic pathlib implementation
this is still lacking locking and cleanup of the folders
2018-10-11 07:15:08 +02:00
Bruno Oliveira
c9a85b0e78 Fix linting 2018-10-10 19:54:39 -03:00
Bruno Oliveira
bf265a424d Minor adjustments found during code review 2018-10-10 19:35:49 -03:00
Bruno Oliveira
5436e42990 Use pytest.fail(..., pytrace=False) when treating user errors
This prevents an enormous and often useless stack trace from showing
to end users.

Fix #3867
Fix #2293
2018-10-10 19:16:53 -03:00
Bruno Oliveira
67f40e18a7 Use attr.s(repr=False) because we customize MarkInfo's repr 2018-10-10 19:13:34 -03:00
Daniel Hahler
52ff1eaf37 _getconftestmodules: optimize 2018-10-10 21:30:33 +02:00
Daniel Hahler
602e74c2a7 Merge pull request #4107 from blueyed/__tracebackhide__
pytester: fix __tracebackhide__ for {re_,fn}match_lines
2018-10-10 20:55:32 +02:00
Daniel Hahler
be511c1a05 tests: add missing expect before sendeof for pdbpp
With pdb++ this additional `expect` is required, otherwise `sendeof()`
will block forever.
2018-10-10 20:50:49 +02:00
Daniel Hahler
f36f9d2698 tests: fix/clarify expect for Pdb
`expect()` expects an regular expression, so "Pdb" is equivalent to
"(Pdb)".

But instead of escaping the parenthesis this patch removes them, to
allow for matching "(Pdb++)", too.
2018-10-10 20:50:49 +02:00
Niklas JQ
d1322570dd Fix issue 4093 2018-10-10 19:45:07 +02:00
Daniel Hahler
4c9015c3b1 Merge pull request #4105 from blueyed/repr
Fix trailing whitespace in FixtureDef.__repr__
2018-10-10 19:28:32 +02:00
Niklas JQ
c14a23d4e4 Fix #4093: multiple string literals on a line 2018-10-10 19:28:31 +02:00
Daniel Hahler
b8fc3e569a pytester: fix __tracebackhide__ for {re_,fn}match_lines 2018-10-10 19:27:43 +02:00
Anthony Sottile
e0f6fce9e9 In python2, display previously warned warnings 2018-10-10 09:37:21 -07:00
Daniel Hahler
d93de6cc67 Fix trailing whitespace in FixtureDef.__repr__ 2018-10-10 18:14:56 +02:00
Anthony Sottile
aeb92accb2 Implement pytest.deprecated_call with pytest.warns 2018-10-10 08:03:23 -07:00
Anthony Sottile
943bbdd8ce Merge pull request #4100 from nicoddemus/merge-master-into-features
Merge master into features
2018-10-10 07:35:09 -07:00
Bruno Oliveira
9a3836a0cf Merge remote-tracking branch 'upstream/master' into merge-master-into-features 2018-10-09 19:34:04 -03:00
Anthony Sottile
4b164d947d Merge pull request #4099 from asottile/re-enable_linting_windows
re-enable linting tox env on windows
2018-10-09 15:16:08 -07:00
Anthony Sottile
11a07211b6 re-enable linting tox env on windows 2018-10-09 13:01:51 -07:00
Anthony Sottile
28a3f0fcb9 Revert "Merge pull request #4094 from pytest-dev/asottile/asottile-patch-1"
This reverts commit c55d641963, reversing
changes made to 8393fdd51d.
2018-10-09 12:50:03 -07:00
Anthony Sottile
b8c30aab2b Merge pull request #4097 from asottile/improve_doc_plugin_load
Fix formatting for plugin loading in conftest
2018-10-09 10:23:04 -07:00
Daniel Hahler
b8958168f5 Merge pull request #4092 from altendky/4073-altendky-subprocessing_timeout-take_2
Correct timing in test_pytester.test_testdir_run_with_timeout()
2018-10-09 18:46:58 +02:00
Anthony Sottile
aaaae0b232 Fix formatting for plugin loading in conftest 2018-10-09 09:30:56 -07:00
Anthony Sottile
c55d641963 Merge pull request #4094 from pytest-dev/asottile/asottile-patch-1
Temporarily disable `linting` env in appveyor
2018-10-08 22:55:34 -07:00
Anthony Sottile
4ec85f934c Temporarily disable linting env in appveyor 2018-10-08 22:54:32 -07:00
Ronny Pfannschmidt
8393fdd51d Merge pull request #4089 from asottile/upgrade_hooks
Upgrade pre-commit hooks
2018-10-09 07:43:28 +02:00
Kyle Altendorf
4071c8a4a8 Correct timing in test_pytester.test_testdir_run_with_timeout() 2018-10-08 21:03:42 -04:00
Anthony Sottile
e8c10d4a98 Merge pull request #4090 from asottile/faster_tests
Improve performance of ~3 of the slowest tests
2018-10-08 17:07:10 -07:00
Anthony Sottile
a86035625c Increase recursion limit (broke xdist tests) 2018-10-08 12:57:45 -07:00
Anthony Sottile
4f631440be Use RuntimeError for py27 + py34 compat 2018-10-08 11:47:06 -07:00
Anthony Sottile
3901569f26 Improve performance of ~3 of the slowest tests 2018-10-08 11:12:55 -07:00
Anthony Sottile
689b856cb7 Merge pull request #4088 from Sup3rGeo/bugfix/announcements-name-victor-maryama
Renamed Victor to full name.
2018-10-08 10:16:20 -07:00
Anthony Sottile
65545d8fb2 Manual fixups of black formatting 2018-10-08 10:12:42 -07:00
Anthony Sottile
1caf6d5907 Upgrade pre-commit hooks 2018-10-08 10:10:46 -07:00
Victor Maryama
55871c68a4 Renamed Victor to full name. 2018-10-08 18:43:50 +02:00
Jeffrey Rackauckas
fc11b81005 Exclude durations that are 0.00 seconds long. 2018-10-07 19:19:48 -07:00
Kyle Altendorf
a6fb4c8268 Merge pull request #4078 from altendky/4073-altendky-subprocessing_timeout
Add timeout for Testdir.runpytest_subprocess() and Testdir.run()
2018-10-07 18:07:06 -04:00
Kyle Altendorf
48dcc67274 Increase timeout in test_testdir_run_with_timeout to decrease false failures 2018-10-06 22:02:33 -04:00
Kyle Altendorf
ccaec8d360 __tracebackhide__ = True 2018-10-06 21:57:03 -04:00
Ronny Pfannschmidt
66609665f2 Merge pull request #4076 from nicoddemus/unittest-param-fixture-msg
Improve error message when TestCase functions use a parametrized fixture
2018-10-06 07:43:57 +02:00
Kyle Altendorf
4b36f9aa64 Tidy timeout checking 2018-10-05 16:46:24 -04:00
Kyle Altendorf
5ecbb0acba Correct new changelog to have newline at end 2018-10-05 10:22:07 -04:00
Kyle Altendorf
20902deb75 Add Kyle Altendorf to AUTHORS 2018-10-05 10:02:59 -04:00
Kyle Altendorf
ed5556bdac Add to docstrings 2018-10-05 10:02:59 -04:00
Kyle Altendorf
ee64f1fb9f Add changelog file for 4073 2018-10-05 10:02:59 -04:00
Kyle Altendorf
8e0e862c84 Stretch out the time assertion for slow AppVeyor 2018-10-05 01:38:01 -04:00
Kyle Altendorf
42422a7f62 Throw away arbitrary args to runpytest_subprocess() 2018-10-05 00:30:25 -04:00
Kyle Altendorf
f3a173b736 Revert "Use signal.alarm() for py2 timeout"
This reverts commit 900cef6397.
2018-10-05 00:05:46 -04:00
Kyle Altendorf
5c38a5160d Slight diff tidy 2018-10-04 23:33:38 -04:00
Kyle Altendorf
dcf9eb0104 Raise an exception on unexpected kwargs 2018-10-04 23:27:01 -04:00
Kyle Altendorf
dd225e1b9d Tidy getting of timeout from kwargs 2018-10-04 23:15:30 -04:00
Kyle Altendorf
900cef6397 Use signal.alarm() for py2 timeout 2018-10-04 23:11:26 -04:00
Kyle Altendorf
0d095fc978 Up timeout to 1 second for test 2018-10-04 23:09:07 -04:00
Kyle Altendorf
dcd635ba0c Correct timeout to check every so often 2018-10-04 23:08:57 -04:00
Kyle Altendorf
33f0338eeb kill and wait for subprocess before raising TimeoutExpired 2018-10-04 22:52:51 -04:00
Kyle Altendorf
d5e5433553 Add descriptive message for timeout 2018-10-04 21:43:41 -04:00
Kyle Altendorf
d2906950ce monotonic.monotonic() -> time.time() 2018-10-04 21:26:08 -04:00
Bruno Oliveira
fe7050ba00 Fix lint 2018-10-04 18:45:30 -03:00
Bruno Oliveira
a1208f5631 Merge pull request #4075 from nicoddemus/dynamic-fixturenames
Fix request.fixturenames to return fixtures created dynamically
2018-10-04 09:21:23 -03:00
Kyle Altendorf
870a93c37b Actually construct TimeoutExpired 2018-10-04 01:02:58 -04:00
Kyle Altendorf
96b2ae6654 Initial pass at timeout for subprocessing pytest
pytest-dev/pytest#4073
2018-10-03 23:56:57 -04:00
Anthony Sottile
b098292352 Merge pull request #4069 from asottile/deindent_4066
Fix source reindenting by using `textwrap.dedent` directly.
2018-10-03 16:11:51 -07:00
Bruno Oliveira
212937eb3e Improve error message when TestCase functions use a parametrized fixture
Fix #2535
2018-10-03 19:43:46 -03:00
Bruno Oliveira
70c7273640 Fix request.fixturenames to return fixtures created dynamically
Fix #3057
2018-10-03 18:50:14 -03:00
Anthony Sottile
e5ab62b1b6 Remove (unused) offset= parameter from deindent() 2018-10-03 13:33:10 -07:00
Anthony Sottile
b8b9e8d41c Remove duplicate test (tested above) 2018-10-03 10:01:06 -07:00
Bruno Oliveira
e712adc226 Merge pull request #4072 from pecey/tmp/issue-4064
Fixes #4064 by correcting the documentation for unit-tests
2018-10-03 12:26:35 -03:00
Palash Chatterjee
f9ac60807c Fixes #4064 by correcting the documentation for unit-tests 2018-10-03 18:11:09 +05:30
Bruno Oliveira
3f03625a5d Merge pull request #4070 from jeffreyrack/4058-update-fixture-docs
Update documentation to indicate a fixture can be invoked more than once in it's scope.
2018-10-03 08:39:41 -03:00
Ronny Pfannschmidt
29d3faed66 Merge pull request #4068 from nicoddemus/merge-master-into-features
Merge master into features
2018-10-03 08:30:39 +02:00
Jeffrey Rackauckas
c5dec6056f Fix wording in fixtures doc. 2018-10-02 21:17:53 -07:00
Jeffrey Rackauckas
642847c079 Update documentation on fixture being invoked more than once in a scope. 2018-10-02 20:53:15 -07:00
Anthony Sottile
f102ccc8f0 Fix source reindenting by using textwrap.dedent directly. 2018-10-02 16:13:35 -07:00
Bruno Oliveira
20f93ae8fa Merge pull request #4067 from beneyal/master
Add pytest.freeze_includes() to reference
2018-10-02 20:02:37 -03:00
Bruno Oliveira
1101a20408 Merge remote-tracking branch 'upstream/master' into merge-master-into-features 2018-10-02 18:13:54 -03:00
Anthony Sottile
df435fa8bd Merge pull request #4065 from asottile/release-3.8.2
Preparing release version 3.8.2
2018-10-02 13:53:52 -07:00
Ben Eyal
a5269b26e0 Add anchor for "Freezing pytest" 2018-10-02 17:54:59 +03:00
Anthony Sottile
d3673c7429 Preparing release version 3.8.2 2018-10-02 07:46:02 -07:00
Ben Eyal
5010e02eda Add pytest.freeze_includes to reference 2018-10-02 17:39:08 +03:00
Bruno Oliveira
25fe3706a4 Merge pull request #4056 from nicoddemus/unicode-vars
Ensure Monkeypatch setenv and delenv use bytes keys in Python 2
2018-10-02 07:56:32 -03:00
Bruno Oliveira
1a323fbd3c Show a warning when non-str is given to Monkeypatch.setenv 2018-10-01 20:07:07 -03:00
Bruno Oliveira
9d971d33be Hide internal pytest.warns traceback 2018-10-01 18:45:08 -03:00
Bruno Oliveira
bc009a8582 Fix test to comply with pypy 6.0 2018-10-01 18:37:27 -03:00
Bruno Oliveira
5e7d427df1 Merge pull request #4060 from nicoddemus/4051-changelog
Improve changelog for #4051
2018-10-01 18:12:52 -03:00
Bruno Oliveira
dd59ed3b18 Merge pull request #4059 from labcodes/documenting-scopes
Add possible values for fixture scope to docs
2018-10-01 17:25:42 -03:00
Bruno Oliveira
20d0f0e56b Improve changelog for #4051 2018-10-01 17:12:05 -03:00
Bruno Oliveira
d24a7e6c5a Issue warning if Monkeypatch.setenv/delenv receive non-strings in Python 2
Fixes the bug described in:

	https://github.com/tox-dev/tox/pull/1025#discussion_r221273830

Which is more evident when using `unicode_literals`.
2018-10-01 17:05:30 -03:00
Jose Carlos Menezes
4dc73bda45 Update changelog 2018-10-01 14:41:30 -03:00
Jose Carlos Menezes
732cc2687d Add possible values for fixture scope to docs 2018-10-01 14:31:28 -03:00
Ronny Pfannschmidt
5d2d64c190 Merge pull request #4057 from jeffreyrack/4051-improve-error-messaging
Improve error messaging when invalid syntax is passed to the -m option
2018-10-01 07:16:59 +02:00
Jeffrey Rackauckas
7a6d16c1eb Adding .rst to end of changelog fragment filename. 2018-09-30 18:45:49 -07:00
Jeffrey Rackauckas
c2179c3127 Improve error messaging when invalid syntax is passed to the -m option 2018-09-30 18:34:56 -07:00
Bruno Oliveira
d8d7f73e1c Merge pull request #4045 from nicoddemus/root-conftest-warning-workaround-4039
Do not issue non-top-level conftest warning when --pyargs is used
2018-09-27 09:26:11 -03:00
Ronny Pfannschmidt
3c23b5b010 Merge pull request #4037 from nicoddemus/deprecate-item-warnings-captured-hook
Document that item parameter of pytest_warning_captured hook is deprecated
2018-09-27 13:20:26 +02:00
Bruno Oliveira
783019a8e6 Rename 4040.trivial.rst to 4040.bugfix.rst 2018-09-27 08:08:03 -03:00
Bruno Oliveira
d2fc7ca6e0 Merge pull request #4041 from Zac-HD/user-properties-type
Ensure user_properties is a list
2018-09-27 08:04:30 -03:00
Bruno Oliveira
2d06927a06 Merge pull request #4040 from ods/summary_passes_less_noisy
Exclude empty reports for passed tests
2018-09-27 08:01:58 -03:00
Denis Otkidach
44d29d887e Changelog entry on excluding empty reports for passed tests 2018-09-26 19:02:35 +03:00
Bruno Oliveira
32c5a113e2 Do not issue non-top-level conftest warning when --pyargs is used
Fix #4039
2018-09-26 12:02:08 -03:00
Bruno Oliveira
ba5630e0f8 Simplify test_pytest_plugins_in_non_top_level_conftest_deprecated 2018-09-26 10:49:14 -03:00
Denis Otkidach
808df48ee8 Test for excluding empty reports for passed tests 2018-09-26 16:44:00 +03:00
Zac-HD
a089a9577e Succinct definition of user_properties 2018-09-26 22:40:11 +10:00
Zac-HD
6be2136f20 Ensure user_properties is a list 2018-09-26 22:04:50 +10:00
Denis Otkidach
f9ab81a493 Exclude empty reports for passed tests 2018-09-26 11:55:39 +03:00
Bruno Oliveira
1636522563 Document that item parameter of pytest_warning_captured hook is deprecated
Our policy is to not deprecate features during bugfix releases, but in this
case I believe it makes sense as we are only documenting it as deprecated,
without issuing warnings which might potentially break test suites.

This will get the word out that hook implementers should not use this parameter
at all.

Fix #4036
2018-09-25 17:38:22 -03:00
Bruno Oliveira
b1fbb2ab92 Merge pull request #4032 from hjwp/patch-1
add documentation of register_assert_rewrite
2018-09-25 08:43:29 -03:00
Daniel Hahler
e85edf5212 Merge pull request #4029 from nicoddemus/warnings-example-deprecation-docs
Add an example on how to update config.warn calls
2018-09-25 13:25:21 +02:00
Bruno Oliveira
b03bad5dbb Fix linting 2018-09-25 08:12:55 -03:00
Harry Percival
19ec300b2a fix rst syntax again 2018-09-25 06:58:47 +01:00
Harry Percival
11442f2ad7 fix rst syntax thing 2018-09-25 06:57:33 +01:00
Harry Percival
97748b6605 mention conftest.py as a good place to do it. 2018-09-25 06:55:28 +01:00
Harry Percival
2b762337bd add documentation of register_assert_rewrite
wip
2018-09-25 06:49:50 +01:00
Bruno Oliveira
9899b8f1fb Add an example on how to update config.warn calls
As commented in https://github.com/pytest-dev/pytest-cov/pull/230#pullrequestreview-157958838
2018-09-23 22:42:09 -03:00
Anthony Sottile
4474beeb82 Typo fix [ci skip] 2018-09-23 18:07:28 -07:00
Ronny Pfannschmidt
5d8467bedc Merge pull request #4023 from nicoddemus/deprecated-3.9
Add 3.9 deprecated features to deprecations.rst
2018-09-23 18:21:32 +02:00
Ronny Pfannschmidt
eca3e781b6 Merge pull request #4022 from iwanb/fix_reload
Fix #3539: reload module with assertion rewrite import hook
2018-09-23 18:17:56 +02:00
Bruno Oliveira
0d04aa7c59 Add 3.9 deprecated features to deprecations.rst 2018-09-23 09:26:12 -03:00
iwanb
c61ff31ffa Fix #3539: reload module with assertion rewrite import hook 2018-09-23 13:05:55 +02:00
Ronny Pfannschmidt
e03a19f88d Merge pull request #4021 from nicoddemus/merge-master-into-features
Merge master into features
2018-09-23 11:57:05 +02:00
Bruno Oliveira
fcc5b6d604 Add "deprecation" to possible changelog entries in pre-commit 2018-09-22 18:43:22 -03:00
Bruno Oliveira
42afce27b3 Merge pull request #4019 from nicoddemus/deprecation-warnings-4013
Show deprecation warnings even if filters are customized
2018-09-22 18:17:07 -03:00
Bruno Oliveira
56d0b5a7e2 Merge remote-tracking branch 'upstream/master' into merge-master-into-features 2018-09-22 18:14:36 -03:00
Ankit Goel
ec57cbf82d Merge pull request #4020 from crazymerlyn/release-3.8.1
Preparing release version 3.8.1
2018-09-23 00:56:27 +05:30
CrazyMerlyn
3f6a46c2a4 Preparing release version 3.8.1 2018-09-22 16:34:06 +00:00
Bruno Oliveira
c30184709d Show deprecation warnings even if filters are customized
Fix #4013
2018-09-22 10:25:57 -03:00
Bruno Oliveira
4ba3cb25b0 Merge pull request #4016 from williamjamir/patch-1
Include Python 3.7 on getting started doc
2018-09-21 20:02:25 -03:00
William Jamir Silva
650c458df9 Include Python 3.7 on getting started doc
Close #3932
2018-09-21 19:18:51 -03:00
Ankit Goel
58aa4f91f5 Merge pull request #4012 from maxalbert/fix-docstring-typo
Fix typo in docstring
2018-09-21 22:07:57 +05:30
Maximilian Albert
9b382ed16c Fix typo in docstring 2018-09-21 17:11:15 +01:00
Ronny Pfannschmidt
f02dbaf97f Merge pull request #4010 from nicoddemus/package-len-error-3749
Fix 'Package has no len()' error during collection
2018-09-21 07:40:20 +02:00
Bruno Oliveira
41f6ea13ce Fix 'Package has no len()' error during collection
Fix #3749
2018-09-20 17:53:21 -03:00
Ronny Pfannschmidt
f6eb39df33 Merge pull request #4001 from asottile/fix_bytes_repr_text_mix_python_2
Fix UnicodeDecodeError in assertion with mixed non-ascii bytes repr + text
2018-09-20 22:16:37 +02:00
Bruno Oliveira
7a5e11bbcf Merge pull request #3997 from nicoddemus/deprecation-docs
Introduce deprecations page
2018-09-20 08:22:51 -03:00
Anthony Sottile
7122fa5613 Fix UnicodeDecodeError in assertion with mixed non-ascii bytes repr + text 2018-09-19 20:24:00 -07:00
Bruno Oliveira
7aff81739e Merge pull request #3998 from blueyed/logging-del
logging: del item.catch_log_handler only in teardown
2018-09-19 20:07:19 -03:00
Bruno Oliveira
27772f67c0 Merge pull request #3987 from nicoddemus/fix-find-scope-3941
Fix scope determination with indirect parameters
2018-09-19 20:06:45 -03:00
Bruno Oliveira
10b3b2dc68 Merge pull request #3990 from blueyed/coverage-source
coverage: use modules
2018-09-19 19:55:11 -03:00
Bruno Oliveira
c2841542af Introduce deprecations page
fix #3996
2018-09-19 19:51:29 -03:00
wim glenn
1f28096587 Merge pull request #4003 from pytest-dev/wimglenn-patch-1
seems this subdir is now .pytest_cache not .cache
2018-09-19 14:49:38 -05:00
wim glenn
e86b01e831 Update customize.rst 2018-09-19 14:06:36 -05:00
Thomas Hisch
83802d1494 Merge pull request #3986 from thisch/fb3964
Add support for logging in collection-phase
2018-09-19 20:47:19 +02:00
Bruno Oliveira
d1fa8ae08e Improve CHANGELOG entry 2018-09-19 12:52:10 -03:00
Ronny Pfannschmidt
29dac03314 Merge pull request #3980 from nicoddemus/rewrite-cwd-changed
Fix assertion rewriter crash if cwd changes mid-testing
2018-09-19 17:45:26 +02:00
Daniel Hahler
e7eb7e799b logging: del item.catch_log_handler only in teardown
Without this caplog.record_tuples etc is not available anymore when using
`--pdb`.
2018-09-19 17:17:47 +02:00
Thomas Hisch
18cc74b8d0 Remove useless comment 2018-09-19 15:18:37 +02:00
Bruno Oliveira
7f48f552c1 Fix linting 2018-09-19 10:18:05 -03:00
Bruno Oliveira
1e2e65f0fa Add references to the relevant Python issues 2018-09-19 08:20:23 -03:00
Daniel Hahler
28c9cc7321 coverage: use modules for source
This should increase coverage for subprocesses, where previously
`source` paths were used only from the config file, but not the initial
`--source` argument.
2018-09-19 07:46:19 +02:00
Bruno Oliveira
ccb90b5c46 [WIP] Introduce deprecations page
fix #3996
2018-09-18 20:56:40 -03:00
Thomas Hisch
048342817b Add testcase for logging to file 2018-09-18 21:47:42 +02:00
Thomas Hisch
d1a3aa7b2b Update 3964.rst 2018-09-18 21:31:20 +02:00
Thomas Hisch
e967d4587a Add support for logging in collection-phase
The logging plugin does not output log messages generated during the
collection-phase when live-logging is enabled. This fixes this.

Fixes #3964
2018-09-18 20:18:24 +02:00
Bruno Oliveira
a79dc12f1e Merge pull request #3970 from sambarluc/raise_on_empty_parameterset
Raise exception if parametrize collects an empty parameter set
2018-09-18 08:11:34 -03:00
Bruno Oliveira
37d2469266 Use a PurePath instance to do matching against patterns in assertion rewrite
This way we don't need to have real file system path, which prevents the
original #3973 bug.
2018-09-17 20:29:09 -03:00
Bruno Oliveira
1df6d28080 Fix assertion rewriter crash if cwd changes mid-testing
Unfortunately we need to get a `py.path.local` object to perform the fnmatch
operation, it is different from the standard `fnmatch` module because it
implements its own custom logic. So we need to use `py.path` to perform
the fnmatch for backward compatibility reasons.

Ideally we should be able to use a "pure path" in `pathlib` terms (a path
not bound to the file system), but we don't have those in pylib.

Fix #3973
2018-09-17 20:04:44 -03:00
Daniel Hahler
03eaad376b tox: coverage factor: combine and report 2018-09-17 14:44:47 +02:00
Daniel Hahler
739f9a4a4b Travis: use codecov-bash
Faster to install and will retry uploads on connection errors.
2018-09-17 14:44:47 +02:00
Daniel Hahler
93224f8cf9 tox: remove obsolete whitelist_externals 2018-09-17 14:44:47 +02:00
Ronny Pfannschmidt
bb57186dd4 Merge pull request #3983 from nicoddemus/update-backward-policy
Review backward compatibility policy
2018-09-16 20:43:55 +02:00
Bruno Oliveira
2803eb9fbb Merge pull request #3984 from nicoddemus/fix-docs-formatting
Fix rendering of the ini example for python_files
2018-09-15 12:05:59 -03:00
Andrea Cimatoribus
913c07e414 Add changelog file and new author 2018-09-15 09:18:03 +02:00
Andrea Cimatoribus
4a9f468aac Update documentation 2018-09-15 09:18:03 +02:00
Andrea Cimatoribus
05155e4db0 Fail at parametrize option for empty parameter set
Optionally raise an exception when parametrize collects no arguments.
Provide the name of the test causing the failure in the exception
message.

See: #3849
2018-09-15 09:18:03 +02:00
Ronny Pfannschmidt
f53eff93db Merge pull request #3982 from nicoddemus/ignore-pytest-cache
Ignore pytest cache
2018-09-15 07:21:45 +02:00
Ronny Pfannschmidt
bceaede198 Merge pull request #3978 from nicoddemus/warn-yield-and-compat-properties
Actually deprecate long standing features
2018-09-15 07:19:59 +02:00
Bruno Oliveira
86a14d007d Fix scope determination with indirect parameters
Fix #3941
2018-09-14 21:33:59 -03:00
Bruno Oliveira
a4dd6ee3ce Fix linting 2018-09-14 17:31:01 -03:00
Bruno Oliveira
130cf7e0db Fix rendering of the ini example for python_files
Also added an example using one pattern per line
2018-09-14 17:27:25 -03:00
Bruno Oliveira
cbb41f1ae2 Ignore Sphinx's .doctrees folder 2018-09-14 16:00:35 -03:00
Bruno Oliveira
fa78da3c03 Update backward compatibility policy with new practices 2018-09-14 15:58:22 -03:00
Bruno Oliveira
ae8f3695b5 Move UnformattedWarning to _pytest.warning_types 2018-09-14 15:31:20 -03:00
Bruno Oliveira
87ddb2dbd5 Change flaky test_request_garbage to provide more debug information
This test fails *very* rarely when running in xdist.
2018-09-14 15:25:45 -03:00
Bruno Oliveira
9aa6b0903b .pytest_cache is now automatically ignored by Git 2018-09-14 15:16:40 -03:00
Bruno Oliveira
da6830f19b Introduce UnformattedWarning to keep warning types and messages in _pytest.deprecated 2018-09-14 14:49:05 -03:00
Bruno Oliveira
32ee0b9c88 Move warning messages to _pytest.deprecated 2018-09-13 15:56:50 -03:00
Bruno Oliveira
49800ea134 Merge pull request #3977 from RonnyPfannschmidt/remove-im-func
Remove im_func
2018-09-13 15:46:33 -03:00
Bruno Oliveira
feb8240410 Use self.Function again during collection 2018-09-13 15:44:02 -03:00
Bruno Oliveira
b7dd9154c3 Deprecate custom node types during collection by using special names 2018-09-13 14:55:28 -03:00
Bruno Oliveira
482bd5efd2 Show deprecation warning for cached_setup 2018-09-13 14:25:46 -03:00
Bruno Oliveira
bf074b37a3 Show deprecation warnings for compat properties
Fix #3616
2018-09-13 14:10:30 -03:00
Bruno Oliveira
495a55725b Separate deprecations and removals in the CHANGELOG 2018-09-13 14:02:01 -03:00
Bruno Oliveira
53c9124fc9 Merge pull request #3947 from nicoddemus/warnings-filter-precedence
-W now takes precedence over filters in ini files
2018-09-13 13:23:00 -03:00
Ronny Pfannschmidt
8fe55b1d18 add changelog for fix #3975 2018-09-13 15:40:45 +02:00
Ronny Pfannschmidt
a0ce9a4441 remove the legacy code about im_func and generalize using fix and compat.getimfunc 2018-09-13 15:38:36 +02:00
Daniel Hahler
2cf2dc3d95 Merge pull request #3967 from blueyed/optional-mock
tests: use unittest.mock with py34+
2018-09-13 07:36:42 +02:00
Daniel Hahler
7537e94ddf tests: use unittest.mock with py34+
Fixes https://github.com/pytest-dev/pytest/issues/3965.

Has to work around https://github.com/tox-dev/tox/issues/706.

No coverage for pluggymaster builds is OK though anyway.
2018-09-12 23:21:47 +02:00
Bruno Oliveira
ab40696007 -W now takes precedence over filters in ini files
Fix #3946
2018-09-11 19:00:42 +02:00
Anthony Sottile
2c90b3db9e Merge pull request #3957 from asottile/changelog_files
Improve pre-commit detection for changelog filenames
2018-09-08 10:58:50 -07:00
Anthony Sottile
826adafe2e Improve pre-commit detection for changelog filenames 2018-09-07 09:36:09 -07:00
Anthony Sottile
3dd2933dbd Merge pull request #3948 from nicoddemus/fix-changelog
Amend CHANGELOG with missing #3251
2018-09-07 09:15:09 -07:00
Ronny Pfannschmidt
d12f46caef Merge pull request #3949 from nicoddemus/merge-master-into-features
Merge master into features
2018-09-07 07:58:36 +02:00
Bruno Oliveira
b55351274e Amend CHANGELOG with missing #3251 2018-09-06 18:56:08 -03:00
Bruno Oliveira
c00d934b21 Merge pull request #3933 from nicoddemus/idval-hypothesis-flaky-3707
Use -n auto now that xdist behaves well in Travis and AppVeyor
2018-09-06 15:15:55 -03:00
Bruno Oliveira
6b526cbe6a Merge pull request #3943 from nicoddemus/no-coverage-on-deploy
Disable coverage during deployment stage
2018-09-06 15:15:32 -03:00
Bruno Oliveira
e0539e6ede Merge pull request #3942 from nicoddemus/merge-features-into-master
Merge features into master
2018-09-06 09:35:28 -03:00
Bruno Oliveira
5eb85efa14 Use -n auto now that xdist behaves well in Travis and AppVeyor
This hopefully fixes the flaky test_idval_hypothesis on AppVeyor

Fix #3707
2018-09-06 09:22:13 -03:00
Bruno Oliveira
9ee8d72fd2 Disable coverage during deployment stage
Otherwise it will fail as there's no coverage data to combine/publish
2018-09-06 09:18:47 -03:00
Bruno Oliveira
8c4ca383ca Merge remote-tracking branch 'upstream/features' into merge-features-into-master 2018-09-06 09:15:56 -03:00
Bruno Oliveira
f2a427da25 Merge pull request #3940 from nicoddemus/release-3.8.0
Release 3.8.0
2018-09-06 09:15:00 -03:00
Bruno Oliveira
e0466d0ad8 Merge pull request #3923 from nicoddemus/codecov
Add support for codecov in AppVeyor
2018-09-06 07:02:56 -03:00
Bruno Oliveira
418a66a09f Replace coveralls' badge by codecov's 2018-09-05 22:37:32 -03:00
Bruno Oliveira
5e2bd17d18 White list external "env" used by Travis for coverage 2018-09-05 22:37:32 -03:00
Bruno Oliveira
ec6fca4aa7 Add codecov support to AppVeyor and remove coveralls 2018-09-05 19:50:06 -03:00
Bruno Oliveira
1f20626618 Preparing release version 3.8.0 2018-09-05 21:06:32 +00:00
Bruno Oliveira
69b34f7658 Merge remote-tracking branch 'upstream/master' into release-3.8.0 2018-09-05 18:02:02 -03:00
Bruno Oliveira
531b76a513 Merge pull request #3931 from nicoddemus/internal-warnings
Use standard warnings for internal pytest warnings
2018-09-05 14:05:52 -03:00
Bruno Oliveira
f63c683faa No longer escape regex in pytest.mark.filterwarnings
Fix #3936
2018-09-05 10:20:25 -03:00
Ronny Pfannschmidt
410d5762c0 Merge pull request #3919 from fabioz/master
Improve import performance of assertion rewrite. Fixes #3918.
2018-09-05 14:33:40 +02:00
Bruno Oliveira
ddb308455a Make sure warn is called in test_parameterset_extractfrom 2018-09-05 09:01:29 -03:00
Bruno Oliveira
f42b5019ec Make code_or_warning parameter private for backward-compatibility 2018-09-04 18:53:58 -03:00
Bruno Oliveira
adc9ed85bc Fix test_idval_hypothesis 2018-09-04 18:49:20 -03:00
Bruno Oliveira
4592def14d Improve test_rewarn_functional 2018-09-04 17:02:56 -03:00
Bruno Oliveira
2e0a7cf78d Revert to having just "runtest" as "when" parameter of the pytest_warning_captured hook 2018-09-04 17:01:23 -03:00
Bruno Oliveira
5a52acaa92 Make config no longer optional in parametrize id functions 2018-09-04 16:55:52 -03:00
Bruno Oliveira
6d497f2c77 Fix stacklevel for warning about Metafunc.addcall 2018-09-04 16:50:24 -03:00
Bruno Oliveira
b7560a8808 Keep backward compatibility for code as kw in Node.warn 2018-09-04 16:48:21 -03:00
Bruno Oliveira
d3ca739c00 Use explicit instances when calling warnings.warn_explicit 2018-09-04 16:29:48 -03:00
Bruno Oliveira
3db76ccf3d Fix Cache.warn function to issue a "config" warning 2018-09-04 15:53:17 -03:00
Bruno Oliveira
438f7a1254 Add "setup", "call" and "teardown" values to "when" parameter of pytest_warning_captured hook 2018-09-04 15:53:17 -03:00
Bruno Oliveira
47bf58d69e Make Node.warn support two forms, new and deprecated
As suggested during review, it now accepts two forms:

Node.warn(warning_instance)  (recommended)

Node.warn(code, message)  (deprecated)
2018-09-04 15:53:17 -03:00
Bruno Oliveira
5ef51262f7 Fix reference to PytestWarning in warningsfilter mark 2018-09-04 15:06:14 -03:00
Bruno Oliveira
a054aa4797 Issue assert rewrite warning if tuple >=1 as suggested in review 2018-09-04 14:45:48 -03:00
Bruno Oliveira
f1cfd10c94 Handle cache warnings in tests 2018-09-04 14:44:02 -03:00
Bruno Oliveira
d3f72ca202 Fix linting for warnings.rst 2018-09-04 14:33:41 -03:00
Bruno Oliveira
022c58bf64 Revert pytest_terminal_summary(tryfirst) in warnings module as this breaks tests 2018-09-04 14:26:34 -03:00
Bruno Oliveira
b42518acd5 Change std_warn to receive a single warning instance, addressed review suggestions 2018-09-04 14:20:42 -03:00
Bruno Oliveira
284a2d110f Move warnings import to top level 2018-09-04 13:46:33 -03:00
Bruno Oliveira
9ae0a3cd85 Do not trigger warning about tuples being always True if the tuple has size != 2 2018-09-04 13:41:11 -03:00
Bruno Oliveira
615c671434 Connect string literals 2018-09-04 13:34:05 -03:00
Bruno Oliveira
29bfa5efa4 Merge pull request #3925 from crazymerlyn/fix-exit-code
Fix exit code for command line errors
2018-09-04 11:53:00 -03:00
Bruno Oliveira
016f8f1536 Improve get_fslocation_from_item's docstring 2018-09-04 11:48:11 -03:00
Bruno Oliveira
e9417be9df Add comment about deprecation warnings being shown by default 2018-09-04 11:35:35 -03:00
Bruno Oliveira
c304998ed7 Remove commented out code 2018-09-04 11:35:35 -03:00
Bruno Oliveira
415a62e373 Fix typo in PytestExperimentalApiWarning 2018-09-04 11:35:35 -03:00
Bruno Oliveira
8ce3aeadbf Move PytestExerimentalApiWarning to warning_types 2018-09-04 11:35:35 -03:00
Bruno Oliveira
b818314045 Improve docs for warnings capture and PEP-0506 remarks 2018-09-04 11:35:34 -03:00
Bruno Oliveira
56d414177a Remove nodeid from messages for warnings generated by standard warnings
Standard warnings already contain the proper location, so we don't need
to also print the node id
2018-09-04 11:35:34 -03:00
Bruno Oliveira
0fffa6ba2f Implement hack to issue warnings during config
Once we can capture warnings during the config stage, we can
then get rid of this function

Related to #2891
2018-09-04 11:35:34 -03:00
Bruno Oliveira
60499d221e Add test to ensure that users can suppress internal warnings 2018-09-04 11:35:34 -03:00
Bruno Oliveira
9965ed84da Show deprecation warnings by default if no other filters are configured
Fix #2908
2018-09-04 11:35:34 -03:00
Bruno Oliveira
7e13593452 Add CHANGELOG entries for #2452
Fix #2452
Fix #2684
2018-09-04 11:35:34 -03:00
Bruno Oliveira
208dd3aad1 Add docs for internal warnings and introduce PytestDeprecationWarning
Fix #2477
2018-09-04 11:35:34 -03:00
Bruno Oliveira
19a01c9849 Make PytestWarning and RemovedInPytest4Warning part of the public API 2018-09-04 11:35:34 -03:00
Bruno Oliveira
78ac7d99f5 Deprecate Config.warn and Node.warn, replaced by standard warnings 2018-09-04 11:35:34 -03:00
Bruno Oliveira
0c8dbdcd92 Fix existing tests now that we are using standard warnings 2018-09-04 11:35:34 -03:00
Bruno Oliveira
8e4501ee29 Use std_warn for warning about applying marks directly to parameters 2018-09-04 11:35:34 -03:00
Bruno Oliveira
0100f61b62 Start the laywork to capture standard warnings 2018-09-04 11:35:15 -03:00
Bruno Oliveira
1a9d913ee1 Capture and display warnings during collection
Fix #3251
2018-09-04 11:35:06 -03:00
Bruno Oliveira
51e32cf7cc Remove Python 2.6 specific warning 2018-09-04 11:35:06 -03:00
Bruno Oliveira
3fcc4cdbd5 Make terminal capture pytest_warning_capture
pytest_logwarning is no longer emitted by the warnings plugin,
only ever emitted from .warn() functions in config and item
2018-09-04 11:35:06 -03:00
Bruno Oliveira
ffd47ceefc Implement new pytest_warning_captured hook 2018-09-04 11:35:00 -03:00
Bruno Oliveira
10f21b423a Remove assert for "reprec" because this is no longer set on the pluginmanager
It seems this has no effect since `pluggy` was developed as a separate
library.
2018-09-04 11:35:00 -03:00
Bruno Oliveira
eec7081b8d Make AssertionRewritingrHook use imp_find_module 2018-09-03 10:18:25 -03:00
CrazyMerlyn
b01704cce1 Fix exit code for command line errors
Fixes #3913
2018-09-03 04:16:35 +00:00
Ronny Pfannschmidt
15ede8aab8 Merge pull request #3924 from nicoddemus/enable-pypy
Reenable pypy now that scandir can be installed without a compiler
2018-09-02 17:34:21 +02:00
Bruno Oliveira
f7dc9b9fef Merge pull request #3922 from nicoddemus/skip-xfail-docs-3219
Mention explicitly when pytest.skip and pytest.xfail can be called
2018-09-02 11:40:34 -03:00
Bruno Oliveira
dc13f0b469 Reenable pypy now that scandir can be installed without a compiler
Ref: benhoyt/scandir#105
Ref: #3111
2018-09-02 11:17:06 -03:00
Bruno Oliveira
a13c6a84df Mention explicitly when pytest.skip and pytest.xfail can be called
Fix #3219
2018-09-02 10:42:05 -03:00
Bruno Oliveira
dfa713163a Merge pull request #3921 from nicoddemus/use-constant
Use EXIT_USAGEERROR instead of magic number
2018-09-01 20:09:39 -03:00
Bruno Oliveira
90c00dfd54 Use EXIT_USAGEERROR instead of magic number 2018-09-01 12:03:28 -03:00
Daniel Hahler
f3b9b21996 Merge pull request #3920 from blueyed/branch
tests/CI: enable branch coverage
2018-09-01 16:52:12 +02:00
Bruno Oliveira
885b8a3b4c Fix linting 2018-09-01 11:13:40 -03:00
Bruno Oliveira
4675912d89 Add tests for early rewrite bailout code and handle patterns with subdirectories 2018-09-01 10:59:21 -03:00
Bruno Oliveira
495b44198f Merge pull request #3917 from dhirensr/docs_for_detailed_info
T3566,T3546: added a blurb in usage.rst for usage of flag -r
2018-09-01 10:09:29 -03:00
Bruno Oliveira
8d8e68cf90 Merge pull request #3911 from wimglenn/i18n_width
improve line width estimate
2018-09-01 08:57:44 -03:00
Bruno Oliveira
f3b0caf299 Improve docs for summary report and move it further up in the doc 2018-09-01 08:54:00 -03:00
Bruno Oliveira
75d29acc06 Fix reference to inter-sphinx objects database 2018-09-01 08:48:47 -03:00
Daniel Hahler
cbbb36fc9b tests/CI: enable branch coverage 2018-08-31 19:26:47 +02:00
Fabio Zadrozny
d53e449296 Improve performance of assertion rewriting. Fixes #3918 2018-08-31 12:27:08 -03:00
Ronny Pfannschmidt
01df368d93 Merge pull request #3914 from nicoddemus/merge-master-into-features
Merge master into features
2018-08-31 15:31:00 +02:00
Bruno Oliveira
2256f2f04d Remove test which is no longer required and improve test_lf_and_ff_prints_no_needless_message
* test_lf_and_ff_obey_verbosity is no longer necessary because
  test_lf_and_ff_prints_no_needless_message already checks if the proper messages
  are displayed when -q is used.

* Improve test_lf_and_ff_prints_no_needless_message so we also check that
  the correct message is displayed when there are failures to run
2018-08-31 08:01:55 -03:00
dhirensr
95881c870e T3566,T3546: added a blurb in usage.rst for usage of flag -r 2018-08-31 11:20:15 +05:30
Ronny Pfannschmidt
019e33ee3f Merge pull request #3915 from nicoddemus/quickstart-book-docs
Add pytest Quick Start Guide to the books section in the docs
2018-08-31 07:40:13 +02:00
Bruno Oliveira
19fa01b91d Tweak changelog 2018-08-30 21:17:14 -03:00
Bruno Oliveira
96aad2983b Move code to get width of current line to a function 2018-08-30 21:16:35 -03:00
wim glenn
c18a5b5179 try to be backwards compat 2018-08-30 19:06:20 -05:00
wim glenn
ed4b94a180 add changelog entry 2018-08-30 18:59:58 -05:00
wim glenn
29c5ac71bc improve line width estimate 2018-08-30 18:59:58 -05:00
Bruno Oliveira
84a9f7a263 Add pytest Quick Start Guide to the books section in the docs 2018-08-30 20:18:51 -03:00
Bruno Oliveira
11e591e442 Merge remote-tracking branch 'upstream/master' into merge-master-into-features 2018-08-30 19:55:23 -03:00
Bruno Oliveira
64f00683f2 Merge pull request #3912 from dhirensr/needless_message
Needless message printed with --failed-first and no failed tests #3853
2018-08-30 19:51:49 -03:00
Bruno Oliveira
84a033fd97 Remove extra newline 2018-08-30 19:48:47 -03:00
Bruno Oliveira
0183d46275 Improve CHANGELOG a bit 2018-08-30 19:44:04 -03:00
Bruno Oliveira
9bd4b0a05e Merge pull request #3910 from hoefling/docfix
doc fix: raises accepts tuples instead of lists
2018-08-30 19:41:44 -03:00
Bruno Oliveira
f0e852b4db Merge pull request #2800 from blueyed/coverage
Travis: report coverage with all builds
2018-08-30 19:41:22 -03:00
dhirensr
ade01b1f5b T3853:Added changelog file 2018-08-30 16:03:18 +05:30
dhirensr
3035b2724d T3853:removed needless message printed with --failed-first,--last-failed and no failed tests 2018-08-30 16:01:42 +05:30
hoefling
8c96eea583 doc fix: raises accepts tuples instead of lists (fixes #3907) 2018-08-30 00:19:59 +02:00
Bruno Oliveira
338953a25d Merge pull request #3908 from nicoddemus/optimize-release
Skip the "test" stage when making a release
2018-08-29 18:53:36 -03:00
Anthony Sottile
f1bd46266b Merge pull request #3909 from asottile/update_release
Update release procedure
2018-08-29 14:37:52 -07:00
Anthony Sottile
77cad3c436 Update release procedure [ci skip] 2018-08-29 14:13:08 -07:00
Bruno Oliveira
3ca70692de Skip the "test" stage when making a release
Given that our guidelines demand that the CI have already passed, it seems
wasteful to run all those jobs again for the exact same commit.

As discussed in https://github.com/pytest-dev/pytest/pull/3906#issuecomment-417094481,
this will skip the "test" stage when building a tag for deployment.
2018-08-29 17:54:58 -03:00
Daniel Hahler
417516c378 squash! Travis: report coverage with all builds
doctesting: remove changedir

With coverage 5 we could use COVERAGE_RCFILE to make it find the
.coveragerc, or we could add `--rcfile` to _PYTEST_TOX_COVERAGE_RUN, but
I've thought that this should not be the job that has to test if
`--pyargs` actually works.
2018-08-29 22:53:20 +02:00
Daniel Hahler
f730291e67 Travis: report coverage with all builds
- Skips pypy for coverage, reports only py37 to coveralls
- tox: allow for TOXENV=py37-coverage
- tracks coverage in subprocesses, using coverage-enable-subprocess, and
  parallel=1
- removes usedevelop with doctesting to match `--source` being used with
  coverage
- keep coveralls for now, used with AppVeyor
2018-08-29 22:30:28 +02:00
Anthony Sottile
d76fb8345c Merge pull request #3906 from asottile/release-3.7.4
Preparing release version 3.7.4
2018-08-29 13:03:34 -07:00
Anthony Sottile
aea962dc21 Preparing release version 3.7.4 2018-08-29 08:57:54 -07:00
Bruno Oliveira
4345efaffc Merge pull request #3902 from stevepiercy/fix-pytest.org-links
Fix pytest.org links
2018-08-29 08:20:01 -03:00
Bruno Oliveira
bf47033169 Fix linting 2018-08-28 21:05:34 -03:00
Steve Piercy
37a65684d6 add changelog entry 2018-08-28 14:51:27 -07:00
Steve Piercy
eab5020e24 Fix hostname 2018-08-28 14:45:04 -07:00
Steve Piercy
8ef21f56d3 Fix 404 2018-08-28 14:42:16 -07:00
Steve Piercy
103d980b2d Use https, save a redirect 2018-08-28 14:41:13 -07:00
Steve Piercy
28c3ef1c77 Use https, save a redirect, fix hostname 2018-08-28 14:40:20 -07:00
Steve Piercy
67c3c28877 Use https, save a redirect 2018-08-28 14:39:32 -07:00
Steve Piercy
e040fd20a3 Use https, save a redirect 2018-08-28 14:38:55 -07:00
Steve Piercy
00e0b43010 Use https, save a redirect 2018-08-28 14:36:47 -07:00
Steve Piercy
f19cfbb825 Fix 404 to a somewhat better historical note 2018-08-28 14:35:08 -07:00
Steve Piercy
bde3d1a0cd Use https; save a redirect 2018-08-28 14:34:39 -07:00
Steve Piercy
2e090896d5 Use https 2018-08-28 14:34:22 -07:00
Steve Piercy
b0a32da0b5 Use https; save a redirect 2018-08-28 14:27:11 -07:00
Bruno Oliveira
10c1c7c41a Merge pull request #3895 from nicoddemus/issue-3506
Avoid possible infinite recursion when writing pyc files in assert rewrite
2018-08-28 18:16:10 -03:00
Daniel Hahler
16f452ef98 Merge pull request #3894 from blueyed/baseline
Travis: add baseline stage
2018-08-28 22:19:08 +02:00
Bruno Oliveira
b77e533693 Merge pull request #3893 from jirikuncar/3892-macos
travis: run tests on macOS
2018-08-28 17:06:17 -03:00
Bruno Oliveira
a605ad4d11 Merge pull request #3880 from jeffreyrack/3829-progress_display_mode
#3829 -- Add the ability to show test progress as number of tests completed instead of a percent.
2018-08-28 16:54:14 -03:00
Jeffrey Rackauckas
4b94760c8e Removed spacing in count display. 2018-08-27 20:23:17 -07:00
Bruno Oliveira
82a7ca9615 Avoid possible infinite recursion when writing pyc files in assert rewrite
What happens is that atomic_write on Python 2.7 on Windows will try
to convert the paths to unicode, but this triggers the import of
the encoding module for the file system codec, which in turn triggers
the rewrite, which in turn again tries to import the module, and so on.

This short-circuits the cases where we try to import another file when
writing a pyc file; I don't expect this to affect anything because
the only modules that could be affected are those imported by
atomic_writes.

Fix #3506
2018-08-27 21:29:45 -03:00
Bruno Oliveira
23295e1e98 Fix docs linting 2018-08-27 20:21:08 -03:00
Bruno Oliveira
32575f92c9 set TOXENV in test-macos template otherwise it will inherit "coveralls" 2018-08-27 20:07:51 -03:00
Bruno Oliveira
a260e58020 Drop 3.6 from OS-X to reduce build time 2018-08-27 20:03:12 -03:00
Bruno Oliveira
b2f7e02a02 Reorganize osx environments to avoid repetition as suggested in review 2018-08-27 20:02:16 -03:00
Bruno Oliveira
29e114b463 Try to fix test in MacOS-X 2018-08-27 19:27:51 -03:00
Bruno Oliveira
2a059b1c1b Merge pull request #3885 from nicoddemus/bad-output-classic
Fix bad console output when using console_output_style=classic
2018-08-27 19:07:02 -03:00
Daniel Hahler
cdc72bf5a3 Travis: add baseline stage
Fixes https://github.com/pytest-dev/pytest/issues/3876.
2018-08-27 23:46:24 +02:00
Jiri Kuncar
f786335dbb travis: run tests on macOS
closes #3892
2018-08-27 17:22:27 +02:00
Jiri Kuncar
ab5af524a4 Fix macOS specific code that uses capturemanager.
https://github.com/pytest-dev/pytest/issues/3888#issuecomment-416206606

closes #3888

Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
2018-08-27 16:07:59 +02:00
Bruno Oliveira
9620b167d9 Merge pull request #3887 from asottile/improve_test_code
Improve the coverage of testing/code
2018-08-27 07:20:45 -03:00
Jeffrey Rackauckas
8f4685e024 Move count display style to be part of console_output_style, fixed test progress for count console output style. 2018-08-26 19:21:00 -07:00
Anthony Sottile
10544c4cb8 Merge pull request #3886 from nicoddemus/ff-quiet
Cache now obeys -q when showing summary for --lf and --ff
2018-08-26 18:32:22 -07:00
Anthony Sottile
1e8e17c01e Improve the coverage of testing/code 2018-08-26 16:13:22 -07:00
Bruno Oliveira
80eef29681 Merge pull request #3884 from nicoddemus/merge-master-into-features
Merge master into features
2018-08-26 19:41:30 -03:00
Bruno Oliveira
47bb53f5cb Cache now obeys -q when showing summary for --lf and --ff
Related to #3853
2018-08-26 18:08:19 -03:00
Bruno Oliveira
6991a16edb Fix bad console output when using console_output_style=classic
Fix #3883
2018-08-26 17:12:55 -03:00
Bruno Oliveira
2f2d5861bb Merge remote-tracking branch 'upstream/master' into merge-master-into-features 2018-08-26 16:45:00 -03:00
Bruno Oliveira
a31967431f Merge pull request #3882 from nicoddemus/release-3.7.3
Release 3.7.3
2018-08-26 16:41:40 -03:00
Bruno Oliveira
e74ad4ff9b Fix typo in CHANGELOG 2018-08-26 12:27:02 -03:00
Bruno Oliveira
70bdacf01a Fix collection example docs 2018-08-26 12:58:47 +00:00
Bruno Oliveira
b69f853acb Tweak CHANGELOG for 3.7.3 2018-08-26 09:46:46 -03:00
Bruno Oliveira
c31018d9bc Preparing release version 3.7.3 2018-08-26 12:43:43 +00:00
Bruno Oliveira
7ae23901d3 Merge pull request #3881 from GandalfSaxe/patch-2
Code block: :: missing and 4 spaces instead of 5
2018-08-26 09:22:30 -03:00
Bruno Oliveira
4d19b94347 Merge pull request #3877 from blueyed/codecov-master
tox: coveralls: also report to codecov
2018-08-26 09:19:03 -03:00
Bruno Oliveira
c15b537e3d Merge pull request #3878 from asottile/skip_install_linting
Use skip_install for testing tox env
2018-08-26 09:17:40 -03:00
Bruno Oliveira
2577a6ce8a Merge pull request #3873 from nicoddemus/sys-path-fix
Remove dangerous sys.path manipulations in test_pluginmanager
2018-08-26 08:53:57 -03:00
Bruno Oliveira
dd5f5ca4cb Merge pull request #3872 from nicoddemus/tests-in-init-files
Collect tests from __init__.py files if they match 'python_files'
2018-08-26 08:50:17 -03:00
Gandalf Saxe
508774742e Code block: :: missing and 4 spaces instead of 5
I just noticed the newly committed code block doesn't format as a code block without `::` in the paragraph before. Perhaps doesn't make a difference, but also corrected 5 spaces to 4 which seems standard.
2018-08-26 11:54:08 +02:00
Jeffrey Rackauckas
2a917a582e Removing accidental change to test 2018-08-25 22:21:50 -07:00
Jeffrey Rackauckas
325319dc3b Fixing xdist test to properly configure an ini file. 2018-08-25 22:18:29 -07:00
Jeffrey Rackauckas
dda5e5ea32 Fixing backticks in changelog file. 2018-08-25 21:55:00 -07:00
Jeffrey Rackauckas
5e260c4d34 Fixing changelog file. 2018-08-25 21:50:19 -07:00
Daniel Hahler
d3f5324386 tox: coveralls: also report to codecov
This is meant to get base coverage on master for codecov.
2018-08-26 02:14:22 +02:00
Anthony Sottile
3da88d794f Use skip_install for testing tox env 2018-08-25 16:48:01 -07:00
Bruno Oliveira
71b4995775 Merge pull request #3874 from blueyed/improve-pre-commit
Travis: use TOXENV=linting for linting stage
2018-08-25 20:14:27 -03:00
Bruno Oliveira
b0541e9d31 Correctly restore sys.path in test and remove dead code in test_pytester
The code in test_pytester has been refactored into a class right
above the dead code, and the code has been left there by mistake
apparently.
2018-08-25 18:17:52 -03:00
Daniel Hahler
415fcb912b Travis: use TOXENV=linting for linting stage
This will run it with `--show-diff-on-failure` then, and helps to keep
it in line / in a central place.
2018-08-25 23:14:09 +02:00
Bruno Oliveira
f872fcb5d0 Remove dangerous sys.path manipulations in test_pluginmanager
Noticed these while working in something else
2018-08-25 17:33:29 -03:00
Bruno Oliveira
de6f2c0336 Collect tests from __init__.py files if they match 'python_files'
Fix #3773
2018-08-25 11:18:52 -03:00
Bruno Oliveira
be4b359c74 Merge pull request #3861 from jonozzz/fix-3854
Fix #3854
2018-08-25 10:44:08 -03:00
Bruno Oliveira
72a58bbafe Merge pull request #3871 from schmamps/cmdclass
Correct cmdclass for doc: Good Integration Practices
2018-08-24 22:09:35 -03:00
turturica
c336449729 Make linting happy. Argh. 2018-08-24 18:05:35 -07:00
turturica
1e4ecda884 Fix the package fixture ordering in Windows. 2018-08-24 18:01:38 -07:00
turturica
8cf0e46bbf test_package_ordering: Collect *.py, but keep a mix of case for filenames. The test doesn't make sense for Windows, because of its case-insensitivity. 2018-08-24 16:23:50 -07:00
Bruno Oliveira
f0226e9329 Fix test_package_ordering on Windows 2018-08-24 20:15:33 -03:00
turturica
dce8df45d5 Added changelog items. 2018-08-24 15:51:42 -07:00
Andrew Champion
f6948597e4 add to changelog 2018-08-24 12:29:18 -07:00
turturica
e3df1031ca Add encoding: utf8 for python 2.7 2018-08-24 12:26:18 -07:00
Andrew Champion
14ffadf004 correct cmdclass 2018-08-24 12:07:22 -07:00
turturica
459b040d21 Fix dedent after merge. 2018-08-24 11:54:04 -07:00
turturica
3396225f74 Merge branch 'master' of github.com:pytest-dev/pytest into fix-3854 2018-08-24 11:47:24 -07:00
Bruno Oliveira
c82906105c Merge pull request #3865 from GandalfSaxe/patch-1
Move information on `pip install -e` to the top
2018-08-24 07:28:51 -03:00
Anthony Sottile
4c14740798 Merge pull request #3868 from asottile/bytes_py26_plus
Use `bytes` directly instead of `binary_type`
2018-08-23 23:16:59 -07:00
Jeffrey Rackauckas
5fefc48f33 Fixing pre-commit hooks 2018-08-23 23:00:02 -07:00
turturica
72e6482994 Make linting happy. 2018-08-23 22:58:36 -07:00
Jeffrey Rackauckas
93f783228c Add the progress_display_mode ini option 2018-08-23 22:56:25 -07:00
turturica
5f8b50c094 Address #3796 and add a test for it. 2018-08-23 22:48:44 -07:00
Anthony Sottile
99e31f6fb1 Use bytes directly instead of binary_type
`bytes` is an alias for `str` in python2.6+
2018-08-23 18:55:21 -07:00
Anthony Sottile
f2e35c8c4f Merge pull request #3859 from asottile/pyupgrade_1_4
Some pyupgrade 1.4.x changes
2018-08-23 18:32:53 -07:00
Bruno Oliveira
40b4fe64af Fix linting 2018-08-23 22:11:17 -03:00
Bruno Oliveira
d10d59c013 Merge pull request #3858 from mimi1vx/test_mock
Use unittest.mock if is only aviable
2018-08-23 19:02:39 -03:00
wim glenn
d54aa8ce13 Merge pull request #3848 from wimglenn/pytester_unicode_bugfixes
fixed a bunch of unicode bugs in pytester.py
2018-08-23 13:45:49 -05:00
Anthony Sottile
52fa8c98bb Merge pull request #3864 from asottile/source_to_dedent
Replace Source with dedent where possible
2018-08-23 10:45:11 -07:00
Gandalf Saxe
3f336869e2 Move information on pip install -e to the top
Should fix complaints in #2421.
2018-08-23 18:07:28 +02:00
Anthony Sottile
85482d575e Replace Source with dedent where possible 2018-08-23 09:06:17 -07:00
Bruno Oliveira
6f7365509d Merge pull request #3860 from asottile/purge_more_py
Purge more usage of `py` module
2018-08-23 06:05:32 -03:00
Anthony Sottile
7099ea9bb0 py.builtin._reraise -> six.reraise 2018-08-22 23:00:58 -07:00
Anthony Sottile
dccac69d82 py.builtin.text -> six.text_type 2018-08-22 23:00:06 -07:00
Anthony Sottile
c2cd337886 py.builtin.exec_ => six.exec_ 2018-08-22 23:00:06 -07:00
Anthony Sottile
0fc4a806e5 py.builtins._totext -> string literals or six.text_type 2018-08-22 23:00:04 -07:00
turturica
4d3c1ab4f0 Fixes #3854 2018-08-22 21:42:59 -07:00
turturica
e4f76f6350 Merge branch 'master' of github.com:pytest-dev/pytest into fix-3854 2018-08-22 20:36:52 -07:00
Anthony Sottile
0d65783dce Fix unicode errors when changing to .format(...) 2018-08-22 19:00:43 -07:00
Anthony Sottile
8bb8b91357 pyupgrade 1.4: tests 2018-08-22 18:47:21 -07:00
Bruno Oliveira
8804c7333a Fix CHANGELOG formatting 2018-08-22 20:06:13 -03:00
Bruno Oliveira
17eec5b97e Merge pull request #3856 from jennirinker/master
Resolving Issue #3824
2018-08-22 19:03:55 -03:00
Ondřej Súkup
cd07c4d4ff Use unittest.mock if is only aviable
from Python 3.3 is mock part of python standard library in unittest namespace
2018-08-22 23:49:40 +02:00
wim glenn
917b99e438 More unicode whack-a-mole
It seems pytest's very comprehensive CI sniffed out a few other places with similar bugs.  Ideally we should find all the places where args are not stringy and solve it at the source, but who knows how many people are relying on the implicit string conversion.  See [here](https://github.com/pytest-dev/pytest/blob/master/src/_pytest/config/__init__.py#L160-L166) for one such problem area (args with a single py.path.local instance is converted here, but a list or tuple containing some are not).
2018-08-22 13:40:21 -05:00
wim glenn
b08e156b79 strip trailing whitespace 2018-08-22 11:27:36 -05:00
wim glenn
8e2c7b4979 Add a failing testcase for PR #3848 2018-08-22 11:00:51 -05:00
Bruno Oliveira
5a7aa123ea Improve docs formatting 2018-08-22 11:22:30 -03:00
Jennifer Rinker
a12eadd9ef resolving Issue #3824 - expanding docs 2018-08-22 15:37:35 +02:00
Bruno Oliveira
2137e2b15b Merge pull request #3846 from nicoddemus/issue-3843
Fix collection error when tests is specified with --doctest-modules
2018-08-22 08:17:07 -03:00
wim glenn
89446af51e fixed a bunch of unicode bugs in pytester.py 2018-08-22 01:30:23 -05:00
Ronny Pfannschmidt
3b521bedf8 Merge pull request #3841 from sankt-petersbug/fix-3816
Fix '--show-capture=no' capture teardown logs
2018-08-22 07:16:53 +02:00
Ronny Pfannschmidt
5b2c8fa007 Merge pull request #3845 from natanlao/patch-1
Remove warning about #568 from documentation
2018-08-22 07:12:40 +02:00
Bruno Oliveira
eb8d145195 Add link to issue in the CHANGELOG entry 2018-08-21 21:08:21 -03:00
Natan Lao
80bea79512 Add changelog entry 2018-08-21 17:04:56 -07:00
Bruno Oliveira
07a560ff24 Fix collection error when tests is specified with --doctest-modules
The problem was that _matchnodes would receive two items: [DoctestModule, Module]. It would then collect the first one, *cache it*, and fail to match against the name in the command line. Next, it would reuse the cached item (DoctestModule) instead of collecting the Module which would eventually find the "test" name on it.

Added the type of the node to the cache key to avoid this problem, although I'm not a big fan of caches that have different key types.

Fix #3843
2018-08-21 21:02:46 -03:00
Natan Lao
717775a1c6 Remove warning about #568 from documentation
The documentation (https://docs.pytest.org/en/latest/skipping.html) references
issue #568, which has since been fixed.
2018-08-21 16:57:33 -07:00
Bruno Oliveira
672f4bb5aa Improve CHANGELOG 2018-08-21 20:19:48 -03:00
Bruno Oliveira
f1079a8222 Merge pull request #3832 from Sup3rGeo/bugfix/capsys-with-cli-logging
Bugfix/capsys with cli logging (again)
2018-08-21 20:12:31 -03:00
Bruno Oliveira
044d2b8e6e Merge pull request #3838 from wimglenn/runresult_xfail
Support xfailed and xpassed outcomes in RunResult.
2018-08-20 20:01:51 -03:00
Sankt Petersbug
223eef6261 Fix '--show-capture=no' capture teardown logs
Add a check before printing teardown logs.

'print_teardown_sections' method does not check '--show-capture' option
value, and teardown logs are always printed.

Resolves: #3816
2018-08-20 15:01:21 -05:00
Bruno Oliveira
43657f252f Merge pull request #3830 from nicoddemus/capfd-fixture-capture
Fixtures during teardown can use capsys and capfd to get output from tests
2018-08-20 15:32:45 -03:00
Victor
70ebab3537 Renamed snap_global_capture to read_global_capture. 2018-08-20 17:48:14 +02:00
Ronny Pfannschmidt
d3bdfc704b Merge pull request #3839 from asottile/more_flexible
Use more flexible `language_version: python3`
2018-08-20 16:13:31 +02:00
Anthony Sottile
4de247cfa0 Use more flexible language_version: python3 2018-08-20 06:27:35 -07:00
Victor
d611b03589 Parametrized tests for capfd as well. Separated global capture test. 2018-08-20 12:23:59 +02:00
Anthony Sottile
308d789d92 Merge pull request #3835 from wimglenn/issue3833
more autodocs for pytester
2018-08-20 00:30:28 -07:00
wim glenn
539a22c750 Added support for xfailed and xpassed outcomes to the `pytester.RunResult.assert_outcomes` signature. 2018-08-20 01:24:19 -05:00
wim glenn
e4bea9068b end of line for this file, perhaps? 2018-08-19 23:39:10 -05:00
wim glenn
e620798d33 more autodocs for pytester 2018-08-19 23:21:45 -05:00
victor
7ea4992f16 Fixed linting. 2018-08-19 15:46:02 +02:00
victor
0564b52c0e Fixed integration with other modules/tests 2018-08-19 15:26:57 +02:00
victor
8b2c91836b Fixed activation and used just runtest_protocol hook 2018-08-19 14:30:50 +02:00
victor
9e382e8d29 Fixed test. 2018-08-19 14:29:57 +02:00
victor
2255892d65 Improved test to cover more cases. 2018-08-19 13:44:12 +02:00
victor
7d9b198f73 Refactoring: Separated suspend from snapping (stopped always snapping when suspending - solves bug but still missing tests), reorganized functions and context managers. 2018-08-19 02:32:36 +02:00
Bruno Oliveira
a6cdd0d9da Merge pull request #3831 from nicoddemus/merge-master-into-features
Merge master into features
2018-08-18 19:43:36 -03:00
Bruno Oliveira
c64a8c9c7f Merge remote-tracking branch 'upstream/master' into merge-master-into-features 2018-08-18 15:54:53 -03:00
Bruno Oliveira
f4c5994d27 Fixtures during teardown can use capsys and capfd to get output from tests
Fix #3033
2018-08-18 14:46:24 -03:00
Tyler Richard
c24c7e75e2 Added regression test for capfd in a fixture 2018-08-18 14:11:04 -03:00
Tyler Richard
273670b2a2 Fixes capfd so data is available after teardown. 2018-08-18 14:11:04 -03:00
Bruno Oliveira
28aff051ab Merge pull request #3822 from Sup3rGeo/bugfix/capsys-with-cli-logging
Bugfix/capsys with cli logging
2018-08-18 14:06:27 -03:00
Bruno Oliveira
29975e5b37 Merge pull request #3827 from Vlad-Shcherbina/funcfixtureinfo-type-hints
Replace broken type annotations with type comments
2018-08-18 12:54:47 -03:00
Bruno Oliveira
5cf7d1dba2 "suspend" method of capture fixture private
Also change the context-manager to global_and_fixture_disabled to
better convey its meaning
2018-08-18 11:38:08 -03:00
Bruno Oliveira
2fe824b8c4 Merge pull request #3821 from nicoddemus/release-3.7.2
Preparing release version 3.7.2
2018-08-18 11:18:18 -03:00
Bruno Oliveira
f674217c43 Moved dummy_context_manager to compat module 2018-08-18 11:15:58 -03:00
Bruno Oliveira
9f7345d663 Avoid leaving a reference to the last item on CaptureManager 2018-08-18 11:08:03 -03:00
victor
eb2d074530 Black changes. 2018-08-18 14:27:09 +02:00
victor
9fa7745795 Refactor, tests passing. 2018-08-18 13:40:08 +02:00
victor
14db2f91ba Fixed global not called if no capsys fixture. Using now capsys context manager as well. 2018-08-18 12:16:47 +02:00
Vlad Shcherbina
c3e494f6cf Replace broken type annotations with type comments
Fixes #3826.
2018-08-18 01:05:30 +03:00
Victor
090f67a980 Refactored implementation and updated tests. 2018-08-17 13:41:26 +02:00
Victor
3059bfb1b3 Update test with another problem. 2018-08-17 13:00:27 +02:00
Victor
e391c47ed8 Update capture suspend test for logging. 2018-08-17 00:44:15 +02:00
Victor
f66764e1c0 Added changelog and updated AUTHORS. 2018-08-17 00:33:56 +02:00
Victor
2b71cb9c38 Added activation/deactivation of capture fixture in logging emit. 2018-08-17 00:26:12 +02:00
Victor
da9d814da4 Added test. 2018-08-17 00:20:51 +02:00
turturica
d0bd01beca Collect any tests from a package's __init__.py 2018-08-09 18:06:38 -07:00
Bruno Oliveira
5f97711377 Merge pull request #3787 from hsoft/no-plugin-autoload
Add option to disable plugin auto-loading
2018-08-08 21:30:50 -03:00
Virgil Dupras
126896f69d Add option to disable plugin auto-loading
If `PYTEST_DISABLE_PLUGIN_AUTOLOAD` is set, disable auto-loading of
plugins through setuptools entrypoints. Only plugins that have been
explicitly specified are loaded.

ref #3784.
2018-08-07 13:16:28 -04:00
165 changed files with 6510 additions and 2428 deletions

View File

@@ -1,4 +1,9 @@
[run]
omit =
# standlonetemplate is read dynamically and tested by test_genscript
*standalonetemplate.py
source = pytest,_pytest,testing/
parallel = 1
branch = 1
[paths]
source = src/
.tox/*/lib/python*/site-packages/
.tox\*\Lib\site-packages\

2
.gitattributes vendored
View File

@@ -1 +1 @@
CHANGELOG merge=union
*.bat text eol=crlf

View File

@@ -3,7 +3,7 @@ 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):
- [ ] Create a new changelog file in the `changelog` folder, with a name like `<ISSUE NUMBER>.<TYPE>.rst`. See [changelog/README.rst](/changelog/README.rst) for details.
- [ ] 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.

6
.gitignore vendored
View File

@@ -24,6 +24,7 @@ src/_pytest/_version.py
.eggs/
doc/*/_build
doc/*/.doctrees
build/
dist/
*.egg-info
@@ -35,6 +36,11 @@ env/
.cache
.pytest_cache
.coverage
.coverage.*
coverage.xml
.ropeproject
.idea
.hypothesis
.pydevproject
.project
.settings

View File

@@ -5,28 +5,31 @@ repos:
hooks:
- id: black
args: [--safe, --quiet]
language_version: python3.6
language_version: python3
- repo: https://github.com/asottile/blacken-docs
rev: v0.2.0
rev: v0.3.0
hooks:
- id: blacken-docs
additional_dependencies: [black==18.6b4]
language_version: python3.6
additional_dependencies: [black==18.9b0]
language_version: python3
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v1.3.0
rev: v1.4.0-1
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: debug-statements
exclude: _pytest/debugging.py
language_version: python3
- id: flake8
language_version: python3
- repo: https://github.com/asottile/pyupgrade
rev: v1.2.0
rev: v1.8.0
hooks:
- id: pyupgrade
- id: pyupgrade
args: [--keep-percent-format]
- repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.0.0
rev: v1.1.0
hooks:
- id: rst-backticks
- repo: local
@@ -37,9 +40,9 @@ repos:
files: ^(CHANGELOG.rst|HOWTORELEASE.rst|README.rst|changelog/.*)$
language: python
additional_dependencies: [pygments, restructuredtext_lint]
python_version: python3.6
- id: changelogs-rst
name: changelog files must end in .rst
entry: ./scripts/fail
language: script
files: 'changelog/.*(?<!\.rst)$'
name: changelog filenames
language: fail
entry: 'changelog files must be named ####.(feature|bugfix|doc|deprecation|removal|vendor|trivial).rst'
exclude: changelog/(\d+\.(feature|bugfix|doc|deprecation|removal|vendor|trivial).rst|README.rst|_template.rst)
files: ^changelog/

View File

@@ -1,8 +1,9 @@
sudo: false
language: python
stages:
- linting
- test
- baseline
- name: test
if: repo = pytest-dev/pytest AND tag IS NOT present
- name: deploy
if: repo = pytest-dev/pytest AND tag IS present
python:
@@ -11,43 +12,54 @@ install:
- pip install --upgrade --pre tox
env:
matrix:
# coveralls is not listed in tox's envlist, but should run in travis
- TOXENV=coveralls
# note: please use "tox --listenvs" to populate the build matrix below
# please remove the linting env in all cases
- TOXENV=py27
- TOXENV=py34
- TOXENV=py36
- TOXENV=py27-pexpect
- TOXENV=py27-xdist
- TOXENV=py27-trial
- TOXENV=py27-numpy
- TOXENV=py27-pluggymaster
- TOXENV=py36-pexpect
- TOXENV=py36-xdist
- TOXENV=py36-trial
- TOXENV=py36-numpy
- TOXENV=py36-pluggymaster
# Specialized factors for py27.
- TOXENV=py27-pexpect,py27-trial,py27-numpy
- TOXENV=py27-nobyte
- TOXENV=doctesting
- TOXENV=docs
- TOXENV=py27-xdist
- TOXENV=py27-pluggymaster PYTEST_NO_COVERAGE=1
# Specialized factors for py36.
- TOXENV=py36-pexpect,py36-trial,py36-numpy
- TOXENV=py36-xdist
- TOXENV=py36-pluggymaster PYTEST_NO_COVERAGE=1
jobs:
include:
- env: TOXENV=pypy
# Coverage tracking is slow with pypy, skip it.
- env: TOXENV=pypy PYTEST_NO_COVERAGE=1
python: 'pypy-5.4'
- env: TOXENV=py35
python: '3.5'
- env: TOXENV=py36-freeze
- env: TOXENV=py36-freeze PYTEST_NO_COVERAGE=1
python: '3.6'
- env: TOXENV=py37
python: '3.7'
sudo: required
dist: xenial
- &test-macos
language: generic
os: osx
osx_image: xcode9.4
sudo: required
install:
- python -m pip install --pre tox
env: TOXENV=py27
- <<: *test-macos
env: TOXENV=py37
before_install:
- brew update
- brew upgrade python
- brew unlink python
- brew link python
- stage: baseline
env: TOXENV=py27
- env: TOXENV=py34
- env: TOXENV=py36
- env: TOXENV=linting,docs,doctesting PYTEST_NO_COVERAGE=1
- stage: deploy
python: '3.6'
env:
env: PYTEST_NO_COVERAGE=1
install: pip install -U setuptools setuptools_scm
script: skip
deploy:
@@ -60,17 +72,35 @@ jobs:
on:
tags: true
repo: pytest-dev/pytest
- stage: linting
python: '3.6'
env:
install:
- pip install pre-commit
- pre-commit install-hooks
script:
- pre-commit run --all-files
before_script:
- |
if [[ "$PYTEST_NO_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
after_success:
- |
if [[ "$PYTEST_NO_COVERAGE" != 1 ]]; then
set -e
pip install coverage
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"
# Coveralls does not support merged reports.
if [[ "$TOXENV" = py37 ]]; then
pip install coveralls
coveralls
fi
fi
notifications:
irc:
channels:

16
AUTHORS
View File

@@ -10,9 +10,11 @@ Ahn Ki-Wook
Alan Velasco
Alexander Johnson
Alexei Kozlenok
Allan Feldman
Anatoly Bubenkoff
Anders Hovmöller
Andras Tim
Andrea Cimatoribus
Andreas Zeidler
Andrzej Ostrowski
Andy Freeland
@@ -46,7 +48,9 @@ Christian Boelsen
Christian Theunert
Christian Tismer
Christopher Gilling
CrazyMerlyn
Cyrus Maden
Dhiren Serai
Daniel Grana
Daniel Hahler
Daniel Nuri
@@ -71,6 +75,7 @@ Endre Galaczi
Eric Hunsberger
Eric Siegerman
Erik M. Bray
Fabio Zadrozny
Feng Ma
Florian Bruhin
Floris Bruynooghe
@@ -90,6 +95,7 @@ Hui Wang (coldnight)
Ian Bicking
Ian Lesperance
Ionuț Turturică
Iwan Briquemont
Jaap Broekhuizen
Jan Balster
Janne Vanhala
@@ -98,6 +104,7 @@ Javier Domingo Cansino
Javier Romero
Jeff Rackauckas
Jeff Widman
Jenni Rinker
John Eddie Ayson
John Towler
Jon Sonesen
@@ -114,6 +121,7 @@ Katerina Koukiou
Kevin Cox
Kodi B. Arfer
Kostis Anagnostopoulos
Kyle Altendorf
Lawrence Mitchell
Lee Kamentsky
Lev Maximov
@@ -174,6 +182,7 @@ Raphael Pierzina
Raquel Alegre
Ravi Chandra
Roberto Polli
Roland Puntaier
Romain Dorgueil
Roman Bolshakov
Ronny Pfannschmidt
@@ -202,19 +211,22 @@ Thomas Hisch
Tim Strazny
Tom Dalton
Tom Viner
Tomer Keren
Trevor Bekolay
Tyler Goodlet
Tzu-ping Chung
Vasily Kuznetsov
Victor Maryama
Victor Uriarte
Vidar T. Fauske
Virgil Dupras
Vitaly Lashmanov
Vlad Dragos
Wil Cooley
William Lee
Wim Glenn
Wouter van Ackooy
Xuan Luong
Xuecong Liao
Zac Hatfield-Dodds
Zoltán Máté
Roland Puntaier
Allan Feldman

View File

@@ -18,6 +18,406 @@ with advance notice in the **Deprecations** section of releases.
.. towncrier release notes start
pytest 3.9.0 (2018-10-15)
=========================
Deprecations
------------
- `#3616 <https://github.com/pytest-dev/pytest/issues/3616>`_: The following accesses have been documented as deprecated for years, but are now actually emitting deprecation warnings.
* Access of ``Module``, ``Function``, ``Class``, ``Instance``, ``File`` and ``Item`` through ``Node`` instances. Now
users will this warning::
usage of Function.Module is deprecated, please use pytest.Module instead
Users should just ``import pytest`` and access those objects using the ``pytest`` module.
* ``request.cached_setup``, this was the precursor of the setup/teardown mechanism available to fixtures. You can
consult `funcarg comparision section in the docs <https://docs.pytest.org/en/latest/funcarg_compare.html>`_.
* Using objects named ``"Class"`` as a way to customize the type of nodes that are collected in ``Collector``
subclasses has been deprecated. Users instead should use ``pytest_collect_make_item`` to customize node types during
collection.
This issue should affect only advanced plugins who create new collection types, so if you see this warning
message please contact the authors so they can change the code.
* The warning that produces the message below has changed to ``RemovedInPytest4Warning``::
getfuncargvalue is deprecated, use getfixturevalue
- `#3988 <https://github.com/pytest-dev/pytest/issues/3988>`_: Add a Deprecation warning for pytest.ensuretemp as it was deprecated since a while.
Features
--------
- `#2293 <https://github.com/pytest-dev/pytest/issues/2293>`_: Improve usage errors messages by hiding internal details which can be distracting and noisy.
This has the side effect that some error conditions that previously raised generic errors (such as
``ValueError`` for unregistered marks) are now raising ``Failed`` exceptions.
- `#3332 <https://github.com/pytest-dev/pytest/issues/3332>`_: Improve the error displayed when a ``conftest.py`` file could not be imported.
In order to implement this, a new ``chain`` parameter was added to ``ExceptionInfo.getrepr``
to show or hide chained tracebacks in Python 3 (defaults to ``True``).
- `#3849 <https://github.com/pytest-dev/pytest/issues/3849>`_: Add ``empty_parameter_set_mark=fail_at_collect`` ini option for raising an exception when parametrize collects an empty set.
- `#3964 <https://github.com/pytest-dev/pytest/issues/3964>`_: Log messages generated in the collection phase are shown when
live-logging is enabled and/or when they are logged to a file.
- `#3985 <https://github.com/pytest-dev/pytest/issues/3985>`_: Introduce ``tmp_path`` as a fixture providing a Path object.
- `#4013 <https://github.com/pytest-dev/pytest/issues/4013>`_: Deprecation warnings are now shown even if you customize the warnings filters yourself. In the previous version
any customization would override pytest's filters and deprecation warnings would fall back to being hidden by default.
- `#4073 <https://github.com/pytest-dev/pytest/issues/4073>`_: Allow specification of timeout for ``Testdir.runpytest_subprocess()`` and ``Testdir.run()``.
- `#4098 <https://github.com/pytest-dev/pytest/issues/4098>`_: Add returncode argument to pytest.exit() to exit pytest with a specific return code.
- `#4102 <https://github.com/pytest-dev/pytest/issues/4102>`_: Reimplement ``pytest.deprecated_call`` using ``pytest.warns`` so it supports the ``match='...'`` keyword argument.
This has the side effect that ``pytest.deprecated_call`` now raises ``pytest.fail.Exception`` instead
of ``AssertionError``.
- `#4149 <https://github.com/pytest-dev/pytest/issues/4149>`_: Require setuptools>=30.3 and move most of the metadata to ``setup.cfg``.
Bug Fixes
---------
- `#2535 <https://github.com/pytest-dev/pytest/issues/2535>`_: Improve error message when test functions of ``unittest.TestCase`` subclasses use a parametrized fixture.
- `#3057 <https://github.com/pytest-dev/pytest/issues/3057>`_: ``request.fixturenames`` now correctly returns the name of fixtures created by ``request.getfixturevalue()``.
- `#3946 <https://github.com/pytest-dev/pytest/issues/3946>`_: Warning filters passed as command line options using ``-W`` now take precedence over filters defined in ``ini``
configuration files.
- `#4066 <https://github.com/pytest-dev/pytest/issues/4066>`_: Fix source reindenting by using ``textwrap.dedent`` directly.
- `#4102 <https://github.com/pytest-dev/pytest/issues/4102>`_: ``pytest.warn`` will capture previously-warned warnings in Python 2. Previously they were never raised.
- `#4108 <https://github.com/pytest-dev/pytest/issues/4108>`_: Resolve symbolic links for args.
This fixes running ``pytest tests/test_foo.py::test_bar``, where ``tests``
is a symlink to ``project/app/tests``:
previously ``project/app/conftest.py`` would be ignored for fixtures then.
- `#4132 <https://github.com/pytest-dev/pytest/issues/4132>`_: Fix duplicate printing of internal errors when using ``--pdb``.
- `#4135 <https://github.com/pytest-dev/pytest/issues/4135>`_: pathlib based tmpdir cleanup now correctly handles symlinks in the folder.
- `#4152 <https://github.com/pytest-dev/pytest/issues/4152>`_: Display the filename when encountering ``SyntaxWarning``.
Improved Documentation
----------------------
- `#3713 <https://github.com/pytest-dev/pytest/issues/3713>`_: Update usefixtures documentation to clarify that it can't be used with fixture functions.
- `#4058 <https://github.com/pytest-dev/pytest/issues/4058>`_: Update fixture documentation to specify that a fixture can be invoked twice in the scope it's defined for.
- `#4064 <https://github.com/pytest-dev/pytest/issues/4064>`_: According to unittest.rst, setUpModule and tearDownModule were not implemented, but it turns out they are. So updated the documentation for unittest.
- `#4151 <https://github.com/pytest-dev/pytest/issues/4151>`_: Add tempir testing example to CONTRIBUTING.rst guide
Trivial/Internal Changes
------------------------
- `#2293 <https://github.com/pytest-dev/pytest/issues/2293>`_: The internal ``MarkerError`` exception has been removed.
- `#3988 <https://github.com/pytest-dev/pytest/issues/3988>`_: Port the implementation of tmpdir to pathlib.
- `#4063 <https://github.com/pytest-dev/pytest/issues/4063>`_: Exclude 0.00 second entries from ``--duration`` output unless ``-vv`` is passed on the command-line.
- `#4093 <https://github.com/pytest-dev/pytest/issues/4093>`_: Fixed formatting of string literals in internal tests.
pytest 3.8.2 (2018-10-02)
=========================
Deprecations and Removals
-------------------------
- `#4036 <https://github.com/pytest-dev/pytest/issues/4036>`_: The ``item`` parameter of ``pytest_warning_captured`` hook is now documented as deprecated. We realized only after
the ``3.8`` release that this parameter is incompatible with ``pytest-xdist``.
Our policy is to not deprecate features during bugfix releases, but in this case we believe it makes sense as we are
only documenting it as deprecated, without issuing warnings which might potentially break test suites. This will get
the word out that hook implementers should not use this parameter at all.
In a future release ``item`` will always be ``None`` and will emit a proper warning when a hook implementation
makes use of it.
Bug Fixes
---------
- `#3539 <https://github.com/pytest-dev/pytest/issues/3539>`_: Fix reload on assertion rewritten modules.
- `#4034 <https://github.com/pytest-dev/pytest/issues/4034>`_: The ``.user_properties`` attribute of ``TestReport`` objects is a list
of (name, value) tuples, but could sometimes be instantiated as a tuple
of tuples. It is now always a list.
- `#4039 <https://github.com/pytest-dev/pytest/issues/4039>`_: No longer issue warnings about using ``pytest_plugins`` in non-top-level directories when using ``--pyargs``: the
current ``--pyargs`` mechanism is not reliable and might give false negatives.
- `#4040 <https://github.com/pytest-dev/pytest/issues/4040>`_: Exclude empty reports for passed tests when ``-rP`` option is used.
- `#4051 <https://github.com/pytest-dev/pytest/issues/4051>`_: Improve error message when an invalid Python expression is passed to the ``-m`` option.
- `#4056 <https://github.com/pytest-dev/pytest/issues/4056>`_: ``MonkeyPatch.setenv`` and ``MonkeyPatch.delenv`` issue a warning if the environment variable name is not ``str`` on Python 2.
In Python 2, adding ``unicode`` keys to ``os.environ`` causes problems with ``subprocess`` (and possible other modules),
making this a subtle bug specially susceptible when used with ``from __future__ import unicode_literals``.
Improved Documentation
----------------------
- `#3928 <https://github.com/pytest-dev/pytest/issues/3928>`_: Add possible values for fixture scope to docs.
pytest 3.8.1 (2018-09-22)
=========================
Bug Fixes
---------
- `#3286 <https://github.com/pytest-dev/pytest/issues/3286>`_: ``.pytest_cache`` directory is now automatically ignored by Git. Users who would like to contribute a solution for other SCMs please consult/comment on this issue.
- `#3749 <https://github.com/pytest-dev/pytest/issues/3749>`_: Fix the following error during collection of tests inside packages::
TypeError: object of type 'Package' has no len()
- `#3941 <https://github.com/pytest-dev/pytest/issues/3941>`_: Fix bug where indirect parametrization would consider the scope of all fixtures used by the test function to determine the parametrization scope, and not only the scope of the fixtures being parametrized.
- `#3973 <https://github.com/pytest-dev/pytest/issues/3973>`_: Fix crash of the assertion rewriter if a test changed the current working directory without restoring it afterwards.
- `#3998 <https://github.com/pytest-dev/pytest/issues/3998>`_: Fix issue that prevented some caplog properties (for example ``record_tuples``) from being available when entering the debugger with ``--pdb``.
- `#3999 <https://github.com/pytest-dev/pytest/issues/3999>`_: Fix ``UnicodeDecodeError`` in python2.x when a class returns a non-ascii binary ``__repr__`` in an assertion which also contains non-ascii text.
Improved Documentation
----------------------
- `#3996 <https://github.com/pytest-dev/pytest/issues/3996>`_: New `Deprecations and Removals <https://docs.pytest.org/en/latest/deprecations.html>`_ page shows all currently
deprecated features, the rationale to do so, and alternatives to update your code. It also list features removed
from pytest in past major releases to help those with ancient pytest versions to upgrade.
Trivial/Internal Changes
------------------------
- `#3955 <https://github.com/pytest-dev/pytest/issues/3955>`_: Improve pre-commit detection for changelog filenames
- `#3975 <https://github.com/pytest-dev/pytest/issues/3975>`_: Remove legacy code around im_func as that was python2 only
pytest 3.8.0 (2018-09-05)
=========================
Deprecations and Removals
-------------------------
- `#2452 <https://github.com/pytest-dev/pytest/issues/2452>`_: ``Config.warn`` and ``Node.warn`` have been
deprecated, see `<https://docs.pytest.org/en/latest/deprecations.html#config-warn-and-node-warn>`_ for rationale and
examples.
- `#3936 <https://github.com/pytest-dev/pytest/issues/3936>`_: ``@pytest.mark.filterwarnings`` second parameter is no longer regex-escaped,
making it possible to actually use regular expressions to check the warning message.
**Note**: regex-escaping the match string was an implementation oversight that might break test suites which depend
on the old behavior.
Features
--------
- `#2452 <https://github.com/pytest-dev/pytest/issues/2452>`_: Internal pytest warnings are now issued using the standard ``warnings`` module, making it possible to use
the standard warnings filters to manage those warnings. This introduces ``PytestWarning``,
``PytestDeprecationWarning`` and ``RemovedInPytest4Warning`` warning types as part of the public API.
Consult `the documentation <https://docs.pytest.org/en/latest/warnings.html#internal-pytest-warnings>`_ for more info.
- `#2908 <https://github.com/pytest-dev/pytest/issues/2908>`_: ``DeprecationWarning`` and ``PendingDeprecationWarning`` are now shown by default if no other warning filter is
configured. This makes pytest more compliant with
`PEP-0506 <https://www.python.org/dev/peps/pep-0565/#recommended-filter-settings-for-test-runners>`_. See
`the docs <https://docs.pytest.org/en/latest/warnings.html#deprecationwarning-and-pendingdeprecationwarning>`_ for
more info.
- `#3251 <https://github.com/pytest-dev/pytest/issues/3251>`_: Warnings are now captured and displayed during test collection.
- `#3784 <https://github.com/pytest-dev/pytest/issues/3784>`_: ``PYTEST_DISABLE_PLUGIN_AUTOLOAD`` environment variable disables plugin auto-loading when set.
- `#3829 <https://github.com/pytest-dev/pytest/issues/3829>`_: Added the ``count`` option to ``console_output_style`` to enable displaying the progress as a count instead of a percentage.
- `#3837 <https://github.com/pytest-dev/pytest/issues/3837>`_: Added support for 'xfailed' and 'xpassed' outcomes to the ``pytester.RunResult.assert_outcomes`` signature.
Bug Fixes
---------
- `#3911 <https://github.com/pytest-dev/pytest/issues/3911>`_: Terminal writer now takes into account unicode character width when writing out progress.
- `#3913 <https://github.com/pytest-dev/pytest/issues/3913>`_: Pytest now returns with correct exit code (EXIT_USAGEERROR, 4) when called with unknown arguments.
- `#3918 <https://github.com/pytest-dev/pytest/issues/3918>`_: Improve performance of assertion rewriting.
Improved Documentation
----------------------
- `#3566 <https://github.com/pytest-dev/pytest/issues/3566>`_: Added a blurb in usage.rst for the usage of -r flag which is used to show an extra test summary info.
- `#3907 <https://github.com/pytest-dev/pytest/issues/3907>`_: Corrected type of the exceptions collection passed to ``xfail``: ``raises`` argument accepts a ``tuple`` instead of ``list``.
Trivial/Internal Changes
------------------------
- `#3853 <https://github.com/pytest-dev/pytest/issues/3853>`_: Removed ``"run all (no recorded failures)"`` message printed with ``--failed-first`` and ``--last-failed`` when there are no failed tests.
pytest 3.7.4 (2018-08-29)
=========================
Bug Fixes
---------
- `#3506 <https://github.com/pytest-dev/pytest/issues/3506>`_: Fix possible infinite recursion when writing ``.pyc`` files.
- `#3853 <https://github.com/pytest-dev/pytest/issues/3853>`_: Cache plugin now obeys the ``-q`` flag when ``--last-failed`` and ``--failed-first`` flags are used.
- `#3883 <https://github.com/pytest-dev/pytest/issues/3883>`_: Fix bad console output when using ``console_output_style=classic``.
- `#3888 <https://github.com/pytest-dev/pytest/issues/3888>`_: Fix macOS specific code using ``capturemanager`` plugin in doctests.
Improved Documentation
----------------------
- `#3902 <https://github.com/pytest-dev/pytest/issues/3902>`_: Fix pytest.org links
pytest 3.7.3 (2018-08-26)
=========================
Bug Fixes
---------
- `#3033 <https://github.com/pytest-dev/pytest/issues/3033>`_: Fixtures during teardown can again use ``capsys`` and ``capfd`` to inspect output captured during tests.
- `#3773 <https://github.com/pytest-dev/pytest/issues/3773>`_: Fix collection of tests from ``__init__.py`` files if they match the ``python_files`` configuration option.
- `#3796 <https://github.com/pytest-dev/pytest/issues/3796>`_: Fix issue where teardown of fixtures of consecutive sub-packages were executed once, at the end of the outer
package.
- `#3816 <https://github.com/pytest-dev/pytest/issues/3816>`_: Fix bug where ``--show-capture=no`` option would still show logs printed during fixture teardown.
- `#3819 <https://github.com/pytest-dev/pytest/issues/3819>`_: Fix ``stdout/stderr`` not getting captured when real-time cli logging is active.
- `#3843 <https://github.com/pytest-dev/pytest/issues/3843>`_: Fix collection error when specifying test functions directly in the command line using ``test.py::test`` syntax together with ``--doctest-modules``.
- `#3848 <https://github.com/pytest-dev/pytest/issues/3848>`_: Fix bugs where unicode arguments could not be passed to ``testdir.runpytest`` on Python 2.
- `#3854 <https://github.com/pytest-dev/pytest/issues/3854>`_: Fix double collection of tests within packages when the filename starts with a capital letter.
Improved Documentation
----------------------
- `#3824 <https://github.com/pytest-dev/pytest/issues/3824>`_: Added example for multiple glob pattern matches in ``python_files``.
- `#3833 <https://github.com/pytest-dev/pytest/issues/3833>`_: Added missing docs for ``pytester.Testdir``.
- `#3870 <https://github.com/pytest-dev/pytest/issues/3870>`_: Correct documentation for setuptools integration.
Trivial/Internal Changes
------------------------
- `#3826 <https://github.com/pytest-dev/pytest/issues/3826>`_: Replace broken type annotations with type comments.
- `#3845 <https://github.com/pytest-dev/pytest/issues/3845>`_: Remove a reference to issue `#568 <https://github.com/pytest-dev/pytest/issues/568>`_ from the documentation, which has since been
fixed.
pytest 3.7.2 (2018-08-16)
=========================
@@ -86,15 +486,10 @@ pytest 3.7.0 (2018-07-30)
Deprecations and Removals
-------------------------
- `#2639 <https://github.com/pytest-dev/pytest/issues/2639>`_: ``pytest_namespace`` has been deprecated.
See the documentation for ``pytest_namespace`` hook for suggestions on how to deal
with this in plugins which use this functionality.
- `#2639 <https://github.com/pytest-dev/pytest/issues/2639>`_: ``pytest_namespace`` has been `deprecated <https://docs.pytest.org/en/latest/deprecations.html#pytest-namespace>`_.
- `#3661 <https://github.com/pytest-dev/pytest/issues/3661>`_: Calling a fixture function directly, as opposed to request them in a test function, now issues a ``RemovedInPytest4Warning``. It will be changed into an error in pytest ``4.0``.
This is a great source of confusion to new users, which will often call the fixture functions and request them from test functions interchangeably, which breaks the fixture resolution model.
- `#3661 <https://github.com/pytest-dev/pytest/issues/3661>`_: Calling a fixture function directly, as opposed to request them in a test function, now issues a ``RemovedInPytest4Warning``. See `the documentation for rationale and examples <https://docs.pytest.org/en/latest/deprecations.html#calling-fixtures-directly>`_.
@@ -456,7 +851,7 @@ Deprecations and Removals
<https://github.com/pytest-dev/pytest/issues/2770>`_)
- Defining ``pytest_plugins`` is now deprecated in non-top-level conftest.py
files, because they "leak" to the entire directory tree. (`#3084
files, because they "leak" to the entire directory tree. `See the docs <https://docs.pytest.org/en/latest/deprecations.html#pytest-plugins-in-non-top-level-conftest-files>`_ for the rationale behind this decision (`#3084
<https://github.com/pytest-dev/pytest/issues/3084>`_)

View File

@@ -280,6 +280,47 @@ Here is a simple overview, with pytest-specific bits:
base: features # if it's a feature
Writing Tests
----------------------------
Writing tests for plugins or for pytest itself is often done using the `testdir fixture <https://docs.pytest.org/en/latest/reference.html#testdir>`_, as a "black-box" test.
For example, to ensure a simple test passes you can write:
.. code-block:: python
def test_true_assertion(testdir):
testdir.makepyfile(
"""
def test_foo():
assert True
"""
)
result = testdir.runpytest()
result.assert_outcomes(failed=0, passed=1)
Alternatively, it is possible to make checks based on the actual output of the termal using
*glob-like* expressions:
.. code-block:: python
def test_true_assertion(testdir):
testdir.makepyfile(
"""
def test_foo():
assert False
"""
)
result = testdir.runpytest()
result.stdout.fnmatch_lines(["*assert False*", "*1 failed*"])
When choosing a file where to write a new test, take a look at the existing files and see if there's
one file which looks like a good fit. For example, a regression test about a bug in the ``--lf`` option
should go into ``test_cacheprovider.py``, given that this option is implemented in ``cacheprovider.py``.
If in doubt, go ahead and open a PR with your best guess and we can discuss this over the code.
Joining the Development Team
----------------------------

View File

@@ -28,10 +28,13 @@ taking a lot of time to make a new one.
#. After all tests pass and the PR has been approved, publish to PyPI by pushing the tag::
git tag <VERSION>
git push git@github.com:pytest-dev/pytest.git <VERSION>
Wait for the deploy to complete, then make sure it is `available on PyPI <https://pypi.org/project/pytest>`_.
#. Merge the PR into ``master``.
#. Send an email announcement with the contents from::
doc/en/announce/release-<VERSION>.rst

View File

@@ -1,5 +1,5 @@
.. image:: http://docs.pytest.org/en/latest/_static/pytest1.png
:target: http://docs.pytest.org
.. image:: https://docs.pytest.org/en/latest/_static/pytest1.png
:target: https://docs.pytest.org/en/latest/
:align: center
:alt: pytest
@@ -15,8 +15,9 @@
.. image:: https://img.shields.io/pypi/pyversions/pytest.svg
:target: https://pypi.org/project/pytest/
.. image:: https://img.shields.io/coveralls/pytest-dev/pytest/master.svg
:target: https://coveralls.io/r/pytest-dev/pytest
.. image:: https://codecov.io/gh/pytest-dev/pytest/branch/master/graph/badge.svg
:target: https://codecov.io/gh/pytest-dev/pytest
:alt: Code coverage Status
.. image:: https://travis-ci.org/pytest-dev/pytest.svg?branch=master
:target: https://travis-ci.org/pytest-dev/pytest
@@ -25,7 +26,7 @@
:target: https://ci.appveyor.com/project/pytestbot/pytest
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/ambv/black
:target: https://github.com/ambv/black
.. image:: https://www.codetriage.com/pytest-dev/pytest/badges/users.svg
:target: https://www.codetriage.com/pytest-dev/pytest
@@ -66,23 +67,23 @@ To execute it::
========================== 1 failed in 0.04 seconds ===========================
Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` statements are used. See `getting-started <http://docs.pytest.org/en/latest/getting-started.html#our-first-test-run>`_ for more examples.
Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` statements are used. See `getting-started <https://docs.pytest.org/en/latest/getting-started.html#our-first-test-run>`_ for more examples.
Features
--------
- Detailed info on failing `assert statements <http://docs.pytest.org/en/latest/assert.html>`_ (no need to remember ``self.assert*`` names);
- Detailed info on failing `assert statements <https://docs.pytest.org/en/latest/assert.html>`_ (no need to remember ``self.assert*`` names);
- `Auto-discovery
<http://docs.pytest.org/en/latest/goodpractices.html#python-test-discovery>`_
<https://docs.pytest.org/en/latest/goodpractices.html#python-test-discovery>`_
of test modules and functions;
- `Modular fixtures <http://docs.pytest.org/en/latest/fixture.html>`_ for
- `Modular fixtures <https://docs.pytest.org/en/latest/fixture.html>`_ for
managing small or parametrized long-lived test resources;
- Can run `unittest <http://docs.pytest.org/en/latest/unittest.html>`_ (or trial),
`nose <http://docs.pytest.org/en/latest/nose.html>`_ test suites out of the box;
- 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);
@@ -92,7 +93,7 @@ Features
Documentation
-------------
For full documentation, including installation, tutorials and PDF documents, please see http://docs.pytest.org.
For full documentation, including installation, tutorials and PDF documents, please see https://docs.pytest.org/en/latest/.
Bugs/Requests
@@ -104,7 +105,7 @@ Please use the `GitHub issue tracker <https://github.com/pytest-dev/pytest/issue
Changelog
---------
Consult the `Changelog <http://docs.pytest.org/en/latest/changelog.html>`__ page for fixes and enhancements of each version.
Consult the `Changelog <https://docs.pytest.org/en/latest/changelog.html>`__ page for fixes and enhancements of each version.
License

View File

@@ -1,35 +1,29 @@
environment:
COVERALLS_REPO_TOKEN:
secure: 2NJ5Ct55cHJ9WEg3xbSqCuv0rdgzzb6pnzOIG5OkMbTndw3wOBrXntWFoQrXiMFi
# this is pytest's token in coveralls.io, encrypted
# using pytestbot account as detailed here:
# https://www.appveyor.com/docs/build-configuration#secure-variables
matrix:
# coveralls is not in the default env list
- TOXENV: "coveralls"
# note: please use "tox --listenvs" to populate the build matrix below
- TOXENV: "linting"
- TOXENV: "py27"
- TOXENV: "py34"
- TOXENV: "py35"
- TOXENV: "py36"
- TOXENV: "py37"
# - TOXENV: "pypy" reenable when we are able to provide a scandir wheel or build scandir
- TOXENV: "py27-pexpect"
- TOXENV: "py27-xdist"
- TOXENV: "py27-trial"
- TOXENV: "py27-numpy"
PYTEST_NO_COVERAGE: "1"
- TOXENV: "linting,docs,doctesting"
- TOXENV: "py36"
- TOXENV: "py35"
- TOXENV: "py34"
- TOXENV: "pypy"
PYTEST_NO_COVERAGE: "1"
# Specialized factors for py27.
- TOXENV: "py27-trial,py27-numpy,py27-nobyte"
- TOXENV: "py27-pluggymaster"
- TOXENV: "py36-pexpect"
- TOXENV: "py36-xdist"
- TOXENV: "py36-trial"
- TOXENV: "py36-numpy"
PYTEST_NO_COVERAGE: "1"
- TOXENV: "py27-xdist"
# Specialized factors for py36.
- TOXENV: "py36-trial,py36-numpy"
- TOXENV: "py36-pluggymaster"
- TOXENV: "py27-nobyte"
- TOXENV: "doctesting"
PYTEST_NO_COVERAGE: "1"
- TOXENV: "py36-freeze"
- TOXENV: "docs"
PYTEST_NO_COVERAGE: "1"
- TOXENV: "py36-xdist"
matrix:
fast_finish: true
install:
- echo Installed Pythons
@@ -37,12 +31,19 @@ install:
- 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:
- call scripts\call-tox.bat
- C:\Python36\python -m tox
on_success:
- call scripts\upload-coverage.bat
cache:
- '%LOCALAPPDATA%\pip\cache'

View File

@@ -1,5 +1,3 @@
# 10000 iterations, just for relative comparison
# 2.7.5 3.3.2
# FilesCompleter 75.1109 69.2116

View File

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

View File

@@ -1,4 +1,3 @@
import pytest

View File

@@ -14,7 +14,8 @@ Each file should be named like ``<ISSUE>.<TYPE>.rst``, where
* ``feature``: new user facing features, like new command-line options and new behavior.
* ``bugfix``: fixes a reported bug.
* ``doc``: documentation improvement, like rewording an entire session or adding missing docs.
* ``removal``: feature deprecation or removal.
* ``deprecation``: feature deprecation.
* ``removal``: feature removal.
* ``vendor``: changes in packages vendored in pytest.
* ``trivial``: fixing a small typo or internal change that might be noteworthy.

View File

@@ -6,6 +6,12 @@ Release announcements
:maxdepth: 2
release-3.9.0
release-3.8.2
release-3.8.1
release-3.8.0
release-3.7.4
release-3.7.3
release-3.7.2
release-3.7.1
release-3.7.0

View File

@@ -20,8 +20,7 @@ Thanks to all who contributed to this release, among them:
* Ondřej Súkup
* Ronny Pfannschmidt
* T.E.A de Souza
* Victor
* victor
* Victor Maryama
Happy testing,

View File

@@ -0,0 +1,32 @@
pytest-3.7.3
=======================================
pytest 3.7.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 http://doc.pytest.org/en/latest/changelog.html.
Thanks to all who contributed to this release, among them:
* Andrew Champion
* Anthony Sottile
* Bruno Oliveira
* Daniel Hahler
* Gandalf Saxe
* Jennifer Rinker
* Natan Lao
* Ondřej Súkup
* Ronny Pfannschmidt
* Sankt Petersbug
* Tyler Richard
* Victor Maryama
* Vlad Shcherbina
* turturica
* wim glenn
Happy testing,
The pytest Development Team

View File

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

View File

@@ -0,0 +1,38 @@
pytest-3.8.0
=======================================
The pytest team is proud to announce the 3.8.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
* CrazyMerlyn
* Daniel Hahler
* Fabio Zadrozny
* Jeffrey Rackauckas
* Ronny Pfannschmidt
* Virgil Dupras
* dhirensr
* hoefling
* wim glenn
Happy testing,
The Pytest Development Team

View File

@@ -0,0 +1,25 @@
pytest-3.8.1
=======================================
pytest 3.8.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:
* Ankit Goel
* Anthony Sottile
* Bruno Oliveira
* Daniel Hahler
* Maximilian Albert
* Ronny Pfannschmidt
* William Jamir Silva
* wim glenn
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,28 @@
pytest-3.8.2
=======================================
pytest 3.8.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:
* Ankit Goel
* Anthony Sottile
* Bruno Oliveira
* Daniel Hahler
* Denis Otkidach
* Harry Percival
* Jeffrey Rackauckas
* Jose Carlos Menezes
* Ronny Pfannschmidt
* Zac-HD
* iwanb
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,43 @@
pytest-3.9.0
=======================================
The pytest team is proud to announce the 3.9.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:
* Andrea Cimatoribus
* Ankit Goel
* Anthony Sottile
* Ben Eyal
* Bruno Oliveira
* Daniel Hahler
* Jeffrey Rackauckas
* Jose Carlos Menezes
* Kyle Altendorf
* Niklas JQ
* Palash Chatterjee
* Ronny Pfannschmidt
* Thomas Hess
* Thomas Hisch
* Tomer Keren
* Victor Maryama
Happy testing,
The Pytest Development Team

View File

@@ -264,8 +264,12 @@ Advanced assertion introspection
Reporting details about a failing assertion is achieved by rewriting assert
statements before they are run. Rewritten assert statements put introspection
information into the assertion failure message. ``pytest`` only rewrites test
modules directly discovered by its test collection process, so asserts in
supporting modules which are not themselves test modules will not be rewritten.
modules directly discovered by its test collection process, so **asserts in
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::

View File

@@ -7,14 +7,16 @@ Keeping backwards compatibility has a very high priority in the pytest project.
With the pytest 3.0 release we introduced a clear communication scheme for when we will actually remove the old busted joint and politely ask you to use the new hotness instead, while giving you enough time to adjust your tests or raise concerns if there are valid reasons to keep deprecated functionality around.
To communicate changes we are already issuing deprecation warnings, but they are not displayed by default. In pytest 3.0 we changed the default setting so that pytest deprecation warnings are displayed if not explicitly silenced (with ``--disable-pytest-warnings``).
To communicate changes we issue deprecation warnings using a custom warning hierarchy (see :ref:`internal-warnings`). These warnings may be suppressed using the standard means: ``-W`` command-line flag or ``filterwarnings`` ini options (see :ref:`warnings`), but we suggest to use these sparingly and temporarily, and heed the warnings when possible.
We will only remove deprecated functionality in major releases (e.g. if we deprecate something in 3.0 we will remove it in 4.0), and keep it around for at least two minor releases (e.g. if we deprecate something in 3.9 and 4.0 is the next release, we will not remove it in 4.0 but in 5.0).
We will only start the removal of deprecated functionality in major releases (e.g. if we deprecate something in 3.0 we will start to remove it in 4.0), and keep it around for at least two minor releases (e.g. if we deprecate something in 3.9 and 4.0 is the next release, we start to remove it in 5.0, not in 4.0).
When the deprecation expires (e.g. 4.0 is released), we won't remove the deprecated functionality immediately, but will use the standard warning filters to turn them into **errors** by default. This approach makes it explicit that removal is imminent, and still gives you time to turn the deprecated feature into a warning instead of an error so it can be dealt with in your own time. In the next minor release (e.g. 4.1), the feature will be effectively removed.
Deprecation Roadmap
-------------------
We track deprecation and removal of features using milestones and the `deprecation <https://github.com/pytest-dev/pytest/issues?q=label%3A%22type%3A+deprecation%22>`_ and `removal <https://github.com/pytest-dev/pytest/labels/type%3A%20removal>`_ labels on GitHub.
Features currently deprecated and removed in previous releases can be found in :ref:`deprecations`.
Following our deprecation policy, after starting issuing deprecation warnings we keep features for *at least* two minor versions before considering removal.
We track future deprecation and removal of features using milestones and the `deprecation <https://github.com/pytest-dev/pytest/issues?q=label%3A%22type%3A+deprecation%22>`_ and `removal <https://github.com/pytest-dev/pytest/labels/type%3A%20removal>`_ labels on GitHub.

View File

@@ -104,7 +104,9 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
See http://docs.python.org/library/warnings.html for information
on warning categories.
tmpdir_factory
Return a TempdirFactory instance for the test session.
Return a :class:`_pytest.tmpdir.TempdirFactory` instance for the test session.
tmp_path_factory
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,
@@ -113,6 +115,16 @@ 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,
created as a sub directory of the base temporary
directory. The returned object is a :class:`pathlib.Path`
object.
.. note::
in python < 3.6 this is a pathlib2.Path
no tests ran in 0.12 seconds

View File

@@ -329,7 +329,7 @@ texinfo_documents = [
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {"python": ("http://docs.python.org/3", None)}
intersphinx_mapping = {"python": ("https://docs.python.org/3", None)}
def setup(app):

View File

@@ -39,6 +39,7 @@ Full pytest documentation
bash-completion
backwards-compatibility
deprecations
historical-notes
license
contributing

View File

@@ -32,7 +32,7 @@ Here's a summary what ``pytest`` uses ``rootdir`` for:
class name, function name and parametrization (if any).
* Is used by plugins as a stable location to store project/test run specific information;
for example, the internal :ref:`cache <cache>` plugin creates a ``.cache`` subdirectory
for example, the internal :ref:`cache <cache>` plugin creates a ``.pytest_cache`` subdirectory
in ``rootdir`` to store its cross-test run state.
Important to emphasize that ``rootdir`` is **NOT** used to modify ``sys.path``/``PYTHONPATH`` or

386
doc/en/deprecations.rst Normal file
View File

@@ -0,0 +1,386 @@
.. _deprecations:
Deprecations and Removals
=========================
This page lists all pytest features that are currently deprecated or have been removed in past major releases.
The objective is to give users a clear rationale why a certain feature has been removed, and what alternatives
should be used instead.
Deprecated Features
-------------------
Below is a complete list of all pytest features which are considered deprecated. Using those features will issue
:class:`_pytest.warning_types.PytestWarning` or subclasses, which can be filtered using
:ref:`standard warning filters <warnings>`.
Internal classes accessed through ``Node``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.9
Access of ``Module``, ``Function``, ``Class``, ``Instance``, ``File`` and ``Item`` through ``Node`` instances now issue
this warning::
usage of Function.Module is deprecated, please use pytest.Module instead
Users should just ``import pytest`` and access those objects using the ``pytest`` module.
This has been documented as deprecated for years, but only now we are actually emitting deprecation warnings.
``cached_setup``
~~~~~~~~~~~~~~~~
.. deprecated:: 3.9
``request.cached_setup`` was the precursor of the setup/teardown mechanism available to fixtures.
Example:
.. code-block:: python
@pytest.fixture
def db_session():
return request.cached_setup(
setup=Session.create, teardown=lambda session: session.close(), scope="module"
)
This should be updated to make use of standard fixture mechanisms:
.. code-block:: python
@pytest.fixture(scope="module")
def db_session():
session = Session.create()
yield session
session.close()
You can consult `funcarg comparision section in the docs <https://docs.pytest.org/en/latest/funcarg_compare.html>`_ for
more information.
This has been documented as deprecated for years, but only now we are actually emitting deprecation warnings.
Using ``Class`` in custom Collectors
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.9
Using objects named ``"Class"`` as a way to customize the type of nodes that are collected in ``Collector``
subclasses has been deprecated. Users instead should use ``pytest_collect_make_item`` to customize node types during
collection.
This issue should affect only advanced plugins who create new collection types, so if you see this warning
message please contact the authors so they can change the code.
``Config.warn`` and ``Node.warn``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.8
Those methods were part of the internal pytest warnings system, but since ``3.8`` pytest is using the builtin warning
system for its own warnings, so those two functions are now deprecated.
``Config.warn`` should be replaced by calls to the standard ``warnings.warn``, example:
.. code-block:: python
config.warn("C1", "some warning")
Becomes:
.. code-block:: python
warnings.warn(pytest.PytestWarning("some warning"))
``Node.warn`` now supports two signatures:
* ``node.warn(PytestWarning("some message"))``: is now the **recommended** way to call this function.
The warning instance must be a PytestWarning or subclass.
* ``node.warn("CI", "some message")``: this code/message form is now **deprecated** and should be converted to the warning instance form above.
``pytest_namespace``
~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.7
This hook is deprecated because it greatly complicates the pytest internals regarding configuration and initialization, making some
bug fixes and refactorings impossible.
Example of usage:
.. code-block:: python
class MySymbol:
...
def pytest_namespace():
return {"my_symbol": MySymbol()}
Plugin authors relying on this hook should instead require that users now import the plugin modules directly (with an appropriate public API).
As a stopgap measure, plugin authors may still inject their names into pytest's namespace, usually during ``pytest_configure``:
.. code-block:: python
import pytest
def pytest_configure():
pytest.my_symbol = MySymbol()
Calling fixtures directly
~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.7
Calling a fixture function directly, as opposed to request them in a test function, is deprecated.
For example:
.. code-block:: python
@pytest.fixture
def cell():
return ...
@pytest.fixture
def full_cell():
cell = cell()
cell.make_full()
return cell
This is a great source of confusion to new users, which will often call the fixture functions and request them from test functions interchangeably, which breaks the fixture resolution model.
In those cases just request the function directly in the dependent fixture:
.. code-block:: python
@pytest.fixture
def cell():
return ...
@pytest.fixture
def full_cell(cell):
cell.make_full()
return cell
``Node.get_marker``
~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.6
As part of a large :ref:`marker-revamp`, :meth:`_pytest.nodes.Node.get_marker` is deprecated. See
:ref:`the documentation <update marker code>` on tips on how to update your code.
record_xml_property
~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.5
The ``record_xml_property`` fixture is now deprecated in favor of the more generic ``record_property``, which
can be used by other consumers (for example ``pytest-html``) to obtain custom information about the test run.
This is just a matter of renaming the fixture as the API is the same:
.. code-block:: python
def test_foo(record_xml_property):
...
Change to:
.. code-block:: python
def test_foo(record_property):
...
pytest_plugins in non-top-level conftest files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.5
Defining ``pytest_plugins`` is now deprecated in non-top-level conftest.py
files because they will activate referenced plugins *globally*, which is surprising because for all other pytest
features ``conftest.py`` files are only *active* for tests at or below it.
Metafunc.addcall
~~~~~~~~~~~~~~~~
.. deprecated:: 3.3
:meth:`_pytest.python.Metafunc.addcall` was a precursor to the current parametrized mechanism. Users should use
:meth:`_pytest.python.Metafunc.parametrize` instead.
marks in ``pytest.mark.parametrize``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.2
Applying marks to values of a ``pytest.mark.parametrize`` call is now deprecated. For example:
.. code-block:: python
@pytest.mark.parametrize(
"a, b", [(3, 9), pytest.mark.xfail(reason="flaky")(6, 36), (10, 100)]
)
def test_foo(a, b):
...
This code applies the ``pytest.mark.xfail(reason="flaky")`` mark to the ``(6, 36)`` value of the above parametrization
call.
This was considered hard to read and understand, and also its implementation presented problems to the code preventing
further internal improvements in the marks architecture.
To update the code, use ``pytest.param``:
.. code-block:: python
@pytest.mark.parametrize(
"a, b",
[(3, 9), pytest.param((6, 36), marks=pytest.mark.xfail(reason="flaky")), (10, 100)],
)
def test_foo(a, b):
...
Passing command-line string to ``pytest.main()``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.0
Passing a command-line string to ``pytest.main()`` is deprecated:
.. code-block:: python
pytest.main("-v -s")
Pass a list instead:
.. code-block:: python
pytest.main(["-v", "-s"])
By passing a string, users expect that pytest will interpret that command-line using the shell rules they are working
on (for example ``bash`` or ``Powershell``), but this is very hard/impossible to do in a portable way.
``yield`` tests
~~~~~~~~~~~~~~~
.. deprecated:: 3.0
pytest supports ``yield``-style tests, where a test function actually ``yield`` functions and values
that are then turned into proper test methods. Example:
.. code-block:: python
def check(x, y):
assert x ** x == y
def test_squared():
yield check, 2, 4
yield check, 3, 9
This would result into two actual test functions being generated.
This form of test function doesn't support fixtures properly, and users should switch to ``pytest.mark.parametrize``:
.. code-block:: python
@pytest.mark.parametrize("x, y", [(2, 4), (3, 9)])
def test_squared(x, y):
assert x ** x == y
``pytest_funcarg__`` prefix
~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.0
In very early pytest versions fixtures could be defined using the ``pytest_funcarg__`` prefix:
.. code-block:: python
def pytest_funcarg__data():
return SomeData()
Switch over to the ``@pytest.fixture`` decorator:
.. code-block:: python
@pytest.fixture
def data():
return SomeData()
[pytest] section in setup.cfg files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.0
``[pytest]`` sections in ``setup.cfg`` files should now be named ``[tool:pytest]``
to avoid conflicts with other distutils commands.
Result log (``--result-log``)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 3.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/>`_.
Removed Features
----------------
As stated in our :ref:`backwards-compatibility` policy, deprecated features are removed only in major releases after
an appropriate period of deprecation has passed.
Reinterpretation mode (``--assert=reinterp``)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*Removed in version 3.0.*
Reinterpretation mode has now been removed and only plain and rewrite
mode are available, consequently the ``--assert=reinterp`` option is
no longer available. This also means files imported from plugins or
``conftest.py`` will not benefit from improved assertions by
default, you should use ``pytest.register_assert_rewrite()`` to
explicitly turn on assertion rewriting for those files.
Removed command-line options
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*Removed in version 3.0.*
The following deprecated commandline options were removed:
* ``--genscript``: no longer supported;
* ``--no-assert``: use ``--assert=plain`` instead;
* ``--nomagic``: use ``--assert=plain`` instead;
* ``--report``: use ``-r`` instead;
py.test-X* entry points
~~~~~~~~~~~~~~~~~~~~~~~
*Removed in version 3.0.*
Removed all ``py.test-X*`` entry points. The versioned, suffixed entry points
were never documented and a leftover from a pre-virtualenv era. These entry
points also created broken entry points in wheels, so removing them also
removes a source of confusion for users.

View File

@@ -1,6 +1,6 @@
from pytest import raises
import _pytest._code
import py
import six
def otherfunc(a, b):
@@ -177,7 +177,7 @@ def test_dynamic_compile_shows_nicely():
name = "abc-123"
module = imp.new_module(name)
code = _pytest._code.compile(src, name, "exec")
py.builtin.exec_(code, module.__dict__)
six.exec_(code, module.__dict__)
sys.modules[name] = module
module.foo()
@@ -247,7 +247,7 @@ class TestCustomAssertMsg(object):
b = 2
assert (
A.a == b
), "A.a appears not to be b\n" "or does not appear to be b\none of those"
), "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):

View File

@@ -10,4 +10,4 @@ def pytest_runtest_setup(item):
return
mod = item.getparent(pytest.Module).obj
if hasattr(mod, "hello"):
print("mod.hello %r" % (mod.hello,))
print("mod.hello {!r}".format(mod.hello))

View File

@@ -1,4 +1,3 @@
hello = "world"

View File

@@ -1,4 +1,3 @@
import py
failure_demo = py.path.local(__file__).dirpath("failure_demo.py")

View File

@@ -1,4 +1,3 @@
import pytest

View File

@@ -200,17 +200,17 @@ You can ask which markers exist for your test suite - the list includes our just
$ pytest --markers
@pytest.mark.webtest: mark a test as a webtest.
@pytest.mark.filterwarnings(warning): add a warning filter to the given test. see http://pytest.org/latest/warnings.html#pytest-mark-filterwarnings
@pytest.mark.filterwarnings(warning): add a warning filter to the given test. see https://docs.pytest.org/en/latest/warnings.html#pytest-mark-filterwarnings
@pytest.mark.skip(reason=None): skip the given test function with an optional reason. Example: skip(reason="no way of currently testing this") skips the test.
@pytest.mark.skipif(condition): skip the given test function if eval(condition) results in a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform. see http://pytest.org/latest/skipping.html
@pytest.mark.skipif(condition): skip the given test function if eval(condition) results in a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform. see https://docs.pytest.org/en/latest/skipping.html
@pytest.mark.xfail(condition, reason=None, run=True, raises=None, strict=False): mark the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See http://pytest.org/latest/skipping.html
@pytest.mark.xfail(condition, reason=None, run=True, raises=None, strict=False): mark the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See https://docs.pytest.org/en/latest/skipping.html
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see http://pytest.org/latest/parametrize.html for more info and examples.
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see https://docs.pytest.org/en/latest/parametrize.html for more info and examples.
@pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures
@pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see https://docs.pytest.org/en/latest/fixture.html#usefixtures
@pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
@@ -376,17 +376,17 @@ The ``--markers`` option always gives you a list of available markers::
$ pytest --markers
@pytest.mark.env(name): mark test to run only on named environment
@pytest.mark.filterwarnings(warning): add a warning filter to the given test. see http://pytest.org/latest/warnings.html#pytest-mark-filterwarnings
@pytest.mark.filterwarnings(warning): add a warning filter to the given test. see https://docs.pytest.org/en/latest/warnings.html#pytest-mark-filterwarnings
@pytest.mark.skip(reason=None): skip the given test function with an optional reason. Example: skip(reason="no way of currently testing this") skips the test.
@pytest.mark.skipif(condition): skip the given test function if eval(condition) results in a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform. see http://pytest.org/latest/skipping.html
@pytest.mark.skipif(condition): skip the given test function if eval(condition) results in a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform. see https://docs.pytest.org/en/latest/skipping.html
@pytest.mark.xfail(condition, reason=None, run=True, raises=None, strict=False): mark the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See http://pytest.org/latest/skipping.html
@pytest.mark.xfail(condition, reason=None, run=True, raises=None, strict=False): mark the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See https://docs.pytest.org/en/latest/skipping.html
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see http://pytest.org/latest/parametrize.html for more info and examples.
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see https://docs.pytest.org/en/latest/parametrize.html for more info and examples.
@pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures
@pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see https://docs.pytest.org/en/latest/fixture.html#usefixtures
@pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.

View File

@@ -2,9 +2,10 @@
module containing a parametrized tests testing cross-python
serialization via the pickle module.
"""
import textwrap
import py
import pytest
import _pytest._code
pythonlist = ["python2.7", "python3.4", "python3.5"]
@@ -24,42 +25,44 @@ class Python(object):
def __init__(self, version, picklefile):
self.pythonpath = py.path.local.sysfind(version)
if not self.pythonpath:
pytest.skip("%r not found" % (version,))
pytest.skip("{!r} not found".format(version))
self.picklefile = picklefile
def dumps(self, obj):
dumpfile = self.picklefile.dirpath("dump.py")
dumpfile.write(
_pytest._code.Source(
"""
import pickle
f = open(%r, 'wb')
s = pickle.dump(%r, f, protocol=2)
f.close()
"""
% (str(self.picklefile), obj)
textwrap.dedent(
"""\
import pickle
f = open({!r}, 'wb')
s = pickle.dump({!r}, f, protocol=2)
f.close()
""".format(
str(self.picklefile), obj
)
)
)
py.process.cmdexec("%s %s" % (self.pythonpath, dumpfile))
py.process.cmdexec("{} {}".format(self.pythonpath, dumpfile))
def load_and_is_true(self, expression):
loadfile = self.picklefile.dirpath("load.py")
loadfile.write(
_pytest._code.Source(
"""
import pickle
f = open(%r, 'rb')
obj = pickle.load(f)
f.close()
res = eval(%r)
if not res:
raise SystemExit(1)
"""
% (str(self.picklefile), expression)
textwrap.dedent(
"""\
import pickle
f = open({!r}, 'rb')
obj = pickle.load(f)
f.close()
res = eval({!r})
if not res:
raise SystemExit(1)
""".format(
str(self.picklefile), expression
)
)
)
print(loadfile)
py.process.cmdexec("%s %s" % (self.pythonpath, loadfile))
py.process.cmdexec("{} {}".format(self.pythonpath, loadfile))
@pytest.mark.parametrize("obj", [42, {}, {1: 3}])

View File

@@ -413,7 +413,7 @@ Running it results in some skips if we don't have all the python interpreters in
. $ pytest -rs -q multipython.py
...sss...sssssssss...sss... [100%]
========================= short test summary info ==========================
SKIP [15] $REGENDOC_TMPDIR/CWD/multipython.py:28: 'python3.4' not found
SKIP [15] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.4' not found
12 passed, 15 skipped in 0.12 seconds
Indirect parametrization of optional implementations/imports

View File

@@ -100,19 +100,21 @@ Changing naming conventions
You can configure different naming conventions by setting
the :confval:`python_files`, :confval:`python_classes` and
:confval:`python_functions` configuration options. Example::
:confval:`python_functions` configuration options.
Here is an example::
# content of pytest.ini
# Example 1: have pytest look for "check" instead of "test"
# can also be defined in tox.ini or setup.cfg file, although the section
# name in setup.cfg files should be "tool:pytest"
[pytest]
python_files=check_*.py
python_classes=Check
python_functions=*_check
python_files = check_*.py
python_classes = Check
python_functions = *_check
This would make ``pytest`` look for tests in files that match the ``check_*
.py`` glob-pattern, ``Check`` prefixes in classes, and functions and methods
that match ``*_check``. For example, if we have::
that match ``*_check``. For example, if we have::
# content of check_myapp.py
class CheckMyApp(object):
@@ -121,7 +123,7 @@ that match ``*_check``. For example, if we have::
def complex_check(self):
pass
then the test collection looks like this::
The test collection would look like this::
$ pytest --collect-only
=========================== test session starts ============================
@@ -136,11 +138,19 @@ then the test collection looks like this::
======================= no tests ran in 0.12 seconds =======================
You can check for multiple glob patterns by adding a space between the patterns::
# Example 2: have pytest look for files with "test" and "example"
# content of pytest.ini, tox.ini, or setup.cfg file (replace "pytest"
# with "tool:pytest" for setup.cfg)
[pytest]
python_files = test_*.py example_*.py
.. note::
the ``python_functions`` and ``python_classes`` options has no effect
for ``unittest.TestCase`` test discovery because pytest delegates
detection of test case methods to unittest code.
discovery of test case methods to unittest code.
Interpreting cmdline arguments as Python packages
-----------------------------------------------------

View File

@@ -423,7 +423,7 @@ get on the terminal - we are working on that)::
name = "abc-123"
module = imp.new_module(name)
code = _pytest._code.compile(src, name, "exec")
py.builtin.exec_(code, module.__dict__)
six.exec_(code, module.__dict__)
sys.modules[name] = module
> module.foo()
@@ -582,7 +582,7 @@ get on the terminal - we are working on that)::
b = 2
> assert (
A.a == b
), "A.a appears not to be b\n" "or does not appear to be b\none of those"
), "A.a appears not to be b\nor does not appear to be b\none of those"
E AssertionError: A.a appears not to be b
E or does not appear to be b
E one of those
@@ -613,9 +613,9 @@ get on the terminal - we are working on that)::
failure_demo.py:261: AssertionError
============================= warnings summary =============================
<undetermined location>
Metafunc.addcall is deprecated and scheduled to be removed in pytest 4.0.
Please use Metafunc.parametrize instead.
$REGENDOC_TMPDIR/assertion/failure_demo.py:24: RemovedInPytest4Warning: Metafunc.addcall is deprecated and scheduled to be removed in pytest 4.0.
Please use Metafunc.parametrize instead.
metafunc.addcall(funcargs=dict(param1=3, param2=6))
-- Docs: http://doc.pytest.org/en/latest/warnings.html
-- Docs: https://docs.pytest.org/en/latest/warnings.html
================== 42 failed, 1 warnings in 0.12 seconds ===================

View File

@@ -574,7 +574,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, record_xml_property, recwarn, tmpdir, tmpdir_factory
> available fixtures: cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, monkeypatch, pytestconfig, record_property, record_xml_attribute, record_xml_property, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory
> use 'pytest --fixtures [testpath]' for help on them.
$REGENDOC_TMPDIR/b/test_error.py:1
@@ -852,6 +852,8 @@ In that order.
can be changed between releases (even bug fixes) so it shouldn't be relied on for scripting
or automation.
.. _freezing-pytest:
Freezing pytest
---------------

View File

@@ -171,6 +171,7 @@ to cause the decorated ``smtp_connection`` fixture function to only be invoked
once per test *module* (the default is to invoke once per test *function*).
Multiple test functions in a test module will thus
each receive the same ``smtp_connection`` fixture instance, thus saving time.
Possible values for ``scope`` are: ``function``, ``class``, ``module``, ``package`` or ``session``.
The next example puts the fixture function into a separate ``conftest.py`` file
so that tests from multiple test modules in the directory can
@@ -258,6 +259,11 @@ instance, you can simply declare it:
Finally, the ``class`` scope will invoke the fixture once per test *class*.
.. note::
Pytest will only cache one instance of a fixture at a time.
This means that when using a parametrized fixture, pytest may invoke a fixture more than once in the given scope.
``package`` scope (experimental)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@@ -7,7 +7,7 @@ pytest-2.3: reasoning for fixture/funcarg evolution
**Target audience**: Reading this document requires basic knowledge of
python testing, xUnit setup methods and the (previous) basic pytest
funcarg mechanism, see http://pytest.org/2.2.4/funcargs.html
funcarg mechanism, see https://docs.pytest.org/en/latest/historical-notes.html#funcargs-and-pytest-funcarg.
If you are new to pytest, then you can simply ignore this
section and read the other sections.

View File

@@ -1,7 +1,7 @@
Installation and Getting Started
===================================
**Pythons**: Python 2.7, 3.4, 3.5, 3.6, Jython, PyPy-2.3
**Pythons**: Python 2.7, 3.4, 3.5, 3.6, 3.7, Jython, PyPy-2.3
**Platforms**: Unix/Posix and Windows

View File

@@ -4,6 +4,27 @@
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.
First you need to place a ``setup.py`` file in the root of your package with the following minimum content::
from setuptools import setup, find_packages
setup(name="PACKAGENAME", packages=find_packages())
Where ``PACKAGENAME`` is the name of your package. You can then install your package in "editable" mode by running from the same directory::
pip install -e .
which lets you change your source code (both tests and application) and rerun tests at will.
This is similar to running ``python setup.py develop`` or ``conda develop`` in that it installs
your package using a symlink to your development code.
.. _`test discovery`:
.. _`Python test discovery`:
@@ -177,19 +198,6 @@ Note that this layout also works in conjunction with the ``src`` layout mentione
tox
------
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.
You can then install your package in "editable" mode::
pip install -e .
which lets you change your source code (both tests and application) and rerun tests at will.
This is similar to running ``python setup.py develop`` or ``conda develop`` in that it installs
your package using a symlink to your development code.
Once you are done with your work and want to make sure that your actual
package passes all tests you may want to look into `tox`_, the
virtualenv test automation tool and its `pytest support
@@ -282,7 +290,7 @@ your own setuptools Test command for invoking pytest.
setup(
# ...,
tests_require=["pytest"],
cmdclass={"test": PyTest},
cmdclass={"pytest": PyTest},
)
Now if you run::

View File

@@ -175,3 +175,13 @@ Previous to version 2.4 to set a break point in code one needed to use ``pytest.
This is no longer needed and one can use the native ``import pdb;pdb.set_trace()`` call directly.
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.
Users should just ``import pytest`` and access those objects using the ``pytest`` module.

View File

@@ -52,6 +52,8 @@ should add ``--strict`` to ``addopts``:
serial
.. _marker-revamp:
Marker revamp and iteration
---------------------------

View File

@@ -69,17 +69,15 @@ You may also discover more plugins through a `pytest- pypi.python.org search`_.
Requiring/Loading plugins in a test module or conftest file
-----------------------------------------------------------
You can require plugins in a test module or a conftest file like this::
You can require plugins in a test module or a conftest file like this:
pytest_plugins = "myapp.testsupport.myplugin",
.. code-block:: python
pytest_plugins = ("myapp.testsupport.myplugin",)
When the test module or conftest plugin is loaded the specified plugins
will be loaded as well.
pytest_plugins = "myapp.testsupport.myplugin"
which will import the specified module as a ``pytest`` plugin.
.. note::
Requiring plugins using a ``pytest_plugins`` variable in non-root
``conftest.py`` files is deprecated. See

View File

@@ -84,6 +84,12 @@ pytest.warns
.. autofunction:: pytest.warns(expected_warning: Exception, [match])
:with:
pytest.freeze_includes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**Tutorial**: :ref:`freezing-pytest`.
.. autofunction:: pytest.freeze_includes
.. _`marks ref`:
@@ -172,7 +178,7 @@ Mark a test function as using the given fixture names.
.. warning::
This mark can be used with *test functions* only, having no affect when applied
This mark has no effect when applied
to a **fixture** function.
.. py:function:: pytest.mark.usefixtures(*names)
@@ -460,7 +466,7 @@ To use it, include in your top-most ``conftest.py`` file::
.. autoclass:: Testdir()
:members: runpytest,runpytest_subprocess,runpytest_inprocess,makeconftest,makepyfile
:members:
.. autoclass:: RunResult()
:members:
@@ -611,6 +617,8 @@ Session related reporting hooks:
.. autofunction:: pytest_terminal_summary
.. autofunction:: pytest_fixture_setup
.. autofunction:: pytest_fixture_post_finalizer
.. autofunction:: pytest_logwarning
.. autofunction:: pytest_warning_captured
And here is the central hook for reporting about
test execution:
@@ -866,6 +874,11 @@ Contains comma-separated list of modules that should be loaded as plugins:
export PYTEST_PLUGINS=mymodule.plugin,xdist
PYTEST_DISABLE_PLUGIN_AUTOLOAD
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When set, disables plugin auto-loading through setuptools entrypoints. Only explicitly specified plugins will be
loaded.
PYTEST_CURRENT_TEST
~~~~~~~~~~~~~~~~~~~
@@ -935,6 +948,7 @@ passed multiple times. The expected format is ``name=value``. For example::
* ``classic``: classic pytest output.
* ``progress``: like classic pytest output, but with a progress indicator.
* ``count``: like progress, but shows progress as the number of tests completed instead of a percent.
The default is ``progress``, but you can fallback to ``classic`` if you prefer or
the new mode is causing unexpected problems:
@@ -968,6 +982,7 @@ passed multiple times. The expected format is ``name=value``. For example::
* ``skip`` skips tests with an empty parameterset (default)
* ``xfail`` marks tests with an empty parameterset as xfail(run=False)
* ``fail_at_collect`` raises an exception if parametrize collects an empty parameter set
.. code-block:: ini
@@ -1229,7 +1244,8 @@ passed multiple times. The expected format is ``name=value``. For example::
.. confval:: python_classes
One or more name prefixes or glob-style patterns determining which classes
are considered for test collection. By default, pytest will consider any
are considered for test collection. Search for multiple glob patterns by
adding a space between patterns. By default, pytest will consider any
class prefixed with ``Test`` as a test collection. Here is an example of how
to collect tests from classes that end in ``Suite``:
@@ -1246,15 +1262,33 @@ passed multiple times. The expected format is ``name=value``. For example::
.. confval:: python_files
One or more Glob-style file patterns determining which python files
are considered as test modules. By default, pytest will consider
any file matching with ``test_*.py`` and ``*_test.py`` globs as a test
module.
are considered as test modules. Search for multiple glob patterns by
adding a space between patterns:
.. code-block:: ini
[pytest]
python_files = test_*.py check_*.py example_*.py
Or one per line:
.. code-block:: ini
[pytest]
python_files =
test_*.py
check_*.py
example_*.py
By default, files matching ``test_*.py`` and ``*_test.py`` will be considered
test modules.
.. confval:: python_functions
One or more name prefixes or glob-patterns determining which test functions
and methods are considered tests. By default, pytest will consider any
and methods are considered tests. Search for multiple glob patterns by
adding a space between patterns. By default, pytest will consider any
function prefixed with ``test`` as a test. Here is an example of how
to collect test functions and methods that end in ``_test``:

View File

@@ -136,12 +136,6 @@ You can use the ``skipif`` marker (as any other marker) on classes::
If the condition is ``True``, this marker will produce a skip result for
each of the test methods of that class.
.. warning::
The use of ``skipif`` on classes that use inheritance is strongly
discouraged. `A Known bug <https://github.com/pytest-dev/pytest/issues/568>`_
in pytest's markers may cause unexpected behavior in super classes.
If you want to skip all test functions of a module, you may use
the ``pytestmark`` name on the global level:
@@ -283,7 +277,7 @@ on a particular platform::
~~~~~~~~~~~~~~~~~~~~
If you want to be more specific as to why the test is failing, you can specify
a single exception, or a list of exceptions, in the ``raises`` argument.
a single exception, or a tuple of exceptions, in the ``raises`` argument.
.. code-block:: python

View File

@@ -14,6 +14,9 @@ Talks and Tutorials
Books
---------------------------------------------
- `pytest Quick Start Guide, by Bruno Oliveira (2018)
<https://www.packtpub.com/web-development/pytest-quick-start-guide>`_.
- `Python Testing with pytest, by Brian Okken (2017)
<https://pragprog.com/book/bopytest/python-testing-with-pytest>`_.

View File

@@ -5,6 +5,76 @@
Temporary directories and files
================================================
The ``tmp_path`` fixture
------------------------
.. versionadded:: 3.9
You can use the ``tmpdir`` fixture which will
provide a temporary directory unique to the test invocation,
created in the `base temporary directory`_.
``tmpdir`` is a ``pathlib/pathlib2.Path`` object. Here is an example test usage:
.. code-block:: python
# content of test_tmp_path.py
import os
CONTENT = u"content"
def test_create_file(tmp_path):
d = tmp_path / "sub"
d.mkdir()
p = d / "hello.txt"
p.write_text(CONTENT)
assert p.read_text() == CONTENT
assert len(list(tmp_path.iterdir())) == 1
assert 0
Running this would result in a passed test except for the last
``assert 0`` line which we use to look at values::
$ pytest test_tmp_path.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 item
test_tmp_path.py F [100%]
================================= FAILURES =================================
_____________________________ test_create_file _____________________________
tmp_path = PosixPath('PYTEST_TMPDIR/test_create_file0')
def test_create_file(tmp_path):
d = tmp_path / "sub"
d.mkdir()
p = d / "hello.txt"
p.write_text(CONTENT)
assert p.read_text() == CONTENT
assert len(list(tmp_path.iterdir())) == 1
> assert 0
E assert 0
test_tmp_path.py:13: AssertionError
========================= 1 failed in 0.12 seconds =========================
The ``tmp_path_factory`` fixture
--------------------------------
.. versionadded:: 3.9
The ``tmp_path_facotry`` is a session-scoped fixture which can be used
to create arbitrary temporary directories from any other fixture or test.
its intended to replace ``tmpdir_factory`` and returns :class:`pathlib.Path` instances.
The 'tmpdir' fixture
--------------------

View File

@@ -22,16 +22,15 @@ Almost all ``unittest`` features are supported:
* ``@unittest.skip`` style decorators;
* ``setUp/tearDown``;
* ``setUpClass/tearDownClass()``;
* ``setUpClass/tearDownClass``;
* ``setUpModule/tearDownModule``;
.. _`load_tests protocol`: https://docs.python.org/3/library/unittest.html#load-tests-protocol
.. _`setUpModule/tearDownModule`: https://docs.python.org/3/library/unittest.html#setupmodule-and-teardownmodule
.. _`subtests`: https://docs.python.org/3/library/unittest.html#distinguishing-test-iterations-using-subtests
Up to this point pytest does not have support for the following features:
* `load_tests protocol`_;
* `setUpModule/tearDownModule`_;
* `subtests`_;
Benefits out of the box

View File

@@ -140,6 +140,49 @@ will be shown (because KeyboardInterrupt is caught by pytest). By using this
option you make sure a trace is shown.
.. _`pytest.detailed_failed_tests_usage`:
Detailed summary report
-----------------------
.. versionadded:: 2.9
The ``-r`` flag can be used to display test results summary 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.
Example::
$ pytest -ra
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
rootdir: $REGENDOC_TMPDIR, inifile:
collected 0 items
======================= no tests ran in 0.12 seconds =======================
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:
- ``f`` - failed
- ``E`` - error
- ``s`` - skipped
- ``x`` - xfailed
- ``X`` - xpassed
- ``p`` - passed
- ``P`` - passed with output
- ``a`` - all except ``pP``
More than one character can be used, so for example to only see failed and skipped tests, you can execute::
$ pytest -rfs
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
rootdir: $REGENDOC_TMPDIR, inifile:
collected 0 items
======================= no tests ran in 0.12 seconds =======================
.. _pdb-option:
Dropping to PDB_ (Python Debugger) on failures
@@ -212,7 +255,7 @@ Pytest supports the use of ``breakpoint()`` with the following behaviours:
- When ``breakpoint()`` is called and ``PYTHONBREAKPOINT`` is set to the default value, pytest will use the custom internal PDB trace UI instead of the system default ``Pdb``.
- When tests are complete, the system will default back to the system ``Pdb`` trace UI.
- If ``--pdb`` is called on execution of pytest, the custom internal Pdb trace UI is used on ``bothbreakpoint()`` and failed tests/unhandled exceptions.
- If ``--pdb`` is called on execution of pytest, the custom internal Pdb trace UI is used on both ``breakpoint()`` and failed tests/unhandled exceptions.
- If ``--pdbcls`` is used, the custom class debugger will be executed when a test fails (as expected within existing behaviour), but also when ``breakpoint()`` is called from within a test, the custom class debugger will be instantiated.
.. _durations:
@@ -226,6 +269,7 @@ To get a list of the slowest 10 test durations::
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.
Creating JUnitXML format files
----------------------------------------------------

View File

@@ -29,15 +29,12 @@ Running pytest now produces this output::
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
warnings.warn(UserWarning("api v1, should use functions from v2"))
$REGENDOC_TMPDIR/test_show_warnings.py:4: UserWarning: api v1, should use functions from v2
warnings.warn(UserWarning("api v1, should use functions from v2"))
-- Docs: http://doc.pytest.org/en/latest/warnings.html
-- Docs: https://docs.pytest.org/en/latest/warnings.html
=================== 1 passed, 1 warnings in 0.12 seconds ===================
Pytest by default catches all warnings except for ``DeprecationWarning`` and ``PendingDeprecationWarning``.
The ``-W`` flag can be passed to control which warnings will be displayed or even turn
them into errors::
@@ -78,6 +75,59 @@ Both ``-W`` command-line option and ``filterwarnings`` ini option are based on P
`-W option`_ and `warnings.simplefilter`_, so please refer to those sections in the Python
documentation for other examples and advanced usage.
Disabling warning summary
-------------------------
Although not recommended, you can use the ``--disable-warnings`` command-line option to suppress the
warning summary entirely from the test run output.
Disabling warning capture entirely
----------------------------------
This plugin is enabled by default but can be disabled entirely in your ``pytest.ini`` file with:
.. code-block:: ini
[pytest]
addopts = -p no:warnings
Or passing ``-p no:warnings`` in the command-line. This might be useful if your test suites handles warnings
using an external system.
.. _`deprecation-warnings`:
DeprecationWarning and PendingDeprecationWarning
------------------------------------------------
.. versionadded:: 3.8
.. versionchanged:: 3.9
By default pytest will display ``DeprecationWarning`` and ``PendingDeprecationWarning``.
Sometimes it is useful to hide some specific deprecation warnings that happen in code that you have no control over
(such as third-party libraries), in which case you might use the standard warning filters options (ini or marks).
For example:
.. code-block:: ini
[pytest]
filterwarnings =
ignore:.*U.*mode is deprecated:DeprecationWarning
.. note::
If warnings are configured at the interpreter level, using
the `PYTHONWARNINGS <https://docs.python.org/3/using/cmdline.html#envvar-PYTHONWARNINGS>`_ environment variable or the
``-W`` command-line option, pytest will not configure any filters by default.
.. note::
This feature makes pytest more compliant with `PEP-0506 <https://www.python.org/dev/peps/pep-0565/#recommended-filter-settings-for-test-runners>`_ which suggests that those warnings should
be shown by default by test runners, but pytest doesn't follow ``PEP-0506`` completely because resetting all
warning filters like suggested in the PEP will break existing test suites that configure warning filters themselves
by calling ``warnings.simplefilter`` (see issue `#2430 <https://github.com/pytest-dev/pytest/issues/2430>`_
for an example of that).
.. _`filterwarnings`:
@@ -144,18 +194,6 @@ decorator or to all tests in a module by setting the ``pytestmark`` variable:
.. _`pytest-warnings`: https://github.com/fschulze/pytest-warnings
Disabling warning capture
-------------------------
This feature is enabled by default but can be disabled entirely in your ``pytest.ini`` file with:
.. code-block:: ini
[pytest]
addopts = -p no:warnings
Or passing ``-p no:warnings`` in the command-line.
.. _`asserting warnings`:
.. _assertwarnings:
@@ -296,3 +334,53 @@ You can also use it as a contextmanager::
def test_global():
with pytest.deprecated_call():
myobject.deprecated_method()
.. _internal-warnings:
Internal pytest warnings
------------------------
.. versionadded:: 3.8
pytest may generate its own warnings in some situations, such as improper usage or deprecated features.
For example, pytest will emit a warning if it encounters a class that matches :confval:`python_classes` but also
defines an ``__init__`` constructor, as this prevents the class from being instantiated:
.. code-block:: python
# content of test_pytest_warnings.py
class Test:
def __init__(self):
pass
def test_foo(self):
assert 1 == 1
::
$ pytest test_pytest_warnings.py -q
============================= warnings summary =============================
$REGENDOC_TMPDIR/test_pytest_warnings.py:1: PytestWarning: cannot collect test class 'Test' because it has a __init__ constructor
class Test:
-- Docs: https://docs.pytest.org/en/latest/warnings.html
1 warnings in 0.12 seconds
These warnings might be filtered using the same builtin mechanisms used to filter other types of warnings.
Please read our :ref:`backwards-compatibility` to learn how we proceed about deprecating and eventually removing
features.
The following warning types ares used by pytest and are part of the public API:
.. autoclass:: pytest.PytestWarning
.. autoclass:: pytest.PytestDeprecationWarning
.. autoclass:: pytest.RemovedInPytest4Warning
.. autoclass:: pytest.PytestExperimentalApiWarning

View File

@@ -418,12 +418,23 @@ additionally it is possible to copy examples for a example folder before running
test_example.py .. [100%]
============================= warnings summary =============================
test_example.py::test_plugin
$REGENDOC_TMPDIR/test_example.py:4: PytestExerimentalApiWarning: testdir.copy_example is an experimental api that may change over time
testdir.copy_example("test_example.py")
$REGENDOC_TMPDIR/test_example.py:4: PytestExperimentalApiWarning: testdir.copy_example is an experimental api that may change over time
testdir.copy_example("test_example.py")
$PYTHON_PREFIX/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Class is deprecated, please use pytest.Class instead
return getattr(object, name, default)
$PYTHON_PREFIX/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.File is deprecated, please use pytest.File instead
return getattr(object, name, default)
$PYTHON_PREFIX/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Function is deprecated, please use pytest.Function instead
return getattr(object, name, default)
$PYTHON_PREFIX/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Instance is deprecated, please use pytest.Instance instead
return getattr(object, name, default)
$PYTHON_PREFIX/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Item is deprecated, please use pytest.Item instead
return getattr(object, name, default)
$PYTHON_PREFIX/lib/python3.6/site-packages/_pytest/compat.py:321: RemovedInPytest4Warning: usage of Session.Module is deprecated, please use pytest.Module instead
return getattr(object, name, default)
-- Docs: http://doc.pytest.org/en/latest/warnings.html
=================== 2 passed, 1 warnings in 0.12 seconds ===================
-- Docs: https://docs.pytest.org/en/latest/warnings.html
=================== 2 passed, 7 warnings in 0.12 seconds ===================
For more information about the result object that ``runpytest()`` returns, and
the methods that it provides please check out the :py:class:`RunResult

View File

@@ -1,6 +1,7 @@
[build-system]
requires = [
"setuptools",
# sync with setup.py until we discard non-pep-517/518
"setuptools>=30.3",
"setuptools-scm",
"wheel",
]
@@ -15,7 +16,12 @@ template = "changelog/_template.rst"
[[tool.towncrier.type]]
directory = "removal"
name = "Deprecations and Removals"
name = "Removals"
showcontent = true
[[tool.towncrier.type]]
directory = "deprecation"
name = "Deprecations"
showcontent = true
[[tool.towncrier.type]]

View File

@@ -1,8 +0,0 @@
REM skip "coveralls" run in PRs or forks
if "%TOXENV%" == "coveralls" (
if not defined COVERALLS_REPO_TOKEN (
echo skipping coveralls run because COVERALLS_REPO_TOKEN is not defined
exit /b 0
)
)
C:\Python36\python -m tox

View File

@@ -1,7 +0,0 @@
#!/usr/bin/env python
"""Used by .pre-commit-config.yaml"""
import sys
if __name__ == "__main__":
print(" ".join(sys.argv[1:]))
sys.exit(1)

View File

@@ -0,0 +1,10 @@
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
)

View File

@@ -9,11 +9,11 @@ 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:
http://doc.pytest.org/en/latest/changelog.html
https://docs.pytest.org/en/latest/changelog.html
For complete documentation, please visit:
http://docs.pytest.org
https://docs.pytest.org/en/latest/
As usual, you can upgrade from pypi via:

View File

@@ -7,7 +7,7 @@ This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at http://doc.pytest.org/en/latest/changelog.html.
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
Thanks to all who contributed to this release, among them:

View File

@@ -0,0 +1,11 @@
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
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,3 +1,55 @@
[metadata]
name = pytest
description = pytest: simple powerful testing with Python
long_description = file: README.rst
url = "https://docs.pytest.org/en/latest/"
project_urls =
Source=https://github.com/pytest-dev/pytest
Tracker=https://github.com/pytest-dev/pytest/issues
author = Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin and others
license = MIT license
license_file = LICENSE
keywords = test, unittest
classifiers =
Development Status :: 6 - Mature
Intended Audience :: Developers
License :: OSI Approved :: MIT License
Operating System :: POSIX
Operating System :: Microsoft :: Windows
Operating System :: MacOS :: MacOS X
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.5
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
platforms = unix, linux, osx, cygwin, win32
[options]
zip_safe = no
packages =
_pytest
_pytest.assertion
_pytest._code
_pytest.mark
_pytest.config
py_modules = pytest
python_requires = >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
[options.entry_points]
console_scripts =
pytest=pytest:main
py.test=pytest:main
[build_sphinx]
source-dir = doc/en/
build-dir = doc/build
@@ -13,8 +65,6 @@ universal = 1
ignore =
_pytest/_version.py
[metadata]
license_file = LICENSE
[devpi:upload]
formats = sdist.tgz,bdist_wheel

130
setup.py
View File

@@ -1,126 +1,34 @@
import os
import sys
import setuptools
import pkg_resources
from setuptools import setup
classifiers = [
"Development Status :: 6 - Mature",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: POSIX",
"Operating System :: Microsoft :: Windows",
"Operating System :: MacOS :: MacOS X",
"Topic :: Software Development :: Testing",
"Topic :: Software Development :: Libraries",
"Topic :: Utilities",
] + [
("Programming Language :: Python :: %s" % x)
for x in "2 2.7 3 3.4 3.5 3.6 3.7".split()
# 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",
"attrs>=17.4.0",
"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"',
]
with open("README.rst") as fd:
long_description = fd.read()
def get_environment_marker_support_level():
"""
Tests how well setuptools supports PEP-426 environment marker.
The first known release to support it is 0.7 (and the earliest on PyPI seems to be 0.7.2
so we're using that), see: https://setuptools.readthedocs.io/en/latest/history.html#id350
The support is later enhanced to allow direct conditional inclusions inside install_requires,
which is now recommended by setuptools. It first appeared in 36.2.0, went broken with 36.2.1, and
again worked since 36.2.2, so we're using that. See:
https://setuptools.readthedocs.io/en/latest/history.html#v36-2-2
https://github.com/pypa/setuptools/issues/1099
References:
* https://wheel.readthedocs.io/en/latest/index.html#defining-conditional-dependencies
* https://www.python.org/dev/peps/pep-0426/#environment-markers
* https://setuptools.readthedocs.io/en/latest/setuptools.html#declaring-platform-specific-dependencies
"""
try:
version = pkg_resources.parse_version(setuptools.__version__)
if version >= pkg_resources.parse_version("36.2.2"):
return 2
if version >= pkg_resources.parse_version("0.7.2"):
return 1
except Exception as exc:
sys.stderr.write("Could not test setuptool's version: %s\n" % exc)
# as of testing on 2018-05-26 fedora was on version 37* and debian was on version 33+
# we should consider erroring on those
return 0
# 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():
extras_require = {}
install_requires = [
"py>=1.5.0",
"six>=1.10.0",
"setuptools",
"attrs>=17.4.0",
"more-itertools>=4.0.0",
"atomicwrites>=1.0",
]
# 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")
environment_marker_support_level = get_environment_marker_support_level()
if environment_marker_support_level >= 2:
install_requires.append('funcsigs;python_version<"3.0"')
install_requires.append('pathlib2>=2.2.0;python_version<"3.6"')
install_requires.append('colorama;sys_platform=="win32"')
elif environment_marker_support_level == 1:
extras_require[':python_version<"3.0"'] = ["funcsigs"]
extras_require[':python_version<"3.6"'] = ["pathlib2>=2.2.0"]
extras_require[':sys_platform=="win32"'] = ["colorama"]
else:
if sys.platform == "win32":
install_requires.append("colorama")
if sys.version_info < (3, 0):
install_requires.append("funcsigs")
if sys.version_info < (3, 6):
install_requires.append("pathlib2>=2.2.0")
setup(
name="pytest",
description="pytest: simple powerful testing with Python",
long_description=long_description,
use_scm_version={"write_to": "src/_pytest/_version.py"},
url="http://pytest.org",
project_urls={
"Source": "https://github.com/pytest-dev/pytest",
"Tracker": "https://github.com/pytest-dev/pytest/issues",
},
license="MIT license",
platforms=["unix", "linux", "osx", "cygwin", "win32"],
author=(
"Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, "
"Floris Bruynooghe, Brianna Laugher, Florian Bruhin and others"
),
entry_points={"console_scripts": ["pytest=pytest:main", "py.test=pytest:main"]},
classifiers=classifiers,
keywords="test unittest",
# the following should be enabled for release
setup_requires=["setuptools-scm"],
setup_requires=["setuptools-scm", "setuptools>=30.3"],
package_dir={"": "src"},
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*",
install_requires=install_requires,
extras_require=extras_require,
packages=[
"_pytest",
"_pytest.assertion",
"_pytest._code",
"_pytest.mark",
"_pytest.config",
],
py_modules=["pytest"],
zip_safe=False,
install_requires=INSTALL_REQUIRES,
)

View File

@@ -1,4 +1,3 @@
"""allow bash-completion for argparse with argcomplete if installed
needs argcomplete>=0.5.6 for python 3.2/3.3 (older versions fail
to find the magic string, so _ARGCOMPLETE env. var is never set, and

View File

@@ -4,6 +4,7 @@ from .code import Code # noqa
from .code import ExceptionInfo # noqa
from .code import Frame # noqa
from .code import Traceback # noqa
from .code import filter_traceback # noqa
from .code import getrawcode # noqa
from .source import Source # noqa
from .source import compile_ as compile # noqa

View File

@@ -2,7 +2,7 @@
# CHANGES:
# - some_str is replaced, trying to create unicode strings
#
from __future__ import absolute_import, division, print_function
from __future__ import absolute_import, division, print_function, unicode_literals
import types
from six import text_type
@@ -51,17 +51,17 @@ def format_exception_only(etype, value):
pass
else:
filename = filename or "<string>"
lines.append(' File "%s", line %d\n' % (filename, lineno))
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(u" %s\n" % badline.strip())
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(" %s^\n" % "".join(caretspace))
lines.append(" {}^\n".format("".join(caretspace)))
value = msg
lines.append(_format_final_exc_line(stype, value))
@@ -72,9 +72,9 @@ 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 = "%s\n" % etype
line = "{}\n".format(etype)
else:
line = "%s: %s\n" % (etype, valuestr)
line = "{}: {}\n".format(etype, valuestr)
return line
@@ -83,7 +83,7 @@ def _some_str(value):
return text_type(value)
except Exception:
try:
return str(value)
return bytes(value).decode("UTF-8", "replace")
except Exception:
pass
return "<unprintable %s object>" % type(value).__name__
return "<unprintable {} object>".format(type(value).__name__)

View File

@@ -6,11 +6,14 @@ import traceback
from inspect import CO_VARARGS, CO_VARKEYWORDS
import attr
import pluggy
import re
from weakref import ref
import _pytest
from _pytest.compat import _PY2, _PY3, PY35, safe_str
from six import text_type
import py
import six
builtin_repr = repr
@@ -128,7 +131,7 @@ class Frame(object):
"""
f_locals = self.f_locals.copy()
f_locals.update(vars)
py.builtin.exec_(code, self.f_globals, f_locals)
six.exec_(code, self.f_globals, f_locals)
def repr(self, object):
""" return a 'safe' (non-recursive, one-line) string repr for 'object'
@@ -450,13 +453,35 @@ class ExceptionInfo(object):
tbfilter=True,
funcargs=False,
truncate_locals=True,
chain=True,
):
""" return str()able representation of this exception info.
showlocals: show locals per traceback entry
style: long|short|no|native traceback style
tbfilter: hide entries (where __tracebackhide__ is true)
"""
Return str()able representation of this exception info.
in case of style==native, tbfilter and showlocals is ignored.
:param bool showlocals:
Show locals per traceback entry.
Ignored if ``style=="native"``.
:param str style: long|short|no|native traceback style
:param bool abspath:
If paths should be changed to absolute or left unchanged.
:param bool tbfilter:
Hide entries that contain a local variable ``__tracebackhide__==True``.
Ignored if ``style=="native"``.
:param bool funcargs:
Show fixtures ("funcargs" for legacy purposes) per traceback entry.
:param bool truncate_locals:
With ``showlocals==True``, make sure locals can be safely represented as strings.
:param bool chain: if chained exceptions in Python 3 should be shown.
.. versionchanged:: 3.9
Added the ``chain`` parameter.
"""
if style == "native":
return ReprExceptionInfo(
@@ -475,6 +500,7 @@ class ExceptionInfo(object):
tbfilter=tbfilter,
funcargs=funcargs,
truncate_locals=truncate_locals,
chain=chain,
)
return fmt.repr_excinfo(self)
@@ -515,6 +541,7 @@ class FormattedExcinfo(object):
tbfilter = attr.ib(default=True)
funcargs = attr.ib(default=False)
truncate_locals = attr.ib(default=True)
chain = attr.ib(default=True)
astcache = attr.ib(default=attr.Factory(dict), init=False, repr=False)
def _getindent(self, source):
@@ -734,7 +761,7 @@ class FormattedExcinfo(object):
reprcrash = None
repr_chain += [(reprtraceback, reprcrash, descr)]
if e.__cause__ is not None:
if e.__cause__ is not None and self.chain:
e = e.__cause__
excinfo = (
ExceptionInfo((type(e), e, e.__traceback__))
@@ -742,7 +769,11 @@ class FormattedExcinfo(object):
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__:
elif (
e.__context__ is not None
and not e.__suppress_context__
and self.chain
):
e = e.__context__
excinfo = (
ExceptionInfo((type(e), e, e.__traceback__))
@@ -978,3 +1009,36 @@ else:
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
# for better maintenance
_PLUGGY_DIR = py.path.local(pluggy.__file__.rstrip("oc"))
# pluggy is either a package or a single module depending on the version
if _PLUGGY_DIR.basename == "__init__.py":
_PLUGGY_DIR = _PLUGGY_DIR.dirpath()
_PYTEST_DIR = py.path.local(_pytest.__file__).dirpath()
_PY_DIR = py.path.local(py.__file__).dirpath()
def filter_traceback(entry):
"""Return True if a TracebackEntry instance should be removed from tracebacks:
* dynamically generated code (no code to show up for it);
* internal traceback from pytest or its internal libraries, py and pluggy.
"""
# entry.path might sometimes return a str object when the entry
# points to dynamically generated code
# see https://bitbucket.org/pytest-dev/py/issues/71
raw_filename = entry.frame.code.raw.co_filename
is_generated = "<" in raw_filename and ">" in raw_filename
if is_generated:
return False
# entry.path might point to a non-existing file, in which case it will
# also return a str object. see #1133
p = py.path.local(entry.path)
return (
not p.relto(_PLUGGY_DIR) and not p.relto(_PYTEST_DIR) and not p.relto(_PY_DIR)
)

View File

@@ -7,6 +7,7 @@ import linecache
import sys
import six
import inspect
import textwrap
import tokenize
import py
@@ -23,7 +24,6 @@ class Source(object):
def __init__(self, *parts, **kwargs):
self.lines = lines = []
de = kwargs.get("deindent", True)
rstrip = kwargs.get("rstrip", True)
for part in parts:
if not part:
partlines = []
@@ -33,11 +33,6 @@ class Source(object):
partlines = [x.rstrip("\n") for x in part]
elif isinstance(part, six.string_types):
partlines = part.split("\n")
if rstrip:
while partlines:
if partlines[-1].strip():
break
partlines.pop()
else:
partlines = getsource(part, deindent=de).lines
if de:
@@ -115,17 +110,10 @@ class Source(object):
ast, start, end = getstatementrange_ast(lineno, self)
return start, end
def deindent(self, offset=None):
""" return a new source object deindented by offset.
If offset is None then guess an indentation offset from
the first non-blank line. Subsequent lines which have a
lower indentation offset will be copied verbatim as
they are assumed to be part of multilines.
"""
# XXX maybe use the tokenizer to properly handle multiline
# strings etc.pp?
def deindent(self):
"""return a new source object deindented."""
newsource = Source()
newsource.lines[:] = deindent(self.lines, offset)
newsource.lines[:] = deindent(self.lines)
return newsource
def isparseable(self, deindent=True):
@@ -268,47 +256,8 @@ def getsource(obj, **kwargs):
return Source(strsrc, **kwargs)
def deindent(lines, offset=None):
if offset is None:
for line in lines:
line = line.expandtabs()
s = line.lstrip()
if s:
offset = len(line) - len(s)
break
else:
offset = 0
if offset == 0:
return list(lines)
newlines = []
def readline_generator(lines):
for line in lines:
yield line + "\n"
it = readline_generator(lines)
try:
for _, _, (sline, _), (eline, _), _ in tokenize.generate_tokens(
lambda: next(it)
):
if sline > len(lines):
break # End of input reached
if sline > len(newlines):
line = lines[sline - 1].expandtabs()
if line.lstrip() and line[:offset].isspace():
line = line[offset:] # Deindent
newlines.append(line)
for i in range(sline, eline):
# Don't deindent continuing lines of
# multiline tokens (i.e. multiline strings)
newlines.append(lines[i])
except (IndentationError, tokenize.TokenError):
pass
# Add any lines we didn't see. E.g. if an exception was raised.
newlines.extend(lines[len(newlines) :])
return newlines
def deindent(lines):
return textwrap.dedent("\n".join(lines)).splitlines()
def get_statement_startend2(lineno, node):

View File

@@ -8,6 +8,7 @@ import marshal
import os
import re
import six
import string
import struct
import sys
import types
@@ -16,7 +17,9 @@ import atomicwrites
import py
from _pytest.assertion import util
from _pytest.pathlib import PurePath
from _pytest.compat import spec_from_file_location
from _pytest.pathlib import fnmatch_ex
# pytest caches rewritten pycs in __pycache__.
if hasattr(imp, "get_tag"):
@@ -45,14 +48,6 @@ else:
return ast.Call(a, b, c, None, None)
if sys.version_info >= (3, 4):
from importlib.util import spec_from_file_location
else:
def spec_from_file_location(*_, **__):
return None
class AssertionRewritingHook(object):
"""PEP302 Import hook which rewrites asserts."""
@@ -64,12 +59,27 @@ class AssertionRewritingHook(object):
self._rewritten_names = set()
self._register_with_pkg_resources()
self._must_rewrite = set()
# flag to guard against trying to rewrite a pyc file while we are already writing another pyc file,
# which might result in infinite recursion (#3506)
self._writing_pyc = False
self._basenames_to_check_rewrite = {"conftest"}
self._marked_for_rewrite_cache = {}
self._session_paths_checked = False
def set_session(self, session):
self.session = session
self._session_paths_checked = False
def _imp_find_module(self, name, path=None):
"""Indirection so we can mock calls to find_module originated from the hook during testing"""
return imp.find_module(name, path)
def find_module(self, name, path=None):
if self._writing_pyc:
return None
state = self.config._assertstate
if self._early_rewrite_bailout(name, state):
return None
state.trace("find_module called for: %s" % name)
names = name.rsplit(".", 1)
lastname = names[-1]
@@ -82,7 +92,7 @@ class AssertionRewritingHook(object):
pth = path[0]
if pth is None:
try:
fd, fn, desc = imp.find_module(lastname, path)
fd, fn, desc = self._imp_find_module(lastname, path)
except ImportError:
return None
if fd is not None:
@@ -151,12 +161,54 @@ class AssertionRewritingHook(object):
# Probably a SyntaxError in the test.
return None
if write:
_write_pyc(state, co, source_stat, pyc)
self._writing_pyc = True
try:
_write_pyc(state, co, source_stat, pyc)
finally:
self._writing_pyc = False
else:
state.trace("found cached rewritten pyc for %r" % (fn,))
self.modules[name] = co, pyc
return self
def _early_rewrite_bailout(self, name, state):
"""
This is a fast way to get out of rewriting modules. Profiling has
shown that the call to imp.find_module (inside of the find_module
from this class) is a major slowdown, so, this method tries to
filter what we're sure won't be rewritten before getting to it.
"""
if self.session is not None and not self._session_paths_checked:
self._session_paths_checked = True
for path in self.session._initialpaths:
# Make something as c:/projects/my_project/path.py ->
# ['c:', 'projects', 'my_project', 'path.py']
parts = str(path).split(os.path.sep)
# add 'path' to basenames to be checked.
self._basenames_to_check_rewrite.add(os.path.splitext(parts[-1])[0])
# Note: conftest already by default in _basenames_to_check_rewrite.
parts = name.split(".")
if parts[-1] in self._basenames_to_check_rewrite:
return False
# For matching the name it must be as if it was a filename.
path = PurePath(os.path.sep.join(parts) + ".py")
for pat in self.fnpats:
# if the pattern contains subdirectories ("tests/**.py" for example) we can't bail out based
# on the name alone because we need to match against the full path
if os.path.dirname(pat):
return False
if fnmatch_ex(pat, path):
return False
if self._is_marked_for_rewrite(name, state):
return False
state.trace("early skip of rewriting module: %s" % (name,))
return True
def _should_rewrite(self, name, fn_pypath, state):
# always rewrite conftest files
fn = str(fn_pypath)
@@ -176,12 +228,20 @@ class AssertionRewritingHook(object):
state.trace("matched test file %r" % (fn,))
return True
for marked in self._must_rewrite:
if name == marked or name.startswith(marked + "."):
state.trace("matched marked file %r (from %r)" % (name, marked))
return True
return self._is_marked_for_rewrite(name, state)
return False
def _is_marked_for_rewrite(self, name, state):
try:
return self._marked_for_rewrite_cache[name]
except KeyError:
for marked in self._must_rewrite:
if name == marked or name.startswith(marked + "."):
state.trace("matched marked file %r (from %r)" % (name, marked))
self._marked_for_rewrite_cache[name] = True
return True
self._marked_for_rewrite_cache[name] = False
return False
def mark_rewrite(self, *names):
"""Mark import names as needing to be rewritten.
@@ -198,24 +258,29 @@ class AssertionRewritingHook(object):
):
self._warn_already_imported(name)
self._must_rewrite.update(names)
self._marked_for_rewrite_cache.clear()
def _warn_already_imported(self, name):
self.config.warn(
"P1", "Module already imported so cannot be rewritten: %s" % name
from _pytest.warning_types import PytestWarning
from _pytest.warnings import _issue_config_warning
_issue_config_warning(
PytestWarning("Module already imported so cannot be rewritten: %s" % name),
self.config,
)
def load_module(self, name):
# If there is an existing module object named 'fullname' in
# sys.modules, the loader must use that existing module. (Otherwise,
# the reload() builtin will not work correctly.)
if name in sys.modules:
return sys.modules[name]
co, pyc = self.modules.pop(name)
# I wish I could just call imp.load_compiled here, but __file__ has to
# be set properly. In Python 3.2+, this all would be handled correctly
# by load_compiled.
mod = sys.modules[name] = imp.new_module(name)
if name in sys.modules:
# If there is an existing module object named 'fullname' in
# sys.modules, the loader must use that existing module. (Otherwise,
# the reload() builtin will not work correctly.)
mod = sys.modules[name]
else:
# I wish I could just call imp.load_compiled here, but __file__ has to
# be set properly. In Python 3.2+, this all would be handled correctly
# by load_compiled.
mod = sys.modules[name] = imp.new_module(name)
try:
mod.__file__ = co.co_filename
# Normally, this attribute is 3.2+.
@@ -223,7 +288,7 @@ class AssertionRewritingHook(object):
mod.__loader__ = self
# Normally, this attribute is 3.4+
mod.__spec__ = spec_from_file_location(name, co.co_filename, loader=self)
py.builtin.exec_(co, mod.__dict__)
six.exec_(co, mod.__dict__)
except: # noqa
if name in sys.modules:
del sys.modules[name]
@@ -232,7 +297,7 @@ class AssertionRewritingHook(object):
def is_package(self, name):
try:
fd, fn, desc = imp.find_module(name)
fd, fn, desc = self._imp_find_module(name)
except ImportError:
return False
if fd is not None:
@@ -334,7 +399,7 @@ def _rewrite_test(config, fn):
finally:
del state._indecode
try:
tree = ast.parse(source)
tree = ast.parse(source, filename=fn.strpath)
except SyntaxError:
# Let this pop up again in the real import.
state.trace("failed to parse: %r" % (fn,))
@@ -402,12 +467,15 @@ def _saferepr(obj):
JSON reprs.
"""
repr = py.io.saferepr(obj)
if isinstance(repr, six.text_type):
t = six.text_type
else:
t = six.binary_type
return repr.replace(t("\n"), t("\\n"))
r = py.io.saferepr(obj)
# only occurs in python2.x, repr must return text in python3+
if isinstance(r, bytes):
# Represent unprintable bytes as `\x##`
r = u"".join(
u"\\x{:x}".format(ord(c)) if c not in string.printable else c.decode()
for c in r
)
return r.replace(u"\n", u"\\n")
from _pytest.assertion.util import format_explanation as _format_explanation # noqa
@@ -446,10 +514,9 @@ def _should_repr_global_name(obj):
def _format_boolop(explanations, is_or):
explanation = "(" + (is_or and " or " or " and ").join(explanations) + ")"
if isinstance(explanation, six.text_type):
t = six.text_type
return explanation.replace(u"%", u"%%")
else:
t = six.binary_type
return explanation.replace(t("%"), t("%%"))
return explanation.replace(b"%", b"%%")
def _call_reprcompare(ops, results, expls, each_obj):
@@ -739,13 +806,17 @@ class AssertionRewriter(ast.NodeVisitor):
the expression is false.
"""
if isinstance(assert_.test, ast.Tuple) and self.config is not None:
fslocation = (self.module_path, assert_.lineno)
self.config.warn(
"R1",
"assertion is always true, perhaps " "remove parentheses?",
fslocation=fslocation,
if isinstance(assert_.test, ast.Tuple) and len(assert_.test.elts) >= 1:
from _pytest.warning_types import PytestWarning
import warnings
warnings.warn_explicit(
PytestWarning("assertion is always true, perhaps remove parentheses?"),
category=None,
filename=str(self.module_path),
lineno=assert_.lineno,
)
self.statements = []
self.variables = []
self.variable_counter = itertools.count()

View File

@@ -187,9 +187,9 @@ def _diff_text(left, right, verbose=False):
r = r.replace(r"\r", "\r")
return r
if isinstance(left, six.binary_type):
if isinstance(left, bytes):
left = escape_for_readable_diff(left)
if isinstance(right, six.binary_type):
if isinstance(right, bytes):
right = escape_for_readable_diff(right)
if not verbose:
i = 0 # just in case left or right has zero length
@@ -199,7 +199,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")
u("Skipping %s identical leading characters in diff, use -v to show")
% i
]
left = left[i:]

View File

@@ -13,10 +13,9 @@ import attr
import pytest
import json
import shutil
from . import paths
from .compat import _PY2 as PY2, Path
from .compat import _PY2 as PY2
from .pathlib import Path, resolve_from_str, rmtree
README_CONTENT = u"""\
# pytest cache directory #
@@ -33,22 +32,27 @@ See [the docs](https://docs.pytest.org/en/latest/cache.html) for more informatio
@attr.s
class Cache(object):
_cachedir = attr.ib(repr=False)
_warn = attr.ib(repr=False)
_config = attr.ib(repr=False)
@classmethod
def for_config(cls, config):
cachedir = cls.cache_dir_from_config(config)
if config.getoption("cacheclear") and cachedir.exists():
shutil.rmtree(str(cachedir))
rmtree(cachedir, force=True)
cachedir.mkdir()
return cls(cachedir, config.warn)
return cls(cachedir, config)
@staticmethod
def cache_dir_from_config(config):
return paths.resolve_from_str(config.getini("cache_dir"), config.rootdir)
return resolve_from_str(config.getini("cache_dir"), config.rootdir)
def warn(self, fmt, **args):
self._warn(code="I9", message=fmt.format(**args) if args else fmt)
from _pytest.warnings import _issue_config_warning
from _pytest.warning_types import PytestWarning
_issue_config_warning(
PytestWarning(fmt.format(**args) if args else fmt), self._config
)
def makedir(self, name):
""" return a directory path object with the given name. If the
@@ -110,15 +114,18 @@ class Cache(object):
else:
with f:
json.dump(value, f, indent=2, sort_keys=True)
self._ensure_readme()
def _ensure_readme(self):
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)
msg = u"# created by pytest automatically, do not change\n*"
self._cachedir.joinpath(".gitignore").write_text(msg, encoding="UTF-8")
class LFPlugin(object):
""" Plugin which implements the --lf (run last-failing) option """
@@ -132,17 +139,14 @@ class LFPlugin(object):
self._no_failures_behavior = self.config.getoption("last_failed_no_failures")
def pytest_report_collectionfinish(self):
if self.active:
if self.active and self.config.getoption("verbose") >= 0:
if not self._previously_failed_count:
mode = "run {} (no recorded failures)".format(
self._no_failures_behavior
)
else:
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 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
def pytest_runtest_logreport(self, report):
@@ -339,7 +343,7 @@ def cacheshow(config, session):
key = valpath.relative_to(vdir)
val = config.cache.get(key, dummy)
if val is dummy:
tw.line("%s contains unreadable content, " "will be ignored" % key)
tw.line("%s contains unreadable content, will be ignored" % key)
else:
tw.line("%s contains:" % key)
for line in pformat(val).splitlines():

View File

@@ -16,7 +16,6 @@ import six
import pytest
from _pytest.compat import CaptureIO
patchsysdict = {0: "stdin", 1: "stdout", 2: "stderr"}
@@ -63,8 +62,9 @@ def pytest_load_initial_conftests(early_config, parser, args):
# finally trigger conftest loading but while capturing (issue93)
capman.start_global_capturing()
outcome = yield
out, err = capman.suspend_global_capture()
capman.suspend_global_capture()
if outcome.excinfo is not None:
out, err = capman.read_global_capture()
sys.stdout.write(out)
sys.stderr.write(err)
@@ -85,6 +85,7 @@ class CaptureManager(object):
def __init__(self, method):
self._method = method
self._global_capturing = None
self._current_item = None
def _getcapture(self, method):
if method == "fd":
@@ -96,6 +97,8 @@ class CaptureManager(object):
else:
raise ValueError("unknown capturing method: %r" % method)
# Global capturing control
def start_global_capturing(self):
assert self._global_capturing is None
self._global_capturing = self._getcapture(self._method)
@@ -110,16 +113,15 @@ class CaptureManager(object):
def resume_global_capture(self):
self._global_capturing.resume_capturing()
def suspend_global_capture(self, item=None, in_=False):
if item is not None:
self.deactivate_fixture(item)
def suspend_global_capture(self, in_=False):
cap = getattr(self, "_global_capturing", None)
if cap is not None:
try:
outerr = cap.readouterr()
finally:
cap.suspend_capturing(in_=in_)
return outerr
cap.suspend_capturing(in_=in_)
def read_global_capture(self):
return self._global_capturing.readouterr()
# Fixture Control (its just forwarding, think about removing this later)
def activate_fixture(self, item):
"""If the current item is using ``capsys`` or ``capfd``, activate them so they take precedence over
@@ -135,12 +137,53 @@ class CaptureManager(object):
if fixture is not None:
fixture.close()
def suspend_fixture(self, item):
fixture = getattr(item, "_capture_fixture", None)
if fixture is not None:
fixture._suspend()
def resume_fixture(self, item):
fixture = getattr(item, "_capture_fixture", None)
if fixture is not None:
fixture._resume()
# Helper context managers
@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)
try:
yield
finally:
self.resume_global_capture()
self.resume_fixture(self._current_item)
@contextlib.contextmanager
def item_capture(self, when, item):
self.resume_global_capture()
self.activate_fixture(item)
try:
yield
finally:
self.deactivate_fixture(item)
self.suspend_global_capture(in_=False)
out, err = self.read_global_capture()
item.add_report_section(when, "stdout", out)
item.add_report_section(when, "stderr", err)
# Hooks
@pytest.hookimpl(hookwrapper=True)
def pytest_make_collect_report(self, collector):
if isinstance(collector, pytest.File):
self.resume_global_capture()
outcome = yield
out, err = self.suspend_global_capture()
self.suspend_global_capture()
out, err = self.read_global_capture()
rep = outcome.get_result()
if out:
rep.sections.append(("Captured stdout", out))
@@ -150,29 +193,25 @@ class CaptureManager(object):
yield
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_setup(self, item):
self.resume_global_capture()
# no need to activate a capture fixture because they activate themselves during creation; this
# only makes sense when a fixture uses a capture fixture, otherwise the capture fixture will
# be activated during pytest_runtest_call
def pytest_runtest_protocol(self, item):
self._current_item = item
yield
self.suspend_capture_item(item, "setup")
self._current_item = None
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_setup(self, item):
with self.item_capture("setup", item):
yield
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_call(self, item):
self.resume_global_capture()
# it is important to activate this fixture during the call phase so it overwrites the "global"
# capture
self.activate_fixture(item)
yield
self.suspend_capture_item(item, "call")
with self.item_capture("call", item):
yield
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_teardown(self, item):
self.resume_global_capture()
self.activate_fixture(item)
yield
self.suspend_capture_item(item, "teardown")
with self.item_capture("teardown", item):
yield
@pytest.hookimpl(tryfirst=True)
def pytest_keyboard_interrupt(self, excinfo):
@@ -182,11 +221,6 @@ class CaptureManager(object):
def pytest_internalerror(self, excinfo):
self.stop_global_capturing()
def suspend_capture_item(self, item, when, in_=False):
out, err = self.suspend_global_capture(item, in_=in_)
item.add_report_section(when, "stdout", out)
item.add_report_section(when, "stderr", err)
capture_fixtures = {"capfd", "capfdbinary", "capsys", "capsysbinary"}
@@ -290,40 +324,54 @@ class CaptureFixture(object):
def __init__(self, captureclass, request):
self.captureclass = captureclass
self.request = request
self._capture = None
self._captured_out = self.captureclass.EMPTY_BUFFER
self._captured_err = self.captureclass.EMPTY_BUFFER
def _start(self):
self._capture = MultiCapture(
out=True, err=True, in_=False, Capture=self.captureclass
)
self._capture.start_capturing()
# Start if not started yet
if getattr(self, "_capture", None) is None:
self._capture = MultiCapture(
out=True, err=True, in_=False, Capture=self.captureclass
)
self._capture.start_capturing()
def close(self):
cap = self.__dict__.pop("_capture", None)
if cap is not None:
self._outerr = cap.pop_outerr_to_orig()
cap.stop_capturing()
if self._capture is not None:
out, err = self._capture.pop_outerr_to_orig()
self._captured_out += out
self._captured_err += err
self._capture.stop_capturing()
self._capture = None
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
"""
try:
return self._capture.readouterr()
except AttributeError:
return self._outerr
captured_out, captured_err = self._captured_out, self._captured_err
if self._capture is not None:
out, err = self._capture.readouterr()
captured_out += out
captured_err += err
self._captured_out = self.captureclass.EMPTY_BUFFER
self._captured_err = self.captureclass.EMPTY_BUFFER
return CaptureResult(captured_out, captured_err)
def _suspend(self):
"""Suspends this fixture's own capturing temporarily."""
self._capture.suspend_capturing()
def _resume(self):
"""Resumes this fixture's own capturing temporarily."""
self._capture.resume_capturing()
@contextlib.contextmanager
def disabled(self):
"""Temporarily disables capture while inside the 'with' block."""
self._capture.suspend_capturing()
capmanager = self.request.config.pluginmanager.getplugin("capturemanager")
capmanager.suspend_global_capture(item=None, in_=False)
try:
with capmanager.global_and_fixture_disabled():
yield
finally:
capmanager.resume_global_capture()
self._capture.resume_capturing()
def safe_text_dupfile(f, mode, default_encoding="UTF8"):
@@ -440,6 +488,7 @@ class MultiCapture(object):
class NoCapture(object):
EMPTY_BUFFER = None
__init__ = start = done = suspend = resume = lambda *args: None
@@ -449,6 +498,8 @@ class FDCaptureBinary(object):
snap() produces `bytes`
"""
EMPTY_BUFFER = bytes()
def __init__(self, targetfd, tmpfile=None):
self.targetfd = targetfd
try:
@@ -522,6 +573,8 @@ class FDCapture(FDCaptureBinary):
snap() produces text
"""
EMPTY_BUFFER = str()
def snap(self):
res = FDCaptureBinary.snap(self)
enc = getattr(self.tmpfile, "encoding", None)
@@ -531,6 +584,9 @@ class FDCapture(FDCaptureBinary):
class SysCapture(object):
EMPTY_BUFFER = str()
def __init__(self, fd, tmpfile=None):
name = patchsysdict[fd]
self._old = getattr(sys, name)
@@ -568,6 +624,8 @@ class SysCapture(object):
class SysCaptureBinary(SysCapture):
EMPTY_BUFFER = bytes()
def snap(self):
res = self.tmpfile.buffer.getvalue()
self.tmpfile.seek(0)
@@ -596,7 +654,7 @@ class DontReadFromInput(six.Iterator):
return self
def fileno(self):
raise UnsupportedOperation("redirected stdin is pseudofile, " "has no fileno()")
raise UnsupportedOperation("redirected stdin is pseudofile, has no fileno()")
def isatty(self):
return False

View File

@@ -8,6 +8,7 @@ import functools
import inspect
import re
import sys
from contextlib import contextmanager
import py
@@ -22,8 +23,6 @@ except ImportError: # pragma: no cover
# Only available in Python 3.4+ or as a backport
enum = None
__all__ = ["Path"]
_PY3 = sys.version_info > (3, 0)
_PY2 = not _PY3
@@ -40,11 +39,6 @@ PY35 = sys.version_info[:2] >= (3, 5)
PY36 = sys.version_info[:2] >= (3, 6)
MODULE_NOT_FOUND_ERROR = "ModuleNotFoundError" if PY36 else "ImportError"
if PY36:
from pathlib import Path
else:
from pathlib2 import Path
if _PY3:
from collections.abc import MutableMapping as MappingMixin
@@ -55,6 +49,14 @@ else:
from collections import Mapping, Sequence # noqa
if sys.version_info >= (3, 4):
from importlib.util import spec_from_file_location
else:
def spec_from_file_location(*_, **__):
return None
def _format_args(func):
return str(signature(func))
@@ -151,6 +153,13 @@ 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
def get_default_arg_names(function):
# Note: this code intentionally mirrors the code at the beginning of getfuncargnames,
# to get the arguments which were excluded from its result because they had default values
@@ -259,7 +268,7 @@ def get_real_func(obj):
obj = new_obj
else:
raise ValueError(
("could not find real function of {start}" "\nstopped at {current}").format(
("could not find real function of {start}\nstopped at {current}").format(
start=py.io.saferepr(start_obj), current=py.io.saferepr(obj)
)
)

View File

@@ -3,7 +3,6 @@ from __future__ import absolute_import, division, print_function
import argparse
import inspect
import shlex
import traceback
import types
import warnings
import copy
@@ -19,6 +18,7 @@ import _pytest._code
import _pytest.hookspec # the extension point definitions
import _pytest.assertion
from pluggy import PluginManager, HookimplMarker, HookspecMarker
from _pytest._code import ExceptionInfo, filter_traceback
from _pytest.compat import safe_str
from .exceptions import UsageError, PrintHelp
from .findpaths import determine_setup, exists
@@ -26,9 +26,6 @@ from .findpaths import determine_setup, exists
hookimpl = HookimplMarker("pytest")
hookspec = HookspecMarker("pytest")
# pytest startup
#
class ConftestImportFailure(Exception):
def __init__(self, path, excinfo):
@@ -36,12 +33,6 @@ class ConftestImportFailure(Exception):
self.path = path
self.excinfo = excinfo
def __str__(self):
etype, evalue, etb = self.excinfo
formatted = traceback.format_tb(etb)
# The level of the tracebacks we want to print is hand crafted :(
return repr(evalue) + "\n" + "".join(formatted[2:])
def main(args=None, plugins=None):
""" return exit code, after performing an in-process test run.
@@ -51,14 +42,26 @@ def main(args=None, plugins=None):
:arg plugins: list of plugin objects to be auto-registered during
initialization.
"""
from _pytest.main import EXIT_USAGEERROR
try:
try:
config = _prepareconfig(args, plugins)
except ConftestImportFailure as e:
exc_info = ExceptionInfo(e.excinfo)
tw = py.io.TerminalWriter(sys.stderr)
for line in traceback.format_exception(*e.excinfo):
tw.line(
"ImportError while loading conftest '{e.path}'.".format(e=e), red=True
)
exc_info.traceback = exc_info.traceback.filter(filter_traceback)
exc_repr = (
exc_info.getrepr(style="short", chain=False)
if exc_info.traceback
else exc_info.exconly()
)
formatted_tb = safe_str(exc_repr)
for line in formatted_tb.splitlines():
tw.line(line.rstrip(), red=True)
tw.line("ERROR: could not load %s\n" % (e.path,), red=True)
return 4
else:
try:
@@ -69,7 +72,7 @@ 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 4
return EXIT_USAGEERROR
class cmdline(object): # compatibility namespace
@@ -176,7 +179,9 @@ def _prepareconfig(args=None, plugins=None):
else:
pluginmanager.register(plugin)
if warning:
config.warn("C1", warning)
from _pytest.warnings import _issue_config_warning
_issue_config_warning(warning, config=config)
return pluginmanager.hook.pytest_cmdline_parse(
pluginmanager=pluginmanager, args=args
)
@@ -347,6 +352,7 @@ class PytestPluginManager(PluginManager):
else None
)
self._noconftest = namespace.noconftest
self._using_pyargs = namespace.pyargs
testpaths = namespace.file_or_dir
foundanchor = False
for path in testpaths:
@@ -373,25 +379,27 @@ class PytestPluginManager(PluginManager):
def _getconftestmodules(self, path):
if self._noconftest:
return []
try:
return self._path2confmods[path]
except KeyError:
if path.isfile():
clist = self._getconftestmodules(path.dirpath())
else:
# 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
clist = []
for parent in path.parts():
if self._confcutdir and self._confcutdir.relto(parent):
continue
conftestpath = parent.join("conftest.py")
if conftestpath.isfile():
mod = self._importconftest(conftestpath)
clist.append(mod)
self._path2confmods[path] = clist
if path.isfile():
directory = path.dirpath()
else:
directory = path
try:
return self._path2confmods[directory]
except KeyError:
# 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
clist = []
for parent in directory.parts():
if self._confcutdir and self._confcutdir.relto(parent):
continue
conftestpath = parent.join("conftest.py")
if conftestpath.isfile():
mod = self._importconftest(conftestpath)
clist.append(mod)
self._path2confmods[directory] = clist
return clist
def _rget_with_confmod(self, name, path):
@@ -412,12 +420,21 @@ class PytestPluginManager(PluginManager):
_ensure_removed_sysmodule(conftestpath.purebasename)
try:
mod = conftestpath.pyimport()
if hasattr(mod, "pytest_plugins") and self._configured:
if (
hasattr(mod, "pytest_plugins")
and self._configured
and not self._using_pyargs
):
from _pytest.deprecated import (
PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST
)
warnings.warn(PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST)
warnings.warn_explicit(
PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST,
category=None,
filename=str(conftestpath),
lineno=0,
)
except Exception:
raise ConftestImportFailure(conftestpath, sys.exc_info())
@@ -602,7 +619,29 @@ class Config(object):
fin()
def warn(self, code, message, fslocation=None, nodeid=None):
""" generate a warning for this test session. """
"""
.. deprecated:: 3.8
Use :py:func:`warnings.warn` or :py:func:`warnings.warn_explicit` directly instead.
Generate a warning for this test session.
"""
from _pytest.warning_types import RemovedInPytest4Warning
if isinstance(fslocation, (tuple, list)) and len(fslocation) > 2:
filename, lineno = fslocation[:2]
else:
filename = "unknown file"
lineno = 0
msg = "config.warn has been deprecated, use warnings.warn instead"
if nodeid:
msg = "{}: {}".format(nodeid, msg)
warnings.warn_explicit(
RemovedInPytest4Warning(msg),
category=None,
filename=filename,
lineno=lineno,
)
self.hook.pytest_logwarning.call_historic(
kwargs=dict(
code=code, message=message, fslocation=fslocation, nodeid=nodeid
@@ -667,8 +706,8 @@ class Config(object):
r = determine_setup(
ns.inifilename,
ns.file_or_dir + unknown_args,
warnfunc=self.warn,
rootdir_cmd_arg=ns.rootdir or None,
config=self,
)
self.rootdir, self.inifile, self.inicfg = r
self._parser.extra_info["rootdir"] = self.rootdir
@@ -706,6 +745,10 @@ class Config(object):
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
@@ -731,7 +774,10 @@ class Config(object):
self._checkversion()
self._consider_importhook(args)
self.pluginmanager.consider_preparse(args)
self.pluginmanager.load_setuptools_entrypoints("pytest11")
if not os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"):
# Don't autoload from setuptools entry point. Only explicitly specified
# plugins are going to be loaded.
self.pluginmanager.load_setuptools_entrypoints("pytest11")
self.pluginmanager.consider_env()
self.known_args_namespace = ns = self._parser.parse_known_args(
args, namespace=copy.copy(self.option)

View File

@@ -2,6 +2,13 @@ import six
import warnings
import argparse
from gettext import gettext as _
import sys as _sys
import py
from ..main import EXIT_USAGEERROR
FILE_OR_DIR = "file_or_dir"
@@ -70,7 +77,8 @@ class Parser(object):
self.optparser = self._getparser()
try_argcomplete(self.optparser)
return self.optparser.parse_args([str(x) for x in args], namespace=namespace)
args = [str(x) if isinstance(x, py.path.local) else x for x in args]
return self.optparser.parse_args(args, namespace=namespace)
def _getparser(self):
from _pytest._argcomplete import filescompleter
@@ -106,7 +114,7 @@ class Parser(object):
the remaining arguments unknown at this point.
"""
optparser = self._getparser()
args = [str(x) for x in args]
args = [str(x) if isinstance(x, py.path.local) else x for x in args]
return optparser.parse_known_args(args, namespace=namespace)
def addini(self, name, help, type=None, default=None):
@@ -326,6 +334,16 @@ class MyOptionParser(argparse.ArgumentParser):
# an usage error to provide more contextual information to the user
self.extra_info = extra_info
def error(self, message):
"""error(message: string)
Prints a usage message incorporating the message to stderr and
exits.
Overrides the method in parent class to change exit code"""
self.print_usage(_sys.stderr)
args = {"prog": self.prog, "message": message}
self.exit(EXIT_USAGEERROR, _("%(prog)s: error: %(message)s\n") % args)
def parse_args(self, args=None, namespace=None):
"""allow splitting of positional arguments"""
args, argv = self.parse_known_args(args, namespace)

View File

@@ -10,15 +10,12 @@ def exists(path, ignore=EnvironmentError):
return False
def getcfg(args, warnfunc=None):
def getcfg(args, config=None):
"""
Search the list of arguments for a valid ini-file for pytest,
and return a tuple of (rootdir, inifile, cfg-dict).
note: warnfunc is an optional function used to warn
about ini-files that use deprecated features.
This parameter should be removed when pytest
adopts standard deprecation warnings (#1804).
note: config is optional and used only to issue warnings explicitly (#2891).
"""
from _pytest.deprecated import CFG_PYTEST_SECTION
@@ -34,9 +31,15 @@ def getcfg(args, warnfunc=None):
if exists(p):
iniconfig = py.iniconfig.IniConfig(p)
if "pytest" in iniconfig.sections:
if inibasename == "setup.cfg" and warnfunc:
warnfunc(
"C1", CFG_PYTEST_SECTION.format(filename=inibasename)
if inibasename == "setup.cfg" and config is not None:
from _pytest.warnings import _issue_config_warning
from _pytest.warning_types import RemovedInPytest4Warning
_issue_config_warning(
RemovedInPytest4Warning(
CFG_PYTEST_SECTION.format(filename=inibasename)
),
config=config,
)
return base, p, iniconfig["pytest"]
if (
@@ -95,33 +98,37 @@ def get_dirs_from_args(args):
return [get_dir_from_path(path) for path in possible_paths if path.exists()]
def determine_setup(inifile, args, warnfunc=None, rootdir_cmd_arg=None):
def determine_setup(inifile, args, rootdir_cmd_arg=None, config=None):
dirs = get_dirs_from_args(args)
if inifile:
iniconfig = py.iniconfig.IniConfig(inifile)
is_cfg_file = str(inifile).endswith(".cfg")
# TODO: [pytest] section in *.cfg files is depricated. Need refactoring.
sections = ["tool:pytest", "pytest"] if is_cfg_file else ["pytest"]
for section in sections:
try:
inicfg = iniconfig[section]
if is_cfg_file and section == "pytest" and warnfunc:
if is_cfg_file and section == "pytest" and config is not None:
from _pytest.deprecated import CFG_PYTEST_SECTION
from _pytest.warnings import _issue_config_warning
warnfunc("C1", CFG_PYTEST_SECTION.format(filename=str(inifile)))
# TODO: [pytest] section in *.cfg files is deprecated. Need refactoring once
# the deprecation expires.
_issue_config_warning(
CFG_PYTEST_SECTION.format(filename=str(inifile)), config
)
break
except KeyError:
inicfg = None
rootdir = get_common_ancestor(dirs)
else:
ancestor = get_common_ancestor(dirs)
rootdir, inifile, inicfg = getcfg([ancestor], warnfunc=warnfunc)
rootdir, inifile, inicfg = getcfg([ancestor], config=config)
if rootdir is None:
for rootdir in ancestor.parts(reverse=True):
if rootdir.join("setup.py").exists():
break
else:
rootdir, inifile, inicfg = getcfg(dirs, warnfunc=warnfunc)
rootdir, inifile, inicfg = getcfg(dirs, config=config)
if rootdir is None:
rootdir = get_common_ancestor([py.path.local(), ancestor])
is_fs_root = os.path.splitdrive(str(rootdir))[1] == "/"

View File

@@ -1,10 +1,12 @@
""" interactive debugging with PDB, the Python Debugger. """
from __future__ import absolute_import, division, print_function
import os
import pdb
import sys
import os
from doctest import UnexpectedException
from _pytest import outcomes
from _pytest.config import hookimpl
try:
@@ -102,15 +104,13 @@ class PdbInvoke(object):
def pytest_exception_interact(self, node, call, report):
capman = node.config.pluginmanager.getplugin("capturemanager")
if capman:
out, err = capman.suspend_global_capture(in_=True)
capman.suspend_global_capture(in_=True)
out, err = capman.read_global_capture()
sys.stdout.write(out)
sys.stdout.write(err)
_enter_pdb(node, call.excinfo, report)
def pytest_internalerror(self, excrepr, excinfo):
for line in str(excrepr).split("\n"):
sys.stderr.write("INTERNALERROR> %s\n" % line)
sys.stderr.flush()
tb = _postmortem_traceback(excinfo)
post_mortem(tb)
@@ -163,8 +163,9 @@ def _enter_pdb(node, excinfo, rep):
rep.toterminal(tw)
tw.sep(">", "entering PDB")
tb = _postmortem_traceback(excinfo)
post_mortem(tb)
rep._pdbshown = True
if post_mortem(tb):
outcomes.exit("Quitting debugger")
return rep
@@ -195,3 +196,4 @@ def post_mortem(t):
p = Pdb()
p.reset()
p.interaction(None, t)
return p.quitting

View File

@@ -4,37 +4,67 @@ that is planned to be removed in the next pytest release.
Keeping it in a central location makes it easy to track what is deprecated and should
be removed when the time comes.
All constants defined in this module should be either PytestWarning instances or UnformattedWarning
in case of warnings which need to format their messages.
"""
from __future__ import absolute_import, division, print_function
class RemovedInPytest4Warning(DeprecationWarning):
"""warning class for features removed in pytest 4.0"""
from _pytest.warning_types import UnformattedWarning, RemovedInPytest4Warning
MAIN_STR_ARGS = "passing a string to pytest.main() is deprecated, " "pass a list of arguments instead."
MAIN_STR_ARGS = RemovedInPytest4Warning(
"passing a string to pytest.main() is deprecated, "
"pass a list of arguments instead."
)
YIELD_TESTS = "yield tests are deprecated, and scheduled to be removed in pytest 4.0"
YIELD_TESTS = RemovedInPytest4Warning(
"yield tests are deprecated, and scheduled to be removed in pytest 4.0"
)
FUNCARG_PREFIX = (
CACHED_SETUP = RemovedInPytest4Warning(
"cached_setup is deprecated and will be removed in a future release. "
"Use standard fixture functions instead."
)
COMPAT_PROPERTY = UnformattedWarning(
RemovedInPytest4Warning,
"usage of {owner}.{name} is deprecated, please use pytest.{name} instead",
)
CUSTOM_CLASS = UnformattedWarning(
RemovedInPytest4Warning,
'use of special named "{name}" objects in collectors of type "{type_name}" to '
"customize the created nodes is deprecated. "
"Use pytest_pycollect_makeitem(...) to create custom "
"collection nodes instead.",
)
FUNCARG_PREFIX = UnformattedWarning(
RemovedInPytest4Warning,
'{name}: declaring fixtures using "pytest_funcarg__" prefix is deprecated '
"and scheduled to be removed in pytest 4.0. "
"Please remove the prefix and use the @pytest.fixture decorator instead."
"Please remove the prefix and use the @pytest.fixture decorator instead.",
)
FIXTURE_FUNCTION_CALL = (
"Fixture {name} called directly. Fixtures are not meant to be called directly, "
FIXTURE_FUNCTION_CALL = UnformattedWarning(
RemovedInPytest4Warning,
'Fixture "{name}" called directly. Fixtures are not meant to be called directly, '
"are created automatically when test functions request them as parameters. "
"See https://docs.pytest.org/en/latest/fixture.html for more information."
"See https://docs.pytest.org/en/latest/fixture.html for more information.",
)
CFG_PYTEST_SECTION = (
"[pytest] section in {filename} files is deprecated, use [tool:pytest] instead."
CFG_PYTEST_SECTION = UnformattedWarning(
RemovedInPytest4Warning,
"[pytest] section in {filename} files is deprecated, use [tool:pytest] instead.",
)
GETFUNCARGVALUE = "use of getfuncargvalue is deprecated, use getfixturevalue"
GETFUNCARGVALUE = RemovedInPytest4Warning(
"getfuncargvalue is deprecated, use getfixturevalue"
)
RESULT_LOG = (
RESULT_LOG = RemovedInPytest4Warning(
"--result-log is deprecated and scheduled for removal in pytest 4.0.\n"
"See https://docs.pytest.org/en/latest/usage.html#creating-resultlog-format-files for more information."
)
@@ -51,17 +81,21 @@ MARK_PARAMETERSET_UNPACKING = RemovedInPytest4Warning(
"For more details, see: https://docs.pytest.org/en/latest/parametrize.html"
)
RECORD_XML_PROPERTY = (
NODE_WARN = RemovedInPytest4Warning(
"Node.warn(code, message) form has been deprecated, use Node.warn(warning_instance) instead."
)
RECORD_XML_PROPERTY = RemovedInPytest4Warning(
'Fixture renamed from "record_xml_property" to "record_property" as user '
"properties are now available to all reporters.\n"
'"record_xml_property" is now deprecated.'
)
COLLECTOR_MAKEITEM = RemovedInPytest4Warning(
"pycollector makeitem was removed " "as it is an accidentially leaked internal api"
"pycollector makeitem was removed as it is an accidentially leaked internal api"
)
METAFUNC_ADD_CALL = (
METAFUNC_ADD_CALL = RemovedInPytest4Warning(
"Metafunc.addcall is deprecated and scheduled to be removed in pytest 4.0.\n"
"Please use Metafunc.parametrize instead."
)
@@ -75,3 +109,8 @@ PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST = RemovedInPytest4Warning(
PYTEST_NAMESPACE = RemovedInPytest4Warning(
"pytest_namespace is deprecated and will be removed soon"
)
PYTEST_ENSURETEMP = RemovedInPytest4Warning(
"pytest/tmpdir_factory.ensuretemp is deprecated, \n"
"please use the tmp_path fixture or tmp_path_factory.mktemp"
)

View File

@@ -203,7 +203,8 @@ class DoctestItem(pytest.Item):
return
capman = self.config.pluginmanager.getplugin("capturemanager")
if capman:
out, err = capman.suspend_global_capture(in_=True)
capman.suspend_global_capture(in_=True)
out, err = capman.read_global_capture()
sys.stdout.write(out)
sys.stderr.write(err)

View File

@@ -1,13 +0,0 @@
class PytestExerimentalApiWarning(FutureWarning):
"warning category used to denote experiments in pytest"
@classmethod
def simple(cls, apiname):
return cls(
"{apiname} is an experimental api that may change over time".format(
apiname=apiname
)
)
PYTESTER_COPY_EXAMPLE = PytestExerimentalApiWarning.simple("testdir.copy_example")

View File

@@ -2,7 +2,6 @@ from __future__ import absolute_import, division, print_function
import functools
import inspect
import os
import sys
import warnings
from collections import OrderedDict, deque, defaultdict
@@ -33,7 +32,7 @@ from _pytest.compat import (
get_real_method,
_PytestWrapper,
)
from _pytest.deprecated import FIXTURE_FUNCTION_CALL, RemovedInPytest4Warning
from _pytest.deprecated import FIXTURE_FUNCTION_CALL
from _pytest.outcomes import fail, TEST_OUTCOME
FIXTURE_MSG = 'fixtures cannot have "pytest_funcarg__" prefix and be decorated with @pytest.fixture:\n{}'
@@ -93,7 +92,7 @@ def get_scope_package(node, fixturedef):
cls = pytest.Package
current = node
fixture_package_name = os.path.join(fixturedef.baseid, "__init__.py")
fixture_package_name = "%s/%s" % (fixturedef.baseid, "__init__.py")
while current and (
type(current) is not cls or fixture_package_name != current.nodeid
):
@@ -307,8 +306,8 @@ class FuncFixtureInfo(object):
# fixture names specified via usefixtures and via autouse=True in fixture
# definitions.
initialnames = attr.ib(type=tuple)
names_closure = attr.ib(type="List[str]")
name2fixturedefs = attr.ib(type="List[str, List[FixtureDef]]")
names_closure = attr.ib() # type: List[str]
name2fixturedefs = attr.ib() # type: List[str, List[FixtureDef]]
def prune_dependency_tree(self):
"""Recompute names_closure from initialnames and name2fixturedefs
@@ -360,8 +359,10 @@ class FixtureRequest(FuncargnamesCompatAttr):
@property
def fixturenames(self):
# backward incompatible note: now a readonly property
return list(self._pyfuncitem._fixtureinfo.names_closure)
"""names of all active fixtures in this request"""
result = list(self._pyfuncitem._fixtureinfo.names_closure)
result.extend(set(self._fixture_defs).difference(result))
return result
@property
def node(self):
@@ -480,6 +481,9 @@ class FixtureRequest(FuncargnamesCompatAttr):
or ``session`` indicating the caching lifecycle of the resource.
:arg extrakey: added to internal caching key of (funcargname, scope).
"""
from _pytest.deprecated import CACHED_SETUP
warnings.warn(CACHED_SETUP, stacklevel=2)
if not hasattr(self.config, "_setupcache"):
self.config._setupcache = {} # XXX weakref?
cachekey = (self.fixturename, self._getscopeitem(scope), extrakey)
@@ -513,7 +517,7 @@ class FixtureRequest(FuncargnamesCompatAttr):
""" Deprecated, use getfixturevalue. """
from _pytest import deprecated
warnings.warn(deprecated.GETFUNCARGVALUE, DeprecationWarning, stacklevel=2)
warnings.warn(deprecated.GETFUNCARGVALUE, stacklevel=2)
return self.getfixturevalue(argname)
def _get_active_fixturedef(self, argname):
@@ -563,7 +567,20 @@ class FixtureRequest(FuncargnamesCompatAttr):
except (AttributeError, ValueError):
param = NOTSET
param_index = 0
if fixturedef.params is not None:
has_params = fixturedef.params is not None
fixtures_not_supported = getattr(funcitem, "nofuncargs", False)
if has_params and fixtures_not_supported:
msg = (
"{name} does not support fixtures, maybe unittest.TestCase subclass?\n"
"Node id: {nodeid}\n"
"Function type: {typename}"
).format(
name=funcitem.name,
nodeid=funcitem.nodeid,
typename=type(funcitem).__name__,
)
fail(msg, pytrace=False)
if has_params:
frame = inspect.stack()[3]
frameinfo = inspect.getframeinfo(frame[0])
source_path = frameinfo.filename
@@ -572,16 +589,18 @@ class FixtureRequest(FuncargnamesCompatAttr):
if source_path.relto(funcitem.config.rootdir):
source_path = source_path.relto(funcitem.config.rootdir)
msg = (
"The requested fixture has no parameter defined for the "
"current test.\n\nRequested fixture '{}' defined in:\n{}"
"The requested fixture has no parameter defined for test:\n"
" {}\n\n"
"Requested fixture '{}' defined in:\n{}"
"\n\nRequested here:\n{}:{}".format(
funcitem.nodeid,
fixturedef.argname,
getlocation(fixturedef.func, funcitem.config.rootdir),
source_path,
source_lineno,
)
)
fail(msg)
fail(msg, pytrace=False)
else:
# indices might not be set if old-style metafunc.addcall() was used
param_index = funcitem.callspec.indices.get(argname, 0)
@@ -699,10 +718,11 @@ def scope2index(scope, descr, where=None):
try:
return scopes.index(scope)
except ValueError:
raise ValueError(
"{} {}has an unsupported scope value '{}'".format(
fail(
"{} {}got an unexpected scope value '{}'".format(
descr, "from {} ".format(where) if where else "", scope
)
),
pytrace=False,
)
@@ -835,7 +855,9 @@ class FixtureDef(object):
self.argname = argname
self.scope = scope
self.scopenum = scope2index(
scope or "function", descr="fixture {}".format(func.__name__), where=baseid
scope or "function",
descr="Fixture '{}'".format(func.__name__),
where=baseid,
)
self.params = params
self.argnames = getfuncargnames(func, is_method=unittest)
@@ -858,7 +880,7 @@ class FixtureDef(object):
if exceptions:
e = exceptions[0]
del exceptions # ensure we don't keep all frames alive because of the traceback
py.builtin._reraise(*e)
six.reraise(*e)
finally:
hook = self._fixturemanager.session.gethookproxy(request.node.fspath)
@@ -885,7 +907,7 @@ class FixtureDef(object):
result, cache_key, err = cached_result
if my_cache_key == cache_key:
if err is not None:
py.builtin._reraise(*err)
six.reraise(*err)
else:
return result
# we have a previous but differently parametrized fixture instance
@@ -897,7 +919,7 @@ class FixtureDef(object):
return hook.pytest_fixture_setup(fixturedef=self, request=request)
def __repr__(self):
return "<FixtureDef name=%r scope=%r baseid=%r >" % (
return "<FixtureDef name=%r scope=%r baseid=%r>" % (
self.argname,
self.scope,
self.baseid,
@@ -957,8 +979,9 @@ def wrap_function_to_warning_if_called_directly(function, fixture_marker):
used as an argument in a test function.
"""
is_yield_function = is_generator(function)
msg = FIXTURE_FUNCTION_CALL.format(name=fixture_marker.name or function.__name__)
warning = RemovedInPytest4Warning(msg)
warning = FIXTURE_FUNCTION_CALL.format(
name=fixture_marker.name or function.__name__
)
if is_yield_function:
@@ -997,7 +1020,7 @@ class FixtureFunctionMarker(object):
def __call__(self, function):
if isclass(function):
raise ValueError("class fixtures not supported (may be in the future)")
raise ValueError("class fixtures not supported (maybe in the future)")
if getattr(function, "_pytestfixturefunction", False):
raise ValueError(
@@ -1152,7 +1175,7 @@ class FixtureManager(object):
def pytest_plugin_registered(self, plugin):
nodeid = None
try:
p = py.path.local(plugin.__file__)
p = py.path.local(plugin.__file__).realpath()
except AttributeError:
pass
else:
@@ -1258,6 +1281,8 @@ class FixtureManager(object):
items[:] = reorder_items(items)
def parsefactories(self, node_or_obj, nodeid=NOTSET, unittest=False):
from _pytest import deprecated
if nodeid is not NOTSET:
holderobj = node_or_obj
else:
@@ -1280,10 +1305,13 @@ class FixtureManager(object):
if not callable(obj):
continue
marker = defaultfuncargprefixmarker
from _pytest import deprecated
self.config.warn(
"C1", deprecated.FUNCARG_PREFIX.format(name=name), nodeid=nodeid
filename, lineno = getfslineno(obj)
warnings.warn_explicit(
deprecated.FUNCARG_PREFIX.format(name=name),
category=None,
filename=str(filename),
lineno=lineno + 1,
)
name = name[len(self._argprefix) :]
elif not isinstance(marker, FixtureFunctionMarker):
@@ -1343,8 +1371,7 @@ class FixtureManager(object):
fixturedefs = self._arg2fixturedefs[argname]
except KeyError:
return None
else:
return tuple(self._matchfactories(fixturedefs, nodeid))
return tuple(self._matchfactories(fixturedefs, nodeid))
def _matchfactories(self, fixturedefs, nodeid):
for fixturedef in fixturedefs:

View File

@@ -139,7 +139,7 @@ def showhelp(config):
tw.line()
tw.line()
tw.line(
"[pytest] ini-options in the first " "pytest.ini|tox.ini|setup.cfg file found:"
"[pytest] ini-options in the first pytest.ini|tox.ini|setup.cfg file found:"
)
tw.line()
@@ -156,6 +156,7 @@ def showhelp(config):
vars = [
("PYTEST_ADDOPTS", "extra command line options"),
("PYTEST_PLUGINS", "comma-separated plugins to load during startup"),
("PYTEST_DISABLE_PLUGIN_AUTOLOAD", "set to disable plugin auto-loading"),
("PYTEST_DEBUG", "set to enable debug tracing of pytest's internals"),
]
for name, help in vars:

View File

@@ -526,7 +526,17 @@ def pytest_terminal_summary(terminalreporter, exitstatus):
@hookspec(historic=True)
def pytest_logwarning(message, code, nodeid, fslocation):
""" process a warning specified by a message, a code string,
"""
.. deprecated:: 3.8
This hook is will stop working in a future release.
pytest no longer triggers this hook, but the
terminal writer still implements it to display warnings issued by
:meth:`_pytest.config.Config.warn` and :meth:`_pytest.nodes.Node.warn`. Calling those functions will be
an error in future releases.
process a warning specified by a message, a code string,
a nodeid and fslocation (both of which may be None
if the warning is not tied to a particular node/location).
@@ -535,6 +545,30 @@ def pytest_logwarning(message, code, nodeid, fslocation):
"""
@hookspec(historic=True)
def pytest_warning_captured(warning_message, when, item):
"""
Process a warning captured by the internal pytest warnings plugin.
:param warnings.WarningMessage warning_message:
The captured warning. This is the same object produced by :py:func:`warnings.catch_warnings`, and contains
the same attributes as the parameters of :py:func:`warnings.showwarning`.
:param str when:
Indicates when the warning was captured. Possible values:
* ``"config"``: during pytest configuration/initialization stage.
* ``"collect"``: during test collection.
* ``"runtest"``: during test execution.
:param pytest.Item|None item:
**DEPRECATED**: This parameter is incompatible with ``pytest-xdist``, and will always receive ``None``
in a future release.
The item being executed if ``when`` is ``"runtest"``, otherwise ``None``.
"""
# -------------------------------------------------------------------------
# doctest hooks
# -------------------------------------------------------------------------

View File

@@ -38,7 +38,7 @@ class Junit(py.xml.Namespace):
# this dynamically instead of hardcoding it. The spec range of valid
# chars is: Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD]
# | [#x10000-#x10FFFF]
_legal_chars = (0x09, 0x0A, 0x0d)
_legal_chars = (0x09, 0x0A, 0x0D)
_legal_ranges = ((0x20, 0x7E), (0x80, 0xD7FF), (0xE000, 0xFFFD), (0x10000, 0x10FFFF))
_legal_xml_re = [
unicode("%s-%s") % (unichr(low), unichr(high))
@@ -258,12 +258,11 @@ def record_property(request):
@pytest.fixture
def record_xml_property(record_property):
def record_xml_property(record_property, request):
"""(Deprecated) use record_property."""
import warnings
from _pytest import deprecated
warnings.warn(deprecated.RECORD_XML_PROPERTY, DeprecationWarning, stacklevel=2)
request.node.warn(deprecated.RECORD_XML_PROPERTY)
return record_property
@@ -274,9 +273,9 @@ def record_xml_attribute(request):
The fixture is callable with ``(name, value)``, with value being
automatically xml-encoded
"""
request.node.warn(
code="C3", message="record_xml_attribute is an experimental feature"
)
from _pytest.warning_types import PytestWarning
request.node.warn(PytestWarning("record_xml_attribute is an experimental feature"))
xml = getattr(request.config, "_xml", None)
if xml is not None:
node_reporter = xml.node_reporter(request.node.nodeid)

View File

@@ -2,10 +2,11 @@
from __future__ import absolute_import, division, print_function
import logging
from contextlib import closing, contextmanager
from contextlib import contextmanager
import re
import six
from _pytest.compat import dummy_context_manager
from _pytest.config import create_terminal_writer
import pytest
import py
@@ -212,7 +213,8 @@ class LogCaptureFixture(object):
def __init__(self, item):
"""Creates a new funcarg."""
self._item = item
self._initial_log_levels = {} # type: Dict[str, int] # dict of log name -> log level
# dict of log name -> log level
self._initial_log_levels = {} # type: Dict[str, int]
def _finalize(self):
"""Finalizes the fixture.
@@ -369,11 +371,6 @@ def pytest_configure(config):
config.pluginmanager.register(LoggingPlugin(config), "logging-plugin")
@contextmanager
def _dummy_context_manager():
yield
class LoggingPlugin(object):
"""Attaches to the logging module and captures log messages for each test.
"""
@@ -418,7 +415,6 @@ class LoggingPlugin(object):
else:
self.log_file_handler = None
# initialized during pytest_runtestloop
self.log_cli_handler = None
def _log_cli_enabled(self):
@@ -429,6 +425,22 @@ class LoggingPlugin(object):
"--log-cli-level"
) is not None or self._config.getini("log_cli")
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_collection(self):
# This has to be called before the first log message is logged,
# so we can access the terminal reporter plugin.
self._setup_cli_logging()
with self.live_logs_context():
if self.log_cli_handler:
self.log_cli_handler.set_when("collection")
if self.log_file_handler is not None:
with catching_logs(self.log_file_handler, level=self.log_file_level):
yield
else:
yield
@contextmanager
def _runtest_for(self, item, when):
"""Implements the internals of pytest_runtest_xxx() hook."""
@@ -449,8 +461,8 @@ class LoggingPlugin(object):
try:
yield # run test
finally:
del item.catch_log_handler
if when == "teardown":
del item.catch_log_handler
del item.catch_log_handlers
if self.print_logs:
@@ -488,22 +500,15 @@ class LoggingPlugin(object):
@pytest.hookimpl(hookwrapper=True)
def pytest_runtestloop(self, session):
"""Runs all collected test items."""
self._setup_cli_logging()
with self.live_logs_context:
with self.live_logs_context():
if self.log_file_handler is not None:
with closing(self.log_file_handler):
with catching_logs(
self.log_file_handler, level=self.log_file_level
):
yield # run all the tests
with catching_logs(self.log_file_handler, level=self.log_file_level):
yield # run all the tests
else:
yield # run all the tests
def _setup_cli_logging(self):
"""Sets up the handler and logger for the Live Logs feature, if enabled.
This must be done right before starting the loop so we can access the terminal reporter plugin.
"""
"""Sets up the handler and logger for the Live Logs feature, if enabled."""
terminal_reporter = self._config.pluginmanager.get_plugin("terminalreporter")
if self._log_cli_enabled() and terminal_reporter is not None:
capture_manager = self._config.pluginmanager.get_plugin("capturemanager")
@@ -533,11 +538,14 @@ class LoggingPlugin(object):
self._config, "log_cli_level", "log_level"
)
self.log_cli_handler = log_cli_handler
self.live_logs_context = catching_logs(
self.live_logs_context = lambda: catching_logs(
log_cli_handler, formatter=log_cli_formatter, level=log_cli_level
)
else:
self.live_logs_context = _dummy_context_manager()
self.live_logs_context = lambda: dummy_context_manager()
# Note that the lambda for the live_logs_context is needed because
# live_logs_context can otherwise not be entered multiple times due
# to limitations of contextlib.contextmanager
class _LiveLoggingStreamHandler(logging.StreamHandler):
@@ -572,9 +580,12 @@ class _LiveLoggingStreamHandler(logging.StreamHandler):
self._test_outcome_written = False
def emit(self, record):
if self.capture_manager is not None:
self.capture_manager.suspend_global_capture()
try:
ctx_manager = (
self.capture_manager.global_and_fixture_disabled()
if self.capture_manager
else dummy_context_manager()
)
with ctx_manager:
if not self._first_record_emitted:
self.stream.write("\n")
self._first_record_emitted = True
@@ -586,6 +597,3 @@ class _LiveLoggingStreamHandler(logging.StreamHandler):
self.stream.section("live log " + self._when, sep="-", bold=True)
self._section_name_shown = True
logging.StreamHandler.emit(self, record)
finally:
if self.capture_manager is not None:
self.capture_manager.resume_global_capture()

View File

@@ -156,7 +156,10 @@ def pytest_addoption(parser):
dest="basetemp",
default=None,
metavar="dir",
help="base temporary directory for this test run.",
help=(
"base temporary directory for this test run."
"(warning: this directory is removed if it exists)"
),
)
@@ -182,10 +185,13 @@ def wrap_session(config, doit):
session.exitstatus = EXIT_TESTSFAILED
except KeyboardInterrupt:
excinfo = _pytest._code.ExceptionInfo()
if initstate < 2 and isinstance(excinfo.value, exit.Exception):
exitstatus = EXIT_INTERRUPTED
if initstate <= 2 and isinstance(excinfo.value, exit.Exception):
sys.stderr.write("{}: {}\n".format(excinfo.typename, excinfo.value.msg))
if excinfo.value.returncode is not None:
exitstatus = excinfo.value.returncode
config.hook.pytest_keyboard_interrupt(excinfo=excinfo)
session.exitstatus = EXIT_INTERRUPTED
session.exitstatus = exitstatus
except: # noqa
excinfo = _pytest._code.ExceptionInfo()
config.notify_exception(excinfo, config.option)
@@ -383,6 +389,7 @@ class Session(nodes.FSCollector):
self.trace = config.trace.root.get("collection")
self._norecursepatterns = config.getini("norecursedirs")
self.startdir = py.path.local()
self._initialpaths = frozenset()
# Keep track of any collected nodes in here, so we don't duplicate fixtures
self._node_cache = {}
@@ -441,13 +448,14 @@ class Session(nodes.FSCollector):
self.trace("perform_collect", self, args)
self.trace.root.indent += 1
self._notfound = []
self._initialpaths = set()
initialpaths = []
self._initialparts = []
self.items = items = []
for arg in args:
parts = self._parsearg(arg)
self._initialparts.append(parts)
self._initialpaths.add(parts[0])
initialpaths.append(parts[0])
self._initialpaths = frozenset(initialpaths)
rep = collect_one_node(self)
self.ihook.pytest_collectreport(report=rep)
self.trace.root.indent -= 1
@@ -485,7 +493,7 @@ class Session(nodes.FSCollector):
from _pytest.python import Package
names = self._parsearg(arg)
argpath = names.pop(0)
argpath = names.pop(0).realpath()
paths = []
root = self
@@ -502,13 +510,14 @@ class Session(nodes.FSCollector):
pkginit = parent.join("__init__.py")
if pkginit.isfile():
if pkginit in self._node_cache:
root = self._node_cache[pkginit]
root = self._node_cache[pkginit][0]
else:
col = root._collectfile(pkginit)
if col:
if isinstance(col[0], Package):
root = col[0]
self._node_cache[root.fspath] = root
# always store a list in the cache, matchnodes expects it
self._node_cache[root.fspath] = [root]
# If it's a directory argument, recurse and look for any Subpackages.
# Let the Package collector deal with subnodes, don't collect here.
@@ -528,8 +537,8 @@ class Session(nodes.FSCollector):
if (type(x), x.fspath) in self._node_cache:
yield self._node_cache[(type(x), x.fspath)]
else:
yield x
self._node_cache[(type(x), x.fspath)] = x
yield x
else:
assert argpath.check(file=1)
@@ -561,10 +570,7 @@ class Session(nodes.FSCollector):
return True
def _tryconvertpyarg(self, x):
"""Convert a dotted module name to path.
"""
"""Convert a dotted module name to path."""
try:
with _patched_find_module():
loader = pkgutil.find_loader(x)
@@ -596,8 +602,7 @@ class Session(nodes.FSCollector):
raise UsageError(
"file or package not found: " + arg + " (missing __init__.py?)"
)
else:
raise UsageError("file not found: " + arg)
raise UsageError("file not found: " + arg)
parts[0] = path
return parts
@@ -625,11 +630,12 @@ class Session(nodes.FSCollector):
resultnodes.append(node)
continue
assert isinstance(node, nodes.Collector)
if node.nodeid in self._node_cache:
rep = self._node_cache[node.nodeid]
key = (type(node), node.nodeid)
if key in self._node_cache:
rep = self._node_cache[key]
else:
rep = collect_one_node(node)
self._node_cache[node.nodeid] = rep
self._node_cache[key] = rep
if rep.passed:
has_matched = False
for x in rep.result:

View File

@@ -24,11 +24,6 @@ __all__ = [
]
class MarkerError(Exception):
"""Error in use of a pytest marker/attribute."""
def param(*values, **kw):
"""Specify a parameter in `pytest.mark.parametrize`_ calls or
:ref:`parametrized fixtures <fixture-parametrize-marks>`.
@@ -163,9 +158,9 @@ def pytest_configure(config):
empty_parameterset = config.getini(EMPTY_PARAMETERSET_OPTION)
if empty_parameterset not in ("skip", "xfail", None, ""):
if empty_parameterset not in ("skip", "xfail", "fail_at_collect", None, ""):
raise UsageError(
"{!s} must be one of skip and xfail,"
"{!s} must be one of skip, xfail or fail_at_collect"
" but it is {!r}".format(EMPTY_PARAMETERSET_OPTION, empty_parameterset)
)

View File

@@ -90,7 +90,10 @@ class MarkEvaluator(object):
else:
if "reason" not in mark.kwargs:
# XXX better be checked at collection time
msg = "you need to specify reason=STRING " "when using booleans as conditions."
msg = (
"you need to specify reason=STRING "
"when using booleans as conditions."
)
fail(msg)
result = bool(expr)
if result:

View File

@@ -66,7 +66,10 @@ python_keywords_allowed_list = ["or", "and", "not"]
def matchmark(colitem, markexpr):
"""Tries to match on any marker names, attached to the given colitem."""
return eval(markexpr, {}, MarkMapping.from_item(colitem))
try:
return eval(markexpr, {}, MarkMapping.from_item(colitem))
except SyntaxError as e:
raise SyntaxError(str(e) + "\nMarker expression must be valid Python!")
def matchkeyword(colitem, keywordexpr):

View File

@@ -6,6 +6,7 @@ from operator import attrgetter
import attr
from _pytest.outcomes import fail
from ..deprecated import MARK_PARAMETERSET_UNPACKING, MARK_INFO_ATTRIBUTE
from ..compat import NOTSET, getfslineno, MappingMixin
from six.moves import map
@@ -32,11 +33,19 @@ def istestfunc(func):
def get_empty_parameterset_mark(config, argnames, func):
from ..nodes import Collector
requested_mark = config.getini(EMPTY_PARAMETERSET_OPTION)
if requested_mark in ("", None, "skip"):
mark = MARK_GEN.skip
elif requested_mark == "xfail":
mark = MARK_GEN.xfail(run=False)
elif requested_mark == "fail_at_collect":
f_name = func.__name__
_, lineno = getfslineno(func)
raise Collector.CollectError(
"Empty parameter set in '%s' at line %d" % (f_name, lineno)
)
else:
raise LookupError(requested_mark)
fs, lineno = getfslineno(func)
@@ -65,7 +74,7 @@ class ParameterSet(namedtuple("ParameterSet", "values, marks, id")):
return cls(values, marks, id_)
@classmethod
def extract_from(cls, parameterset, legacy_force_tuple=False):
def extract_from(cls, parameterset, belonging_definition, legacy_force_tuple=False):
"""
:param parameterset:
a legacy style parameterset that may or may not be a tuple,
@@ -75,6 +84,7 @@ class ParameterSet(namedtuple("ParameterSet", "values, marks, id")):
enforce tuple wrapping so single argument tuple values
don't get decomposed and break tests
:param belonging_definition: the item that we will be extracting the parameters from.
"""
if isinstance(parameterset, cls):
@@ -93,20 +103,24 @@ class ParameterSet(namedtuple("ParameterSet", "values, marks, id")):
if legacy_force_tuple:
argval = (argval,)
if newmarks:
warnings.warn(MARK_PARAMETERSET_UNPACKING)
if newmarks and belonging_definition is not None:
belonging_definition.warn(MARK_PARAMETERSET_UNPACKING)
return cls(argval, marks=newmarks, id=None)
@classmethod
def _for_parametrize(cls, argnames, argvalues, func, config):
def _for_parametrize(cls, argnames, argvalues, func, config, function_definition):
if not isinstance(argnames, (tuple, list)):
argnames = [x.strip() for x in argnames.split(",") if x.strip()]
force_tuple = len(argnames) == 1
else:
force_tuple = False
parameters = [
ParameterSet.extract_from(x, legacy_force_tuple=force_tuple)
ParameterSet.extract_from(
x,
legacy_force_tuple=force_tuple,
belonging_definition=function_definition,
)
for x in argvalues
]
del argvalues
@@ -302,7 +316,7 @@ def _marked(func, mark):
return any(mark == info.combined for info in func_mark)
@attr.s
@attr.s(repr=False)
class MarkInfo(object):
""" Marking object created by :class:`MarkDecorator` instances. """
@@ -380,7 +394,7 @@ class MarkGenerator(object):
x = marker.split("(", 1)[0]
values.add(x)
if name not in self._markers:
raise AttributeError("%r not a registered marker" % (name,))
fail("{!r} not a registered marker".format(name), pytrace=False)
MARK_GEN = MarkGenerator()

View File

@@ -4,9 +4,12 @@ from __future__ import absolute_import, division, print_function
import os
import sys
import re
import warnings
from contextlib import contextmanager
import six
import pytest
from _pytest.fixtures import fixture
RE_IMPORT_ERROR_NAME = re.compile("^No module named (.*)$")
@@ -209,22 +212,41 @@ class MonkeyPatch(object):
self._setitem.append((dic, name, dic.get(name, notset)))
del dic[name]
def _warn_if_env_name_is_not_str(self, name):
"""On Python 2, warn if the given environment variable name is not a native str (#4056)"""
if six.PY2 and not isinstance(name, str):
warnings.warn(
pytest.PytestWarning(
"Environment variable name {!r} should be str".format(name)
)
)
def setenv(self, name, value, prepend=None):
""" Set environment variable ``name`` to ``value``. If ``prepend``
is a character, read the current environment variable value
and prepend the ``value`` adjoined with the ``prepend`` character."""
value = str(value)
if not isinstance(value, str):
warnings.warn(
pytest.PytestWarning(
"Environment variable value {!r} should be str, converted to str implicitly".format(
value
)
)
)
value = str(value)
if prepend and name in os.environ:
value = value + prepend + os.environ[name]
self._warn_if_env_name_is_not_str(name)
self.setitem(os.environ, name, value)
def delenv(self, name, raising=True):
""" Delete ``name`` from the environment. Raise KeyError it does not
exist.
""" Delete ``name`` from the environment. Raise KeyError if it does
not exist.
If ``raising`` is set to False, no exception will be raised if the
environment variable is missing.
"""
self._warn_if_env_name_is_not_str(name)
self.delitem(os.environ, name, raising=raising)
def syspath_prepend(self, path):

View File

@@ -1,5 +1,6 @@
from __future__ import absolute_import, division, print_function
import os
import warnings
import six
import py
@@ -7,6 +8,8 @@ import attr
import _pytest
import _pytest._code
from _pytest.compat import getfslineno
from _pytest.outcomes import fail
from _pytest.mark.structures import NodeKeywords, MarkInfo
@@ -59,11 +62,11 @@ class _CompatProperty(object):
if obj is None:
return self
# TODO: reenable in the features branch
# warnings.warn(
# "usage of {owner!r}.{name} is deprecated, please use pytest.{name} instead".format(
# name=self.name, owner=type(owner).__name__),
# PendingDeprecationWarning, stacklevel=2)
from _pytest.deprecated import COMPAT_PROPERTY
warnings.warn(
COMPAT_PROPERTY.format(name=self.name, owner=owner.__name__), stacklevel=2
)
return getattr(__import__("pytest"), self.name)
@@ -124,29 +127,107 @@ class Node(object):
if isinstance(maybe_compatprop, _CompatProperty):
return getattr(__import__("pytest"), name)
else:
from _pytest.deprecated import CUSTOM_CLASS
cls = getattr(self, name)
# TODO: reenable in the features branch
# warnings.warn("use of node.%s is deprecated, "
# "use pytest_pycollect_makeitem(...) to create custom "
# "collection nodes" % name, category=DeprecationWarning)
self.warn(CUSTOM_CLASS.format(name=name, type_name=type(self).__name__))
return cls
def __repr__(self):
return "<%s %r>" % (self.__class__.__name__, getattr(self, "name", None))
def warn(self, code, message):
""" generate a warning with the given code and message for this
item. """
def warn(self, _code_or_warning=None, message=None, code=None):
"""Issue a warning for this item.
Warnings will be displayed after the test session, unless explicitly suppressed.
This can be called in two forms:
**Warning instance**
This was introduced in pytest 3.8 and uses the standard warning mechanism to issue warnings.
.. code-block:: python
node.warn(PytestWarning("some message"))
The warning instance must be a subclass of :class:`pytest.PytestWarning`.
**code/message (deprecated)**
This form was used in pytest prior to 3.8 and is considered deprecated. Using this form will emit another
warning about the deprecation:
.. code-block:: python
node.warn("CI", "some message")
:param Union[Warning,str] _code_or_warning:
warning instance or warning code (legacy). This parameter receives an underscore for backward
compatibility with the legacy code/message form, and will be replaced for something
more usual when the legacy form is removed.
:param Union[str,None] message: message to display when called in the legacy form.
:param str code: code for the warning, in legacy form when using keyword arguments.
:return:
"""
if message is None:
if _code_or_warning is None:
raise ValueError("code_or_warning must be given")
self._std_warn(_code_or_warning)
else:
if _code_or_warning and code:
raise ValueError(
"code_or_warning and code cannot both be passed to this function"
)
code = _code_or_warning or code
self._legacy_warn(code, message)
def _legacy_warn(self, code, message):
"""
.. deprecated:: 3.8
Use :meth:`Node.std_warn <_pytest.nodes.Node.std_warn>` instead.
Generate a warning with the given code and message for this item.
"""
from _pytest.deprecated import NODE_WARN
self._std_warn(NODE_WARN)
assert isinstance(code, str)
fslocation = getattr(self, "location", None)
if fslocation is None:
fslocation = getattr(self, "fspath", None)
fslocation = get_fslocation_from_item(self)
self.ihook.pytest_logwarning.call_historic(
kwargs=dict(
code=code, message=message, nodeid=self.nodeid, fslocation=fslocation
)
)
def _std_warn(self, warning):
"""Issue a warning for this item.
Warnings will be displayed after the test session, unless explicitly suppressed
:param Warning warning: the warning instance to issue. Must be a subclass of PytestWarning.
:raise ValueError: if ``warning`` instance is not a subclass of PytestWarning.
"""
from _pytest.warning_types import PytestWarning
if not isinstance(warning, PytestWarning):
raise ValueError(
"warning must be an instance of PytestWarning or subclass, got {!r}".format(
warning
)
)
path, lineno = get_fslocation_from_item(self)
warnings.warn_explicit(
warning,
category=None,
filename=str(path),
lineno=lineno + 1 if lineno is not None else None,
)
# methods for ordering nodes
@property
def nodeid(self):
@@ -266,6 +347,9 @@ class Node(object):
pass
def _repr_failure_py(self, excinfo, style=None):
if excinfo.errisinstance(fail.Exception):
if not excinfo.value.pytrace:
return six.text_type(excinfo.value)
fm = self.session._fixturemanager
if excinfo.errisinstance(fm.FixtureLookupError):
return excinfo.value.formatrepr()
@@ -310,6 +394,24 @@ class Node(object):
repr_failure = _repr_failure_py
def get_fslocation_from_item(item):
"""Tries to extract the actual location from an item, depending on available attributes:
* "fslocation": a pair (path, lineno)
* "obj": a Python object that the item wraps.
* "fspath": just a path
:rtype: a tuple of (str|LocalPath, int) with filename and line number.
"""
result = getattr(item, "location", None)
if result is not None:
return result[:2]
obj = getattr(item, "obj", None)
if obj is not None:
return getfslineno(obj)
return getattr(item, "fspath", "unknown location"), -1
class Collector(Node):
""" Collector instances create children through collect()
and thus iteratively build a tree.

View File

@@ -3,7 +3,6 @@ exception classes and constants handling test outcomes
as well as functions creating them
"""
from __future__ import absolute_import, division, print_function
import py
import sys
@@ -21,7 +20,7 @@ class OutcomeException(BaseException):
if self.msg:
val = self.msg
if isinstance(val, bytes):
val = py._builtin._totext(val, errors="replace")
val = val.decode("UTF-8", errors="replace")
return val
return "<%s instance>" % (self.__class__.__name__,)
@@ -50,31 +49,43 @@ class Failed(OutcomeException):
class Exit(KeyboardInterrupt):
""" raised for immediate program exits (no tracebacks/summaries)"""
def __init__(self, msg="unknown reason"):
def __init__(self, msg="unknown reason", returncode=None):
self.msg = msg
self.returncode = returncode
KeyboardInterrupt.__init__(self, msg)
# exposed helper methods
def exit(msg):
""" exit testing process as if KeyboardInterrupt was triggered. """
def exit(msg, returncode=None):
"""
Exit testing process as if KeyboardInterrupt was triggered.
:param str msg: message to display upon exit.
:param int returncode: return code to be used when exiting pytest.
"""
__tracebackhide__ = True
raise Exit(msg)
raise Exit(msg, returncode)
exit.Exception = Exit
def skip(msg="", **kwargs):
""" skip an executing test with the given message. Note: it's usually
better to use the pytest.mark.skipif marker to declare a test to be
skipped under certain conditions like mismatching platforms or
dependencies. See the pytest_skipping plugin for details.
"""
Skip an executing test with the given message.
This function should be called only during testing (setup, call or teardown) or
during collection by using the ``allow_module_level`` flag.
:kwarg bool allow_module_level: allows this function to be called at
module level, skipping the rest of the module. Default to False.
.. note::
It is better to use the :ref:`pytest.mark.skipif ref` marker when possible to declare a test to be
skipped under certain conditions like mismatching platforms or
dependencies.
"""
__tracebackhide__ = True
allow_module_level = kwargs.pop("allow_module_level", False)
@@ -88,10 +99,12 @@ skip.Exception = Skipped
def fail(msg="", pytrace=True):
""" explicitly fail a currently-executing test with the given Message.
"""
Explicitly fail an executing test with the given message.
:arg pytrace: if false the msg represents the full failure information
and no python traceback will be reported.
:param str msg: the message to show the user as reason for the failure.
:param bool pytrace: if false the msg represents the full failure information and no
python traceback will be reported.
"""
__tracebackhide__ = True
raise Failed(msg=msg, pytrace=pytrace)
@@ -105,7 +118,15 @@ class XFailed(fail.Exception):
def xfail(reason=""):
""" xfail an executing test or setup functions with the given reason."""
"""
Imperatively xfail an executing test or setup functions with the given reason.
This function should be called only during testing (setup, call or teardown).
.. note::
It is better to use the :ref:`pytest.mark.xfail ref` marker when possible to declare a test to be
xfailed under certain conditions like known bugs or missing features.
"""
__tracebackhide__ = True
raise XFailed(reason)

283
src/_pytest/pathlib.py Normal file
View File

@@ -0,0 +1,283 @@
import os
import errno
import atexit
import operator
import six
import sys
from functools import reduce
import uuid
from six.moves import map
import itertools
import shutil
from os.path import expanduser, expandvars, isabs, sep
from posixpath import sep as posix_sep
import fnmatch
import stat
from .compat import PY36
if PY36:
from pathlib import Path, PurePath
else:
from pathlib2 import Path, PurePath
__all__ = ["Path", "PurePath"]
LOCK_TIMEOUT = 60 * 60 * 3
get_lock_path = operator.methodcaller("joinpath", ".lock")
def ensure_reset_dir(path):
"""
ensures the given path is a empty directory
"""
if path.exists():
rmtree(path, force=True)
path.mkdir()
def _shutil_rmtree_remove_writable(func, fspath, _):
"Clear the readonly bit and reattempt the removal"
os.chmod(fspath, stat.S_IWRITE)
func(fspath)
def rmtree(path, force=False):
if force:
# ignore_errors leaves dead folders around
# python needs a rm -rf as a followup
# the trick with _shutil_rmtree_remove_writable is unreliable
shutil.rmtree(str(path), ignore_errors=True)
else:
shutil.rmtree(str(path))
def find_prefixed(root, prefix):
"""finds all elements in root that begin with the prefix, case insensitive"""
l_prefix = prefix.lower()
for x in root.iterdir():
if x.name.lower().startswith(l_prefix):
yield x
def extract_suffixes(iter, prefix):
"""
:param iter: iterator over path names
:param prefix: expected prefix of the path names
:returns: the parts of the paths following the prefix
"""
p_len = len(prefix)
for p in iter:
yield p.name[p_len:]
def find_suffixes(root, prefix):
"""combines find_prefixes and extract_suffixes
"""
return extract_suffixes(find_prefixed(root, prefix), prefix)
def parse_num(maybe_num):
"""parses number path suffixes, returns -1 on error"""
try:
return int(maybe_num)
except ValueError:
return -1
if six.PY2:
def _max(iterable, default):
"""needed due to python2.7 lacking the default argument for max"""
return reduce(max, iterable, default)
else:
_max = max
def make_numbered_dir(root, prefix):
"""create a directory with a increased number as suffix for the given prefix"""
for i in range(10):
# try up to 10 times to create the folder
max_existing = _max(map(parse_num, find_suffixes(root, prefix)), default=-1)
new_number = max_existing + 1
new_path = root.joinpath("{}{}".format(prefix, new_number))
try:
new_path.mkdir()
except Exception:
pass
else:
return new_path
else:
raise EnvironmentError(
"could not create numbered dir with prefix "
"{prefix} in {root} after 10 tries".format(prefix=prefix, root=root)
)
def create_cleanup_lock(p):
"""crates a lock to prevent premature folder cleanup"""
lock_path = get_lock_path(p)
try:
fd = os.open(str(lock_path), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644)
except OSError as e:
if e.errno == errno.EEXIST:
six.raise_from(
EnvironmentError("cannot create lockfile in {path}".format(path=p)), e
)
else:
raise
else:
pid = os.getpid()
spid = str(pid)
if not isinstance(spid, six.binary_type):
spid = spid.encode("ascii")
os.write(fd, spid)
os.close(fd)
if not lock_path.is_file():
raise EnvironmentError("lock path got renamed after sucessfull creation")
return lock_path
def register_cleanup_lock_removal(lock_path, register=atexit.register):
"""registers a cleanup function for removing a lock, by default on atexit"""
pid = os.getpid()
def cleanup_on_exit(lock_path=lock_path, original_pid=pid):
current_pid = os.getpid()
if current_pid != original_pid:
# fork
return
try:
lock_path.unlink()
except (OSError, IOError):
pass
return register(cleanup_on_exit)
def delete_a_numbered_dir(path):
"""removes a numbered directory"""
create_cleanup_lock(path)
parent = path.parent
garbage = parent.joinpath("garbage-{}".format(uuid.uuid4()))
path.rename(garbage)
rmtree(garbage, force=True)
def ensure_deletable(path, consider_lock_dead_if_created_before):
"""checks if a lock exists and breaks it if its considered dead"""
if path.is_symlink():
return False
lock = get_lock_path(path)
if not lock.exists():
return True
try:
lock_time = lock.stat().st_mtime
except Exception:
return False
else:
if lock_time < consider_lock_dead_if_created_before:
lock.unlink()
return True
else:
return False
def try_cleanup(path, consider_lock_dead_if_created_before):
"""tries to cleanup a folder if we can ensure its deletable"""
if ensure_deletable(path, consider_lock_dead_if_created_before):
delete_a_numbered_dir(path)
def cleanup_candidates(root, prefix, keep):
"""lists candidates for numbered directories to be removed - follows py.path"""
max_existing = _max(map(parse_num, find_suffixes(root, prefix)), default=-1)
max_delete = max_existing - keep
paths = find_prefixed(root, prefix)
paths, paths2 = itertools.tee(paths)
numbers = map(parse_num, extract_suffixes(paths2, prefix))
for path, number in zip(paths, numbers):
if number <= max_delete:
yield path
def cleanup_numbered_dir(root, prefix, keep, consider_lock_dead_if_created_before):
"""cleanup for lock driven numbered directories"""
for path in cleanup_candidates(root, prefix, keep):
try_cleanup(path, consider_lock_dead_if_created_before)
for path in root.glob("garbage-*"):
try_cleanup(path, consider_lock_dead_if_created_before)
def make_numbered_dir_with_cleanup(root, prefix, keep, lock_timeout):
"""creates a numbered dir with a cleanup lock and removes old ones"""
e = None
for i in range(10):
try:
p = make_numbered_dir(root, prefix)
lock_path = create_cleanup_lock(p)
register_cleanup_lock_removal(lock_path)
except Exception as e:
pass
else:
consider_lock_dead_if_created_before = p.stat().st_mtime - lock_timeout
cleanup_numbered_dir(
root=root,
prefix=prefix,
keep=keep,
consider_lock_dead_if_created_before=consider_lock_dead_if_created_before,
)
return p
assert e is not None
raise e
def resolve_from_str(input, root):
assert not isinstance(input, Path), "would break on py2"
root = Path(root)
input = expanduser(input)
input = expandvars(input)
if isabs(input):
return Path(input)
else:
return root.joinpath(input)
def fnmatch_ex(pattern, path):
"""FNMatcher port from py.path.common which works with PurePath() instances.
The difference between this algorithm and PurePath.match() is that the latter matches "**" glob expressions
for each part of the path, while this algorithm uses the whole path instead.
For example:
"tests/foo/bar/doc/test_foo.py" matches pattern "tests/**/doc/test*.py" with this algorithm, but not with
PurePath.match().
This algorithm was ported to keep backward-compatibility with existing settings which assume paths match according
this logic.
References:
* https://bugs.python.org/issue29249
* https://bugs.python.org/issue34731
"""
path = PurePath(path)
iswin32 = sys.platform.startswith("win")
if iswin32 and sep not in pattern and posix_sep in pattern:
# Running on Windows, the pattern has no Windows path separators,
# and the pattern has one or more Posix path separators. Replace
# the Posix path separators with the Windows path separator.
pattern = pattern.replace(posix_sep, sep)
if sep not in pattern:
name = path.name
else:
name = six.text_type(path)
return fnmatch.fnmatch(name, pattern)

View File

@@ -1,13 +0,0 @@
from .compat import Path
from os.path import expanduser, expandvars, isabs
def resolve_from_str(input, root):
assert not isinstance(input, Path), "would break on py2"
root = Path(root)
input = expanduser(input)
input = expandvars(input)
if isabs(input):
return Path(input)
else:
return root.joinpath(input)

View File

@@ -17,11 +17,13 @@ from weakref import WeakKeyDictionary
from _pytest.capture import MultiCapture, SysCapture
from _pytest._code import Source
from _pytest.main import Session, EXIT_INTERRUPTED, EXIT_OK
from _pytest.assertion.rewrite import AssertionRewritingHook
from _pytest.pathlib import Path
from _pytest.compat import safe_str
import py
import pytest
from _pytest.main import Session, EXIT_OK
from _pytest.assertion.rewrite import AssertionRewritingHook
from _pytest.compat import Path
IGNORE_PAM = [ # filenames added when obtaining details about the current user
u"/var/lib/sss/mc/passwd"
@@ -34,7 +36,7 @@ def pytest_addoption(parser):
action="store_true",
dest="lsof",
default=False,
help=("run FD checks if lsof is available"),
help="run FD checks if lsof is available",
)
parser.addoption(
@@ -60,6 +62,11 @@ def pytest_configure(config):
config.pluginmanager.register(checker)
def raise_on_kwargs(kwargs):
if kwargs:
raise TypeError("Unexpected arguments: {}".format(", ".join(sorted(kwargs))))
class LsofFdLeakChecker(object):
def get_open_files(self):
out = self._exec_lsof()
@@ -125,7 +132,7 @@ class LsofFdLeakChecker(object):
error.append(error[0])
error.append("*** function %s:%s: %s " % item.location)
error.append("See issue #2366")
item.warn("", "\n".join(error))
item.warn(pytest.PytestWarning("\n".join(error)))
# XXX copied from execnet's conftest.py - needs to be merged
@@ -273,7 +280,7 @@ class HookRecorder(object):
del self.calls[i]
return call
lines = ["could not find call %r, in:" % (name,)]
lines.extend([" %s" % str(x) for x in self.calls])
lines.extend([" %s" % x for x in self.calls])
pytest.fail("\n".join(lines))
def getcall(self, name):
@@ -406,7 +413,9 @@ class RunResult(object):
return d
raise ValueError("Pytest terminal report not found")
def assert_outcomes(self, passed=0, skipped=0, failed=0, error=0):
def assert_outcomes(
self, passed=0, skipped=0, failed=0, error=0, xpassed=0, xfailed=0
):
"""Assert that the specified outcomes appear with the respective
numbers (0 means it didn't occur) in the text output from a test run.
@@ -417,10 +426,18 @@ class RunResult(object):
"skipped": d.get("skipped", 0),
"failed": d.get("failed", 0),
"error": d.get("error", 0),
"xpassed": d.get("xpassed", 0),
"xfailed": d.get("xfailed", 0),
}
assert obtained == dict(
passed=passed, skipped=skipped, failed=failed, error=error
)
expected = {
"passed": passed,
"skipped": skipped,
"failed": failed,
"error": error,
"xpassed": xpassed,
"xfailed": xfailed,
}
assert obtained == expected
class CwdSnapshot(object):
@@ -471,11 +488,16 @@ class Testdir(object):
"""
class TimeoutExpired(Exception):
pass
def __init__(self, request, tmpdir_factory):
self.request = request
self._mod_collections = WeakKeyDictionary()
name = request.function.__name__
self.tmpdir = tmpdir_factory.mktemp(name, numbered=True)
self.test_tmproot = tmpdir_factory.mktemp("tmp-" + name, numbered=True)
os.environ["PYTEST_DEBUG_TEMPROOT"] = str(self.test_tmproot)
self.plugins = []
self._cwd_snapshot = CwdSnapshot()
self._sys_path_snapshot = SysPathsSnapshot()
@@ -502,6 +524,7 @@ class Testdir(object):
self._sys_modules_snapshot.restore()
self._sys_path_snapshot.restore()
self._cwd_snapshot.restore()
os.environ.pop("PYTEST_DEBUG_TEMPROOT", None)
def __take_sys_modules_snapshot(self):
# some zope modules used by twisted-related tests keep internal state
@@ -514,7 +537,6 @@ class Testdir(object):
def make_hook_recorder(self, pluginmanager):
"""Create a new :py:class:`HookRecorder` for a PluginManager."""
assert not hasattr(pluginmanager, "reprec")
pluginmanager.reprec = reprec = HookRecorder(pluginmanager)
self.request.addfinalizer(reprec.finish_recording)
return reprec
@@ -550,18 +572,22 @@ class Testdir(object):
return ret
def makefile(self, ext, *args, **kwargs):
"""Create a new file in the testdir.
r"""Create new file(s) in the testdir.
ext: The extension the file should use, including the dot, e.g. `.py`.
args: All args will be treated as strings and joined using newlines.
:param str ext: The extension the file(s) should use, including the dot, e.g. `.py`.
:param list[str] args: All args will be treated as strings and joined using newlines.
The result will be written as contents to the file. The name of the
file will be based on the test function requesting this fixture.
E.g. "testdir.makefile('.txt', 'line1', 'line2')"
kwargs: Each keyword is the name of a file, while the value of it will
:param kwargs: Each keyword is the name of a file, while the value of it will
be written as contents of the file.
E.g. "testdir.makefile('.ini', pytest='[pytest]\naddopts=-rs\n')"
Examples:
.. code-block:: python
testdir.makefile(".txt", "line1", "line2")
testdir.makefile(".ini", pytest="[pytest]\naddopts=-rs\n")
"""
return self._makefile(ext, args, kwargs)
@@ -628,10 +654,10 @@ class Testdir(object):
return p
def copy_example(self, name=None):
from . import experiments
import warnings
from _pytest.warning_types import PYTESTER_COPY_EXAMPLE
warnings.warn(experiments.PYTESTER_COPY_EXAMPLE, stacklevel=2)
warnings.warn(PYTESTER_COPY_EXAMPLE, stacklevel=2)
example_dir = self.request.config.getini("pytester_example_dir")
if example_dir is None:
raise ValueError("pytester_example_dir is unset, can't copy examples")
@@ -667,7 +693,9 @@ class Testdir(object):
example_path.copy(result)
return result
else:
raise LookupError("example is not found as a file or directory")
raise LookupError(
'example "{}" is not found as a file or directory'.format(example_path)
)
Session = Session
@@ -829,7 +857,7 @@ class Testdir(object):
# typically we reraise keyboard interrupts from the child run
# because it's our user requesting interruption of the testing
if ret == 2 and not kwargs.get("no_reraise_ctrlc"):
if ret == EXIT_INTERRUPTED and not kwargs.get("no_reraise_ctrlc"):
calls = reprec.getcalls("pytest_keyboard_interrupt")
if calls and calls[-1].excinfo.type == KeyboardInterrupt:
raise KeyboardInterrupt()
@@ -881,14 +909,12 @@ class Testdir(object):
return self._runpytest_method(*args, **kwargs)
def _ensure_basetemp(self, args):
args = [str(x) for x in args]
args = list(args)
for x in args:
if str(x).startswith("--basetemp"):
# print("basedtemp exists: %s" %(args,))
if safe_str(x).startswith("--basetemp"):
break
else:
args.append("--basetemp=%s" % self.tmpdir.dirpath("basetemp"))
# print("added basetemp: %s" %(args,))
return args
def parseconfig(self, *args):
@@ -1014,7 +1040,7 @@ class Testdir(object):
"""
env = os.environ.copy()
env["PYTHONPATH"] = os.pathsep.join(
filter(None, [str(os.getcwd()), env.get("PYTHONPATH", "")])
filter(None, [os.getcwd(), env.get("PYTHONPATH", "")])
)
kw["env"] = env
@@ -1025,22 +1051,30 @@ class Testdir(object):
return popen
def run(self, *cmdargs):
def run(self, *cmdargs, **kwargs):
"""Run a command with arguments.
Run a process using subprocess.Popen saving the stdout and stderr.
:param args: the sequence of arguments to pass to `subprocess.Popen()`
:param timeout: the period in seconds after which to timeout and raise
:py:class:`Testdir.TimeoutExpired`
Returns a :py:class:`RunResult`.
"""
return self._run(*cmdargs)
__tracebackhide__ = True
def _run(self, *cmdargs):
cmdargs = [str(x) for x in cmdargs]
timeout = kwargs.pop("timeout", None)
raise_on_kwargs(kwargs)
cmdargs = [
str(arg) if isinstance(arg, py.path.local) else arg for arg in cmdargs
]
p1 = self.tmpdir.join("stdout")
p2 = self.tmpdir.join("stderr")
print("running:", " ".join(cmdargs))
print(" in:", str(py.path.local()))
print("running:", *cmdargs)
print(" in:", py.path.local())
f1 = codecs.open(str(p1), "w", encoding="utf8")
f2 = codecs.open(str(p2), "w", encoding="utf8")
try:
@@ -1048,7 +1082,40 @@ class Testdir(object):
popen = self.popen(
cmdargs, stdout=f1, stderr=f2, close_fds=(sys.platform != "win32")
)
ret = popen.wait()
def handle_timeout():
__tracebackhide__ = True
timeout_message = (
"{seconds} second timeout expired running:"
" {command}".format(seconds=timeout, command=cmdargs)
)
popen.kill()
popen.wait()
raise self.TimeoutExpired(timeout_message)
if timeout is None:
ret = popen.wait()
elif six.PY3:
try:
ret = popen.wait(timeout)
except subprocess.TimeoutExpired:
handle_timeout()
else:
end = time.time() + timeout
resolution = min(0.1, timeout / 10)
while True:
ret = popen.poll()
if ret is not None:
break
if time.time() > end:
handle_timeout()
time.sleep(resolution)
finally:
f1.close()
f2.close()
@@ -1072,7 +1139,7 @@ class Testdir(object):
print("couldn't print to %s because of encoding" % (fp,))
def _getpytestargs(self):
return (sys.executable, "-mpytest")
return sys.executable, "-mpytest"
def runpython(self, script):
"""Run a python script using sys.executable as interpreter.
@@ -1095,9 +1162,15 @@ class Testdir(object):
with "runpytest-" so they do not conflict with the normal numbered
pytest location for temporary files and directories.
:param args: the sequence of arguments to pass to the pytest subprocess
:param timeout: the period in seconds after which to timeout and raise
:py:class:`Testdir.TimeoutExpired`
Returns a :py:class:`RunResult`.
"""
__tracebackhide__ = True
p = py.path.local.make_numbered_dir(
prefix="runpytest-", keep=None, rootdir=self.tmpdir
)
@@ -1106,7 +1179,7 @@ class Testdir(object):
if plugins:
args = ("-p", plugins[0]) + args
args = self._getpytestargs() + args
return self.run(*args)
return self.run(*args, timeout=kwargs.get("timeout"))
def spawn_pytest(self, string, expect_timeout=10.0):
"""Run pytest using pexpect.
@@ -1254,6 +1327,7 @@ class LineMatcher(object):
matches and non-matches are also printed on stdout.
"""
__tracebackhide__ = True
self._match_lines(lines2, fnmatch, "fnmatch")
def re_match_lines(self, lines2):
@@ -1265,6 +1339,7 @@ class LineMatcher(object):
The matches and non-matches are also printed on stdout.
"""
__tracebackhide__ = True
self._match_lines(lines2, lambda name, pat: re.match(pat, name), "re.match")
def _match_lines(self, lines2, match_func, match_nickname):

View File

@@ -13,11 +13,10 @@ from textwrap import dedent
import py
import six
from _pytest.main import FSHookProxy
from _pytest.mark import MarkerError
from _pytest.config import hookimpl
import _pytest
import pluggy
from _pytest._code import filter_traceback
from _pytest import fixtures
from _pytest import nodes
from _pytest import deprecated
@@ -37,6 +36,7 @@ from _pytest.compat import (
getlocation,
enum,
get_default_arg_names,
getimfunc,
)
from _pytest.outcomes import fail
from _pytest.mark.structures import (
@@ -44,38 +44,7 @@ from _pytest.mark.structures import (
get_unpacked_marks,
normalize_mark_list,
)
# 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
# for better maintenance
_pluggy_dir = py.path.local(pluggy.__file__.rstrip("oc"))
# pluggy is either a package or a single module depending on the version
if _pluggy_dir.basename == "__init__.py":
_pluggy_dir = _pluggy_dir.dirpath()
_pytest_dir = py.path.local(_pytest.__file__).dirpath()
_py_dir = py.path.local(py.__file__).dirpath()
def filter_traceback(entry):
"""Return True if a TracebackEntry instance should be removed from tracebacks:
* dynamically generated code (no code to show up for it);
* internal traceback from pytest or its internal libraries, py and pluggy.
"""
# entry.path might sometimes return a str object when the entry
# points to dynamically generated code
# see https://bitbucket.org/pytest-dev/py/issues/71
raw_filename = entry.frame.code.raw.co_filename
is_generated = "<" in raw_filename and ">" in raw_filename
if is_generated:
return False
# entry.path might point to a non-existing file, in which case it will
# also return a str object. see #1133
p = py.path.local(entry.path)
return (
not p.relto(_pluggy_dir) and not p.relto(_pytest_dir) and not p.relto(_py_dir)
)
from _pytest.warning_types import RemovedInPytest4Warning, PytestWarning
def pyobj_property(name):
@@ -130,7 +99,7 @@ def pytest_addoption(parser):
"python_functions",
type="args",
default=["test"],
help="prefixes or glob names for Python test function and " "method discovery",
help="prefixes or glob names for Python test function and method discovery",
)
group.addoption(
@@ -158,8 +127,8 @@ def pytest_generate_tests(metafunc):
alt_spellings = ["parameterize", "parametrise", "parameterise"]
for attr in alt_spellings:
if hasattr(metafunc.function, attr):
msg = "{0} has '{1}', spelling should be 'parametrize'"
raise MarkerError(msg.format(metafunc.function.__name__, attr))
msg = "{0} has '{1}' mark, spelling should be 'parametrize'"
fail(msg.format(metafunc.function.__name__, attr), pytrace=False)
for marker in metafunc.definition.iter_markers(name="parametrize"):
metafunc.parametrize(*marker.args, **marker.kwargs)
@@ -173,13 +142,14 @@ def pytest_configure(config):
"or a list of tuples of values if argnames specifies multiple names. "
"Example: @parametrize('arg1', [1,2]) would lead to two calls of the "
"decorated test function, one with arg1=1 and another with arg1=2."
"see http://pytest.org/latest/parametrize.html for more info and "
"examples.",
"see https://docs.pytest.org/en/latest/parametrize.html for more info "
"and examples.",
)
config.addinivalue_line(
"markers",
"usefixtures(fixturename1, fixturename2, ...): mark tests as needing "
"all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures ",
"all of the specified fixtures. see "
"https://docs.pytest.org/en/latest/fixture.html#usefixtures ",
)
@@ -201,15 +171,19 @@ def pytest_collect_file(path, parent):
ext = path.ext
if ext == ".py":
if not parent.session.isinitpath(path):
for pat in parent.config.getini("python_files") + ["__init__.py"]:
if path.fnmatch(pat):
break
else:
if not path_matches_patterns(
path, parent.config.getini("python_files") + ["__init__.py"]
):
return
ihook = parent.session.gethookproxy(path)
return ihook.pytest_pycollect_makemodule(path=path, parent=parent)
def path_matches_patterns(path, patterns):
"""Returns True if the given py.path.local matches one of the patterns in the list of globs given"""
return any(path.fnmatch(pattern) for pattern in patterns)
def pytest_pycollect_makemodule(path, parent):
if path.basename == "__init__.py":
return Package(path, parent)
@@ -234,9 +208,14 @@ def pytest_pycollect_makeitem(collector, name, obj):
# or a funtools.wrapped.
# We musn't if it's been wrapped with mock.patch (python 2 only)
if not (isfunction(obj) or isfunction(get_real_func(obj))):
collector.warn(
code="C2",
message="cannot collect %r because it is not a function." % name,
filename, lineno = getfslineno(obj)
warnings.warn_explicit(
message=PytestWarning(
"cannot collect %r because it is not a function." % name
),
category=None,
filename=str(filename),
lineno=lineno + 1,
)
elif getattr(obj, "__test__", True):
if is_generator(obj):
@@ -344,11 +323,6 @@ class PyCollector(PyobjMixin, nodes.Collector):
if isinstance(obj, staticmethod):
# static methods need to be unwrapped
obj = safe_getattr(obj, "__func__", False)
if obj is False:
# Python 2.6 wraps in a different way that we won't try to handle
msg = "cannot collect static method %r because it is not a function"
self.warn(code="C2", message=msg % name)
return False
return (
safe_getattr(obj, "__call__", False)
and fixtures.getfixturemarker(obj) is None
@@ -590,17 +564,33 @@ class Package(Module):
self.session.config.pluginmanager._duplicatepaths.remove(path)
this_path = self.fspath.dirpath()
pkg_prefix = None
init_module = this_path.join("__init__.py")
if init_module.check(file=1) and path_matches_patterns(
init_module, self.config.getini("python_files")
):
yield Module(init_module, self)
pkg_prefixes = set()
for path in this_path.visit(rec=self._recurse, bf=True, sort=True):
# we will visit our own __init__.py file, in which case we skip it
skip = False
if path.basename == "__init__.py" and path.dirpath() == this_path:
continue
if pkg_prefix and pkg_prefix in path.parts():
for pkg_prefix in pkg_prefixes:
if (
pkg_prefix in path.parts()
and pkg_prefix.join("__init__.py") != path
):
skip = True
if skip:
continue
if path.isdir() and path.join("__init__.py").check(file=1):
pkg_prefixes.add(path)
for x in self._collectfile(path):
yield x
if isinstance(x, Package):
pkg_prefix = path.dirpath()
def _get_xunit_setup_teardown(holder, attr_name, param_obj=None):
@@ -641,16 +631,18 @@ class Class(PyCollector):
return []
if hasinit(self.obj):
self.warn(
"C1",
"cannot collect test class %r because it has a "
"__init__ constructor" % self.obj.__name__,
PytestWarning(
"cannot collect test class %r because it has a "
"__init__ constructor" % self.obj.__name__
)
)
return []
elif hasnew(self.obj):
self.warn(
"C1",
"cannot collect test class %r because it has a "
"__new__ constructor" % self.obj.__name__,
PytestWarning(
"cannot collect test class %r because it has a "
"__new__ constructor" % self.obj.__name__
)
)
return []
return [self._getcustomclass("Instance")(name="()", parent=self)]
@@ -658,14 +650,12 @@ class Class(PyCollector):
def setup(self):
setup_class = _get_xunit_func(self.obj, "setup_class")
if setup_class is not None:
setup_class = getattr(setup_class, "im_func", setup_class)
setup_class = getattr(setup_class, "__func__", setup_class)
setup_class = getimfunc(setup_class)
setup_class(self.obj)
fin_class = getattr(self.obj, "teardown_class", None)
if fin_class is not None:
fin_class = getattr(fin_class, "im_func", fin_class)
fin_class = getattr(fin_class, "__func__", fin_class)
fin_class = getimfunc(fin_class)
self.addfinalizer(lambda: fin_class(self.obj))
@@ -738,12 +728,6 @@ class FunctionMixin(PyobjMixin):
for entry in excinfo.traceback[1:-1]:
entry.set_repr_style("short")
def _repr_failure_py(self, excinfo, style="long"):
if excinfo.errisinstance(fail.Exception):
if not excinfo.value.pytrace:
return py._builtin._totext(excinfo.value)
return super(FunctionMixin, self)._repr_failure_py(excinfo, style=style)
def repr_failure(self, excinfo, outerr=None):
assert outerr is None, "XXX outerr usage is deprecated"
style = self.config.option.tbstyle
@@ -777,8 +761,11 @@ class Generator(FunctionMixin, PyCollector):
"%r generated tests with non-unique name %r" % (self, name)
)
seen[name] = True
values.append(self.Function(name, self, args=args, callobj=call))
self.warn("C1", deprecated.YIELD_TESTS)
with warnings.catch_warnings():
# ignore our own deprecation warning
function_class = self.Function
values.append(function_class(name, self, args=args, callobj=call))
self.warn(deprecated.YIELD_TESTS)
return values
def getcallargs(self, obj):
@@ -945,7 +932,11 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
from _pytest.mark import ParameterSet
argnames, parameters = ParameterSet._for_parametrize(
argnames, argvalues, self.function, self.config
argnames,
argvalues,
self.function,
self.config,
function_definition=self.definition,
)
del argvalues
@@ -956,9 +947,11 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
arg_values_types = self._resolve_arg_value_types(argnames, indirect)
ids = self._resolve_arg_ids(argnames, ids, parameters)
ids = self._resolve_arg_ids(argnames, ids, parameters, item=self.definition)
scopenum = scope2index(scope, descr="call to {}".format(self.parametrize))
scopenum = scope2index(
scope, descr="parametrize() call in {}".format(self.function.__name__)
)
# create the new calls: if we are parametrize() multiple times (by applying the decorator
# more than once) then we accumulate those calls generating the cartesian product
@@ -979,13 +972,14 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
newcalls.append(newcallspec)
self._calls = newcalls
def _resolve_arg_ids(self, argnames, ids, parameters):
def _resolve_arg_ids(self, argnames, ids, parameters, item):
"""Resolves the actual ids for the given argnames, based on the ``ids`` parameter given
to ``parametrize``.
:param List[str] argnames: list of argument names passed to ``parametrize()``.
:param ids: the ids parameter of the parametrized call (see docs).
:param List[ParameterSet] parameters: the list of parameter values, same size as ``argnames``.
:param Item item: the item that generated this parametrized call.
:rtype: List[str]
:return: the list of ids for each argname given
"""
@@ -996,17 +990,18 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
idfn = ids
ids = None
if ids:
func_name = self.function.__name__
if len(ids) != len(parameters):
raise ValueError(
"%d tests specified with %d ids" % (len(parameters), len(ids))
)
msg = "In {}: {} parameter sets specified, with different number of ids: {}"
fail(msg.format(func_name, len(parameters), len(ids)), pytrace=False)
for id_value in ids:
if id_value is not None and not isinstance(id_value, six.string_types):
msg = "ids must be list of strings, found: %s (type: %s)"
raise ValueError(
msg % (saferepr(id_value), type(id_value).__name__)
msg = "In {}: ids must be list of strings, found: {} (type: {!r})"
fail(
msg.format(func_name, saferepr(id_value), type(id_value)),
pytrace=False,
)
ids = idmaker(argnames, parameters, idfn, ids, self.config)
ids = idmaker(argnames, parameters, idfn, ids, self.config, item=item)
return ids
def _resolve_arg_value_types(self, argnames, indirect):
@@ -1029,9 +1024,11 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
valtypes = dict.fromkeys(argnames, "funcargs")
for arg in indirect:
if arg not in argnames:
raise ValueError(
"indirect given to %r: fixture %r doesn't exist"
% (self.function, arg)
fail(
"In {}: indirect fixture '{}' doesn't exist".format(
self.function.__name__, arg
),
pytrace=False,
)
valtypes[arg] = "params"
return valtypes
@@ -1045,19 +1042,25 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
:raise ValueError: if validation fails.
"""
default_arg_names = set(get_default_arg_names(self.function))
func_name = self.function.__name__
for arg in argnames:
if arg not in self.fixturenames:
if arg in default_arg_names:
raise ValueError(
"%r already takes an argument %r with a default value"
% (self.function, arg)
fail(
"In {}: function already takes an argument '{}' with a default value".format(
func_name, arg
),
pytrace=False,
)
else:
if isinstance(indirect, (tuple, list)):
name = "fixture" if arg in indirect else "argument"
else:
name = "fixture" if indirect else "argument"
raise ValueError("%r uses no %s %r" % (self.function, name, arg))
fail(
"In {}: function uses no {} '{}'".format(func_name, name, arg),
pytrace=False,
)
def addcall(self, funcargs=None, id=NOTSET, param=NOTSET):
""" Add a new call to the underlying test function during the collection phase of a test run.
@@ -1079,10 +1082,8 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
:arg param: a parameter which will be exposed to a later fixture function
invocation through the ``request.param`` attribute.
"""
if self.config:
self.config.warn(
"C1", message=deprecated.METAFUNC_ADD_CALL, fslocation=None
)
warnings.warn(deprecated.METAFUNC_ADD_CALL, stacklevel=2)
assert funcargs is None or isinstance(funcargs, dict)
if funcargs is not None:
for name in funcargs:
@@ -1116,13 +1117,18 @@ def _find_parametrized_scope(argnames, arg2fixturedefs, indirect):
"""
from _pytest.fixtures import scopes
indirect_as_list = isinstance(indirect, (list, tuple))
all_arguments_are_fixtures = (
indirect is True or indirect_as_list and len(indirect) == argnames
)
if isinstance(indirect, (list, tuple)):
all_arguments_are_fixtures = len(indirect) == len(argnames)
else:
all_arguments_are_fixtures = bool(indirect)
if all_arguments_are_fixtures:
fixturedefs = arg2fixturedefs or {}
used_scopes = [fixturedef[0].scope for name, fixturedef in fixturedefs.items()]
used_scopes = [
fixturedef[0].scope
for name, fixturedef in fixturedefs.items()
if name in argnames
]
if used_scopes:
# Takes the most narrow scope from used fixtures
for scope in reversed(scopes):
@@ -1132,21 +1138,20 @@ def _find_parametrized_scope(argnames, arg2fixturedefs, indirect):
return "function"
def _idval(val, argname, idx, idfn, config=None):
def _idval(val, argname, idx, idfn, item, config):
if idfn:
s = None
try:
s = idfn(val)
except Exception:
except Exception as e:
# See issue https://github.com/pytest-dev/pytest/issues/2169
import warnings
msg = (
"Raised while trying to determine id of parameter %s at position %d."
% (argname, idx)
"While trying to determine id of parameter {} at position "
"{} the following exception was raised:\n".format(argname, idx)
)
msg += "\nUpdate your code as this will raise an error in pytest-4.0."
warnings.warn(msg, DeprecationWarning)
msg += " {}: {}\n".format(type(e).__name__, e)
msg += "This warning will be an error error in pytest-4.0."
item.warn(RemovedInPytest4Warning(msg))
if s:
return ascii_escaped(s)
@@ -1170,12 +1175,12 @@ def _idval(val, argname, idx, idfn, config=None):
return str(argname) + str(idx)
def _idvalset(idx, parameterset, argnames, idfn, ids, config=None):
def _idvalset(idx, parameterset, argnames, idfn, ids, item, config):
if parameterset.id is not None:
return parameterset.id
if ids is None or (idx >= len(ids) or ids[idx] is None):
this_id = [
_idval(val, argname, idx, idfn, config)
_idval(val, argname, idx, idfn, item=item, config=config)
for val, argname in zip(parameterset.values, argnames)
]
return "-".join(this_id)
@@ -1183,9 +1188,9 @@ def _idvalset(idx, parameterset, argnames, idfn, ids, config=None):
return ascii_escaped(ids[idx])
def idmaker(argnames, parametersets, idfn=None, ids=None, config=None):
def idmaker(argnames, parametersets, idfn=None, ids=None, config=None, item=None):
ids = [
_idvalset(valindex, parameterset, argnames, idfn, ids, config)
_idvalset(valindex, parameterset, argnames, idfn, ids, config=config, item=item)
for valindex, parameterset in enumerate(parametersets)
]
if len(set(ids)) != len(ids):
@@ -1408,7 +1413,7 @@ class Function(FunctionMixin, nodes.Item, fixtures.FuncargnamesCompatAttr):
@property
def function(self):
"underlying python 'function' object"
return getattr(self.obj, "im_func", self.obj)
return getimfunc(self.obj)
def _getobj(self):
name = self.name

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