Compare commits

...

455 Commits
3.4.1 ... 3.6.0

Author SHA1 Message Date
Bruno Oliveira
45e7703133 Move mark revamp note to the top of the CHANGELOG 2018-05-22 21:12:51 -03:00
Bruno Oliveira
d70e910b65 Preparing release version 3.6.0 2018-05-23 00:07:48 +00:00
Bruno Oliveira
c55db1faac Merge remote-tracking branch 'upstream/master' into release-3.6.0 2018-05-23 00:03:58 +00:00
Bruno Oliveira
16583a6d43 Add missing libs to tasks/requirements.txt 2018-05-23 00:03:47 +00:00
Bruno Oliveira
7985eff5b4 Merge pull request #3497 from hroncok/approx_compat_import
Import Mapping and Sequence from compat in python_api::approx
2018-05-22 20:57:29 -03:00
Miro Hrončok
5072226f69 Import Mapping and Sequence from compat in python_api::approx
Related to https://github.com/pytest-dev/pytest/issues/3339

Fixes a DeprecationWarning on Python 3.7

Adds Mapping to compat
2018-05-22 22:25:15 +02:00
Ronny Pfannschmidt
6c8d46d8ea Merge pull request #3487 from tgoodlet/detect_pytest_hook_prefix
Detect `pytest_` prefixed hooks
2018-05-22 21:36:58 +02:00
Bruno Oliveira
7d0c9837ce Merge pull request #3459 from RonnyPfannschmidt/mark-iter-name-filter
introduce name filtering for marker iteration again
2018-05-21 13:24:39 -03:00
Bruno Oliveira
8e17e32253 Merge pull request #3492 from timhughes/patch-1
fix typo in docs
2018-05-20 15:51:03 -03:00
Tim Hughes
f0b855369c fix typo 2018-05-20 19:14:06 +01:00
Ronny Pfannschmidt
4aa7ebaf52 Merge pull request #3490 from nicoddemus/merge-master-into-features
Merge master into features
2018-05-19 11:36:11 +02:00
Tyler Goodlet
486b786cb2 Add trivial changelog entry 2018-05-18 12:02:50 -04:00
Bruno Oliveira
674879976f Merge remote-tracking branch 'upstream/master' into merge-master-into-features 2018-05-18 12:30:08 -03:00
Tyler Goodlet
d4065a9166 Detect pytest_ prefixed hooks
`pluggy` is deprecating the `implprefix` argument in the next major
release so implement this detection in our derived plugin manager.

Relates to pytest-dev/pluggy#145
2018-05-17 20:38:54 -04:00
Bruno Oliveira
e7f75f69f2 Small grammar fix in CHANGELOG 2018-05-17 21:20:43 -03:00
Bruno Oliveira
5be85a1f55 Merge changelog marks refactoring changelog entries as discussed in review 2018-05-17 21:18:12 -03:00
Bruno Oliveira
bb626fe8a7 Fix typo in mark.rst 2018-05-17 16:06:00 -03:00
Ronny Pfannschmidt
45faaeca7a Merge pull request #3480 from nicoddemus/deprecated-deadlne-hypothesis
Attempt to fix CI problems related to Hypothesis
2018-05-17 20:18:27 +02:00
Bruno Oliveira
11fb384efb Fix link to skipif tutorial in ref docs 2018-05-17 10:15:47 -03:00
Bruno Oliveira
5edad01d4e Merge pull request #3476 from avirlrma/master
change doc copyright year to range and auto update year
2018-05-16 17:53:23 -03:00
Bruno Oliveira
f5361a302c Use a more recent hypothesis version on CI 2018-05-16 07:35:02 -03:00
Bruno Oliveira
94e62dfc50 Increase hypothesis deadline
The value is pretty close to the new default that will come out in
future hypothesis versions, which is generating a deprecation error
on CI.

Using a loose deadline because CI boxes are not reliable in how much
CPU power they have available
2018-05-16 07:34:53 -03:00
Bruno Oliveira
afe4800daf Fix deprecation notice in Node.get_marker 2018-05-15 21:47:40 -03:00
Bruno Oliveira
2cd159e8c5 Add section on how to update marks and add ref to the deprecation notice 2018-05-15 21:35:27 -03:00
Bruno Oliveira
718ba83600 Small adjustment to the CHANGELOG 2018-05-15 21:02:05 -03:00
Bruno Oliveira
1b2f4f4483 Small rewording to the CHANGELOG 2018-05-15 21:01:37 -03:00
Bruno Oliveira
f68bab06b4 Reword the changelog 2018-05-15 18:24:14 -03:00
Bruno Oliveira
a4425cb4af Merge pull request #3469 from scop/dropthedot
#dropthedot
2018-05-15 08:00:28 -03:00
Bruno Oliveira
01d2d81d1f Merge pull request #3468 from scop/spelling
Spelling and grammar fixes
2018-05-15 07:59:56 -03:00
aviral1701
f14e097635 added changelog and added my name to the authors 2018-05-15 12:54:48 +05:30
aviral1701
a59f677d93 change doc copyright year to range and auto update year 2018-05-15 12:48:04 +05:30
Bruno Oliveira
36614b0a3d Fix formatting in CHANGELOG entry 2018-05-14 09:56:36 -03:00
Ville Skyttä
b4370c08b9 #dropthedot 2018-05-13 12:09:47 +02:00
Ville Skyttä
aa51fcb2b6 Spelling and grammar fixes 2018-05-13 12:06:09 +02:00
Ronny Pfannschmidt
4914135fdf introduce name filtering for marker iteration again 2018-05-11 21:48:15 +02:00
Ronny Pfannschmidt
84b37e1b57 Merge pull request #3452 from nicoddemus/fix-flaky-xdist-test
Attempt to fix flaky test on Python 2
2018-05-05 20:18:06 +02:00
Bruno Oliveira
fa76a5c8fe Attempt to fix flaky test on Python 2
This test sometimes fails on AppVeyor with:

```
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.0 = <listiterator object at 0x0549DC50>
    leaked_types = sum(1 for _ in gc.garbage
>                   if 'PseudoFixtureDef' in str(_))
E   UnicodeEncodeError: 'ascii' codec can't encode character u'\u263a' in position 23: ordinal not in range(128)
test_request_garbage.py:19: UnicodeEncodeError
====================== 1 passed, 1 error in 1.39 seconds ======================
```

Use our internal "safe_repr" function to handle Encode errors: who knows
what objects are in garbage after all.
2018-05-04 17:56:51 -03:00
Ronny Pfannschmidt
27651f4032 Merge pull request #3444 from nicoddemus/escape-whitespace-diffs
Escape whitespace only strings when diffing them on failed assertions
2018-05-04 18:14:35 +02:00
Bruno Oliveira
413b1aa4e9 Rename internal function as suggested during review 2018-05-04 11:08:35 -03:00
Bruno Oliveira
dca77b2273 Escape whitespace only strings when diffing them on failed assertions
Fix #3443
2018-05-03 20:38:00 -03:00
Bruno Oliveira
35f53a7353 Merge pull request #3442 from RonnyPfannschmidt/fix-3441-markexpr-markstorage
Fix #3441 let mark expressions be populated from the modern marker storage
2018-05-03 19:27:07 -03:00
Ronny Pfannschmidt
e6a86e0f4c add tests for #3441 2018-05-03 17:33:59 +02:00
Bruno Oliveira
b03b387861 Merge pull request #3436 from brianmaissy/feature/enter_pdb_on_keyboard_interrupt
--pdb treats KeyboardInterrupt as exception
2018-05-03 12:20:43 -03:00
Ronny Pfannschmidt
a5cf55dd4a fix test_mark_option_custom - it used the legacy keyword storage for addign markers 2018-05-03 16:33:16 +02:00
Ronny Pfannschmidt
63ef46dd91 fix #3441 - use iter-marks to populate markexpr namespace 2018-05-03 16:28:47 +02:00
Ronny Pfannschmidt
7834b45002 Merge pull request #3437 from nicoddemus/mark-changelog
Improve changelog note about markers
2018-05-02 06:45:39 +02:00
Bruno Oliveira
ccaa979f27 Improve changelog note about markers
Also changed the title in the docs because the previous title had
the same level as the title, making it appear in a separate entry
in `contents.rst`
2018-05-01 19:27:53 -03:00
Bruno Oliveira
1a880be85b Remove unused import to fix linting 2018-05-01 19:08:29 -03:00
Brian Maissy
c258fe1459 --pdb treats KeyboardInterrupt as exception 2018-05-02 00:58:35 +03:00
Ronny Pfannschmidt
08aed1a6bf Merge pull request #3433 from nicoddemus/conda-forge-badge
Use conda-forge badge instead of anaconda's
2018-04-27 16:11:58 +02:00
Bruno Oliveira
b49e9191ac Use conda-forge badge instead of anaconda's
Just noticed that conda-forge has its own badge so let's use that
2018-04-27 10:23:28 -03:00
Bruno Oliveira
febccae037 Merge pull request #3431 from jdufresne/pypi
Update all pypi.python.org URLs to pypi.org
2018-04-26 13:15:46 -03:00
Jon Dufresne
d2bf0bf9bb Update all pypi.python.org URLs to pypi.org
For details on the new PyPI, see the blog post:

https://pythoninsider.blogspot.ca/2018/04/new-pypi-launched-legacy-pypi-shutting.html
2018-04-26 06:49:33 -07:00
Ronny Pfannschmidt
5ba0663827 Merge pull request #3428 from nicoddemus/merge-master-into-features
Merge master into features
2018-04-25 14:04:41 +02:00
Bruno Oliveira
63368e07ea Merge remote-tracking branch 'upstream/master' into merge-master-into-features 2018-04-24 21:26:45 -03:00
Bruno Oliveira
0b2b73c36a Remove CHANGELOG entries left by old bug in towncrier 2018-04-24 21:25:13 -03:00
Bruno Oliveira
69b1c2d4f6 Merge pull request #3424 from nicoddemus/release-3.5.1
Preparing release version 3.5.1
2018-04-24 19:30:54 -03:00
Bruno Oliveira
4a92011e6e Fix note about importing from collections.abc 2018-04-24 07:06:52 -03:00
Ronny Pfannschmidt
6e62fc98ff Merge pull request #2849 from ApaDoctor/disable-repeated-fixture
provide error fixture applied to the same func
2018-04-24 06:36:54 +02:00
Bruno Oliveira
132fb61eba Merge remote-tracking branch 'upstream/features' into ApaDoctor/disable-repeated-fixture 2018-04-23 22:24:53 -03:00
Bruno Oliveira
03850cf962 Add test for applying fixture twice
Fix #2334
2018-04-23 22:17:46 -03:00
Bruno Oliveira
f05230c679 Move #3221 to trivial section in the CHANGELOG 2018-04-23 19:27:07 -03:00
Bruno Oliveira
d61a7670a1 Merge pull request #3422 from rachel1792/master
updated doc to delete deprecated hook: Issue #3417
2018-04-23 19:24:52 -03:00
Bruno Oliveira
8d56641590 Preparing release version 3.5.1 2018-04-23 22:24:21 +00:00
Bruno Oliveira
2a480c59ae Merge pull request #3367 from brianmaissy/feature/indicative_error_for_parametrize_with_default_argument
added indicative error when parametrizing an argument with a default …
2018-04-23 11:04:36 -03:00
Rachel Kogan
6ea4c12da7 updated doc to delete deprecated hook 2018-04-22 21:56:18 -04:00
Bruno Oliveira
f0084608cc Merge pull request #3407 from rachel1792/master
Fix typo in caplog fixture documentation: Issue #3406
2018-04-20 17:41:04 -03:00
Bruno Oliveira
cefeba33ef Update changelog formatting 2018-04-20 17:40:19 -03:00
Bruno Oliveira
3318e53d01 Merge pull request #3382 from feuillemorte/3290-improve-monkeypatch
#3290 improve monkeypatch
2018-04-19 17:05:52 -03:00
Ronny Pfannschmidt
1cc79ffc10 Merge pull request #3408 from MarSoft/patch-2
Fix typo in the comment
2018-04-19 09:21:11 +02:00
Семён Марьясин
d8015764e6 Fix typo in the comment 2018-04-18 12:47:52 +03:00
Rachel Kogan
48c99f62e3 add changelog 2018-04-17 16:24:32 -04:00
Rachel Kogan
8ff8a82c51 Merge branch 'master' of github.com:pytest-dev/pytest 2018-04-17 16:21:04 -04:00
Rachel Kogan
bb8984f5ed fix error in caplog docs. 2018-04-17 16:17:29 -04:00
Bruno Oliveira
159cd39777 Merge pull request #3401 from kohr-h/underscore_fixtures
Mention omission of fixtures with leading '_', closes #3398
2018-04-16 13:25:10 -03:00
Brian Maissy
857098fe0f added indicative error when parametrizing an argument with a default value 2018-04-15 20:52:57 +03:00
feuillemorte
283ac8bbf4 #3290 Fix doc 2018-04-14 21:06:58 +03:00
Holger Kohr
6626d2aef9 Mention omission of fixtures with leading '_', closes #3398 2018-04-14 11:04:00 +02:00
feuillemorte
ba7cad3962 #3290 Fix comments 2018-04-13 16:00:07 +03:00
Ronny Pfannschmidt
36f6687b70 Merge pull request #3397 from darrenburns/patch-2
Fix some mistakes in contributing.rst
2018-04-13 13:20:31 +02:00
Darren Burns
86def48b25 Fix some mistakes in contributing.rst 2018-04-13 11:33:58 +01:00
Bruno Oliveira
0024b71f1c Merge pull request #3395 from darrenburns/patch-1
Update CONTRIBUTING.rst
2018-04-12 18:42:33 -03:00
Ronny Pfannschmidt
17a43dc4a5 firther enhance the links in contributing.rst 2018-04-12 22:06:06 +02:00
Darren Burns
958f146125 Update CONTRIBUTING.rst
Fixes a broken link in CONTRIBUTING.rst
2018-04-12 20:54:32 +01:00
Ronny Pfannschmidt
13a6f63cd9 Merge pull request #3390 from nicoddemus/atomic-pyc-writes
Attempt to solve race-condition which corrupts .pyc files on Windows
2018-04-12 13:57:39 +02:00
Bruno Oliveira
aa95a425d7 Attempt to solve race-condition which corrupts .pyc files on Windows
This uses of the `atomicwrites` library.

This is very hard to create a reliable test for.

Fix #3008
2018-04-12 08:19:28 -03:00
Bruno Oliveira
015626ce69 Merge pull request #3384 from nicoddemus/leak-frame
Reset reference to failed test frame before each test executes
2018-04-12 07:53:34 -03:00
Ronny Pfannschmidt
f9a908abb8 Merge pull request #3388 from nicoddemus/simplify-tox
Simplify tox to avoid repetition
2018-04-12 11:32:48 +02:00
Ronny Pfannschmidt
160c309371 Merge pull request #3391 from nicoddemus/project-urls
Add additional project urls to setup.py
2018-04-12 08:54:12 +02:00
Ronny Pfannschmidt
f79b0324fe Merge pull request #3387 from nicoddemus/merge-master-into-features
Merge master into features
2018-04-12 08:39:22 +02:00
Bruno Oliveira
37ee4fbc48 Add additional project urls to setup.py
According to:

https://packaging.python.org/tutorials/distributing-packages/#project-urls

Those URLs are displayed in the project page in Warehouse (new PyPI).
2018-04-11 23:46:17 -03:00
Bruno Oliveira
888fcbc4b4 Simplify tox to avoid repetition
Also took the opportunity to:

* Use more cores for py*-xdist environments
* Update freeze test to use py36
2018-04-11 18:30:15 -03:00
Bruno Oliveira
10a7160549 Merge remote-tracking branch 'upstream/master' into merge-master-into-features 2018-04-11 18:07:47 -03:00
feuillemorte
a4daac7eb0 #3290 Fix doc 2018-04-11 12:46:02 +03:00
feuillemorte
97be076f29 #3290 Added changelog file 2018-04-11 12:45:46 +03:00
feuillemorte
3d60f955f0 #3290 Added test 2018-04-11 12:45:37 +03:00
feuillemorte
659c044372 #3290 Improved monkeypatch to support some form of with statement 2018-04-11 12:45:26 +03:00
Bruno Oliveira
6e8e3c967a Add changelog entry for #2798 2018-04-10 21:09:57 -03:00
Bruno Oliveira
78c900448e Add acceptance test for #2798 2018-04-10 21:04:30 -03:00
Ronny Pfannschmidt
372bcdba0c Merge pull request #3380 from nicoddemus/marks-fixes
Add the list of issues fixed by the new marker implementation to the docs
2018-04-10 14:33:58 +02:00
Bruno Oliveira
d1ba19acad Add the list of issues fixed by the new marker implementation to the docs
This will help make users more understanding if their code breaks under the
new implementation.
2018-04-10 08:00:32 -03:00
Ronny Pfannschmidt
2241c98b18 Merge pull request #3331 from tonybaloney/breakpoint_support
Support for the new builtin breakpoint function in Python 3.7
2018-04-10 07:15:29 +02:00
Bruno Oliveira
715337011b Merge pull request #3317 from RonnyPfannschmidt/marker-pristine-node-storage
introduce a distinct searchable non-broken storage for markers
2018-04-09 19:40:12 -03:00
Bruno Oliveira
e012dbe346 Merge pull request #3373 from backbord/master
Fix issue #3372
2018-04-06 22:10:57 -03:00
Tim Strazny
5bd8561016 linting: unfortunate dedent be gone. 2018-04-06 17:36:35 +02:00
Tim Strazny
846d91fb95 Follow Ronny's advice and use `type in base_type`. 2018-04-06 16:23:04 +02:00
Tim Strazny
0cd74dc324 Ensure object is class before calling issubclass. 2018-04-06 14:40:30 +02:00
Tim Strazny
ec2d8223cf Fix issue #3372 2018-04-06 14:16:12 +02:00
Ronny Pfannschmidt
4df8f2b153 fix doc build, use noindex on the mark reference 2018-04-06 08:13:28 +02:00
Ronny Pfannschmidt
5d4fe87b72 Merge pull request #3371 from nicoddemus/remove-labels-descriptions
Remove label descriptions from development guide
2018-04-06 07:39:15 +02:00
Bruno Oliveira
f17dfa4292 Remove label descriptions from development guide
The descriptions have been moved directly to GitHub's new label description
slot.
2018-04-05 20:55:08 -03:00
Bruno Oliveira
ab91771efc Merge pull request #3370 from dchudz/patch-1
no-capture in sample code demo'ing plugin
2018-04-05 20:25:04 -03:00
Bruno Oliveira
ef34de960c Merge pull request #3360 from RonnyPfannschmidt/xml_property_yay
record_property is no longer experimental
2018-04-05 20:03:22 -03:00
David Chudzicki
db24723b61 no-capture in sample code demo'ing plugin 2018-04-05 20:34:26 +02:00
Bruno Oliveira
e534cc81a3 Fix typos in docs 2018-04-05 14:45:30 -03:00
Ronny Pfannschmidt
3582e1f6be include more detail on the marker api issues 2018-04-05 18:46:33 +02:00
Ronny Pfannschmidt
a8ad89cdb3 fix documentation references 2018-04-05 18:39:57 +02:00
Bruno Oliveira
48bcc3419f Reword the docs on markers a bit 2018-04-05 12:39:21 -03:00
Ronny Pfannschmidt
1fcadeb2ce extend marker docs with reasons on marker iteration 2018-04-05 15:30:31 +02:00
Ronny Pfannschmidt
2018cf12b1 fix the record_property test 2018-04-05 14:05:26 +02:00
Bruno Oliveira
ba407b5eb6 Clear sys.last_* attributes before running an item
Otherwise we will keep the last failed exception around forever

Related to #2798
2018-04-04 20:36:07 -03:00
Ronny Pfannschmidt
ad0b4330e7 Merge pull request #3364 from sscherfke/more-itertools
Use correct spelling for "more-itertools"
2018-04-03 21:30:42 +02:00
Stefan Scherfke
9aa2a83785 Use correct spelling for "more-itertools" 2018-04-03 16:48:32 +02:00
Bruno Oliveira
0762666bd1 Remove unused pytestPDB import 2018-04-03 08:58:40 -03:00
Ronny Pfannschmidt
7c0c91a7a2 fix record_property test 2018-04-03 08:00:57 +02:00
Ronny Pfannschmidt
9326759a63 Merge pull request #3357 from nicoddemus/session-hooks-ref
Add reference docs for pytest_sessionstart/finish and update their docstrings
2018-04-03 07:08:47 +02:00
Anthony Shaw
4d847593b3 remove a test that would fail because pytest is being used to test itself 2018-04-03 10:40:56 +10:00
Bruno Oliveira
9a62ebf490 Merge pull request #3355 from irmen/py37deprfixes
change collections.abc import to fix deprecation warnings on python 3.7
2018-04-02 20:31:26 -03:00
Ronny Pfannschmidt
211f3c47b5 record_property is no longer experimental 2018-04-01 15:39:43 +02:00
Ronny Pfannschmidt
a2974dd067 fix doc building 2018-03-30 16:48:27 +02:00
Floris Bruynooghe
77128ee2dc Merge pull request #3356 from nicoddemus/remove-stale-changelog-entries
Remove stale CHANGELOG entries
2018-03-30 14:19:38 +02:00
Ronny Pfannschmidt
7454a381e2 update configuration examples to new mark api 2018-03-30 08:36:58 +02:00
Ronny Pfannschmidt
e4a52c1795 prevent doubleing of function level marks 2018-03-30 08:19:45 +02:00
Ronny Pfannschmidt
802da781c6 fix method reference to iter_markers in warning 2018-03-30 07:50:12 +02:00
Bruno Oliveira
3fc2c94b5e Switch imports based on python versions, my bad 2018-03-29 23:15:03 -03:00
Bruno Oliveira
daf1de0fed Add reference docs for pytest_sessionstart/finish and update their docstrings 2018-03-29 19:48:48 -03:00
Bruno Oliveira
e5eba8419a Remove stale CHANGELOG entries
Those were not removed in the last release because of a bug in
towncrier; I was about to fallback to don't use the .rst extension
for entries but just noticed that this was fixed in towncrier 17.8.0
so it should no longer be a problem.
2018-03-29 19:21:59 -03:00
Bruno Oliveira
6a81aae4f2 Move compatibility imports to compat.py and fix linting 2018-03-29 19:05:15 -03:00
Bruno Oliveira
8ca9321940 Move compatibility imports to compat.py and fix linting 2018-03-29 19:02:35 -03:00
Irmen de Jong
faded25ee8 chage collections.abc import to fix deprecation warnings on python 3.7 2018-03-29 20:02:00 +02:00
Ronny Pfannschmidt
dbb1b5a227 remove NodeMarkers, turn own_markers into a list and use iter_markers api exclusively 2018-03-29 18:24:10 +02:00
Ronny Pfannschmidt
8805036fd8 add node iteration apis
TODO: add tests
2018-03-29 17:52:01 +02:00
Ronny Pfannschmidt
ee51fa5881 add api to iterate over all marerks of a node 2018-03-29 17:52:01 +02:00
Ronny Pfannschmidt
2cb7e725ce document the hack used to avoid duplicate markers due Instance collectors 2018-03-29 17:52:01 +02:00
Ronny Pfannschmidt
02315c0489 remove unnecessary of in the code figuring the fixture names 2018-03-29 17:52:01 +02:00
Ronny Pfannschmidt
a92a51b01b clarify find_markers return value 2018-03-29 17:52:01 +02:00
Ronny Pfannschmidt
159ea9b7c0 turn Markinfo into atts clsas, and return correct instances of it from node.get_marker 2018-03-29 17:52:01 +02:00
Ronny Pfannschmidt
775fb96ac3 first changelog entry 2018-03-29 17:52:01 +02:00
Ronny Pfannschmidt
ced1316bc8 add docstrings for nodemarkers 2018-03-29 17:52:01 +02:00
Ronny Pfannschmidt
5e56e9b4f6 refactor node markers, remove need to be aware of nodes 2018-03-29 17:52:01 +02:00
Ronny Pfannschmidt
2d06ae0f65 base metafunc fixtureinfo on the functiondefinition to caputure its marks 2018-03-29 17:52:01 +02:00
Ronny Pfannschmidt
99015bfc86 fix most of metafunc tests by mocking 2018-03-29 17:52:01 +02:00
Ronny Pfannschmidt
180ae09202 deprecate markinfo and fix up most marker scoping access while completely breaking metafunc testing 2018-03-29 17:52:01 +02:00
Ronny Pfannschmidt
e8feee0612 fix up the mark evaluator validity check 2018-03-29 17:52:01 +02:00
Ronny Pfannschmidt
f1a1695aaa enable deep merging test - new structure fixed it 2018-03-29 17:52:01 +02:00
Ronny Pfannschmidt
2707221559 port mark evaluation to the new storage and fix a bug in evaluation 2018-03-29 17:52:01 +02:00
Ronny Pfannschmidt
360d608da4 introduce a own storage for markers 2018-03-29 17:52:01 +02:00
Ronny Pfannschmidt
f1c9efc358 Merge pull request #3353 from nicoddemus/fixture-param-marks-docs
Add docs about using pytest.param in parametrized fixtures
2018-03-29 10:57:28 +02:00
Bruno Oliveira
804392e5f2 Fix tests that check that breakpoint function is configured/restored
* Execute pytest in a subprocess in cases of tests which change global
  state
* Parametrize with --pdb and without the flag
2018-03-28 20:19:28 -03:00
Bruno Oliveira
0a3cd881f6 Add docs about using pytest.param in parametrized fixtures 2018-03-28 19:35:10 -03:00
Anthony Shaw
09e5a226dc add broken test 2018-03-29 09:03:20 +11:00
Ronny Pfannschmidt
2efaf39ed8 Merge pull request #3349 from jeffreyrack/3348-unknown-argument
3348: raise error on unknown arguments to raises
2018-03-28 21:45:59 +02:00
Bruno Oliveira
7656581302 Update changelog formatting 2018-03-28 07:31:26 -03:00
Bruno Oliveira
bfe773bfc8 Use shorter 'if kwargs' check as requested during review 2018-03-28 07:30:14 -03:00
Jeffrey Rackauckas
a5d9fbe2b0 Change pytest.raises to use match instead of matches 2018-03-27 20:24:15 -07:00
Jeffrey Rackauckas
34afded06d Update pytest.raises to raise a TypeError when an invalid keyword argument is passed. 2018-03-27 19:57:15 -07:00
Anthony Shaw
3998b70ff6 add test for custom environment variable 2018-03-28 09:02:37 +11:00
Bruno Oliveira
060f047a7e Use full link in CHANGELOG
Our rst-linter unfortunately doesn't accept `ref` directives in the CHANGELOG files
2018-03-27 18:35:47 -03:00
Bruno Oliveira
2962c7367c Merge pull request #3307 from KKoukiou/rhv-qe-fix
logging.py: Don't change log level of the root logger to bigger numeric value
2018-03-27 18:03:22 -03:00
Bruno Oliveira
0a4200bbb3 Improve docs formatting 2018-03-27 07:40:52 -03:00
Anthony Shaw
b45006e9a3 fix syntax 2018-03-27 21:26:55 +11:00
Anthony Shaw
671ab5a36c update documentation for new feature 2018-03-27 21:02:43 +11:00
Anthony Shaw
f1f4c8c104 updates for code review recommendations 2018-03-27 17:38:17 +11:00
Bruno Oliveira
6cfed00a61 Tweak changelog entry 2018-03-26 20:48:05 -03:00
Bruno Oliveira
ff3d13ed0e Update typo in fixture.rst 2018-03-26 11:20:33 -03:00
Katerina Koukiou
d895f5d6fc logging.py: Don't change log level of the root logger to bigger numeric value 2018-03-26 10:11:49 +02:00
Anthony Shaw
e97bd87ee2 fix assertion when hypothesis is installed (which is should be for developing in pytest) 2018-03-23 16:24:15 +11:00
Anthony Shaw
242fb7852b linting and removed double test 2018-03-23 15:39:34 +11:00
Anthony Shaw
1ec99132e6 add myself to authors 2018-03-23 15:31:57 +11:00
Anthony Shaw
dcbba381d4 add changelog entry 2018-03-23 15:31:16 +11:00
Anthony Shaw
db581eabcb add tests to validate that --pdbcls custom debugger classes will be called when breakpoint() is used 2018-03-23 15:30:05 +11:00
Anthony Shaw
0e83e4f292 conditional for resetting of sys.breakpointhook for cleanup where breakpoint() not supported 2018-03-23 14:26:16 +11:00
Anthony Shaw
21ada0fa23 add check for support of breakpoint() and use Custom Pdb class when system default is set 2018-03-23 14:20:10 +11:00
Anthony Shaw
a1ff758d0d "Added acceptance tests for configuration of sys.breakpointhook and resetting back when system default (pdb) is used"" 2018-03-23 14:18:56 +11:00
Bruno Oliveira
ed118d7f20 Merge remote-tracking branch 'upstream/features' 2018-03-22 21:06:09 -03:00
Bruno Oliveira
5ecff65285 Merge pull request #3327 from nicoddemus/release-3.5.0
Release 3.5.0
2018-03-22 20:58:15 -03:00
Ronny Pfannschmidt
fc4f769888 Merge pull request #3336 from nicoddemus/filter-warnings-ref-doc
Add missing reference doc for filterwarnings mark
2018-03-22 18:43:17 +01:00
Bruno Oliveira
f78953fd81 Add missing reference doc for filterwarnings mark 2018-03-22 13:02:30 -03:00
Ronny Pfannschmidt
d7d4afea17 Merge pull request #3330 from nicoddemus/remove-custom-flatten
Remove terminal.flatten function in favor of collapse from more_itertools
2018-03-22 14:22:47 +01:00
Anthony Shaw
5a53b9aabb move tests to test_pdb 2018-03-22 20:40:35 +11:00
Anthony Shaw
91d99affb7 assert that custom PDB class is used as breakpoint hook where supported 2018-03-22 17:40:14 +11:00
Anthony Shaw
3bca983a95 add a module global for whether the current runtime supports the builtin breakpoint function 2018-03-22 17:27:28 +11:00
Anthony Shaw
9edcb7edc6 start acceptance testing 2018-03-22 17:25:27 +11:00
Bruno Oliveira
6c2739d1e6 Add CHANGELOG for #3330 2018-03-21 20:23:17 -03:00
Bruno Oliveira
4e717eb626 Remove terminal.flatten function in favor of collapse from more_itertools 2018-03-21 20:20:46 -03:00
Bruno Oliveira
beacecf29b Preparing release version 3.5.0 2018-03-21 20:46:07 +00:00
Bruno Oliveira
b148770066 Fix example in usage.rst 2018-03-21 20:45:28 +00:00
Bruno Oliveira
f3c87a77a7 Merge remote-tracking branch 'upstream/master' into release-3.5.0 2018-03-21 20:39:44 +00:00
Bruno Oliveira
6f95189cf7 Merge pull request #3306 from nicoddemus/2405-scope-fixture-order
Instantiate fixtures by scope order in test function requests
2018-03-21 17:37:05 -03:00
Ronny Pfannschmidt
add5ce0fb8 Merge pull request #3230 from brianmaissy/features
deprecate pytest_plugins in non-top-level conftest
2018-03-21 07:44:34 +01:00
Bruno Oliveira
59e7fd478e Sort fixtures by scope when determining fixture closure
Fix #2405
2018-03-20 20:10:53 -03:00
Bruno Oliveira
9e24b09a9f Use re_match_lines in test_class_ordering
"[1-a]" works fine using fnmatch_lines, but "[a-1]" breaks horribly
inside `re`.
2018-03-20 20:10:52 -03:00
Bruno Oliveira
d03e38941b Merge pull request #3295 from brianmaissy/feature/last-failed-no-failures-behavior
implemented --last-failed-no-failures
2018-03-20 19:17:25 -03:00
Bruno Oliveira
672239b149 Merge pull request #3321 from jeffreyrack/'test_from_base_fix'
3245: Fix tests to not fail when running from base directory
2018-03-20 18:45:56 -03:00
Jeffrey Rackauckas
9b449eee15 Add self to authors 2018-03-18 21:28:20 -07:00
Jeffrey Rackauckas
c1d73bb535 Modifying changelog to reflect new changes for 3245 2018-03-18 21:27:48 -07:00
Jeffrey Rackauckas
a4cf380343 Fix tests to not fail when running from root. 2018-03-18 21:23:39 -07:00
Ronny Pfannschmidt
f61d0525a5 Merge pull request #3318 from nicoddemus/merge-master-into-features
Merge master into features
2018-03-17 09:40:32 +01:00
Kale Kundert
86d6804e60 Merge pull request #3313 from tadeu/approx-array-scalar
Add support for `pytest.approx` comparisons between array and scalar
2018-03-16 23:29:46 -07:00
Bruno Oliveira
1fff81e21d Merge remote-tracking branch 'upstream/master' into merge-master-into-features 2018-03-16 18:15:28 -03:00
Bruno Oliveira
93847bfeb4 Merge pull request #3315 from pytest-dev/issue/3314
Allow DontReadFromInput to produce iterator without error.
2018-03-16 10:05:54 -03:00
Tadeu Manoel
a754f00ae7 Improve numpy.approx array-scalar comparisons
So that `self.expected` in ApproxNumpy is always a numpy array.
2018-03-16 09:01:18 -03:00
Bruno Oliveira
278d8ac74e Fix caplog docstring: indentation caused errors during docs build 2018-03-16 00:06:15 -03:00
Bruno Oliveira
17468fc99c Fix caplog docstring: indentation caused errors during docs build 2018-03-16 00:04:28 -03:00
Jason R. Coombs
b66019202e Fix test failure on Python 2. Ref #3314. 2018-03-15 21:33:58 -04:00
Jason R. Coombs
965a030564 Add changelog. Ref #3314. 2018-03-15 21:31:12 -04:00
Jason R. Coombs
2f47624b19 Allow DontReadFromInput to produce iterator without error. Fixes #3314. 2018-03-15 21:26:40 -04:00
Jason R. Coombs
3c3fc3bb9d Add test capturing new expectation. Ref #3314. 2018-03-15 21:25:50 -04:00
Tadeu Manoel
42c84f4f30 Add fixes to numpy.approx array-scalar comparisons (from PR suggestions) 2018-03-15 13:41:58 -03:00
Bruno Oliveira
fbcf1a90c9 Merge pull request #3193 from pytest-dev/ref-docs
Reference docs
2018-03-15 12:04:34 -03:00
Tadeu Manoel
97f9a8bfdf Add fixes to make numpy.approx array-scalar comparisons work with older numpy versions 2018-03-14 17:10:35 -03:00
Tadeu Manoel
161d4e5fe4 Add support for pytest.approx comparisons between scalar and array (inverted order) 2018-03-14 16:29:04 -03:00
Tadeu Manoel
c34dde7a3f Add support for pytest.approx comparisons between array and scalar 2018-03-14 15:47:21 -03:00
Ronny Pfannschmidt
1b535387bf Merge pull request #3309 from tareqalayan/issue-3308
issue #3308: fix example in documentation
2018-03-14 15:52:55 +01:00
Tareq Alayan
7e53f9432c issue #3308: fix example in documentation
https://github.com/pytest-dev/pytest/issues/3308
2018-03-14 15:44:43 +02:00
Ronny Pfannschmidt
b2b629f462 Merge pull request #3305 from nicoddemus/2370-tmpfile-close-py27
Suppress ``IOError`` when closing the temporary file used for capturing streams in Python 2.7
2018-03-14 06:42:06 +01:00
Bruno Oliveira
cbb2c55dea Merge pull request #3304 from RonnyPfannschmidt/cmdoptions-removal
remove CmdOptions since we can use argparse.Namespace()
2018-03-13 17:48:21 -03:00
Bruno Oliveira
9517c3a2aa Suppress `IOError` when closing the temporary file used for capturing streams in Python 2.7.
Fix #2370
2018-03-13 17:41:42 -03:00
Bruno Oliveira
4175ed8b43 Merge pull request #3296 from RonnyPfannschmidt/verbosity-actions
unify cli verbosity handling
2018-03-13 17:38:41 -03:00
Ronny Pfannschmidt
87f2003245 remove CmdOptions since we can use argparse.Namespace() 2018-03-13 18:13:56 +01:00
Ronny Pfannschmidt
2612d967f8 Merge pull request #3301 from ankostis/caplog_clear_text
Fix #3297 where caplog.clear() did not clear text, just records
2018-03-13 16:39:53 +01:00
Ronny Pfannschmidt
37a52607c2 unify cli verbosity handling
based on https://github.com/pytest-dev/pytest/issues/3294#issuecomment-372190084

we really shouldnt have N options we post mortem hack together to determine verbosity
this change starts by unifying the data, we still need to handle deprecation/removal of config.quiet
2018-03-13 07:05:22 +01:00
Bruno Oliveira
51c0256cd4 Remove :py:mod: ref from changelog: fails our linting check 2018-03-12 20:57:51 -03:00
Bruno Oliveira
f8791c9246 Merge pull request #3292 from RonnyPfannschmidt/exception-attrs
internal refactor port exc FOrmattedExcinfo to attrs, remove old code
2018-03-12 20:54:52 -03:00
Kostis Anagnostopoulos
02bec7a3bb Add changelog & myself (ankostis) to authors 2018-03-13 01:46:44 +02:00
Bruno Oliveira
4459aa3d8b Remove redundant "# content of pytest.ini" headers from examples 2018-03-12 20:09:31 -03:00
Bruno Oliveira
2e347643a3 Add ini options for logging 2018-03-12 20:09:05 -03:00
Bruno Oliveira
8629ef6a78 Add missing ini options: markers, usefixtures and xfail_strict 2018-03-12 19:43:31 -03:00
Thomas Hisch
5e92644f94 Properly reset LogCaptureHandler in caplog
Closes #3297
2018-03-13 00:36:51 +02:00
Bruno Oliveira
3198596f8a Sort ini options 2018-03-12 19:33:34 -03:00
Kostis Anagnostopoulos
68375513f3 Add TC to demonstrate #3297 that caplog.clear() does not clean text 2018-03-13 00:28:47 +02:00
Bruno Oliveira
61b8ea8656 Add doctest_encoding and junit_suite_name ini options 2018-03-12 19:27:17 -03:00
Bruno Oliveira
0557ab431f Merge pull request #3291 from RonnyPfannschmidt/small-moves
move some code and make nodeid computed early
2018-03-12 18:49:13 -03:00
Bruno Oliveira
8035f6cced Fix typo in docs 2018-03-12 18:32:51 -03:00
Bruno Oliveira
0302622310 Improve CHANGELOG entry a bit 2018-03-12 18:22:12 -03:00
Bruno Oliveira
3909225bf9 Merge pull request #3300 from JulienPalard/typo
Typo in example, os.getcwd does not take any parameter.
2018-03-12 12:16:18 -03:00
Julien Palard
e71d907d34 Typo in example, os.getcwd does not take any parameter. 2018-03-12 15:19:26 +01:00
Brian Maissy
d2e533b8a3 implemented --last-failed-no-failures 2018-03-10 22:45:45 +02:00
Brian Maissy
54b15f5826 deprecated pytest_plugins in non-top-level conftest 2018-03-10 21:20:12 +02:00
Bruno Oliveira
d2dbbd4caa Add CHANGELOG entry 2018-03-09 17:46:44 -03:00
Bruno Oliveira
5f9bc557ea Fix linting 2018-03-09 17:44:39 -03:00
Ronny Pfannschmidt
543bac925a fix if-chain in _code.source 2018-03-09 16:50:46 +01:00
Ronny Pfannschmidt
2fe56b97c9 remove unused assertion parameter in source and minor cleanups 2018-03-09 15:03:57 +01:00
Ronny Pfannschmidt
3284d575e8 readline generator no longer needs to yield empty strings 2018-03-09 14:58:11 +01:00
Ronny Pfannschmidt
a406ca14d6 remove getstatementrange_old - its documented for python <= 2.4 2018-03-09 14:48:28 +01:00
Ronny Pfannschmidt
2e5337f5e3 makr strutures: lint fixings, removal of shadowing 2018-03-09 14:39:54 +01:00
Ronny Pfannschmidt
74884b1901 turn FormattedExcinfo into a attrs class 2018-03-09 14:21:56 +01:00
Ronny Pfannschmidt
c67f45b716 simplify a few imports in _pytest._code.source 2018-03-09 12:06:50 +01:00
Ronny Pfannschmidt
50e682d2db add changelog 2018-03-09 11:06:26 +01:00
Ronny Pfannschmidt
5e5935759e make nodeids precalculated, there is no sane reason to commpute lazyly 2018-03-09 10:52:59 +01:00
Ronny Pfannschmidt
45b6b7df92 move nodekeywords to the mark structures 2018-03-09 10:23:18 +01:00
Ronny Pfannschmidt
c9b9d796e6 remove dead code 2018-03-09 10:22:43 +01:00
Ronny Pfannschmidt
09ce84e64e remove unneeded commas 2018-03-09 10:21:42 +01:00
Bruno Oliveira
8243900960 Move definition of ini options to reference 2018-03-07 20:45:41 -03:00
Bruno Oliveira
c0fe4d483d Add environment variables 2018-03-06 20:41:21 -03:00
Bruno Oliveira
18a47bfd22 Add docs for CaptureFixture 2018-03-06 20:41:21 -03:00
Bruno Oliveira
3979f9ba3a Add pytest_plugins and pytestmark special variables 2018-03-06 20:41:21 -03:00
Bruno Oliveira
443888248a Improve fixture reference a bit 2018-03-06 20:41:21 -03:00
Bruno Oliveira
69d49f18e9 Add pytest_addhooks 2018-03-06 20:41:21 -03:00
Bruno Oliveira
8b7e6df73d Add register_assert_rewrite and improve signature of other functions 2018-03-06 20:41:21 -03:00
Bruno Oliveira
9c2203d381 Reorder top level: functions, marks, fixtures, hooks and objects 2018-03-06 20:41:21 -03:00
Bruno Oliveira
df3a4111a9 Sort objects by name 2018-03-06 20:41:20 -03:00
Bruno Oliveira
988ace9eb2 Add documentation for marks 2018-03-06 20:41:20 -03:00
Bruno Oliveira
ed8b1efc16 Add a CHANGELOG entry for the reference docs
Fix #1713
2018-03-06 20:41:20 -03:00
Bruno Oliveira
f7178654e5 Add recwarn and tmpdir fixtures to the reference docs 2018-03-06 20:41:20 -03:00
Bruno Oliveira
f1df6c5a60 Add testdir fixture to reference 2018-03-06 20:41:20 -03:00
Bruno Oliveira
66009b0f91 Add tutorial links to all references 2018-03-06 20:41:20 -03:00
Bruno Oliveira
215c6b281e Add Monkeypatch and tutorial links for logging 2018-03-06 20:41:20 -03:00
Bruno Oliveira
adcb28f61b Add caplog to reference 2018-03-06 20:41:20 -03:00
Bruno Oliveira
111c6d6a22 Improve pytestconfig and add record_xml_property 2018-03-06 20:41:20 -03:00
Bruno Oliveira
7ba8a4ee75 Use `pytest.` prefix for outcomes 2018-03-06 20:41:20 -03:00
Bruno Oliveira
400558cc9c Add pytestconfig fixture 2018-03-06 20:41:20 -03:00
Bruno Oliveira
918dffba96 Add :with: modifier to raises() and deprecated_call() 2018-03-06 20:41:20 -03:00
Bruno Oliveira
44e2715529 Add sections to pytest's builtin API 2018-03-06 20:41:20 -03:00
Bruno Oliveira
b53c4246ef Move most of 'builtin' to 'reference' 2018-03-06 20:41:19 -03:00
Bruno Oliveira
e73d4f4e1f Add local table of contents in reference.rst 2018-03-06 20:41:19 -03:00
Bruno Oliveira
e7bce90d29 Remove basepython from testenv:docs so it can be built with Python 3 2018-03-06 20:41:19 -03:00
Bruno Oliveira
623bab4447 Use :no-auto-options: in capsys fixtures
Otherwise they are recognized as iterators
2018-03-06 20:41:19 -03:00
Bruno Oliveira
70f93263e9 Add reference docs to doctest_namespace 2018-03-06 20:41:19 -03:00
Bruno Oliveira
749288dcb6 Add reference docs to cache and capture fixtures 2018-03-06 20:41:19 -03:00
Bruno Oliveira
6fa9768545 Use sphinxcontrib-trio extension
This adds some more capabilities to sphinx, like marking
functions as decorators or with contexts
2018-03-06 20:41:19 -03:00
Bruno Oliveira
6b4565f8d1 Reorder the global toc and include 'contributing' section 2018-03-06 20:41:19 -03:00
Bruno Oliveira
6d4e72e1eb Cleanup the links section to only external links
Internal links should be displayed in the side-bar
2018-03-06 20:41:19 -03:00
Bruno Oliveira
162557c2b2 Remove internal plugin list
This gets out of sync and shows internal plugins which don't make
much sense
2018-03-06 20:41:19 -03:00
Bruno Oliveira
d4c3850231 Create reference and moved Objects and Hooks reference from writing_plugins 2018-03-06 20:40:55 -03:00
Bruno Oliveira
28df322500 Merge pull request #3283 from jeffreyrack/3245-doc-test-failures
3245: Add doc to norecursedirs in tox.ini
2018-03-06 19:22:55 -03:00
Bruno Oliveira
d6ddeb395b Merge pull request #3285 from RonnyPfannschmidt/minor-refactor
Minor refactors
2018-03-06 19:22:48 -03:00
Ronny Pfannschmidt
c4430e4354 extract _warn_about_missing_assertion into freestanding function 2018-03-06 10:32:41 +01:00
Ronny Pfannschmidt
fbf01bd31a enhance skip except clause by directly using the Skipped exception 2018-03-06 10:29:26 +01:00
Jeffrey Rackauckas
e42fe5f0f9 Add doc to norecursedirs in tox.ini 2018-03-05 19:35:41 -08:00
Bruno Oliveira
ade7ad25c7 Merge remote-tracking branch 'upstream/master' into features 2018-03-05 19:48:25 -03:00
Bruno Oliveira
27c4de242f Merge pull request #3281 from nicoddemus/release-3.4.2
Preparing release version 3.4.2
2018-03-05 19:42:59 -03:00
Bruno Oliveira
e8368e6c2e Fix duplicated link in CHANGELOG 2018-03-05 07:26:38 -03:00
Bruno Oliveira
fac8208e8f Preparing release version 3.4.2 2018-03-04 19:37:49 +00:00
Bruno Oliveira
9879ac5911 Merge pull request #3279 from nicoddemus/introduce-more_itertools
Re-Introduce more_itertools
2018-03-04 16:33:58 -03:00
Bruno Oliveira
51abdb80db Merge pull request #3278 from nicoddemus/revert-more_itertools-master
Revert introduction of more_itertools on master
2018-03-04 16:33:31 -03:00
Bruno Oliveira
25399da904 Reintroduce more_itertools dependency
Reintroduce more_itertools that was added in #3265, now in the features branch
2018-03-04 10:59:46 -03:00
Bruno Oliveira
54884b8c87 Merge branch 'revert-more_itertools-master' into introduce-more_itertools 2018-03-04 10:58:52 -03:00
Bruno Oliveira
65534682aa Revert introduction of more_itertools
This was merged on master but really should be on features: we should not
add new dependencies in bug-fix releases

This reverts commits:

* cfaf3600c1
* 14a9b1ec83
2018-03-04 10:56:24 -03:00
Bruno Oliveira
e980fbbe39 Merge pull request #3277 from maiksensi/bug/3241-check-if-dir-exists
Use `isdir` instead of `exists`
2018-03-04 10:49:25 -03:00
Bruno Oliveira
07e768ab68 Merge pull request #3275 from maiksensi/docs/3209-add-logging-plugin
Add logging plugin to plugins list
2018-03-03 23:17:05 -03:00
Bruno Oliveira
9a2e0c061d Update 3241.bugfix.rst 2018-03-03 23:14:57 -03:00
Maik Figura
056d9e8dc2 Use isdir instead of exists 2018-03-04 00:55:04 +01:00
Maik Figura
46f5d7a1bb Add filetype to changelog fragment 2018-03-04 00:52:27 +01:00
Maik Figura
30453057e8 Add logging plugin to plugins list 2018-03-03 23:50:13 +01:00
Bruno Oliveira
08831396a5 Merge pull request #3218 from feuillemorte/3034-new-tests-first
#3034 Added new option "--new-first"
2018-03-02 18:33:25 -03:00
Bruno Oliveira
1549d61379 Merge pull request #3274 from feuillemorte/3268-add-deprecation
Added warning when "[pytest]" section is used in a ".cfg" file passed with "-c"
2018-03-02 16:34:31 -03:00
feuillemorte
f501d0021c #3268 Fix warning variable 2018-03-02 11:28:30 +03:00
feuillemorte
a506052d12 #3268 Added changelog file 2018-03-02 10:54:31 +03:00
feuillemorte
99aab2c3f5 #3268 Added deprecation to custom configs 2018-03-02 10:52:38 +03:00
Ronny Pfannschmidt
3b757b1b8c Merge pull request #3265 from pytest-dev/feature/always-iterable-refactor
Consolidate behavior by using filterfalse and always_iterable
2018-03-01 08:51:44 +01:00
Bruno Oliveira
14a9b1ec83 Add CHANGELOG for #3265 2018-02-28 17:31:11 -03:00
Bruno Oliveira
ea854086e1 Rename 3236.trivial.rst to 3236.feature.rst 2018-02-27 22:46:12 -03:00
Bruno Oliveira
0a5a6c19be Merge pull request #3269 from nicoddemus/merge-master-into-features
Merge master into features
2018-02-27 22:42:08 -03:00
Bruno Oliveira
90638b661d Merge pull request #3270 from pytest-dev/blueyed-patch-1
Fix typo with test_summary_list_after_errors
2018-02-27 18:27:31 -03:00
Daniel Hahler
8239103aa9 Fix typo with test_summary_list_after_errors 2018-02-27 21:07:00 +01:00
Bruno Oliveira
a2a64546eb Merge remote-tracking branch 'upstream/master' into features 2018-02-27 16:43:45 -03:00
Bruno Oliveira
9fcbf57163 Merge pull request #3267 from feuillemorte/3260-fix-pytest-section
#3260 fix pytest section
2018-02-27 16:41:45 -03:00
Bruno Oliveira
dab96cbf27 Merge pull request #3255 from nicoddemus/post-summary
Show "short test summary info" after tracebacks and warnings
2018-02-27 16:36:13 -03:00
Bruno Oliveira
1fb2457018 Adjust CHANGELOG 2018-02-27 14:58:27 -03:00
feuillemorte
92219e576b #3260 Remove deprecation 2018-02-27 20:00:46 +03:00
feuillemorte
143ac5af99 #3260 Fix config.py for py27 2018-02-27 19:26:35 +03:00
feuillemorte
409b919fc0 #3260 Added test 2018-02-27 19:16:45 +03:00
feuillemorte
eadd15fe45 #3260 Added changelog file 2018-02-27 19:05:53 +03:00
feuillemorte
3f7223af44 #3260 Fix pytest section in setup ini file 2018-02-27 19:02:49 +03:00
Bruno Oliveira
a968c0fa05 Merge pull request #3264 from jeffreyrack/3236-skipif-using-platform
#3236 Use platform module in pytest.mark
2018-02-27 09:44:52 -03:00
Bruno Oliveira
5cb72b6188 Merge pull request #3257 from will133/continue-on-failure
Continue on doctest failure
2018-02-27 07:45:41 -03:00
Bruno Oliveira
20085542e2 Merge pull request #3263 from feuillemorte/3203-remove-statistic-indicator
#3203 Remove progress when no-capture
2018-02-27 07:33:45 -03:00
Bruno Oliveira
94050a8aaf Remove config paramter from pytest_terminal_summary as discussed during review 2018-02-27 07:26:25 -03:00
Bruno Oliveira
9479bda392 Adjust the CHANGELOG 2018-02-27 07:03:20 -03:00
Jason R. Coombs
cfaf3600c1 Consolidate behavior by using filterfalse and always_iterable 2018-02-27 03:38:56 -05:00
Jeffrey Rackauckas
f6ad25928e Fixing grammar. 2018-02-26 19:15:10 -08:00
Jeffrey Rackauckas
a6762f7328 Update test_skipping to test that platform can be used in xfail 2018-02-26 19:11:13 -08:00
Bruno Oliveira
4e405dd9f9 Show "short test summary info" after tracebacks and warnings 2018-02-26 21:12:33 -03:00
Bruno Oliveira
d196ab45d3 Change 1642 entry from "trivial" to "feature" 2018-02-26 19:56:53 -03:00
Bruno Oliveira
188df8100c Small adjustment to the CHANGELOG 2018-02-26 17:14:28 -03:00
Bruno Oliveira
e8f9a91056 Small adjustment to CHANGELOG entry 2018-02-26 17:10:59 -03:00
Bruno Oliveira
44fa5a77d4 Merge pull request #3262 from nicoddemus/fix-tmpdir-docs
Fix broken links in getting-started
2018-02-26 16:49:15 -03:00
feuillemorte
31476c69ab #3203 Fix tests 2018-02-26 17:39:32 +03:00
feuillemorte
6200920dc3 #3203 Added changelog file 2018-02-26 17:24:16 +03:00
feuillemorte
46c5d5355e #3203 Remove progress when no-capture 2018-02-26 17:19:58 +03:00
Bruno Oliveira
39024a7536 Fix broken links in getting-started
Fix #3256
2018-02-26 10:56:27 -03:00
Jeffrey Rackauckas
307cd6630f Add the ability to use platform in pytest.mark.skipif 2018-02-25 22:38:25 -08:00
Florian Bruhin
ae62ced080 Merge pull request #3259 from joshm91/fix_typo
Fix minor typo in fixture.rst
2018-02-24 21:37:40 +01:00
joshm91
6166151ee4 Fix minor typo in fixture.rst 2018-02-24 19:12:40 +00:00
Ronny Pfannschmidt
da3f4045e7 Merge pull request #3250 from RonnyPfannschmidt/mark-package
Mark package
2018-02-24 18:19:27 +01:00
William Lee
c21eb72924 Added documentation for the continue-on-failure flag 2018-02-23 22:42:22 -06:00
William Lee
f4cc45bb41 Turn on the continue on failure only when the flag is given 2018-02-23 22:31:11 -06:00
William Lee
7f2dd74ae9 Fixed test for the continue run 2018-02-23 21:20:14 -06:00
feuillemorte
c032d4c5d5 Merge branch 'features' of https://github.com/feuillemorte/pytest into 3034-new-tests-first 2018-02-23 23:55:28 +03:00
feuillemorte
e865f2a235 #3034 Fix comments 2018-02-23 22:49:17 +03:00
William Lee
8d90591b33 Merge remote-tracking branch 'upstream/features' into continue-on-failure 2018-02-22 21:01:31 -06:00
William Lee
14cd1e9d94 Added the feature in change log for #3149 2018-02-22 21:01:19 -06:00
William Lee
fbc45be83f Fixed #3149 where doctest does not continue to run when there is a failure 2018-02-22 21:00:54 -06:00
Bruno Oliveira
0a3c80e959 Merge pull request #3253 from thisch/nolooggingtest
Add pdb test with disabled logging plugin
2018-02-22 22:19:47 -03:00
Bruno Oliveira
bedceaacc4 Merge pull request #3249 from a-feld/request-fixture-reference-cycle
Fix PseudoFixtureDef reference cycle.
2018-02-22 19:23:56 -03:00
Bruno Oliveira
1127d519db Merge pull request #3254 from altendky/patch-1
Correct docs to config.pluginmanager.get_plugin()
2018-02-22 19:14:47 -03:00
Bruno Oliveira
9959164c9a Add CHANGELOG entry for #3250 2018-02-22 18:55:25 -03:00
Bruno Oliveira
60358b6db8 Fix linting 2018-02-22 18:49:20 -03:00
Kyle Altendorf
b5ac61657a Correct docs to config.pluginmanager.get_plugin()
`getplugin()` is deprecated in favor of `get_plugin()`.

dd97c94035/_pytest/config.py (L261)
2018-02-22 13:48:59 -05:00
Thomas Hisch
0f58fc881b Add pdb test with disabled logging plugin
Implement the test from #3210, which was not merged yet, because the PR was
abandoned in favor or #3234.
2018-02-22 19:26:46 +01:00
Ronny Pfannschmidt
2cd69cf632 sort out import misstake 2018-02-22 15:18:17 +01:00
Ronny Pfannschmidt
935dd3aaa5 simplify complexyity in mark plugin modifyitems 2018-02-22 15:11:55 +01:00
Ronny Pfannschmidt
c8d24739ed move current mark datastructures to own module 2018-02-22 14:43:01 +01:00
Ronny Pfannschmidt
be2e3a973e remove complexity from match_keywords 2018-02-22 14:30:32 +01:00
Ronny Pfannschmidt
cef0423b27 move the keyword/mark matching to the "legacy" module 2018-02-22 14:26:22 +01:00
Ronny Pfannschmidt
de2de00de9 update setup.py for the mark package 2018-02-22 14:19:31 +01:00
Ronny Pfannschmidt
25a3e9296a reduce the complexity of skipping terminal summary 2018-02-22 14:18:49 +01:00
Ronny Pfannschmidt
cf40c0743c move mark evaluator into mark package 2018-02-22 14:05:10 +01:00
Ronny Pfannschmidt
c31e1a3797 turn mark into a package 2018-02-22 13:36:33 +01:00
Allan Feldman
48548767fc Use a frozen attr class for PseudoFixtureDef. 2018-02-21 23:15:39 -08:00
Allan Feldman
7536e949b1 Add changelog entry. 2018-02-21 22:42:44 -08:00
Allan Feldman
287c003cfd Add myself to AUTHORS. 2018-02-21 22:42:44 -08:00
Allan Feldman
aa53e37fa2 Add a test to expose leaked PseudoFixtureDef types. 2018-02-21 22:42:44 -08:00
Ronny Pfannschmidt
54e63b7dd5 Merge pull request #2770 from HPENetworking/master
Make record_xml_property generic and compatible with xdist and markers.
2018-02-22 07:38:55 +01:00
Ronny Pfannschmidt
dd97c94035 Merge pull request #3240 from codetriage-readme-bot/codetriage-badge
Add CodeTriage badge to pytest-dev/pytest
2018-02-22 07:38:31 +01:00
Ronny Pfannschmidt
264e455410 Merge pull request #3248 from durocher/master
Fix approx default tolerances for Decimal
2018-02-22 07:35:56 +01:00
Allan Feldman
75f11f0b65 Fix reference cycle caused by PseudoFixtureDef.
Python types have reference cycles to themselves when they are created. This is
partially caused by descriptors which get / set values from the __dict__
attribute for getattr / setattr on classes.

This is not normally an issue since types tend to remain referenced for the
lifetime of the Python process (and thus never become garbage).

However, in the case of PseudoFixtureDef, the class is generated in
_get_active_fixturedef and later discarded when pytest_fixture_setup returns.
As a result, the generated PseudoFixtureDef type becomes garbage.

This is not really a performance issue but it can lead to some problems when
making tests and assertions about garbage when using pytest.

This garbage creation problem can be rectified by returning a namedtuple
instance which is functionally the same. In the modified code, the namedtuple
is allocated / deallocated using reference counting rather than having to use
the garbage collector.
2018-02-21 21:40:54 -08:00
Bruno Oliveira
45d0a21294 Fix README because of code triage badge 2018-02-21 20:42:09 -03:00
Bruno Oliveira
147b43f832 Small changelog tweak 2018-02-21 20:31:33 -03:00
Bruno Oliveira
7336dbb979 Merge pull request #3246 from RonnyPfannschmidt/remove-addcall-in-tests
remove addcall in the terminal tests
2018-02-21 19:42:53 -03:00
mike
6e14585ca2 Fix approx default tolerances for Decimal 2018-02-21 23:08:23 +01:00
Bruno Oliveira
567b1ea7a1 Move user_properties to the end of parameter list for backward compatibility 2018-02-21 17:56:49 -03:00
Bruno Oliveira
d838193d2d Add note about deprecating record_xml_property
Also make record_xml_property return record_property directly
2018-02-21 17:45:52 -03:00
Bruno Oliveira
d844ad18c2 Fix formatting of CHANGELOG entry 2018-02-21 15:40:25 -03:00
Ronny Pfannschmidt
3d4d0a2614 remove addcall in the terminal tests 2018-02-21 18:56:20 +01:00
Bruno Oliveira
7a62619a75 Merge remote-tracking branch 'upstream/master' into features 2018-02-21 10:39:55 -03:00
Bruno Oliveira
dae74b674e Merge pull request #3242 from nicoddemus/delete-changelog-3.4.1
Delete changelog entries for 3.4.1 release
2018-02-21 10:38:53 -03:00
Bruno Oliveira
2a99e5dd2a Delete changelog entries for 3.4.1 release
Unfortunately due to hawkowl/towncrier#99 those news fragments were
not deleted at the time of CHANGELOG generation.
2018-02-20 23:45:21 -03:00
Carlos Jenkins
8b49ddfa58 Renamed the fixture record_xml_property to record_property and adapted logic so that the properties are passed to the TestReport object and thus allow compatibility with pytest-xdist. 2018-02-20 15:46:26 -06:00
Bruno Oliveira
9361d48b61 Merge pull request #3239 from nicoddemus/release-3.4.1
Release 3.4.1
2018-02-20 18:08:47 -03:00
codetriage-readme-bot
ebddac6a5c Add CodeTriage badge to pytest-dev/pytest
Adds a badge showing the number of people helping this repo on CodeTriage.

[![Open Source Helpers](https://www.codetriage.com/pytest-dev/pytest/badges/users.svg)](https://www.codetriage.com/pytest-dev/pytest)

## What is CodeTriage?

CodeTriage is an Open Source app that is designed to make contributing to Open Source projects easier. It works by sending subscribers a few open issues in their inbox. If subscribers get busy, there is an algorithm that backs off issue load so they do not get overwhelmed

[Read more about the CodeTriage project](https://www.codetriage.com/what).

## Why am I getting this PR?

Your project was picked by the human, @schneems. They selected it from the projects submitted to https://www.codetriage.com and hand edited the PR. How did your project get added to [CodeTriage](https://www.codetriage.com/what)? Roughly 11 months ago, [cacoze](https://github.com/cacoze) added this project to CodeTriage in order to start contributing. Since then, 32 people have subscribed to help this repo.

## What does adding a badge accomplish?

Adding a badge invites people to help contribute to your project. It also lets developers know that others are invested in the longterm success and maintainability of the project.

You can see an example of a CodeTriage badge on these popular OSS READMEs:

- [![](https://www.codetriage.com/rails/rails/badges/users.svg)](https://www.codetriage.com/rails/rails) https://github.com/rails/rails
- [![](https://www.codetriage.com/crystal-lang/crystal/badges/users.svg)](https://www.codetriage.com/crystal-lang/crystal) https://github.com/crystal-lang/crystal

## Have a question or comment?

While I am a bot, this PR was manually reviewed and monitored by a human - @schneems. My job is writing commit messages and handling PR logistics.

If you have any questions, you can reply back to this PR and they will be answered by @schneems. If you do not want a badge right now, no worries, close the PR, you will not hear from me again.

Thanks for making your project Open Source! Any feedback is greatly appreciated.
2018-02-20 10:51:51 -06:00
Bruno Oliveira
97bb6abcfa Merge pull request #3234 from thisch/showcapture_log_support
Add captured-log support to --show-capture
2018-02-19 20:46:50 -03:00
Thomas Hisch
acda6c46fb Partially revert "Remove --no-print-logs option"
We'll deprecate --no-print-logs beginning with pytest-4.0.

This reverts commit ac7eb63a6b.
2018-02-19 20:34:11 +01:00
Thomas Hisch
ac7eb63a6b Remove --no-print-logs option
This option is superseded by the --show-capture option. With --no-print-logs
it was possible to only disable the reporting of captured logs, which is no
longer possible with --show-capture. If --show-capture=no is used, no
captured content (stdout, stderr and logs) is reported for failed tests.
2018-02-18 20:48:07 +01:00
Thomas Hisch
51ece00923 Add captured-log support to --show-capture
Fixes: #3233
2018-02-18 20:18:37 +01:00
Bruno Oliveira
3bc8b50a0d Merge pull request #3228 from thisch/update_attr
Update minimal attrs library to v17.4.0
2018-02-17 20:19:07 -02:00
Bruno Oliveira
81fa547fa8 Add CHANGELOG entry about changed attrs req 2018-02-17 20:18:32 -02:00
Brian Maissy
069f32a8c4 print captured logs before entering pdb 2018-02-17 20:32:49 +02:00
Bruno Oliveira
f263932883 Merge pull request #3232 from nicoddemus/merge-upstream
Merge master into features
2018-02-17 12:59:55 -02:00
Bruno Oliveira
69d608aec3 Merge remote-tracking branch 'upstream/master' into merge-upstream 2018-02-17 09:38:06 -02:00
Bruno Oliveira
dfbaa20240 Bring test_live_logs_unknown_sections directly due to merge conflicts 2018-02-17 09:36:46 -02:00
Bruno Oliveira
fa8354e872 Merge pull request #3213 from thisch/collection_deselection
Show deselection count before tests are executed
2018-02-17 09:03:26 -02:00
Bruno Oliveira
05faa69c37 Merge pull request #3194 from s0undt3ch/feature/logstart-logfinish
Fix issue where a new line was always written for the live log finish section
2018-02-16 21:19:38 -02:00
Bruno Oliveira
b1abe5db23 Merge pull request #3201 from uSpike/deselect_cli
Add "--deselect" command line option
2018-02-16 20:50:22 -02:00
Jordan Speicher
774c539f1a Add --deselect command line option
Fixes #3198
2018-02-16 08:17:37 -06:00
Bruno Oliveira
df2f019997 Slight rewording in the CHANGELOG 2018-02-15 19:45:05 -02:00
Thomas Hisch
82cdc487ce Fix raised warning when attrs 17.4.0 is used
Related: #3223
2018-02-15 21:09:44 +01:00
Thomas Hisch
6496131b79 Show deselection count before tests are exectued
Fixes #1527
2018-02-13 22:27:02 +01:00
feuillemorte
dff0500114 #3034 Added new option "--new-first" 2018-02-13 22:49:28 +03:00
Bruno Oliveira
063e2da967 Merge pull request #3176 from feuillemorte/1478_no_stdout_option
#1478 Added --no-stdout option
2018-02-09 18:36:36 -02:00
feuillemorte
da5882c2d5 #1478 Add doc and remove print 2018-02-09 21:36:48 +03:00
Pedro Algarvio
d776e5610e Fix issue where a new line was always written for the live log finish section 2018-02-09 11:17:01 +00:00
Bruno Oliveira
bba258aa5e Merge pull request #3190 from s0undt3ch/feature/logs-stream
Expose `log_cli` as a CLI parser option.
2018-02-08 17:07:48 -02:00
Bruno Oliveira
a4cbd03535 Fix linting 2018-02-08 13:22:32 -02:00
feuillemorte
71367881ed #1478 Added --show-capture=both option (fix comments) 2018-02-08 16:21:22 +03:00
Bruno Oliveira
ad7d63df97 Rename _stream_logs_enabled to _log_cli_enabled and remove _stream_logs 2018-02-08 09:48:51 -02:00
Pedro Algarvio
0b71255dda Expose log_cli as a CLI parser option. 2018-02-08 09:42:53 +00:00
Bruno Oliveira
ce0a9aadec Merge pull request #3127 from feuillemorte/1642-add-rootdir-option
#1642 add rootdir option
2018-02-07 18:18:59 -02:00
Bruno Oliveira
ed12cf3fb3 Merge pull request #3189 from s0undt3ch/feature/logstart-logfinish
Additionally handle logstart and logfinish hooks
2018-02-07 18:09:45 -02:00
Bruno Oliveira
29a074eae3 Merge pull request #3156 from thisch/junit_xml_log_fix
Add captured log msgs to junit xml file
2018-02-06 21:42:11 -02:00
feuillemorte
1a650a9eb9 #1478 Added --show-capture option 2018-02-06 23:38:51 +03:00
Bruno Oliveira
ea06c1345f Update changelog wording slightly 2018-02-06 15:45:37 +00:00
Pedro Algarvio
00d8787bb8 Add name to AUTHORS 2018-02-06 15:45:37 +00:00
Pedro Algarvio
67558e0e22 Additionally handle logstart and logfinish hooks 2018-02-06 15:45:36 +00:00
Bruno Oliveira
2d0c1e941e Fix versionadded tag in caplog function 2018-02-05 20:22:21 -02:00
Thomas Hisch
c0ef4a4d35 Add captured log msgs to junit xml file
For each test this adds the captured log msgs to a system-* tag in the junit
xml output file. The destination of the system-* tag is specified by
junit_logging ini option.
2018-02-03 09:39:29 +01:00
Bruno Oliveira
37d836d754 Reword docs slightly 2018-02-01 19:34:15 -02:00
feuillemorte
3eb6cad222 #1642 Fix comments 2018-02-01 11:20:37 +03:00
feuillemorte
936651702b #1642 Fix tests 2018-02-01 00:27:48 +03:00
feuillemorte
9f1772e679 #1642 Resolve conflicts 2018-02-01 00:18:28 +03:00
feuillemorte
741b571f3b #1642 fix tests and config.py 2018-02-01 00:03:24 +03:00
feuillemorte
949a620d3a #1478 Added --no-stdout option 2018-01-31 22:36:28 +03:00
feuillemorte
ace772c743 Merge branch '1642-add-rootdir-option' of github.com:feuillemorte/pytest into 1642-add-rootdir-option 2018-01-25 19:46:43 +03:00
feuillemorte
3a004a4507 added rootdir description to customize.rst 2018-01-25 19:46:22 +03:00
feuillemorte
503e00f7ff #1642 Remove adding rootdir to sys path 2018-01-25 15:57:29 +03:00
feuillemorte
0cfa975930 #1642 Fix tests 2018-01-25 15:57:04 +03:00
Bruno Oliveira
a7066ba837 Update formatting in the CHANGELOG 2018-01-23 17:31:07 -02:00
feuillemorte
83034bbd48 #1642 Fix rootdir option 2018-01-20 22:30:01 +03:00
feuillemorte
a7c39c894b #1642 fix flake8 2018-01-17 23:48:04 +03:00
feuillemorte
4a18d76160 #1642 remove print 2018-01-17 23:14:40 +03:00
feuillemorte
86f01967e1 #1642 Added changelog entry 2018-01-17 23:05:22 +03:00
feuillemorte
d784155fd2 #1642 Add rootdir option 2018-01-17 23:02:31 +03:00
Bruno Oliveira
a7a39f1364 Update CHANGELOG formatting 2017-10-18 19:26:10 -02:00
ApaDoctor
cfd16d0dac provide error fixture applied to the same func
provide error fixture applied to the same func

provide error fixture applied to the same func
2017-10-18 12:01:08 +02:00
133 changed files with 5284 additions and 1903 deletions

13
AUTHORS
View File

@@ -17,11 +17,13 @@ Andreas Zeidler
Andrzej Ostrowski
Andy Freeland
Anthon van der Neut
Anthony Shaw
Anthony Sottile
Antony Lee
Armin Rigo
Aron Coyle
Aron Curzon
Aviral Verma
Aviv Palivoda
Barney Gale
Ben Webb
@@ -35,6 +37,7 @@ Brianna Laugher
Bruno Oliveira
Cal Leeming
Carl Friedrich Bolz
Carlos Jenkins
Ceridwen
Charles Cloud
Charnjit SiNGH (CCSJ)
@@ -91,6 +94,7 @@ Janne Vanhala
Jason R. Coombs
Javier Domingo Cansino
Javier Romero
Jeff Rackauckas
Jeff Widman
John Eddie Ayson
John Towler
@@ -98,13 +102,16 @@ Jon Sonesen
Jonas Obrist
Jordan Guymon
Jordan Moldow
Jordan Speicher
Joshua Bronson
Jurko Gospodnetić
Justyna Janczyszyn
Kale Kundert
Katarzyna Jachim
Katerina Koukiou
Kevin Cox
Kodi B. Arfer
Kostis Anagnostopoulos
Lawrence Mitchell
Lee Kamentsky
Lev Maximov
@@ -139,16 +146,19 @@ Michael Seifert
Michal Wajszczuk
Mihai Capotă
Mike Lundy
Miro Hrončok
Nathaniel Waisbrot
Ned Batchelder
Neven Mundar
Nicolas Delaby
Oleg Pidsadnyi
Oleg Sushchenko
Oliver Bestwalter
Omar Kohl
Omer Hadari
Patrick Hayes
Paweł Adamczak
Pedro Algarvio
Pieter Mulder
Piotr Banaszkiewicz
Punyashloka Biswal
@@ -182,6 +192,7 @@ Tareq Alayan
Ted Xiao
Thomas Grainger
Thomas Hisch
Tim Strazny
Tom Dalton
Tom Viner
Trevor Bekolay
@@ -192,8 +203,10 @@ Victor Uriarte
Vidar T. Fauske
Vitaly Lashmanov
Vlad Dragos
William Lee
Wouter van Ackooy
Xuan Luong
Xuecong Liao
Zoltán Máté
Roland Puntaier
Allan Feldman

View File

@@ -1,4 +1,4 @@
..
..
You should *NOT* be adding new change log entries to this file, this
file is managed by towncrier. You *may* edit previous change logs to
fix problems like typo corrections or such.
@@ -8,6 +8,333 @@
.. towncrier release notes start
Pytest 3.6.0 (2018-05-23)
=========================
Features
--------
- Revamp the internals of the ``pytest.mark`` implementation with correct per
node handling which fixes a number of long standing bugs caused by the old
design. This introduces new ``Node.iter_markers(name)`` and
``Node.get_closest_mark(name)`` APIs. Users are **strongly encouraged** to
read the `reasons for the revamp in the docs
<https://docs.pytest.org/en/latest/mark.html#marker-revamp-and-iteration>`_,
or jump over to details about `updating existing code to use the new APIs
<https://docs.pytest.org/en/latest/mark.html#updating-code>`_. (`#3317
<https://github.com/pytest-dev/pytest/issues/3317>`_)
- Now when ``@pytest.fixture`` is applied more than once to the same function a
``ValueError`` is raised. This buggy behavior would cause surprising problems
and if was working for a test suite it was mostly by accident. (`#2334
<https://github.com/pytest-dev/pytest/issues/2334>`_)
- Support for Python 3.7's builtin ``breakpoint()`` method, see `Using the
builtin breakpoint function
<https://docs.pytest.org/en/latest/usage.html#breakpoint-builtin>`_ for
details. (`#3180 <https://github.com/pytest-dev/pytest/issues/3180>`_)
- ``monkeypatch`` now supports a ``context()`` function which acts as a context
manager which undoes all patching done within the ``with`` block. (`#3290
<https://github.com/pytest-dev/pytest/issues/3290>`_)
- The ``--pdb`` option now causes KeyboardInterrupt to enter the debugger,
instead of stopping the test session. On python 2.7, hitting CTRL+C again
exits the debugger. On python 3.2 and higher, use CTRL+D. (`#3299
<https://github.com/pytest-dev/pytest/issues/3299>`_)
- pytest not longer changes the log level of the root logger when the
``log-level`` parameter has greater numeric value than that of the level of
the root logger, which makes it play better with custom logging configuration
in user code. (`#3307 <https://github.com/pytest-dev/pytest/issues/3307>`_)
Bug Fixes
---------
- A rare race-condition which might result in corrupted ``.pyc`` files on
Windows has been hopefully solved. (`#3008
<https://github.com/pytest-dev/pytest/issues/3008>`_)
- Also use iter_marker for discovering the marks applying for marker
expressions from the cli to avoid the bad data from the legacy mark storage.
(`#3441 <https://github.com/pytest-dev/pytest/issues/3441>`_)
- When showing diffs of failed assertions where the contents contain only
whitespace, escape them using ``repr()`` first to make it easy to spot the
differences. (`#3443 <https://github.com/pytest-dev/pytest/issues/3443>`_)
Improved Documentation
----------------------
- Change documentation copyright year to a range which auto-updates itself each
time it is published. (`#3303
<https://github.com/pytest-dev/pytest/issues/3303>`_)
Trivial/Internal Changes
------------------------
- ``pytest`` now depends on the `python-atomicwrites
<https://github.com/untitaker/python-atomicwrites>`_ library. (`#3008
<https://github.com/pytest-dev/pytest/issues/3008>`_)
- Update all pypi.python.org URLs to pypi.org. (`#3431
<https://github.com/pytest-dev/pytest/issues/3431>`_)
- Detect `pytest_` prefixed hooks using the internal plugin manager since
``pluggy`` is deprecating the ``implprefix`` argument to ``PluginManager``.
(`#3487 <https://github.com/pytest-dev/pytest/issues/3487>`_)
- Import ``Mapping`` and ``Sequence`` from ``_pytest.compat`` instead of
directly from ``collections`` in ``python_api.py::approx``. Add ``Mapping``
to ``_pytest.compat``, import it from ``collections`` on python 2, but from
``collections.abc`` on Python 3 to avoid a ``DeprecationWarning`` on Python
3.7 or newer. (`#3497 <https://github.com/pytest-dev/pytest/issues/3497>`_)
Pytest 3.5.1 (2018-04-23)
=========================
Bug Fixes
---------
- Reset ``sys.last_type``, ``sys.last_value`` and ``sys.last_traceback`` before
each test executes. Those attributes are added by pytest during the test run
to aid debugging, but were never reset so they would create a leaking
reference to the last failing test's frame which in turn could never be
reclaimed by the garbage collector. (`#2798
<https://github.com/pytest-dev/pytest/issues/2798>`_)
- ``pytest.raises`` now raises ``TypeError`` when receiving an unknown keyword
argument. (`#3348 <https://github.com/pytest-dev/pytest/issues/3348>`_)
- ``pytest.raises`` now works with exception classes that look like iterables.
(`#3372 <https://github.com/pytest-dev/pytest/issues/3372>`_)
Improved Documentation
----------------------
- Fix typo in ``caplog`` fixture documentation, which incorrectly identified
certain attributes as methods. (`#3406
<https://github.com/pytest-dev/pytest/issues/3406>`_)
Trivial/Internal Changes
------------------------
- Added a more indicative error message when parametrizing a function whose
argument takes a default value. (`#3221
<https://github.com/pytest-dev/pytest/issues/3221>`_)
- Remove internal ``_pytest.terminal.flatten`` function in favor of
``more_itertools.collapse``. (`#3330
<https://github.com/pytest-dev/pytest/issues/3330>`_)
- Import some modules from ``collections.abc`` instead of ``collections`` as
the former modules trigger ``DeprecationWarning`` in Python 3.7. (`#3339
<https://github.com/pytest-dev/pytest/issues/3339>`_)
- record_property is no longer experimental, removing the warnings was
forgotten. (`#3360 <https://github.com/pytest-dev/pytest/issues/3360>`_)
- Mention in documentation and CLI help that fixtures with leading ``_`` are
printed by ``pytest --fixtures`` only if the ``-v`` option is added. (`#3398
<https://github.com/pytest-dev/pytest/issues/3398>`_)
Pytest 3.5.0 (2018-03-21)
=========================
Deprecations and Removals
-------------------------
- ``record_xml_property`` fixture is now deprecated in favor of the more
generic ``record_property``. (`#2770
<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
<https://github.com/pytest-dev/pytest/issues/3084>`_)
Features
--------
- New ``--show-capture`` command-line option that allows to specify how to
display captured output when tests fail: ``no``, ``stdout``, ``stderr``,
``log`` or ``all`` (the default). (`#1478
<https://github.com/pytest-dev/pytest/issues/1478>`_)
- New ``--rootdir`` command-line option to override the rules for discovering
the root directory. See `customize
<https://docs.pytest.org/en/latest/customize.html>`_ in the documentation for
details. (`#1642 <https://github.com/pytest-dev/pytest/issues/1642>`_)
- Fixtures are now instantiated based on their scopes, with higher-scoped
fixtures (such as ``session``) being instantiated first than lower-scoped
fixtures (such as ``function``). The relative order of fixtures of the same
scope is kept unchanged, based in their declaration order and their
dependencies. (`#2405 <https://github.com/pytest-dev/pytest/issues/2405>`_)
- ``record_xml_property`` renamed to ``record_property`` and is now compatible
with xdist, markers and any reporter. ``record_xml_property`` name is now
deprecated. (`#2770 <https://github.com/pytest-dev/pytest/issues/2770>`_)
- New ``--nf``, ``--new-first`` options: run new tests first followed by the
rest of the tests, in both cases tests are also sorted by the file modified
time, with more recent files coming first. (`#3034
<https://github.com/pytest-dev/pytest/issues/3034>`_)
- New ``--last-failed-no-failures`` command-line option that allows to specify
the behavior of the cache plugin's ```--last-failed`` feature when no tests
failed in the last run (or no cache was found): ``none`` or ``all`` (the
default). (`#3139 <https://github.com/pytest-dev/pytest/issues/3139>`_)
- New ``--doctest-continue-on-failure`` command-line option to enable doctests
to show multiple failures for each snippet, instead of stopping at the first
failure. (`#3149 <https://github.com/pytest-dev/pytest/issues/3149>`_)
- Captured log messages are added to the ``<system-out>`` tag in the generated
junit xml file if the ``junit_logging`` ini option is set to ``system-out``.
If the value of this ini option is ``system-err``, the logs are written to
``<system-err>``. The default value for ``junit_logging`` is ``no``, meaning
captured logs are not written to the output file. (`#3156
<https://github.com/pytest-dev/pytest/issues/3156>`_)
- Allow the logging plugin to handle ``pytest_runtest_logstart`` and
``pytest_runtest_logfinish`` hooks when live logs are enabled. (`#3189
<https://github.com/pytest-dev/pytest/issues/3189>`_)
- Passing `--log-cli-level` in the command-line now automatically activates
live logging. (`#3190 <https://github.com/pytest-dev/pytest/issues/3190>`_)
- Add command line option ``--deselect`` to allow deselection of individual
tests at collection time. (`#3198
<https://github.com/pytest-dev/pytest/issues/3198>`_)
- Captured logs are printed before entering pdb. (`#3204
<https://github.com/pytest-dev/pytest/issues/3204>`_)
- Deselected item count is now shown before tests are run, e.g. ``collected X
items / Y deselected``. (`#3213
<https://github.com/pytest-dev/pytest/issues/3213>`_)
- The builtin module ``platform`` is now available for use in expressions in
``pytest.mark``. (`#3236
<https://github.com/pytest-dev/pytest/issues/3236>`_)
- The *short test summary info* section now is displayed after tracebacks and
warnings in the terminal. (`#3255
<https://github.com/pytest-dev/pytest/issues/3255>`_)
- New ``--verbosity`` flag to set verbosity level explicitly. (`#3296
<https://github.com/pytest-dev/pytest/issues/3296>`_)
- ``pytest.approx`` now accepts comparing a numpy array with a scalar. (`#3312
<https://github.com/pytest-dev/pytest/issues/3312>`_)
Bug Fixes
---------
- Suppress ``IOError`` when closing the temporary file used for capturing
streams in Python 2.7. (`#2370
<https://github.com/pytest-dev/pytest/issues/2370>`_)
- Fixed ``clear()`` method on ``caplog`` fixture which cleared ``records``, but
not the ``text`` property. (`#3297
<https://github.com/pytest-dev/pytest/issues/3297>`_)
- During test collection, when stdin is not allowed to be read, the
``DontReadFromStdin`` object still allow itself to be iterable and resolved
to an iterator without crashing. (`#3314
<https://github.com/pytest-dev/pytest/issues/3314>`_)
Improved Documentation
----------------------
- Added a `reference <https://docs.pytest.org/en/latest/reference.html>`_ page
to the docs. (`#1713 <https://github.com/pytest-dev/pytest/issues/1713>`_)
Trivial/Internal Changes
------------------------
- Change minimum requirement of ``attrs`` to ``17.4.0``. (`#3228
<https://github.com/pytest-dev/pytest/issues/3228>`_)
- Renamed example directories so all tests pass when ran from the base
directory. (`#3245 <https://github.com/pytest-dev/pytest/issues/3245>`_)
- Internal ``mark.py`` module has been turned into a package. (`#3250
<https://github.com/pytest-dev/pytest/issues/3250>`_)
- ``pytest`` now depends on the `more-itertools
<https://github.com/erikrose/more-itertools>`_ package. (`#3265
<https://github.com/pytest-dev/pytest/issues/3265>`_)
- Added warning when ``[pytest]`` section is used in a ``.cfg`` file passed
with ``-c`` (`#3268 <https://github.com/pytest-dev/pytest/issues/3268>`_)
- ``nodeids`` can now be passed explicitly to ``FSCollector`` and ``Node``
constructors. (`#3291 <https://github.com/pytest-dev/pytest/issues/3291>`_)
- Internal refactoring of ``FormattedExcinfo`` to use ``attrs`` facilities and
remove old support code for legacy Python versions. (`#3292
<https://github.com/pytest-dev/pytest/issues/3292>`_)
- Refactoring to unify how verbosity is handled internally. (`#3296
<https://github.com/pytest-dev/pytest/issues/3296>`_)
- Internal refactoring to better integrate with argparse. (`#3304
<https://github.com/pytest-dev/pytest/issues/3304>`_)
- Fix a python example when calling a fixture in doc/en/usage.rst (`#3308
<https://github.com/pytest-dev/pytest/issues/3308>`_)
Pytest 3.4.2 (2018-03-04)
=========================
Bug Fixes
---------
- Removed progress information when capture option is ``no``. (`#3203
<https://github.com/pytest-dev/pytest/issues/3203>`_)
- Refactor check of bindir from ``exists`` to ``isdir``. (`#3241
<https://github.com/pytest-dev/pytest/issues/3241>`_)
- Fix ``TypeError`` issue when using ``approx`` with a ``Decimal`` value.
(`#3247 <https://github.com/pytest-dev/pytest/issues/3247>`_)
- Fix reference cycle generated when using the ``request`` fixture. (`#3249
<https://github.com/pytest-dev/pytest/issues/3249>`_)
- ``[tool:pytest]`` sections in ``*.cfg`` files passed by the ``-c`` option are
now properly recognized. (`#3260
<https://github.com/pytest-dev/pytest/issues/3260>`_)
Improved Documentation
----------------------
- Add logging plugin to plugins list. (`#3209
<https://github.com/pytest-dev/pytest/issues/3209>`_)
Trivial/Internal Changes
------------------------
- Fix minor typo in fixture.rst (`#3259
<https://github.com/pytest-dev/pytest/issues/3259>`_)
Pytest 3.4.1 (2018-02-20)
=========================
@@ -340,7 +667,7 @@ Features
- Match ``warns`` signature to ``raises`` by adding ``match`` keyword. (`#2708
<https://github.com/pytest-dev/pytest/issues/2708>`_)
- Pytest now captures and displays output from the standard `logging` module.
- Pytest now captures and displays output from the standard ``logging`` module.
The user can control the logging level to be captured by specifying options
in ``pytest.ini``, the command line and also during individual tests using
markers. Also, a ``caplog`` fixture is available that enables users to test
@@ -965,7 +1292,7 @@ Changes
* Testcase reports with a ``url`` attribute will now properly write this to junitxml.
Thanks `@fushi`_ for the PR (`#1874`_).
* Remove common items from dict comparision output when verbosity=1. Also update
* Remove common items from dict comparison output when verbosity=1. Also update
the truncation message to make it clearer that pytest truncates all
assertion messages if verbosity < 2 (`#1512`_).
Thanks `@mattduck`_ for the PR
@@ -977,7 +1304,7 @@ Changes
* fix `#2013`_: turn RecordedWarning into ``namedtuple``,
to give it a comprehensible repr while preventing unwarranted modification.
* fix `#2208`_: ensure a iteration limit for _pytest.compat.get_real_func.
* fix `#2208`_: ensure an iteration limit for _pytest.compat.get_real_func.
Thanks `@RonnyPfannschmidt`_ for the report and PR.
* Hooks are now verified after collection is complete, rather than right after loading installed plugins. This
@@ -1081,7 +1408,7 @@ Bug Fixes
Notably, importing the ``anydbm`` module is fixed. (`#2248`_).
Thanks `@pfhayes`_ for the PR.
* junitxml: Fix problematic case where system-out tag occured twice per testcase
* junitxml: Fix problematic case where system-out tag occurred twice per testcase
element in the XML report. Thanks `@kkoukiou`_ for the PR.
* Fix regression, pytest now skips unittest correctly if run with ``--pdb``
@@ -2677,7 +3004,7 @@ time or change existing behaviors in order to make them less surprising/more use
"::" node id specifications (copy pasted from "-v" output)
- fix issue544 by only removing "@NUM" at the end of "::" separated parts
and if the part has an ".py" extension
and if the part has a ".py" extension
- don't use py.std import helper, rather import things directly.
Thanks Bruno Oliveira.
@@ -2948,7 +3275,7 @@ time or change existing behaviors in order to make them less surprising/more use
would not work correctly because pytest assumes @pytest.mark.some
gets a function to be decorated already. We now at least detect if this
arg is an lambda and thus the example will work. Thanks Alex Gaynor
arg is a lambda and thus the example will work. Thanks Alex Gaynor
for bringing it up.
- xfail a test on pypy that checks wrong encoding/ascii (pypy does
@@ -3261,7 +3588,7 @@ Bug fixes:
rather use the post-2.0 parametrize features instead of yield, see:
http://pytest.org/latest/example/parametrize.html
- fix autouse-issue where autouse-fixtures would not be discovered
if defined in a a/conftest.py file and tests in a/tests/test_some.py
if defined in an a/conftest.py file and tests in a/tests/test_some.py
- fix issue226 - LIFO ordering for fixture teardowns
- fix issue224 - invocations with >256 char arguments now work
- fix issue91 - add/discuss package/directory level setups in example
@@ -3831,7 +4158,7 @@ Bug fixes:
- make path.bestrelpath(path) return ".", note that when calling
X.bestrelpath the assumption is that X is a directory.
- make initial conftest discovery ignore "--" prefixed arguments
- fix resultlog plugin when used in an multicpu/multihost xdist situation
- fix resultlog plugin when used in a multicpu/multihost xdist situation
(thanks Jakub Gustak)
- perform distributed testing related reporting in the xdist-plugin
rather than having dist-related code in the generic py.test

View File

@@ -48,8 +48,7 @@ fix the bug itself.
Fix bugs
--------
Look through the GitHub issues for bugs. Here is a filter you can use:
https://github.com/pytest-dev/pytest/labels/type%3A%20bug
Look through the `GitHub issues for bugs <https://github.com/pytest-dev/pytest/labels/type:%20bug>`_.
:ref:`Talk <contact>` to developers to find out how you can fix specific bugs.
@@ -60,8 +59,7 @@ Don't forget to check the issue trackers of your favourite plugins, too!
Implement features
------------------
Look through the GitHub issues for enhancements. Here is a filter you can use:
https://github.com/pytest-dev/pytest/labels/enhancement
Look through the `GitHub issues for enhancements <https://github.com/pytest-dev/pytest/labels/type:%20enhancement>`_.
:ref:`Talk <contact>` to developers to find out how you can implement specific
features.

View File

@@ -6,13 +6,13 @@
------
.. image:: https://img.shields.io/pypi/v/pytest.svg
:target: https://pypi.python.org/pypi/pytest
:target: https://pypi.org/project/pytest/
.. image:: https://anaconda.org/conda-forge/pytest/badges/version.svg
.. image:: https://img.shields.io/conda/vn/conda-forge/pytest.svg
:target: https://anaconda.org/conda-forge/pytest
.. image:: https://img.shields.io/pypi/pyversions/pytest.svg
:target: https://pypi.python.org/pypi/pytest
: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
@@ -23,6 +23,9 @@
.. image:: https://ci.appveyor.com/api/projects/status/mrgbjaua7t33pg6b?svg=true
:target: https://ci.appveyor.com/project/pytestbot/pytest
.. image:: https://www.codetriage.com/pytest-dev/pytest/badges/users.svg
:target: https://www.codetriage.com/pytest-dev/pytest
The ``pytest`` framework makes it easy to write small tests, yet
scales to support complex functional testing for applications and libraries.

View File

@@ -3,6 +3,8 @@ import inspect
import sys
import traceback
from inspect import CO_VARARGS, CO_VARKEYWORDS
import attr
import re
from weakref import ref
from _pytest.compat import _PY2, _PY3, PY35, safe_str
@@ -458,19 +460,19 @@ class ExceptionInfo(object):
return True
@attr.s
class FormattedExcinfo(object):
""" presenting information about failing Functions and Generators. """
# for traceback entries
flow_marker = ">"
fail_marker = "E"
def __init__(self, showlocals=False, style="long", abspath=True, tbfilter=True, funcargs=False):
self.showlocals = showlocals
self.style = style
self.tbfilter = tbfilter
self.funcargs = funcargs
self.abspath = abspath
self.astcache = {}
showlocals = attr.ib(default=False)
style = attr.ib(default="long")
abspath = attr.ib(default=True)
tbfilter = attr.ib(default=True)
funcargs = attr.ib(default=False)
astcache = attr.ib(default=attr.Factory(dict), init=False, repr=False)
def _getindent(self, source):
# figure out indent for given source

View File

@@ -14,7 +14,7 @@ cpy_compile = compile
class Source(object):
""" a immutable object holding a source code fragment,
""" an immutable object holding a source code fragment,
possibly deindenting it.
"""
_compilecounter = 0
@@ -26,7 +26,7 @@ class Source(object):
for part in parts:
if not part:
partlines = []
if isinstance(part, Source):
elif isinstance(part, Source):
partlines = part.lines
elif isinstance(part, (tuple, list)):
partlines = [x.rstrip("\n") for x in part]
@@ -98,14 +98,14 @@ class Source(object):
newsource.lines = [(indent + line) for line in self.lines]
return newsource
def getstatement(self, lineno, assertion=False):
def getstatement(self, lineno):
""" return Source statement which contains the
given linenumber (counted from 0).
"""
start, end = self.getstatementrange(lineno, assertion)
start, end = self.getstatementrange(lineno)
return self[start:end]
def getstatementrange(self, lineno, assertion=False):
def getstatementrange(self, lineno):
""" return (start, end) tuple which spans the minimal
statement region which containing the given lineno.
"""
@@ -131,13 +131,7 @@ class Source(object):
""" return True if source is parseable, heuristically
deindenting it by default.
"""
try:
import parser
except ImportError:
def syntax_checker(x):
return compile(x, 'asd', 'exec')
else:
syntax_checker = parser.suite
from parser import suite as syntax_checker
if deindent:
source = str(self.deindent())
@@ -219,9 +213,9 @@ def getfslineno(obj):
""" Return source location (path, lineno) for the given object.
If the source cannot be determined return ("", -1)
"""
import _pytest._code
from .code import Code
try:
code = _pytest._code.Code(obj)
code = Code(obj)
except TypeError:
try:
fn = inspect.getsourcefile(obj) or inspect.getfile(obj)
@@ -259,8 +253,8 @@ def findsource(obj):
def getsource(obj, **kwargs):
import _pytest._code
obj = _pytest._code.getrawcode(obj)
from .code import getrawcode
obj = getrawcode(obj)
try:
strsrc = inspect.getsource(obj)
except IndentationError:
@@ -286,8 +280,6 @@ def deindent(lines, offset=None):
def readline_generator(lines):
for line in lines:
yield line + '\n'
while True:
yield ''
it = readline_generator(lines)
@@ -318,9 +310,9 @@ def get_statement_startend2(lineno, node):
# AST's line numbers start indexing at 1
values = []
for x in ast.walk(node):
if isinstance(x, ast.stmt) or isinstance(x, ast.ExceptHandler):
if isinstance(x, (ast.stmt, ast.ExceptHandler)):
values.append(x.lineno - 1)
for name in "finalbody", "orelse":
for name in ("finalbody", "orelse"):
val = getattr(x, name, None)
if val:
# treat the finally/orelse part as its own statement
@@ -338,11 +330,8 @@ def get_statement_startend2(lineno, node):
def getstatementrange_ast(lineno, source, assertion=False, astnode=None):
if astnode is None:
content = str(source)
try:
astnode = compile(content, "source", "exec", 1024) # 1024 for AST
except ValueError:
start, end = getstatementrange_old(lineno, source, assertion)
return None, start, end
astnode = compile(content, "source", "exec", 1024) # 1024 for AST
start, end = get_statement_startend2(lineno, astnode)
# we need to correct the end:
# - ast-parsing strips comments
@@ -374,38 +363,3 @@ def getstatementrange_ast(lineno, source, assertion=False, astnode=None):
else:
break
return astnode, start, end
def getstatementrange_old(lineno, source, assertion=False):
""" return (start, end) tuple which spans the minimal
statement region which containing the given lineno.
raise an IndexError if no such statementrange can be found.
"""
# XXX this logic is only used on python2.4 and below
# 1. find the start of the statement
from codeop import compile_command
for start in range(lineno, -1, -1):
if assertion:
line = source.lines[start]
# the following lines are not fully tested, change with care
if 'super' in line and 'self' in line and '__init__' in line:
raise IndexError("likely a subclass")
if "assert" not in line and "raise" not in line:
continue
trylines = source.lines[start:lineno + 1]
# quick hack to prepare parsing an indented line with
# compile_command() (which errors on "return" outside defs)
trylines.insert(0, 'def xxx():')
trysource = '\n '.join(trylines)
# ^ space here
try:
compile_command(trysource)
except (SyntaxError, OverflowError, ValueError):
continue
# 2. find the end of the statement
for end in range(lineno + 1, len(source) + 1):
trysource = source[start:end]
if trysource.isparseable():
return start, end
raise SyntaxError("no valid source range around line %d " % (lineno,))

View File

@@ -12,7 +12,9 @@ import struct
import sys
import types
import atomicwrites
import py
from _pytest.assertion import util
@@ -140,7 +142,7 @@ class AssertionRewritingHook(object):
# Probably a SyntaxError in the test.
return None
if write:
_make_rewritten_pyc(state, source_stat, pyc, co)
_write_pyc(state, co, source_stat, pyc)
else:
state.trace("found cached rewritten pyc for %r" % (fn,))
self.modules[name] = co, pyc
@@ -258,22 +260,21 @@ def _write_pyc(state, co, source_stat, pyc):
# sometime to be able to use imp.load_compiled to load them. (See
# the comment in load_module above.)
try:
fp = open(pyc, "wb")
except IOError:
err = sys.exc_info()[1].errno
state.trace("error writing pyc file at %s: errno=%s" % (pyc, err))
with atomicwrites.atomic_write(pyc, mode="wb", overwrite=True) as fp:
fp.write(imp.get_magic())
mtime = int(source_stat.mtime)
size = source_stat.size & 0xFFFFFFFF
fp.write(struct.pack("<ll", mtime, size))
if six.PY2:
marshal.dump(co, fp.file)
else:
marshal.dump(co, fp)
except EnvironmentError as e:
state.trace("error writing pyc file at %s: errno=%s" % (pyc, e.errno))
# we ignore any failure to write the cache file
# there are many reasons, permission-denied, __pycache__ being a
# file etc.
return False
try:
fp.write(imp.get_magic())
mtime = int(source_stat.mtime)
size = source_stat.size & 0xFFFFFFFF
fp.write(struct.pack("<ll", mtime, size))
marshal.dump(co, fp)
finally:
fp.close()
return True
@@ -338,20 +339,6 @@ def _rewrite_test(config, fn):
return stat, co
def _make_rewritten_pyc(state, source_stat, pyc, co):
"""Try to dump rewritten code to *pyc*."""
if sys.platform.startswith("win"):
# Windows grants exclusive access to open files and doesn't have atomic
# rename, so just write into the final file.
_write_pyc(state, co, source_stat, pyc)
else:
# When not on windows, assume rename is atomic. Dump the code object
# into a file specific to this process and atomically replace it.
proc_pyc = pyc + "." + str(os.getpid())
if _write_pyc(state, co, source_stat, proc_pyc):
os.rename(proc_pyc, pyc)
def _read_pyc(source, pyc, trace=lambda x: None):
"""Possibly read a pytest pyc containing rewritten code.

View File

@@ -5,7 +5,7 @@ import pprint
import _pytest._code
import py
import six
from collections import Sequence
from ..compat import Sequence
u = six.text_type
@@ -171,10 +171,22 @@ def _diff_text(left, right, verbose=False):
"""
from difflib import ndiff
explanation = []
def escape_for_readable_diff(binary_text):
"""
Ensures that the internal string is always valid unicode, converting any bytes safely to valid unicode.
This is done using repr() which then needs post-processing to fix the encompassing quotes and un-escape
newlines and carriage returns (#429).
"""
r = six.text_type(repr(binary_text)[1:-1])
r = r.replace(r'\n', '\n')
r = r.replace(r'\r', '\r')
return r
if isinstance(left, six.binary_type):
left = u(repr(left)[1:-1]).replace(r'\n', '\n')
left = escape_for_readable_diff(left)
if isinstance(right, six.binary_type):
right = u(repr(right)[1:-1]).replace(r'\n', '\n')
right = escape_for_readable_diff(right)
if not verbose:
i = 0 # just in case left or right has zero length
for i in range(min(len(left), len(right))):
@@ -197,6 +209,10 @@ def _diff_text(left, right, verbose=False):
left = left[:-i]
right = right[:-i]
keepends = True
if left.isspace() or right.isspace():
left = repr(str(left))
right = repr(str(right))
explanation += [u'Strings contain only whitespace, escaping them using repr()']
explanation += [line.strip('\n')
for line in ndiff(left.splitlines(keepends),
right.splitlines(keepends))]

View File

@@ -5,7 +5,12 @@ the name cache was not chosen to ensure pluggy automatically
ignores the external pytest-cache
"""
from __future__ import absolute_import, division, print_function
from collections import OrderedDict
import py
import six
import pytest
import json
import os
@@ -107,11 +112,12 @@ class LFPlugin(object):
self.active = any(config.getoption(key) for key in active_keys)
self.lastfailed = config.cache.get("cache/lastfailed", {})
self._previously_failed_count = None
self._no_failures_behavior = self.config.getoption('last_failed_no_failures')
def pytest_report_collectionfinish(self):
if self.active:
if not self._previously_failed_count:
mode = "run all (no recorded failures)"
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(
@@ -139,24 +145,28 @@ class LFPlugin(object):
self.lastfailed[report.nodeid] = True
def pytest_collection_modifyitems(self, session, config, items):
if self.active and self.lastfailed:
previously_failed = []
previously_passed = []
for item in items:
if item.nodeid in self.lastfailed:
previously_failed.append(item)
if self.active:
if self.lastfailed:
previously_failed = []
previously_passed = []
for item in items:
if item.nodeid in self.lastfailed:
previously_failed.append(item)
else:
previously_passed.append(item)
self._previously_failed_count = len(previously_failed)
if not previously_failed:
# running a subset of all tests with recorded failures outside
# of the set of tests currently executing
return
if self.config.getoption("lf"):
items[:] = previously_failed
config.hook.pytest_deselected(items=previously_passed)
else:
previously_passed.append(item)
self._previously_failed_count = len(previously_failed)
if not previously_failed:
# running a subset of all tests with recorded failures outside
# of the set of tests currently executing
return
if self.config.getoption("lf"):
items[:] = previously_failed
config.hook.pytest_deselected(items=previously_passed)
else:
items[:] = previously_failed + previously_passed
items[:] = previously_failed + previously_passed
elif self._no_failures_behavior == 'none':
config.hook.pytest_deselected(items=items)
items[:] = []
def pytest_sessionfinish(self, session):
config = self.config
@@ -168,6 +178,39 @@ class LFPlugin(object):
config.cache.set("cache/lastfailed", self.lastfailed)
class NFPlugin(object):
""" Plugin which implements the --nf (run new-first) option """
def __init__(self, config):
self.config = config
self.active = config.option.newfirst
self.cached_nodeids = config.cache.get("cache/nodeids", [])
def pytest_collection_modifyitems(self, session, config, items):
if self.active:
new_items = OrderedDict()
other_items = OrderedDict()
for item in items:
if item.nodeid not in self.cached_nodeids:
new_items[item.nodeid] = item
else:
other_items[item.nodeid] = item
items[:] = self._get_increasing_order(six.itervalues(new_items)) + \
self._get_increasing_order(six.itervalues(other_items))
self.cached_nodeids = [x.nodeid for x in items if isinstance(x, pytest.Item)]
def _get_increasing_order(self, items):
return sorted(items, key=lambda item: item.fspath.mtime(), reverse=True)
def pytest_sessionfinish(self, session):
config = self.config
if config.getoption("cacheshow") or hasattr(config, "slaveinput"):
return
config.cache.set("cache/nodeids", self.cached_nodeids)
def pytest_addoption(parser):
group = parser.getgroup("general")
group.addoption(
@@ -179,6 +222,10 @@ def pytest_addoption(parser):
help="run all tests but run the last failures first. "
"This may re-order tests and thus lead to "
"repeated fixture setup/teardown")
group.addoption(
'--nf', '--new-first', action='store_true', dest="newfirst",
help="run tests from new files first, then the rest of the tests "
"sorted by file mtime")
group.addoption(
'--cache-show', action='store_true', dest="cacheshow",
help="show cache contents, don't perform collection or tests")
@@ -188,6 +235,12 @@ def pytest_addoption(parser):
parser.addini(
"cache_dir", default='.pytest_cache',
help="cache directory path.")
group.addoption(
'--lfnf', '--last-failed-no-failures', action='store',
dest='last_failed_no_failures', choices=('all', 'none'), default='all',
help='change the behavior when no test failed in the last run or no '
'information about the last failures was found in the cache'
)
def pytest_cmdline_main(config):
@@ -200,6 +253,7 @@ def pytest_cmdline_main(config):
def pytest_configure(config):
config.cache = Cache(config)
config.pluginmanager.register(LFPlugin(config), "lfplugin")
config.pluginmanager.register(NFPlugin(config), "nfplugin")
@pytest.fixture

View File

@@ -197,9 +197,9 @@ def _ensure_only_one_capture_fixture(request, name):
@pytest.fixture
def capsys(request):
"""Enable capturing of writes to sys.stdout/sys.stderr and make
"""Enable capturing of writes to ``sys.stdout`` and ``sys.stderr`` and make
captured output available via ``capsys.readouterr()`` method calls
which return a ``(out, err)`` tuple. ``out`` and ``err`` will be ``text``
which return a ``(out, err)`` namedtuple. ``out`` and ``err`` will be ``text``
objects.
"""
_ensure_only_one_capture_fixture(request, 'capsys')
@@ -209,7 +209,7 @@ def capsys(request):
@pytest.fixture
def capsysbinary(request):
"""Enable capturing of writes to sys.stdout/sys.stderr and make
"""Enable capturing of writes to ``sys.stdout`` and ``sys.stderr`` and make
captured output available via ``capsys.readouterr()`` method calls
which return a ``(out, err)`` tuple. ``out`` and ``err`` will be ``bytes``
objects.
@@ -225,7 +225,7 @@ def capsysbinary(request):
@pytest.fixture
def capfd(request):
"""Enable capturing of writes to file descriptors 1 and 2 and make
"""Enable capturing of writes to file descriptors ``1`` and ``2`` and make
captured output available via ``capfd.readouterr()`` method calls
which return a ``(out, err)`` tuple. ``out`` and ``err`` will be ``text``
objects.
@@ -272,6 +272,10 @@ def _install_capture_fixture_on_item(request, capture_class):
class CaptureFixture(object):
"""
Object returned by :py:func:`capsys`, :py:func:`capsysbinary`, :py:func:`capfd` and :py:func:`capfdbinary`
fixtures.
"""
def __init__(self, captureclass, request):
self.captureclass = captureclass
self.request = request
@@ -288,6 +292,10 @@ class CaptureFixture(object):
cap.stop_capturing()
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:
@@ -295,6 +303,7 @@ class CaptureFixture(object):
@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)
@@ -306,7 +315,7 @@ class CaptureFixture(object):
def safe_text_dupfile(f, mode, default_encoding="UTF8"):
""" return a open text file object that's a duplicate of f on the
""" return an open text file object that's a duplicate of f on the
FD-level if possible.
"""
encoding = getattr(f, "encoding", None)
@@ -476,7 +485,7 @@ class FDCaptureBinary(object):
os.dup2(targetfd_save, self.targetfd)
os.close(targetfd_save)
self.syscapture.done()
self.tmpfile.close()
_attempt_to_close_capture_file(self.tmpfile)
def suspend(self):
self.syscapture.suspend()
@@ -530,7 +539,7 @@ class SysCapture(object):
def done(self):
setattr(sys, self.name, self._old)
del self._old
self.tmpfile.close()
_attempt_to_close_capture_file(self.tmpfile)
def suspend(self):
setattr(sys, self.name, self._old)
@@ -551,7 +560,7 @@ class SysCaptureBinary(SysCapture):
return res
class DontReadFromInput(object):
class DontReadFromInput(six.Iterator):
"""Temporary stub class. Ideally when stdin is accessed, the
capturing should be turned off, with possibly all data captured
so far sent to the screen. This should be configurable, though,
@@ -565,7 +574,10 @@ class DontReadFromInput(object):
raise IOError("reading from stdin while output is captured")
readline = read
readlines = read
__iter__ = read
__next__ = read
def __iter__(self):
return self
def fileno(self):
raise UnsupportedOperation("redirected stdin is pseudofile, "
@@ -681,3 +693,14 @@ def _py36_windowsconsoleio_workaround(stream):
sys.__stdin__ = sys.stdin = _reopen_stdio(sys.stdin, 'rb')
sys.__stdout__ = sys.stdout = _reopen_stdio(sys.stdout, 'wb')
sys.__stderr__ = sys.stderr = _reopen_stdio(sys.stderr, 'wb')
def _attempt_to_close_capture_file(f):
"""Suppress IOError when closing the temporary file used for capturing streams in py27 (#2370)"""
if six.PY2:
try:
f.close()
except IOError:
pass
else:
f.close()

View File

@@ -38,6 +38,14 @@ PY35 = sys.version_info[:2] >= (3, 5)
PY36 = sys.version_info[:2] >= (3, 6)
MODULE_NOT_FOUND_ERROR = 'ModuleNotFoundError' if PY36 else 'ImportError'
if _PY3:
from collections.abc import MutableMapping as MappingMixin # noqa
from collections.abc import Mapping, Sequence # noqa
else:
# those raise DeprecationWarnings in Python >=3.7
from collections import MutableMapping as MappingMixin # noqa
from collections import Mapping, Sequence # noqa
def _format_args(func):
return str(signature(func))
@@ -127,6 +135,14 @@ def getfuncargnames(function, is_method=False, cls=None):
return arg_names
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
return tuple(p.name for p in signature(function).parameters.values()
if p.kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY) and
p.default is not Parameter.empty)
if _PY3:
STRING_TYPES = bytes, str
UNICODE_TYPES = str,
@@ -241,7 +257,7 @@ def safe_getattr(object, name, default):
def _is_unittest_unexpected_success_a_failure():
"""Return if the test suite should fail if a @expectedFailure unittest test PASSES.
"""Return if the test suite should fail if an @expectedFailure unittest test PASSES.
From https://docs.python.org/3/library/unittest.html?highlight=unittest#unittest.TestResult.wasSuccessful:
Changed in version 3.4: Returns False if there were any

View File

@@ -5,12 +5,14 @@ import shlex
import traceback
import types
import warnings
import copy
import six
import py
# DON't import pytest here because it causes import cycle troubles
import sys
import os
from _pytest.outcomes import Skipped
import _pytest._code
import _pytest.hookspec # the extension point definitions
import _pytest.assertion
@@ -52,7 +54,7 @@ def main(args=None, plugins=None):
tw = py.io.TerminalWriter(sys.stderr)
for line in traceback.format_exception(*e.excinfo):
tw.line(line.rstrip(), red=True)
tw.line("ERROR: could not load %s\n" % (e.path), red=True)
tw.line("ERROR: could not load %s\n" % (e.path,), red=True)
return 4
else:
try:
@@ -66,7 +68,7 @@ def main(args=None, plugins=None):
return 4
class cmdline(object): # compatibility namespace
class cmdline(object): # NOQA compatibility namespace
main = staticmethod(main)
@@ -169,13 +171,13 @@ class PytestPluginManager(PluginManager):
Overwrites :py:class:`pluggy.PluginManager <pluggy.PluginManager>` to add pytest-specific
functionality:
* loading plugins from the command line, ``PYTEST_PLUGIN`` env variable and
* loading plugins from the command line, ``PYTEST_PLUGINS`` env variable and
``pytest_plugins`` global variables found in plugins being loaded;
* ``conftest.py`` loading during start-up;
"""
def __init__(self):
super(PytestPluginManager, self).__init__("pytest", implprefix="pytest_")
super(PytestPluginManager, self).__init__("pytest")
self._conftest_plugins = set()
# state related to local conftest plugins
@@ -199,6 +201,8 @@ class PytestPluginManager(PluginManager):
# Config._consider_importhook will set a real object if required.
self.rewrite_hook = _pytest.assertion.DummyRewriteHook()
# Used to know when we are importing conftests after the pytest_configure stage
self._configured = False
def addhooks(self, module_or_class):
"""
@@ -227,6 +231,11 @@ class PytestPluginManager(PluginManager):
method = getattr(plugin, name)
opts = super(PytestPluginManager, self).parse_hookimpl_opts(plugin, name)
# collect unmarked hooks as long as they have the `pytest_' prefix
if opts is None and name.startswith("pytest_"):
opts = {}
if opts is not None:
for name in ("tryfirst", "trylast", "optionalhook", "hookwrapper"):
opts.setdefault(name, hasattr(method, name))
@@ -274,6 +283,7 @@ class PytestPluginManager(PluginManager):
config.addinivalue_line("markers",
"trylast: mark a hook implementation function such that the "
"plugin machinery will try to call it last/as late as possible.")
self._configured = True
def _warn(self, message):
kwargs = message if isinstance(message, dict) else {
@@ -364,6 +374,9 @@ class PytestPluginManager(PluginManager):
_ensure_removed_sysmodule(conftestpath.purebasename)
try:
mod = conftestpath.pyimport()
if hasattr(mod, 'pytest_plugins') and self._configured:
from _pytest.deprecated import PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST
warnings.warn(PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST)
except Exception:
raise ConftestImportFailure(conftestpath, sys.exc_info())
@@ -435,10 +448,7 @@ class PytestPluginManager(PluginManager):
six.reraise(new_exc_type, new_exc, sys.exc_info()[2])
except Exception as e:
import pytest
if not hasattr(pytest, 'skip') or not isinstance(e, pytest.skip.Exception):
raise
except Skipped as e:
self._warn("skipped plugin %r: %s" % ((modname, e.msg)))
else:
mod = sys.modules[importspec]
@@ -846,19 +856,6 @@ def _ensure_removed_sysmodule(modname):
pass
class CmdOptions(object):
""" holds cmdline options as attributes."""
def __init__(self, values=()):
self.__dict__.update(values)
def __repr__(self):
return "<CmdOptions %r>" % (self.__dict__,)
def copy(self):
return CmdOptions(self.__dict__)
class Notset(object):
def __repr__(self):
return "<NOTSET>"
@@ -886,7 +883,7 @@ class Config(object):
def __init__(self, pluginmanager):
#: access to command line option as attributes.
#: (deprecated), use :py:func:`getoption() <_pytest.config.Config.getoption>` instead
self.option = CmdOptions()
self.option = argparse.Namespace()
_a = FILE_OR_DIR
self._parser = Parser(
usage="%%(prog)s [options] [%s] [%s] [...]" % (_a, _a),
@@ -990,8 +987,9 @@ class Config(object):
self.pluginmanager._set_initial_conftests(early_config.known_args_namespace)
def _initini(self, args):
ns, unknown_args = self._parser.parse_known_and_unknown_args(args, namespace=self.option.copy())
r = determine_setup(ns.inifilename, ns.file_or_dir + unknown_args, warnfunc=self.warn)
ns, unknown_args = self._parser.parse_known_and_unknown_args(args, namespace=copy.copy(self.option))
r = determine_setup(ns.inifilename, ns.file_or_dir + unknown_args, warnfunc=self.warn,
rootdir_cmd_arg=ns.rootdir or None)
self.rootdir, self.inifile, self.inicfg = r
self._parser.extra_info['rootdir'] = self.rootdir
self._parser.extra_info['inifile'] = self.inifile
@@ -1016,7 +1014,7 @@ class Config(object):
mode = 'plain'
else:
self._mark_plugins_for_rewrite(hook)
self._warn_about_missing_assertion(mode)
_warn_about_missing_assertion(mode)
def _mark_plugins_for_rewrite(self, hook):
"""
@@ -1043,23 +1041,6 @@ class Config(object):
for name in _iter_rewritable_modules(package_files):
hook.mark_rewrite(name)
def _warn_about_missing_assertion(self, mode):
try:
assert False
except AssertionError:
pass
else:
if mode == 'plain':
sys.stderr.write("WARNING: ASSERTIONS ARE NOT EXECUTED"
" and FAILING TESTS WILL PASS. Are you"
" using python -O?")
else:
sys.stderr.write("WARNING: assertions not in test modules or"
" plugins will be ignored"
" because assert statements are not executed "
"by the underlying Python interpreter "
"(are you using python -O?)\n")
def _preparse(self, args, addopts=True):
if addopts:
args[:] = shlex.split(os.environ.get('PYTEST_ADDOPTS', '')) + args
@@ -1071,7 +1052,8 @@ class Config(object):
self.pluginmanager.consider_preparse(args)
self.pluginmanager.load_setuptools_entrypoints('pytest11')
self.pluginmanager.consider_env()
self.known_args_namespace = ns = self._parser.parse_known_args(args, namespace=self.option.copy())
self.known_args_namespace = ns = self._parser.parse_known_args(
args, namespace=copy.copy(self.option))
if self.known_args_namespace.confcutdir is None and self.inifile:
confcutdir = py.path.local(self.inifile).dirname
self.known_args_namespace.confcutdir = confcutdir
@@ -1233,6 +1215,29 @@ class Config(object):
return self.getoption(name, skip=True)
def _assertion_supported():
try:
assert False
except AssertionError:
return True
else:
return False
def _warn_about_missing_assertion(mode):
if not _assertion_supported():
if mode == 'plain':
sys.stderr.write("WARNING: ASSERTIONS ARE NOT EXECUTED"
" and FAILING TESTS WILL PASS. Are you"
" using python -O?")
else:
sys.stderr.write("WARNING: assertions not in test modules or"
" plugins will be ignored"
" because assert statements are not executed "
"by the underlying Python interpreter "
"(are you using python -O?)\n")
def exists(path, ignore=EnvironmentError):
try:
return path.check()
@@ -1250,7 +1255,7 @@ def getcfg(args, warnfunc=None):
This parameter should be removed when pytest
adopts standard deprecation warnings (#1804).
"""
from _pytest.deprecated import SETUP_CFG_PYTEST
from _pytest.deprecated import CFG_PYTEST_SECTION
inibasenames = ["pytest.ini", "tox.ini", "setup.cfg"]
args = [x for x in args if not str(x).startswith("-")]
if not args:
@@ -1264,7 +1269,7 @@ def getcfg(args, warnfunc=None):
iniconfig = py.iniconfig.IniConfig(p)
if 'pytest' in iniconfig.sections:
if inibasename == 'setup.cfg' and warnfunc:
warnfunc('C1', SETUP_CFG_PYTEST)
warnfunc('C1', CFG_PYTEST_SECTION.format(filename=inibasename))
return base, p, iniconfig['pytest']
if inibasename == 'setup.cfg' and 'tool:pytest' in iniconfig.sections:
return base, p, iniconfig['tool:pytest']
@@ -1323,14 +1328,22 @@ def get_dirs_from_args(args):
]
def determine_setup(inifile, args, warnfunc=None):
def determine_setup(inifile, args, warnfunc=None, rootdir_cmd_arg=None):
dirs = get_dirs_from_args(args)
if inifile:
iniconfig = py.iniconfig.IniConfig(inifile)
try:
inicfg = iniconfig["pytest"]
except KeyError:
inicfg = None
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:
from _pytest.deprecated import CFG_PYTEST_SECTION
warnfunc('C1', CFG_PYTEST_SECTION.format(filename=str(inifile)))
break
except KeyError:
inicfg = None
rootdir = get_common_ancestor(dirs)
else:
ancestor = get_common_ancestor(dirs)
@@ -1346,6 +1359,11 @@ def determine_setup(inifile, args, warnfunc=None):
is_fs_root = os.path.splitdrive(str(rootdir))[1] == '/'
if is_fs_root:
rootdir = ancestor
if rootdir_cmd_arg:
rootdir_abs_path = py.path.local(os.path.expandvars(rootdir_cmd_arg))
if not os.path.isdir(str(rootdir_abs_path)):
raise UsageError("Directory '{}' not found. Check your '--rootdir' option.".format(rootdir_abs_path))
rootdir = rootdir_abs_path
return rootdir, inifile, inicfg or {}

View File

@@ -2,14 +2,21 @@
from __future__ import absolute_import, division, print_function
import pdb
import sys
import os
from doctest import UnexpectedException
try:
from builtins import breakpoint # noqa
SUPPORTS_BREAKPOINT_BUILTIN = True
except ImportError:
SUPPORTS_BREAKPOINT_BUILTIN = False
def pytest_addoption(parser):
group = parser.getgroup("general")
group._addoption(
'--pdb', dest="usepdb", action="store_true",
help="start the interactive Python debugger on errors.")
help="start the interactive Python debugger on errors or KeyboardInterrupt.")
group._addoption(
'--pdbcls', dest="usepdb_cls", metavar="modulename:classname",
help="start a custom interactive Python debugger on errors. "
@@ -27,12 +34,20 @@ def pytest_configure(config):
if config.getvalue("usepdb"):
config.pluginmanager.register(PdbInvoke(), 'pdbinvoke')
# Use custom Pdb class set_trace instead of default Pdb on breakpoint() call
if SUPPORTS_BREAKPOINT_BUILTIN:
_environ_pythonbreakpoint = os.environ.get('PYTHONBREAKPOINT', '')
if _environ_pythonbreakpoint == '':
sys.breakpointhook = pytestPDB.set_trace
old = (pdb.set_trace, pytestPDB._pluginmanager)
def fin():
pdb.set_trace, pytestPDB._pluginmanager = old
pytestPDB._config = None
pytestPDB._pdb_cls = pdb.Pdb
if SUPPORTS_BREAKPOINT_BUILTIN:
sys.breakpointhook = sys.__breakpointhook__
pdb.set_trace = pytestPDB.set_trace
pytestPDB._pluginmanager = config.pluginmanager
@@ -87,15 +102,16 @@ def _enter_pdb(node, excinfo, rep):
tw = node.config.pluginmanager.getplugin("terminalreporter")._tw
tw.line()
captured_stdout = rep.capstdout
if len(captured_stdout) > 0:
tw.sep(">", "captured stdout")
tw.line(captured_stdout)
showcapture = node.config.option.showcapture
captured_stderr = rep.capstderr
if len(captured_stderr) > 0:
tw.sep(">", "captured stderr")
tw.line(captured_stderr)
for sectionname, content in (('stdout', rep.capstdout),
('stderr', rep.capstderr),
('log', rep.caplog)):
if showcapture in (sectionname, 'all') and content:
tw.sep(">", "captured " + sectionname)
if content[-1:] == "\n":
content = content[:-1]
tw.line(content)
tw.sep(">", "traceback")
rep.toterminal(tw)

View File

@@ -22,7 +22,7 @@ FUNCARG_PREFIX = (
'and scheduled to be removed in pytest 4.0. '
'Please remove the prefix and use the @pytest.fixture decorator instead.')
SETUP_CFG_PYTEST = '[pytest] section in setup.cfg files is deprecated, use [tool:pytest] instead.'
CFG_PYTEST_SECTION = '[pytest] section in {filename} files is deprecated, use [tool:pytest] instead.'
GETFUNCARGVALUE = "use of getfuncargvalue is deprecated, use getfixturevalue"
@@ -32,7 +32,9 @@ RESULT_LOG = (
)
MARK_INFO_ATTRIBUTE = RemovedInPytest4Warning(
"MarkInfo objects are deprecated as they contain the merged marks"
"MarkInfo objects are deprecated as they contain merged marks which are hard to deal with correctly.\n"
"Please use node.get_closest_marker(name) or node.iter_markers(name).\n"
"Docs: https://docs.pytest.org/en/latest/mark.html#updating-code"
)
MARK_PARAMETERSET_UNPACKING = RemovedInPytest4Warning(
@@ -41,6 +43,12 @@ MARK_PARAMETERSET_UNPACKING = RemovedInPytest4Warning(
"For more details, see: https://docs.pytest.org/en/latest/parametrize.html"
)
RECORD_XML_PROPERTY = (
'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"
@@ -50,3 +58,9 @@ METAFUNC_ADD_CALL = (
"Metafunc.addcall is deprecated and scheduled to be removed in pytest 4.0.\n"
"Please use Metafunc.parametrize instead."
)
PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST = RemovedInPytest4Warning(
"Defining pytest_plugins in a non-top-level conftest is deprecated, "
"because it affects the entire directory tree in a non-explicit way.\n"
"Please move it to the top level conftest file instead."
)

View File

@@ -24,6 +24,9 @@ DOCTEST_REPORT_CHOICES = (
DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE,
)
# Lazy definition of runner class
RUNNER_CLASS = None
def pytest_addoption(parser):
parser.addini('doctest_optionflags', 'option flags for doctests',
@@ -47,6 +50,10 @@ def pytest_addoption(parser):
action="store_true", default=False,
help="ignore doctest ImportErrors",
dest="doctest_ignore_import_errors")
group.addoption("--doctest-continue-on-failure",
action="store_true", default=False,
help="for a given doctest, continue to run after the first failure",
dest="doctest_continue_on_failure")
def pytest_collect_file(path, parent):
@@ -77,14 +84,63 @@ def _is_doctest(config, path, parent):
class ReprFailDoctest(TerminalRepr):
def __init__(self, reprlocation, lines):
self.reprlocation = reprlocation
self.lines = lines
def __init__(self, reprlocation_lines):
# List of (reprlocation, lines) tuples
self.reprlocation_lines = reprlocation_lines
def toterminal(self, tw):
for line in self.lines:
tw.line(line)
self.reprlocation.toterminal(tw)
for reprlocation, lines in self.reprlocation_lines:
for line in lines:
tw.line(line)
reprlocation.toterminal(tw)
class MultipleDoctestFailures(Exception):
def __init__(self, failures):
super(MultipleDoctestFailures, self).__init__()
self.failures = failures
def _init_runner_class():
import doctest
class PytestDoctestRunner(doctest.DebugRunner):
"""
Runner to collect failures. Note that the out variable in this case is
a list instead of a stdout-like object
"""
def __init__(self, checker=None, verbose=None, optionflags=0,
continue_on_failure=True):
doctest.DebugRunner.__init__(
self, checker=checker, verbose=verbose, optionflags=optionflags)
self.continue_on_failure = continue_on_failure
def report_failure(self, out, test, example, got):
failure = doctest.DocTestFailure(test, example, got)
if self.continue_on_failure:
out.append(failure)
else:
raise failure
def report_unexpected_exception(self, out, test, example, exc_info):
failure = doctest.UnexpectedException(test, example, exc_info)
if self.continue_on_failure:
out.append(failure)
else:
raise failure
return PytestDoctestRunner
def _get_runner(checker=None, verbose=None, optionflags=0,
continue_on_failure=True):
# We need this in order to do a lazy import on doctest
global RUNNER_CLASS
if RUNNER_CLASS is None:
RUNNER_CLASS = _init_runner_class()
return RUNNER_CLASS(
checker=checker, verbose=verbose, optionflags=optionflags,
continue_on_failure=continue_on_failure)
class DoctestItem(pytest.Item):
@@ -106,7 +162,10 @@ class DoctestItem(pytest.Item):
def runtest(self):
_check_all_skipped(self.dtest)
self._disable_output_capturing_for_darwin()
self.runner.run(self.dtest)
failures = []
self.runner.run(self.dtest, out=failures)
if failures:
raise MultipleDoctestFailures(failures)
def _disable_output_capturing_for_darwin(self):
"""
@@ -122,42 +181,51 @@ class DoctestItem(pytest.Item):
def repr_failure(self, excinfo):
import doctest
failures = None
if excinfo.errisinstance((doctest.DocTestFailure,
doctest.UnexpectedException)):
doctestfailure = excinfo.value
example = doctestfailure.example
test = doctestfailure.test
filename = test.filename
if test.lineno is None:
lineno = None
else:
lineno = test.lineno + example.lineno + 1
message = excinfo.type.__name__
reprlocation = ReprFileLocation(filename, lineno, message)
checker = _get_checker()
report_choice = _get_report_choice(self.config.getoption("doctestreport"))
if lineno is not None:
lines = doctestfailure.test.docstring.splitlines(False)
# add line numbers to the left of the error message
lines = ["%03d %s" % (i + test.lineno + 1, x)
for (i, x) in enumerate(lines)]
# trim docstring error lines to 10
lines = lines[max(example.lineno - 9, 0):example.lineno + 1]
else:
lines = ['EXAMPLE LOCATION UNKNOWN, not showing all tests of that example']
indent = '>>>'
for line in example.source.splitlines():
lines.append('??? %s %s' % (indent, line))
indent = '...'
if excinfo.errisinstance(doctest.DocTestFailure):
lines += checker.output_difference(example,
doctestfailure.got, report_choice).split("\n")
else:
inner_excinfo = ExceptionInfo(excinfo.value.exc_info)
lines += ["UNEXPECTED EXCEPTION: %s" %
repr(inner_excinfo.value)]
lines += traceback.format_exception(*excinfo.value.exc_info)
return ReprFailDoctest(reprlocation, lines)
failures = [excinfo.value]
elif excinfo.errisinstance(MultipleDoctestFailures):
failures = excinfo.value.failures
if failures is not None:
reprlocation_lines = []
for failure in failures:
example = failure.example
test = failure.test
filename = test.filename
if test.lineno is None:
lineno = None
else:
lineno = test.lineno + example.lineno + 1
message = type(failure).__name__
reprlocation = ReprFileLocation(filename, lineno, message)
checker = _get_checker()
report_choice = _get_report_choice(self.config.getoption("doctestreport"))
if lineno is not None:
lines = failure.test.docstring.splitlines(False)
# add line numbers to the left of the error message
lines = ["%03d %s" % (i + test.lineno + 1, x)
for (i, x) in enumerate(lines)]
# trim docstring error lines to 10
lines = lines[max(example.lineno - 9, 0):example.lineno + 1]
else:
lines = ['EXAMPLE LOCATION UNKNOWN, not showing all tests of that example']
indent = '>>>'
for line in example.source.splitlines():
lines.append('??? %s %s' % (indent, line))
indent = '...'
if isinstance(failure, doctest.DocTestFailure):
lines += checker.output_difference(example,
failure.got,
report_choice).split("\n")
else:
inner_excinfo = ExceptionInfo(failure.exc_info)
lines += ["UNEXPECTED EXCEPTION: %s" %
repr(inner_excinfo.value)]
lines += traceback.format_exception(*failure.exc_info)
reprlocation_lines.append((reprlocation, lines))
return ReprFailDoctest(reprlocation_lines)
else:
return super(DoctestItem, self).repr_failure(excinfo)
@@ -187,6 +255,16 @@ def get_optionflags(parent):
return flag_acc
def _get_continue_on_failure(config):
continue_on_failure = config.getvalue('doctest_continue_on_failure')
if continue_on_failure:
# We need to turn off this if we use pdb since we should stop at
# the first failure
if config.getvalue("usepdb"):
continue_on_failure = False
return continue_on_failure
class DoctestTextfile(pytest.Module):
obj = None
@@ -202,8 +280,11 @@ class DoctestTextfile(pytest.Module):
globs = {'__name__': '__main__'}
optionflags = get_optionflags(self)
runner = doctest.DebugRunner(verbose=0, optionflags=optionflags,
checker=_get_checker())
runner = _get_runner(
verbose=0, optionflags=optionflags,
checker=_get_checker(),
continue_on_failure=_get_continue_on_failure(self.config))
_fix_spoof_python2(runner, encoding)
parser = doctest.DocTestParser()
@@ -238,8 +319,10 @@ class DoctestModule(pytest.Module):
# uses internal doctest module parsing mechanism
finder = doctest.DocTestFinder()
optionflags = get_optionflags(self)
runner = doctest.DebugRunner(verbose=0, optionflags=optionflags,
checker=_get_checker())
runner = _get_runner(
verbose=0, optionflags=optionflags,
checker=_get_checker(),
continue_on_failure=_get_continue_on_failure(self.config))
for test in finder.find(module, module.__name__):
if test.examples: # skip empty doctests
@@ -379,6 +462,6 @@ def _fix_spoof_python2(runner, encoding):
@pytest.fixture(scope='session')
def doctest_namespace():
"""
Inject names into the doctest namespace.
Fixture that returns a :py:class:`dict` that will be injected into the namespace of doctests.
"""
return dict()

View File

@@ -5,6 +5,7 @@ import inspect
import sys
import warnings
from collections import OrderedDict, deque, defaultdict
from more_itertools import flatten
import attr
import py
@@ -24,6 +25,12 @@ from _pytest.compat import (
from _pytest.outcomes import fail, TEST_OUTCOME
@attr.s(frozen=True)
class PseudoFixtureDef(object):
cached_result = attr.ib()
scope = attr.ib()
def pytest_sessionstart(session):
import _pytest.python
import _pytest.nodes
@@ -284,7 +291,7 @@ class FixtureRequest(FuncargnamesCompatAttr):
def _getnextfixturedef(self, argname):
fixturedefs = self._arg2fixturedefs.get(argname, None)
if fixturedefs is None:
# we arrive here because of a a dynamic call to
# we arrive here because of a dynamic call to
# getfixturevalue(argname) usage which was naturally
# not known at parsing/collection time
parentid = self._pyfuncitem.parent.nodeid
@@ -365,10 +372,7 @@ class FixtureRequest(FuncargnamesCompatAttr):
:arg marker: a :py:class:`_pytest.mark.MarkDecorator` object
created by a call to ``pytest.mark.NAME(...)``.
"""
try:
self.node.keywords[marker.markname] = marker
except AttributeError:
raise ValueError(marker)
self.node.add_marker(marker)
def raiseerror(self, msg):
""" raise a FixtureLookupError with the given message. """
@@ -440,10 +444,9 @@ class FixtureRequest(FuncargnamesCompatAttr):
fixturedef = self._getnextfixturedef(argname)
except FixtureLookupError:
if argname == "request":
class PseudoFixtureDef(object):
cached_result = (self, [0], None)
scope = "function"
return PseudoFixtureDef
cached_result = (self, [0], None)
scope = "function"
return PseudoFixtureDef(cached_result, scope)
raise
# remove indent to prevent the python3 exception
# from leaking into the call
@@ -839,21 +842,26 @@ def _ensure_immutable_ids(ids):
@attr.s(frozen=True)
class FixtureFunctionMarker(object):
scope = attr.ib()
params = attr.ib(convert=attr.converters.optional(tuple))
params = attr.ib(converter=attr.converters.optional(tuple))
autouse = attr.ib(default=False)
ids = attr.ib(default=None, convert=_ensure_immutable_ids)
ids = attr.ib(default=None, converter=_ensure_immutable_ids)
name = attr.ib(default=None)
def __call__(self, function):
if isclass(function):
raise ValueError(
"class fixtures not supported (may be in the future)")
if getattr(function, "_pytestfixturefunction", False):
raise ValueError(
"fixture is being applied more than once to the same function")
function._pytestfixturefunction = self
return function
def fixture(scope="function", params=None, autouse=False, ids=None, name=None):
""" (return a) decorator to mark a fixture factory function.
"""Decorator to mark a fixture factory function.
This decorator can be used (with or without parameters) to define a
fixture function. The name of the fixture function can later be
@@ -918,7 +926,15 @@ defaultfuncargprefixmarker = fixture()
@fixture(scope="session")
def pytestconfig(request):
""" the pytest config object with access to command line opts."""
"""Session-scoped fixture that returns the :class:`_pytest.config.Config` object.
Example::
def test_foo(pytestconfig):
if pytestconfig.getoption("verbose"):
...
"""
return request.config
@@ -972,10 +988,9 @@ class FixtureManager(object):
argnames = getfuncargnames(func, cls=cls)
else:
argnames = ()
usefixtures = getattr(func, "usefixtures", None)
usefixtures = flatten(mark.args for mark in node.iter_markers(name="usefixtures"))
initialnames = argnames
if usefixtures is not None:
initialnames = usefixtures.args + initialnames
initialnames = tuple(usefixtures) + initialnames
fm = node.session._fixturemanager
names_closure, arg2fixturedefs = fm.getfixtureclosure(initialnames,
node)
@@ -1008,15 +1023,12 @@ class FixtureManager(object):
if nextchar and nextchar not in ":/":
continue
autousenames.extend(basenames)
# make sure autousenames are sorted by scope, scopenum 0 is session
autousenames.sort(
key=lambda x: self._arg2fixturedefs[x][-1].scopenum)
return autousenames
def getfixtureclosure(self, fixturenames, parentnode):
# collect the closure of all fixtures , starting with the given
# fixturenames as the initial set. As we have to visit all
# factory definitions anyway, we also return a arg2fixturedefs
# factory definitions anyway, we also return an arg2fixturedefs
# mapping so that the caller can reuse it and does not have
# to re-discover fixturedefs again for each fixturename
# (discovering matching fixtures for a given name/node is expensive)
@@ -1041,6 +1053,16 @@ class FixtureManager(object):
if fixturedefs:
arg2fixturedefs[argname] = fixturedefs
merge(fixturedefs[-1].argnames)
def sort_by_scope(arg_name):
try:
fixturedefs = arg2fixturedefs[arg_name]
except KeyError:
return scopes.index('function')
else:
return fixturedefs[-1].scopenum
fixturenames_closure.sort(key=sort_by_scope)
return fixturenames_closure, arg2fixturedefs
def pytest_generate_tests(self, metafunc):
@@ -1050,6 +1072,8 @@ class FixtureManager(object):
fixturedef = faclist[-1]
if fixturedef.params is not None:
parametrize_func = getattr(metafunc.function, 'parametrize', None)
if parametrize_func is not None:
parametrize_func = parametrize_func.combined
func_params = getattr(parametrize_func, 'args', [[None]])
func_kwargs = getattr(parametrize_func, 'kwargs', {})
# skip directly parametrized arguments

View File

@@ -7,7 +7,7 @@ from __future__ import absolute_import, division, print_function
def freeze_includes():
"""
Returns a list of module names used by py.test that should be
Returns a list of module names used by pytest that should be
included by cx_freeze.
"""
import py

View File

@@ -138,7 +138,8 @@ def showhelp(config):
tw.line("to see available markers type: pytest --markers")
tw.line("to see available fixtures type: pytest --fixtures")
tw.line("(shown according to specified file_or_dir or current dir "
"if not specified)")
"if not specified; fixtures with leading '_' are only shown "
"with the '-v' option")
for warningreport in reporter.stats.get('warnings', []):
tw.line("warning : " + warningreport.message, red=True)

View File

@@ -413,14 +413,15 @@ def pytest_fixture_post_finalizer(fixturedef, request):
def pytest_sessionstart(session):
""" before session.main() is called.
""" called after the ``Session`` object has been created and before performing collection
and entering the run test loop.
:param _pytest.main.Session session: the pytest session object
"""
def pytest_sessionfinish(session, exitstatus):
""" whole test run finishes.
""" called after whole test run finished, right before returning the exit status to the system.
:param _pytest.main.Session session: the pytest session object
:param int exitstatus: the status which pytest will return to the system
@@ -490,7 +491,14 @@ def pytest_report_teststatus(report):
def pytest_terminal_summary(terminalreporter, exitstatus):
""" add additional section in terminal summary reporting. """
"""Add a section to terminal summary reporting.
:param _pytest.terminal.TerminalReporter terminalreporter: the internal terminal reporter object
:param int exitstatus: the exit status that will be reported back to the OS
.. versionadded:: 3.5
The ``config`` parameter.
"""
@hookspec(historic=True)

View File

@@ -130,10 +130,47 @@ class _NodeReporter(object):
self.append(node)
def write_captured_output(self, report):
for capname in ('out', 'err'):
content = getattr(report, 'capstd' + capname)
content_out = report.capstdout
content_log = report.caplog
content_err = report.capstderr
if content_log or content_out:
if content_log and self.xml.logging == 'system-out':
if content_out:
# syncing stdout and the log-output is not done yet. It's
# probably not worth the effort. Therefore, first the captured
# stdout is shown and then the captured logs.
content = '\n'.join([
' Captured Stdout '.center(80, '-'),
content_out,
'',
' Captured Log '.center(80, '-'),
content_log])
else:
content = content_log
else:
content = content_out
if content:
tag = getattr(Junit, 'system-' + capname)
tag = getattr(Junit, 'system-out')
self.append(tag(bin_xml_escape(content)))
if content_log or content_err:
if content_log and self.xml.logging == 'system-err':
if content_err:
content = '\n'.join([
' Captured Stderr '.center(80, '-'),
content_err,
'',
' Captured Log '.center(80, '-'),
content_log])
else:
content = content_log
else:
content = content_err
if content:
tag = getattr(Junit, 'system-err')
self.append(tag(bin_xml_escape(content)))
def append_pass(self, report):
@@ -196,31 +233,42 @@ class _NodeReporter(object):
@pytest.fixture
def record_xml_property(request):
"""Add extra xml properties to the tag for the calling test.
def record_property(request):
"""Add an extra properties the calling test.
User properties become part of the test report and are available to the
configured reporters, like JUnit XML.
The fixture is callable with ``(name, value)``, with value being automatically
xml-encoded.
"""
request.node.warn(
code='C3',
message='record_xml_property is an experimental feature',
)
xml = getattr(request.config, "_xml", None)
if xml is not None:
node_reporter = xml.node_reporter(request.node.nodeid)
return node_reporter.add_property
else:
def add_property_noop(name, value):
pass
return add_property_noop
Example::
def test_function(record_property):
record_property("example_key", 1)
"""
def append_property(name, value):
request.node.user_properties.append((name, value))
return append_property
@pytest.fixture
def record_xml_property(record_property):
"""(Deprecated) use record_property."""
import warnings
from _pytest import deprecated
warnings.warn(
deprecated.RECORD_XML_PROPERTY,
DeprecationWarning,
stacklevel=2
)
return record_property
@pytest.fixture
def record_xml_attribute(request):
"""Add extra xml attributes to the tag for the calling test.
The fixture is callable with ``(name, value)``, with value being automatically
xml-encoded
The fixture is callable with ``(name, value)``, with value being
automatically xml-encoded
"""
request.node.warn(
code='C3',
@@ -254,13 +302,18 @@ def pytest_addoption(parser):
default=None,
help="prepend prefix to classnames in junit-xml output")
parser.addini("junit_suite_name", "Test suite name for JUnit report", default="pytest")
parser.addini("junit_logging", "Write captured log messages to JUnit report: "
"one of no|system-out|system-err",
default="no") # choices=['no', 'stdout', 'stderr'])
def pytest_configure(config):
xmlpath = config.option.xmlpath
# prevent opening xmllog on slave nodes (xdist)
if xmlpath and not hasattr(config, 'slaveinput'):
config._xml = LogXML(xmlpath, config.option.junitprefix, config.getini("junit_suite_name"))
config._xml = LogXML(xmlpath, config.option.junitprefix,
config.getini("junit_suite_name"),
config.getini("junit_logging"))
config.pluginmanager.register(config._xml)
@@ -287,11 +340,12 @@ def mangle_test_address(address):
class LogXML(object):
def __init__(self, logfile, prefix, suite_name="pytest"):
def __init__(self, logfile, prefix, suite_name="pytest", logging="no"):
logfile = os.path.expanduser(os.path.expandvars(logfile))
self.logfile = os.path.normpath(os.path.abspath(logfile))
self.prefix = prefix
self.suite_name = suite_name
self.logging = logging
self.stats = dict.fromkeys([
'error',
'passed',
@@ -399,6 +453,10 @@ class LogXML(object):
if report.when == "teardown":
reporter = self._opentestcase(report)
reporter.write_captured_output(report)
for propname, propvalue in report.user_properties:
reporter.add_property(propname, propvalue)
self.finalize(report)
report_wid = getattr(report, "worker_id", None)
report_ii = getattr(report, "item_index", None)

View File

@@ -1,3 +1,4 @@
""" Access and control log capturing. """
from __future__ import absolute_import, division, print_function
import logging
@@ -152,7 +153,7 @@ def catching_logs(handler, formatter=None, level=None):
root_logger.addHandler(handler)
if level is not None:
orig_level = root_logger.level
root_logger.setLevel(level)
root_logger.setLevel(min(orig_level, level))
try:
yield handler
finally:
@@ -175,6 +176,10 @@ class LogCaptureHandler(logging.StreamHandler):
self.records.append(record)
logging.StreamHandler.emit(self, record)
def reset(self):
self.records = []
self.stream = py.io.TextIO()
class LogCaptureFixture(object):
"""Provides access and control of log capturing."""
@@ -196,6 +201,9 @@ class LogCaptureFixture(object):
@property
def handler(self):
"""
:rtype: LogCaptureHandler
"""
return self._item.catch_log_handler
def get_records(self, when):
@@ -238,8 +246,8 @@ class LogCaptureFixture(object):
return [(r.name, r.levelno, r.getMessage()) for r in self.records]
def clear(self):
"""Reset the list of log records."""
self.handler.records = []
"""Reset the list of log records and the captured log text."""
self.handler.reset()
def set_level(self, level, logger=None):
"""Sets the level for capturing of logs. The level will be restored to its previous value at the end of
@@ -281,9 +289,10 @@ def caplog(request):
Captured logs are available through the following methods::
* caplog.text() -> string containing formatted log output
* caplog.records() -> list of logging.LogRecord instances
* caplog.record_tuples() -> list of (logger_name, level, message) tuples
* caplog.text -> string containing formatted log output
* caplog.records -> list of logging.LogRecord instances
* caplog.record_tuples -> list of (logger_name, level, message) tuples
* caplog.clear() -> clear captured records and formatted log output string
"""
result = LogCaptureFixture(request.node)
yield result
@@ -338,7 +347,7 @@ class LoggingPlugin(object):
self._config = config
# enable verbose output automatically if live logging is enabled
if self._config.getini('log_cli') and not config.getoption('verbose'):
if self._log_cli_enabled() and not config.getoption('verbose'):
# sanity check: terminal reporter should not have been loaded at this point
assert self._config.pluginmanager.get_plugin('terminalreporter') is None
config.option.verbose = 1
@@ -364,6 +373,13 @@ class LoggingPlugin(object):
# initialized during pytest_runtestloop
self.log_cli_handler = None
def _log_cli_enabled(self):
"""Return True if log_cli should be considered enabled, either explicitly
or because --log-cli-level was given in the command-line.
"""
return self._config.getoption('--log-cli-level') is not None or \
self._config.getini('log_cli')
@contextmanager
def _runtest_for(self, item, when):
"""Implements the internals of pytest_runtest_xxx() hook."""
@@ -371,6 +387,11 @@ class LoggingPlugin(object):
formatter=self.formatter, level=self.log_level) as log_handler:
if self.log_cli_handler:
self.log_cli_handler.set_when(when)
if item is None:
yield # run the test
return
if not hasattr(item, 'catch_log_handlers'):
item.catch_log_handlers = {}
item.catch_log_handlers[when] = log_handler
@@ -402,9 +423,17 @@ class LoggingPlugin(object):
with self._runtest_for(item, 'teardown'):
yield
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_logstart(self):
if self.log_cli_handler:
self.log_cli_handler.reset()
with self._runtest_for(None, 'start'):
yield
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_logfinish(self):
with self._runtest_for(None, 'finish'):
yield
@pytest.hookimpl(hookwrapper=True)
def pytest_runtestloop(self, session):
@@ -425,7 +454,7 @@ class LoggingPlugin(object):
This must be done right before starting the loop so we can access the terminal reporter plugin.
"""
terminal_reporter = self._config.pluginmanager.get_plugin('terminalreporter')
if self._config.getini('log_cli') and terminal_reporter is not None:
if self._log_cli_enabled() and terminal_reporter is not None:
capture_manager = self._config.pluginmanager.get_plugin('capturemanager')
log_cli_handler = _LiveLoggingStreamHandler(terminal_reporter, capture_manager)
log_cli_format = get_option_ini(self._config, 'log_cli_format', 'log_format')
@@ -460,6 +489,7 @@ class _LiveLoggingStreamHandler(logging.StreamHandler):
self.capture_manager = capture_manager
self.reset()
self.set_when(None)
self._test_outcome_written = False
def reset(self):
"""Reset the handler; should be called before the start of each test"""
@@ -469,14 +499,20 @@ class _LiveLoggingStreamHandler(logging.StreamHandler):
"""Prepares for the given test phase (setup/call/teardown)"""
self._when = when
self._section_name_shown = False
if when == 'start':
self._test_outcome_written = False
def emit(self, record):
if self.capture_manager is not None:
self.capture_manager.suspend_global_capture()
try:
if not self._first_record_emitted or self._when == 'teardown':
if not self._first_record_emitted:
self.stream.write('\n')
self._first_record_emitted = True
elif self._when in ('teardown', 'finish'):
if not self._test_outcome_written:
self._test_outcome_written = True
self.stream.write('\n')
if not self._section_name_shown and self._when:
self.stream.section('live log ' + self._when, sep='-', bold=True)
self._section_name_shown = True

View File

@@ -53,6 +53,11 @@ def pytest_addoption(parser):
group._addoption("--continue-on-collection-errors", action="store_true",
default=False, dest="continue_on_collection_errors",
help="Force test execution even if collection errors occur.")
group._addoption("--rootdir", action="store",
dest="rootdir",
help="Define root directory for tests. Can be relative path: 'root_dir', './root_dir', "
"'root_dir/another_dir/'; absolute path: '/home/user/root_dir'; path with variables: "
"'$HOME/root_dir'.")
group = parser.getgroup("collect", "collection")
group.addoption('--collectonly', '--collect-only', action="store_true",
@@ -61,6 +66,8 @@ def pytest_addoption(parser):
help="try to interpret all arguments as python packages.")
group.addoption("--ignore", action="append", metavar="path",
help="ignore path during collection (multi-allowed).")
group.addoption("--deselect", action="append", metavar="nodeid_prefix",
help="deselect item during collection (multi-allowed).")
# when changing this to --conf-cut-dir, config.py Conftest.setinitial
# needs upgrading as well
group.addoption('--confcutdir', dest="confcutdir", default=None,
@@ -83,7 +90,7 @@ def pytest_addoption(parser):
def pytest_configure(config):
__import__('pytest').config = config # compatibiltiy
__import__('pytest').config = config # compatibility
def wrap_session(config, doit):
@@ -170,7 +177,7 @@ def _in_venv(path):
"""Attempts to detect if ``path`` is the root of a Virtual Environment by
checking for the existence of the appropriate activate script"""
bindir = path.join('Scripts' if sys.platform.startswith('win') else 'bin')
if not bindir.exists():
if not bindir.isdir():
return False
activates = ('activate', 'activate.csh', 'activate.fish',
'Activate', 'Activate.bat', 'Activate.ps1')
@@ -203,6 +210,24 @@ def pytest_ignore_collect(path, config):
return False
def pytest_collection_modifyitems(items, config):
deselect_prefixes = tuple(config.getoption("deselect") or [])
if not deselect_prefixes:
return
remaining = []
deselected = []
for colitem in items:
if colitem.nodeid.startswith(deselect_prefixes):
deselected.append(colitem)
else:
remaining.append(colitem)
if deselected:
config.hook.pytest_deselected(items=deselected)
items[:] = remaining
@contextlib.contextmanager
def _patched_find_module():
"""Patch bug in pkgutil.ImpImporter.find_module
@@ -265,7 +290,7 @@ class Interrupted(KeyboardInterrupt):
class Failed(Exception):
""" signals an stop as failed test run. """
""" signals a stop as failed test run. """
class Session(nodes.FSCollector):
@@ -275,7 +300,7 @@ class Session(nodes.FSCollector):
def __init__(self, config):
nodes.FSCollector.__init__(
self, config.rootdir, parent=None,
config=config, session=self)
config=config, session=self, nodeid="")
self.testsfailed = 0
self.testscollected = 0
self.shouldstop = False
@@ -283,10 +308,8 @@ class Session(nodes.FSCollector):
self.trace = config.trace.root.get("collection")
self._norecursepatterns = config.getini("norecursedirs")
self.startdir = py.path.local()
self.config.pluginmanager.register(self, name="session")
def _makeid(self):
return ""
self.config.pluginmanager.register(self, name="session")
@hookimpl(tryfirst=True)
def pytest_collectstart(self):
@@ -310,7 +333,7 @@ class Session(nodes.FSCollector):
def gethookproxy(self, fspath):
# check if we have the common case of running
# hooks with all conftest.py filesall conftest.py
# hooks with all conftest.py files
pm = self.config.pluginmanager
my_conftestmodules = pm._getconftestmodules(fspath)
remove_mods = pm._conftest_plugins.difference(my_conftestmodules)

157
_pytest/mark/__init__.py Normal file
View File

@@ -0,0 +1,157 @@
""" generic mechanism for marking and selecting python functions. """
from __future__ import absolute_import, division, print_function
from _pytest.config import UsageError
from .structures import (
ParameterSet, EMPTY_PARAMETERSET_OPTION, MARK_GEN,
Mark, MarkInfo, MarkDecorator, MarkGenerator,
transfer_markers, get_empty_parameterset_mark
)
from .legacy import matchkeyword, matchmark
__all__ = [
'Mark', 'MarkInfo', 'MarkDecorator', 'MarkGenerator',
'transfer_markers', 'get_empty_parameterset_mark'
]
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>`.
.. code-block:: python
@pytest.mark.parametrize("test_input,expected", [
("3+5", 8),
pytest.param("6*9", 42, marks=pytest.mark.xfail),
])
def test_eval(test_input, expected):
assert eval(test_input) == expected
:param values: variable args of the values of the parameter set, in order.
:keyword marks: a single mark or a list of marks to be applied to this parameter set.
:keyword str id: the id to attribute to this parameter set.
"""
return ParameterSet.param(*values, **kw)
def pytest_addoption(parser):
group = parser.getgroup("general")
group._addoption(
'-k',
action="store", dest="keyword", default='', metavar="EXPRESSION",
help="only run tests which match the given substring expression. "
"An expression is a python evaluatable expression "
"where all names are substring-matched against test names "
"and their parent classes. Example: -k 'test_method or test_"
"other' matches all test functions and classes whose name "
"contains 'test_method' or 'test_other', while -k 'not test_method' "
"matches those that don't contain 'test_method' in their names. "
"Additionally keywords are matched to classes and functions "
"containing extra names in their 'extra_keyword_matches' set, "
"as well as functions which have names assigned directly to them."
)
group._addoption(
"-m",
action="store", dest="markexpr", default="", metavar="MARKEXPR",
help="only run tests matching given mark expression. "
"example: -m 'mark1 and not mark2'."
)
group.addoption(
"--markers", action="store_true",
help="show markers (builtin, plugin and per-project ones)."
)
parser.addini("markers", "markers for test functions", 'linelist')
parser.addini(
EMPTY_PARAMETERSET_OPTION,
"default marker for empty parametersets")
def pytest_cmdline_main(config):
import _pytest.config
if config.option.markers:
config._do_configure()
tw = _pytest.config.create_terminal_writer(config)
for line in config.getini("markers"):
parts = line.split(":", 1)
name = parts[0]
rest = parts[1] if len(parts) == 2 else ''
tw.write("@pytest.mark.%s:" % name, bold=True)
tw.line(rest)
tw.line()
config._ensure_unconfigure()
return 0
pytest_cmdline_main.tryfirst = True
def deselect_by_keyword(items, config):
keywordexpr = config.option.keyword.lstrip()
if keywordexpr.startswith("-"):
keywordexpr = "not " + keywordexpr[1:]
selectuntil = False
if keywordexpr[-1:] == ":":
selectuntil = True
keywordexpr = keywordexpr[:-1]
remaining = []
deselected = []
for colitem in items:
if keywordexpr and not matchkeyword(colitem, keywordexpr):
deselected.append(colitem)
else:
if selectuntil:
keywordexpr = None
remaining.append(colitem)
if deselected:
config.hook.pytest_deselected(items=deselected)
items[:] = remaining
def deselect_by_mark(items, config):
matchexpr = config.option.markexpr
if not matchexpr:
return
remaining = []
deselected = []
for item in items:
if matchmark(item, matchexpr):
remaining.append(item)
else:
deselected.append(item)
if deselected:
config.hook.pytest_deselected(items=deselected)
items[:] = remaining
def pytest_collection_modifyitems(items, config):
deselect_by_keyword(items, config)
deselect_by_mark(items, config)
def pytest_configure(config):
config._old_mark_config = MARK_GEN._config
if config.option.strict:
MARK_GEN._config = config
empty_parameterset = config.getini(EMPTY_PARAMETERSET_OPTION)
if empty_parameterset not in ('skip', 'xfail', None, ''):
raise UsageError(
"{!s} must be one of skip and xfail,"
" but it is {!r}".format(EMPTY_PARAMETERSET_OPTION, empty_parameterset))
def pytest_unconfigure(config):
MARK_GEN._config = getattr(config, '_old_mark_config', None)

118
_pytest/mark/evaluate.py Normal file
View File

@@ -0,0 +1,118 @@
import os
import six
import sys
import platform
import traceback
from ..outcomes import fail, TEST_OUTCOME
def cached_eval(config, expr, d):
if not hasattr(config, '_evalcache'):
config._evalcache = {}
try:
return config._evalcache[expr]
except KeyError:
import _pytest._code
exprcode = _pytest._code.compile(expr, mode="eval")
config._evalcache[expr] = x = eval(exprcode, d)
return x
class MarkEvaluator(object):
def __init__(self, item, name):
self.item = item
self._marks = None
self._mark = None
self._mark_name = name
def __bool__(self):
# dont cache here to prevent staleness
return bool(self._get_marks())
__nonzero__ = __bool__
def wasvalid(self):
return not hasattr(self, 'exc')
def _get_marks(self):
return list(self.item.iter_markers(name=self._mark_name))
def invalidraise(self, exc):
raises = self.get('raises')
if not raises:
return
return not isinstance(exc, raises)
def istrue(self):
try:
return self._istrue()
except TEST_OUTCOME:
self.exc = sys.exc_info()
if isinstance(self.exc[1], SyntaxError):
msg = [" " * (self.exc[1].offset + 4) + "^", ]
msg.append("SyntaxError: invalid syntax")
else:
msg = traceback.format_exception_only(*self.exc[:2])
fail("Error evaluating %r expression\n"
" %s\n"
"%s"
% (self._mark_name, self.expr, "\n".join(msg)),
pytrace=False)
def _getglobals(self):
d = {'os': os, 'sys': sys, 'platform': platform, 'config': self.item.config}
if hasattr(self.item, 'obj'):
d.update(self.item.obj.__globals__)
return d
def _istrue(self):
if hasattr(self, 'result'):
return self.result
self._marks = self._get_marks()
if self._marks:
self.result = False
for mark in self._marks:
self._mark = mark
if 'condition' in mark.kwargs:
args = (mark.kwargs['condition'],)
else:
args = mark.args
for expr in args:
self.expr = expr
if isinstance(expr, six.string_types):
d = self._getglobals()
result = cached_eval(self.item.config, expr, d)
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."
fail(msg)
result = bool(expr)
if result:
self.result = True
self.reason = mark.kwargs.get('reason', None)
self.expr = expr
return self.result
if not args:
self.result = True
self.reason = mark.kwargs.get('reason', None)
return self.result
return False
def get(self, attr, default=None):
if self._mark is None:
return default
return self._mark.kwargs.get(attr, default)
def getexplanation(self):
expl = getattr(self, 'reason', None) or self.get('reason', None)
if not expl:
if not hasattr(self, 'expr'):
return ""
else:
return "condition: " + str(self.expr)
return expl

92
_pytest/mark/legacy.py Normal file
View File

@@ -0,0 +1,92 @@
"""
this is a place where we put datastructures used by legacy apis
we hope ot remove
"""
import attr
import keyword
from _pytest.config import UsageError
@attr.s
class MarkMapping(object):
"""Provides a local mapping for markers where item access
resolves to True if the marker is present. """
own_mark_names = attr.ib()
@classmethod
def from_item(cls, item):
mark_names = set(mark.name for mark in item.iter_markers())
return cls(mark_names)
def __getitem__(self, name):
return name in self.own_mark_names
class KeywordMapping(object):
"""Provides a local mapping for keywords.
Given a list of names, map any substring of one of these names to True.
"""
def __init__(self, names):
self._names = names
@classmethod
def from_item(cls, item):
mapped_names = set()
# Add the names of the current item and any parent items
import pytest
for item in item.listchain():
if not isinstance(item, pytest.Instance):
mapped_names.add(item.name)
# Add the names added as extra keywords to current or parent items
for name in item.listextrakeywords():
mapped_names.add(name)
# Add the names attached to the current function through direct assignment
if hasattr(item, 'function'):
for name in item.function.__dict__:
mapped_names.add(name)
return cls(mapped_names)
def __getitem__(self, subname):
for name in self._names:
if subname in name:
return True
return False
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))
def matchkeyword(colitem, keywordexpr):
"""Tries to match given keyword expression to given collector item.
Will match on the name of colitem, including the names of its parents.
Only matches names of items which are either a :class:`Class` or a
:class:`Function`.
Additionally, matches on names in the 'extra_keyword_matches' set of
any item, as well as names directly assigned to test functions.
"""
mapping = KeywordMapping.from_item(colitem)
if " " not in keywordexpr:
# special case to allow for simple "-k pass" and "-k 1.3"
return mapping[keywordexpr]
elif keywordexpr.startswith("not ") and " " not in keywordexpr[4:]:
return not mapping[keywordexpr[4:]]
for kwd in keywordexpr.split():
if keyword.iskeyword(kwd) and kwd not in python_keywords_allowed_list:
raise UsageError("Python keyword '{}' not accepted in expressions passed to '-k'".format(kwd))
try:
return eval(keywordexpr, {}, mapping)
except SyntaxError:
raise UsageError("Wrong expression passed to '-k': {}".format(keywordexpr))

View File

@@ -1,17 +1,14 @@
""" generic mechanism for marking and selecting python functions. """
from __future__ import absolute_import, division, print_function
import inspect
import keyword
import warnings
import attr
from collections import namedtuple
from operator import attrgetter
from six.moves import map
from _pytest.config import UsageError
from .deprecated import MARK_PARAMETERSET_UNPACKING
from .compat import NOTSET, getfslineno
import attr
from ..deprecated import MARK_PARAMETERSET_UNPACKING, MARK_INFO_ATTRIBUTE
from ..compat import NOTSET, getfslineno, MappingMixin
from six.moves import map, reduce
EMPTY_PARAMETERSET_OPTION = "empty_parameter_set_mark"
@@ -26,6 +23,25 @@ def alias(name, warning=None):
return property(getter if warning is None else warned, doc='alias for ' + name)
def istestfunc(func):
return hasattr(func, "__call__") and \
getattr(func, "__name__", "<lambda>") != "<lambda>"
def get_empty_parameterset_mark(config, argnames, func):
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)
else:
raise LookupError(requested_mark)
fs, lineno = getfslineno(func)
reason = "got empty parameter set %r, function %s at %s:%d" % (
argnames, func.__name__, fs, lineno)
return mark(reason=reason)
class ParameterSet(namedtuple('ParameterSet', 'values, marks, id')):
@classmethod
def param(cls, *values, **kw):
@@ -38,8 +54,8 @@ class ParameterSet(namedtuple('ParameterSet', 'values, marks, id')):
def param_extract_id(id=None):
return id
id = param_extract_id(**kw)
return cls(values, marks, id)
id_ = param_extract_id(**kw)
return cls(values, marks, id_)
@classmethod
def extract_from(cls, parameterset, legacy_force_tuple=False):
@@ -75,7 +91,7 @@ class ParameterSet(namedtuple('ParameterSet', 'values, marks, id')):
return cls(argval, marks=newmarks, id=None)
@classmethod
def _for_parametrize(cls, argnames, argvalues, function, config):
def _for_parametrize(cls, argnames, argvalues, func, config):
if not isinstance(argnames, (tuple, list)):
argnames = [x.strip() for x in argnames.split(",") if x.strip()]
force_tuple = len(argnames) == 1
@@ -87,7 +103,7 @@ class ParameterSet(namedtuple('ParameterSet', 'values, marks, id')):
del argvalues
if not parameters:
mark = get_empty_parameterset_mark(config, argnames, function)
mark = get_empty_parameterset_mark(config, argnames, func)
parameters.append(ParameterSet(
values=(NOTSET,) * len(argnames),
marks=[mark],
@@ -96,265 +112,23 @@ class ParameterSet(namedtuple('ParameterSet', 'values, marks, id')):
return argnames, parameters
def get_empty_parameterset_mark(config, argnames, function):
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)
else:
raise LookupError(requested_mark)
fs, lineno = getfslineno(function)
reason = "got empty parameter set %r, function %s at %s:%d" % (
argnames, function.__name__, fs, lineno)
return mark(reason=reason)
class MarkerError(Exception):
"""Error in use of a pytest marker/attribute."""
def param(*values, **kw):
return ParameterSet.param(*values, **kw)
def pytest_addoption(parser):
group = parser.getgroup("general")
group._addoption(
'-k',
action="store", dest="keyword", default='', metavar="EXPRESSION",
help="only run tests which match the given substring expression. "
"An expression is a python evaluatable expression "
"where all names are substring-matched against test names "
"and their parent classes. Example: -k 'test_method or test_"
"other' matches all test functions and classes whose name "
"contains 'test_method' or 'test_other', while -k 'not test_method' "
"matches those that don't contain 'test_method' in their names. "
"Additionally keywords are matched to classes and functions "
"containing extra names in their 'extra_keyword_matches' set, "
"as well as functions which have names assigned directly to them."
)
group._addoption(
"-m",
action="store", dest="markexpr", default="", metavar="MARKEXPR",
help="only run tests matching given mark expression. "
"example: -m 'mark1 and not mark2'."
)
group.addoption(
"--markers", action="store_true",
help="show markers (builtin, plugin and per-project ones)."
)
parser.addini("markers", "markers for test functions", 'linelist')
parser.addini(
EMPTY_PARAMETERSET_OPTION,
"default marker for empty parametersets")
def pytest_cmdline_main(config):
import _pytest.config
if config.option.markers:
config._do_configure()
tw = _pytest.config.create_terminal_writer(config)
for line in config.getini("markers"):
parts = line.split(":", 1)
name = parts[0]
rest = parts[1] if len(parts) == 2 else ''
tw.write("@pytest.mark.%s:" % name, bold=True)
tw.line(rest)
tw.line()
config._ensure_unconfigure()
return 0
pytest_cmdline_main.tryfirst = True
def pytest_collection_modifyitems(items, config):
keywordexpr = config.option.keyword.lstrip()
matchexpr = config.option.markexpr
if not keywordexpr and not matchexpr:
return
# pytest used to allow "-" for negating
# but today we just allow "-" at the beginning, use "not" instead
# we probably remove "-" altogether soon
if keywordexpr.startswith("-"):
keywordexpr = "not " + keywordexpr[1:]
selectuntil = False
if keywordexpr[-1:] == ":":
selectuntil = True
keywordexpr = keywordexpr[:-1]
remaining = []
deselected = []
for colitem in items:
if keywordexpr and not matchkeyword(colitem, keywordexpr):
deselected.append(colitem)
else:
if selectuntil:
keywordexpr = None
if matchexpr:
if not matchmark(colitem, matchexpr):
deselected.append(colitem)
continue
remaining.append(colitem)
if deselected:
config.hook.pytest_deselected(items=deselected)
items[:] = remaining
@attr.s
class MarkMapping(object):
"""Provides a local mapping for markers where item access
resolves to True if the marker is present. """
own_mark_names = attr.ib()
@classmethod
def from_keywords(cls, keywords):
mark_names = set()
for key, value in keywords.items():
if isinstance(value, MarkInfo) or isinstance(value, MarkDecorator):
mark_names.add(key)
return cls(mark_names)
def __getitem__(self, name):
return name in self.own_mark_names
class KeywordMapping(object):
"""Provides a local mapping for keywords.
Given a list of names, map any substring of one of these names to True.
"""
def __init__(self, names):
self._names = names
def __getitem__(self, subname):
for name in self._names:
if subname in name:
return True
return False
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_keywords(colitem.keywords))
def matchkeyword(colitem, keywordexpr):
"""Tries to match given keyword expression to given collector item.
Will match on the name of colitem, including the names of its parents.
Only matches names of items which are either a :class:`Class` or a
:class:`Function`.
Additionally, matches on names in the 'extra_keyword_matches' set of
any item, as well as names directly assigned to test functions.
"""
mapped_names = set()
# Add the names of the current item and any parent items
import pytest
for item in colitem.listchain():
if not isinstance(item, pytest.Instance):
mapped_names.add(item.name)
# Add the names added as extra keywords to current or parent items
for name in colitem.listextrakeywords():
mapped_names.add(name)
# Add the names attached to the current function through direct assignment
if hasattr(colitem, 'function'):
for name in colitem.function.__dict__:
mapped_names.add(name)
mapping = KeywordMapping(mapped_names)
if " " not in keywordexpr:
# special case to allow for simple "-k pass" and "-k 1.3"
return mapping[keywordexpr]
elif keywordexpr.startswith("not ") and " " not in keywordexpr[4:]:
return not mapping[keywordexpr[4:]]
for kwd in keywordexpr.split():
if keyword.iskeyword(kwd) and kwd not in python_keywords_allowed_list:
raise UsageError("Python keyword '{}' not accepted in expressions passed to '-k'".format(kwd))
try:
return eval(keywordexpr, {}, mapping)
except SyntaxError:
raise UsageError("Wrong expression passed to '-k': {}".format(keywordexpr))
def pytest_configure(config):
config._old_mark_config = MARK_GEN._config
if config.option.strict:
MARK_GEN._config = config
empty_parameterset = config.getini(EMPTY_PARAMETERSET_OPTION)
if empty_parameterset not in ('skip', 'xfail', None, ''):
raise UsageError(
"{!s} must be one of skip and xfail,"
" but it is {!r}".format(EMPTY_PARAMETERSET_OPTION, empty_parameterset))
def pytest_unconfigure(config):
MARK_GEN._config = getattr(config, '_old_mark_config', None)
class MarkGenerator(object):
""" Factory for :class:`MarkDecorator` objects - exposed as
a ``pytest.mark`` singleton instance. Example::
import pytest
@pytest.mark.slowtest
def test_function():
pass
will set a 'slowtest' :class:`MarkInfo` object
on the ``test_function`` object. """
_config = None
def __getattr__(self, name):
if name[0] == "_":
raise AttributeError("Marker name must NOT start with underscore")
if self._config is not None:
self._check(name)
return MarkDecorator(Mark(name, (), {}))
def _check(self, name):
try:
if name in self._markers:
return
except AttributeError:
pass
self._markers = values = set()
for line in self._config.getini("markers"):
marker = line.split(":", 1)[0]
marker = marker.rstrip()
x = marker.split("(", 1)[0]
values.add(x)
if name not in self._markers:
raise AttributeError("%r not a registered marker" % (name,))
def istestfunc(func):
return hasattr(func, "__call__") and \
getattr(func, "__name__", "<lambda>") != "<lambda>"
@attr.s(frozen=True)
class Mark(object):
name = attr.ib()
args = attr.ib()
kwargs = attr.ib()
#: name of the mark
name = attr.ib(type=str)
#: positional arguments of the mark decorator
args = attr.ib(type="List[object]")
#: keyword arguments of the mark decorator
kwargs = attr.ib(type="Dict[str, object]")
def combined_with(self, other):
"""
:param other: the mark to combine with
:type other: Mark
:rtype: Mark
combines by appending aargs and merging the mappings
"""
assert self.name == other.name
return Mark(
self.name, self.args + other.args,
@@ -441,7 +215,7 @@ class MarkDecorator(object):
def get_unpacked_marks(obj):
"""
obtain the unpacked marks that are stored on a object
obtain the unpacked marks that are stored on an object
"""
mark_list = getattr(obj, 'pytestmark', [])
@@ -454,7 +228,7 @@ def get_unpacked_marks(obj):
def store_mark(obj, mark):
"""store a Mark on a object
"""store a Mark on an object
this is used to implement the Mark declarations/decorators correctly
"""
assert isinstance(mark, Mark), mark
@@ -470,52 +244,12 @@ def store_legacy_markinfo(func, mark):
raise TypeError("got {mark!r} instead of a Mark".format(mark=mark))
holder = getattr(func, mark.name, None)
if holder is None:
holder = MarkInfo(mark)
holder = MarkInfo.for_mark(mark)
setattr(func, mark.name, holder)
else:
holder.add_mark(mark)
class MarkInfo(object):
""" Marking object created by :class:`MarkDecorator` instances. """
def __init__(self, mark):
assert isinstance(mark, Mark), repr(mark)
self.combined = mark
self._marks = [mark]
name = alias('combined.name')
args = alias('combined.args')
kwargs = alias('combined.kwargs')
def __repr__(self):
return "<MarkInfo {0!r}>".format(self.combined)
def add_mark(self, mark):
""" add a MarkInfo with the given args and kwargs. """
self._marks.append(mark)
self.combined = self.combined.combined_with(mark)
def __iter__(self):
""" yield MarkInfo objects each relating to a marking-call. """
return map(MarkInfo, self._marks)
MARK_GEN = MarkGenerator()
def _marked(func, mark):
""" Returns True if :func: is already marked with :mark:, False otherwise.
This can happen if marker is applied to class and the test file is
invoked more than once.
"""
try:
func_mark = getattr(func, mark.name)
except AttributeError:
return False
return mark.args == func_mark.args and mark.kwargs == func_mark.kwargs
def transfer_markers(funcobj, cls, mod):
"""
this function transfers class level markers and module level markers
@@ -529,3 +263,152 @@ def transfer_markers(funcobj, cls, mod):
for mark in get_unpacked_marks(obj):
if not _marked(funcobj, mark):
store_legacy_markinfo(funcobj, mark)
def _marked(func, mark):
""" Returns True if :func: is already marked with :mark:, False otherwise.
This can happen if marker is applied to class and the test file is
invoked more than once.
"""
try:
func_mark = getattr(func, getattr(mark, 'combined', mark).name)
except AttributeError:
return False
return any(mark == info.combined for info in func_mark)
@attr.s
class MarkInfo(object):
""" Marking object created by :class:`MarkDecorator` instances. """
_marks = attr.ib()
combined = attr.ib(
repr=False,
default=attr.Factory(lambda self: reduce(Mark.combined_with, self._marks),
takes_self=True))
name = alias('combined.name', warning=MARK_INFO_ATTRIBUTE)
args = alias('combined.args', warning=MARK_INFO_ATTRIBUTE)
kwargs = alias('combined.kwargs', warning=MARK_INFO_ATTRIBUTE)
@classmethod
def for_mark(cls, mark):
return cls([mark])
def __repr__(self):
return "<MarkInfo {0!r}>".format(self.combined)
def add_mark(self, mark):
""" add a MarkInfo with the given args and kwargs. """
self._marks.append(mark)
self.combined = self.combined.combined_with(mark)
def __iter__(self):
""" yield MarkInfo objects each relating to a marking-call. """
return map(MarkInfo.for_mark, self._marks)
class MarkGenerator(object):
""" Factory for :class:`MarkDecorator` objects - exposed as
a ``pytest.mark`` singleton instance. Example::
import pytest
@pytest.mark.slowtest
def test_function():
pass
will set a 'slowtest' :class:`MarkInfo` object
on the ``test_function`` object. """
_config = None
def __getattr__(self, name):
if name[0] == "_":
raise AttributeError("Marker name must NOT start with underscore")
if self._config is not None:
self._check(name)
return MarkDecorator(Mark(name, (), {}))
def _check(self, name):
try:
if name in self._markers:
return
except AttributeError:
pass
self._markers = values = set()
for line in self._config.getini("markers"):
marker = line.split(":", 1)[0]
marker = marker.rstrip()
x = marker.split("(", 1)[0]
values.add(x)
if name not in self._markers:
raise AttributeError("%r not a registered marker" % (name,))
MARK_GEN = MarkGenerator()
class NodeKeywords(MappingMixin):
def __init__(self, node):
self.node = node
self.parent = node.parent
self._markers = {node.name: True}
def __getitem__(self, key):
try:
return self._markers[key]
except KeyError:
if self.parent is None:
raise
return self.parent.keywords[key]
def __setitem__(self, key, value):
self._markers[key] = value
def __delitem__(self, key):
raise ValueError("cannot delete key in keywords dict")
def __iter__(self):
seen = self._seen()
return iter(seen)
def _seen(self):
seen = set(self._markers)
if self.parent is not None:
seen.update(self.parent.keywords)
return seen
def __len__(self):
return len(self._seen())
def __repr__(self):
return "<NodeKeywords for node %s>" % (self.node, )
@attr.s(cmp=False, hash=False)
class NodeMarkers(object):
"""
internal strucutre for storing marks belongong to a node
..warning::
unstable api
"""
own_markers = attr.ib(default=attr.Factory(list))
def update(self, add_markers):
"""update the own markers
"""
self.own_markers.extend(add_markers)
def find(self, name):
"""
find markers in own nodes or parent nodes
needs a better place
"""
for mark in self.own_markers:
if mark.name == name:
yield mark
def __iter__(self):
return iter(self.own_markers)

View File

@@ -4,6 +4,8 @@ from __future__ import absolute_import, division, print_function
import os
import sys
import re
from contextlib import contextmanager
import six
from _pytest.fixtures import fixture
@@ -106,6 +108,29 @@ class MonkeyPatch(object):
self._cwd = None
self._savesyspath = None
@contextmanager
def context(self):
"""
Context manager that returns a new :class:`MonkeyPatch` object which
undoes any patching done inside the ``with`` block upon exit:
.. code-block:: python
import functools
def test_partial(monkeypatch):
with monkeypatch.context() as m:
m.setattr(functools, "partial", 3)
Useful in situations where it is desired to undo some patches before the test ends,
such as mocking ``stdlib`` functions that might break pytest itself if mocked (for examples
of this see `#3290 <https://github.com/pytest-dev/pytest/issues/3290>`_.
"""
m = MonkeyPatch()
try:
yield m
finally:
m.undo()
def setattr(self, target, name, value=notset, raising=True):
""" Set attribute value on target, memorizing the old value.
By default raise AttributeError if the attribute did not exist.
@@ -113,7 +138,7 @@ class MonkeyPatch(object):
For convenience you can specify a string as ``target`` which
will be interpreted as a dotted import path, with the last part
being the attribute name. Example:
``monkeypatch.setattr("os.getcwd", lambda x: "/")``
``monkeypatch.setattr("os.getcwd", lambda: "/")``
would set the ``getcwd`` function of the ``os`` module.
The ``raising`` value determines if the setattr should fail

View File

@@ -1,5 +1,4 @@
from __future__ import absolute_import, division, print_function
from collections import MutableMapping as MappingMixin
import os
import six
@@ -7,7 +6,9 @@ import py
import attr
import _pytest
import _pytest._code
from _pytest.mark.structures import NodeKeywords, MarkInfo
SEP = "/"
@@ -66,47 +67,11 @@ class _CompatProperty(object):
return getattr(__import__('pytest'), self.name)
class NodeKeywords(MappingMixin):
def __init__(self, node):
self.node = node
self.parent = node.parent
self._markers = {node.name: True}
def __getitem__(self, key):
try:
return self._markers[key]
except KeyError:
if self.parent is None:
raise
return self.parent.keywords[key]
def __setitem__(self, key, value):
self._markers[key] = value
def __delitem__(self, key):
raise ValueError("cannot delete key in keywords dict")
def __iter__(self):
seen = set(self._markers)
if self.parent is not None:
seen.update(self.parent.keywords)
return iter(seen)
def __len__(self):
return len(self.__iter__())
def keys(self):
return list(self)
def __repr__(self):
return "<NodeKeywords for node %s>" % (self.node, )
class Node(object):
""" base class for Collector and Item the test collection tree.
Collector subclasses have children, Items are terminal nodes."""
def __init__(self, name, parent=None, config=None, session=None):
def __init__(self, name, parent=None, config=None, session=None, fspath=None, nodeid=None):
#: a unique name within the scope of the parent node
self.name = name
@@ -120,17 +85,26 @@ class Node(object):
self.session = session or parent.session
#: filesystem path where this node was collected from (can be None)
self.fspath = getattr(parent, 'fspath', None)
self.fspath = fspath or getattr(parent, 'fspath', None)
#: keywords/markers collected from all scopes
self.keywords = NodeKeywords(self)
#: the marker objects belonging to this node
self.own_markers = []
#: allow adding of extra keywords to use for matching
self.extra_keyword_matches = set()
# used for storing artificial fixturedefs for direct parametrization
self._name2pseudofixturedef = {}
if nodeid is not None:
self._nodeid = nodeid
else:
assert parent is not None
self._nodeid = self.parent.nodeid + "::" + self.name
@property
def ihook(self):
""" fspath sensitive hook proxy used to call pytest hooks"""
@@ -174,14 +148,7 @@ class Node(object):
@property
def nodeid(self):
""" a ::-separated string denoting its collection tree address. """
try:
return self._nodeid
except AttributeError:
self._nodeid = x = self._makeid()
return x
def _makeid(self):
return self.parent.nodeid + "::" + self.name
return self._nodeid
def __hash__(self):
return hash(self.nodeid)
@@ -214,20 +181,54 @@ class Node(object):
elif not isinstance(marker, MarkDecorator):
raise ValueError("is not a string or pytest.mark.* Marker")
self.keywords[marker.name] = marker
self.own_markers.append(marker)
def iter_markers(self, name=None):
"""
:param name: if given, filter the results by the name attribute
iterate over all markers of the node
"""
return (x[1] for x in self.iter_markers_with_node(name=name))
def iter_markers_with_node(self, name=None):
"""
:param name: if given, filter the results by the name attribute
iterate over all markers of the node
returns sequence of tuples (node, mark)
"""
for node in reversed(self.listchain()):
for mark in node.own_markers:
if name is None or getattr(mark, 'name', None) == name:
yield node, mark
def get_closest_marker(self, name, default=None):
"""return the first marker matching the name, from closest (for example function) to farther level (for example
module level).
:param default: fallback return value of no marker was found
:param name: name to filter by
"""
return next(self.iter_markers(name=name), default)
def get_marker(self, name):
""" get a marker object from this node or None if
the node doesn't have a marker with that name. """
val = self.keywords.get(name, None)
if val is not None:
from _pytest.mark import MarkInfo, MarkDecorator
if isinstance(val, (MarkDecorator, MarkInfo)):
return val
the node doesn't have a marker with that name.
.. deprecated:: 3.6
This function has been deprecated in favor of
:meth:`Node.get_closest_marker <_pytest.nodes.Node.get_closest_marker>` and
:meth:`Node.iter_markers <_pytest.nodes.Node.iter_markers>`, see :ref:`update marker code`
for more details.
"""
markers = list(self.iter_markers(name=name))
if markers:
return MarkInfo(markers)
def listextrakeywords(self):
""" Return a set of all extra keywords in self and any parents."""
extra_keywords = set()
item = self
for item in self.listchain():
extra_keywords.update(item.extra_keyword_matches)
return extra_keywords
@@ -319,8 +320,14 @@ class Collector(Node):
excinfo.traceback = ntraceback.filter()
def _check_initialpaths_for_relpath(session, fspath):
for initial_path in session._initialpaths:
if fspath.common(initial_path) == initial_path:
return fspath.relto(initial_path.dirname)
class FSCollector(Collector):
def __init__(self, fspath, parent=None, config=None, session=None):
def __init__(self, fspath, parent=None, config=None, session=None, nodeid=None):
fspath = py.path.local(fspath) # xxx only for test_resultlog.py?
name = fspath.basename
if parent is not None:
@@ -328,22 +335,19 @@ class FSCollector(Collector):
if rel:
name = rel
name = name.replace(os.sep, SEP)
super(FSCollector, self).__init__(name, parent, config, session)
self.fspath = fspath
def _check_initialpaths_for_relpath(self):
for initialpath in self.session._initialpaths:
if self.fspath.common(initialpath) == initialpath:
return self.fspath.relto(initialpath.dirname)
session = session or parent.session
def _makeid(self):
relpath = self.fspath.relto(self.config.rootdir)
if nodeid is None:
nodeid = self.fspath.relto(session.config.rootdir)
if not relpath:
relpath = self._check_initialpaths_for_relpath()
if os.sep != SEP:
relpath = relpath.replace(os.sep, SEP)
return relpath
if not nodeid:
nodeid = _check_initialpaths_for_relpath(session, fspath)
if os.sep != SEP:
nodeid = nodeid.replace(os.sep, SEP)
super(FSCollector, self).__init__(name, parent, config, session, nodeid=nodeid, fspath=fspath)
class File(FSCollector):
@@ -356,10 +360,14 @@ class Item(Node):
"""
nextitem = None
def __init__(self, name, parent=None, config=None, session=None):
super(Item, self).__init__(name, parent, config, session)
def __init__(self, name, parent=None, config=None, session=None, nodeid=None):
super(Item, self).__init__(name, parent, config, session, nodeid=nodeid)
self._report_sections = []
#: user properties is a list of tuples (name, value) that holds user
#: defined properties for this test.
self.user_properties = []
def add_report_section(self, when, key, content):
"""
Adds a new report section, similar to what's done internally to add stdout and

View File

@@ -83,7 +83,7 @@ skip.Exception = Skipped
def fail(msg="", pytrace=True):
""" explicitly fail an currently-executing test with the given Message.
""" explicitly fail a currently-executing test with the given Message.
:arg pytrace: if false the msg represents the full failure information
and no python traceback will be reported.

View File

@@ -714,7 +714,7 @@ class Testdir(object):
"""
finalizers = []
try:
# When running py.test inline any plugins active in the main test
# When running pytest inline any plugins active in the main test
# process are already imported. So this disables the warning which
# will trigger to say they can no longer be rewritten, which is
# fine as they have already been rewritten.
@@ -725,7 +725,7 @@ class Testdir(object):
finalizers.append(revert_warn_already_imported)
AssertionRewritingHook._warn_already_imported = lambda *a: None
# Any sys.module or sys.path changes done while running py.test
# Any sys.module or sys.path changes done while running pytest
# inline should be reverted after the test run completes to avoid
# clashing with later inline tests run within the same pytest test,
# e.g. just because they use matching test module names.

View File

@@ -25,10 +25,10 @@ from _pytest.compat import (
isclass, isfunction, is_generator, ascii_escaped,
REGEX_TYPE, STRING_TYPES, NoneType, NOTSET,
get_real_func, getfslineno, safe_getattr,
safe_str, getlocation, enum,
safe_str, getlocation, enum, get_default_arg_names
)
from _pytest.outcomes import fail
from _pytest.mark import transfer_markers
from _pytest.mark.structures import transfer_markers, get_unpacked_marks
# relative paths that we use to filter traceback entries from appearing to the user;
@@ -55,7 +55,7 @@ def filter_traceback(entry):
is_generated = '<' in raw_filename and '>' in raw_filename
if is_generated:
return False
# entry.path might point to an non-existing file, in which case it will
# 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)
@@ -75,7 +75,8 @@ def pytest_addoption(parser):
group = parser.getgroup("general")
group.addoption('--fixtures', '--funcargs',
action="store_true", dest="showfixtures", default=False,
help="show available fixtures, sorted by plugin appearance")
help="show available fixtures, sorted by plugin appearance "
"(fixtures with leading '_' are only shown with '-v')")
group.addoption(
'--fixtures-per-test',
action="store_true",
@@ -117,11 +118,7 @@ def pytest_generate_tests(metafunc):
if hasattr(metafunc.function, attr):
msg = "{0} has '{1}', spelling should be 'parametrize'"
raise MarkerError(msg.format(metafunc.function.__name__, attr))
try:
markers = metafunc.function.parametrize
except AttributeError:
return
for marker in markers:
for marker in metafunc.definition.iter_markers(name='parametrize'):
metafunc.parametrize(*marker.args, **marker.kwargs)
@@ -212,11 +209,20 @@ class PyobjContext(object):
class PyobjMixin(PyobjContext):
_ALLOW_MARKERS = True
def __init__(self, *k, **kw):
super(PyobjMixin, self).__init__(*k, **kw)
def obj():
def fget(self):
obj = getattr(self, '_obj', None)
if obj is None:
self._obj = obj = self._getobj()
# XXX evil hack
# used to avoid Instance collector marker duplication
if self._ALLOW_MARKERS:
self.own_markers.extend(get_unpacked_marks(self.obj))
return obj
def fset(self, value):
@@ -363,9 +369,15 @@ class PyCollector(PyobjMixin, nodes.Collector):
cls = clscol and clscol.obj or None
transfer_markers(funcobj, cls, module)
fm = self.session._fixturemanager
fixtureinfo = fm.getfixtureinfo(self, funcobj, cls)
metafunc = Metafunc(funcobj, fixtureinfo, self.config,
cls=cls, module=module)
definition = FunctionDefinition(
name=name,
parent=self,
callobj=funcobj,
)
fixtureinfo = fm.getfixtureinfo(definition, funcobj, cls)
metafunc = Metafunc(definition, fixtureinfo, self.config, cls=cls, module=module)
methods = []
if hasattr(module, "pytest_generate_tests"):
methods.append(module.pytest_generate_tests)
@@ -524,6 +536,11 @@ class Class(PyCollector):
class Instance(PyCollector):
_ALLOW_MARKERS = False # hack, destroy later
# instances share the object with their parents in a way
# that duplicates markers instances if not taken out
# can be removed at node strucutre reorganization time
def _getobj(self):
return self.parent.obj()
@@ -717,21 +734,23 @@ class CallSpec2(object):
class Metafunc(fixtures.FuncargnamesCompatAttr):
"""
Metafunc objects are passed to the ``pytest_generate_tests`` hook.
Metafunc objects are passed to the :func:`pytest_generate_tests <_pytest.hookspec.pytest_generate_tests>` hook.
They help to inspect a test function and to generate tests according to
test configuration or values specified in the class or module where a
test function is defined.
"""
def __init__(self, function, fixtureinfo, config, cls=None, module=None):
def __init__(self, definition, fixtureinfo, config, cls=None, module=None):
#: access to the :class:`_pytest.config.Config` object for the test session
assert isinstance(definition, FunctionDefinition) or type(definition).__name__ == "DefinitionMock"
self.definition = definition
self.config = config
#: the module object where the test function is defined in.
self.module = module
#: underlying python test function
self.function = function
self.function = definition.obj
#: set of fixture names required by the test function
self.fixturenames = fixtureinfo.names_closure
@@ -789,6 +808,7 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
argnames, parameters = ParameterSet._for_parametrize(
argnames, argvalues, self.function, self.config)
del argvalues
default_arg_names = set(get_default_arg_names(self.function))
if scope is None:
scope = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect)
@@ -797,13 +817,16 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
valtypes = {}
for arg in argnames:
if arg not in self.fixturenames:
if isinstance(indirect, (tuple, list)):
name = 'fixture' if arg in indirect else 'argument'
if arg in default_arg_names:
raise ValueError("%r already takes an argument %r with a default value" % (self.function, arg))
else:
name = 'fixture' if indirect else 'argument'
raise ValueError(
"%r uses no %s %r" % (
self.function, name, arg))
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))
if indirect is True:
valtypes = dict.fromkeys(argnames, "params")
@@ -1103,6 +1126,8 @@ class Function(FunctionMixin, nodes.Item, fixtures.FuncargnamesCompatAttr):
Python test function.
"""
_genid = None
# disable since functions handle it themselfes
_ALLOW_MARKERS = False
def __init__(self, name, parent, args=None, config=None,
callspec=None, callobj=NOTSET, keywords=None, session=None,
@@ -1114,6 +1139,7 @@ class Function(FunctionMixin, nodes.Item, fixtures.FuncargnamesCompatAttr):
self.obj = callobj
self.keywords.update(self.obj.__dict__)
self.own_markers.extend(get_unpacked_marks(self.obj))
if callspec:
self.callspec = callspec
# this is total hostile and a mess
@@ -1123,6 +1149,7 @@ class Function(FunctionMixin, nodes.Item, fixtures.FuncargnamesCompatAttr):
# feel free to cry, this was broken for years before
# and keywords cant fix it per design
self.keywords[mark.name] = mark
self.own_markers.extend(callspec.marks)
if keywords:
self.keywords.update(keywords)
@@ -1181,3 +1208,15 @@ class Function(FunctionMixin, nodes.Item, fixtures.FuncargnamesCompatAttr):
def setup(self):
super(Function, self).setup()
fixtures.fillfixtures(self)
class FunctionDefinition(Function):
"""
internal hack until we get actual definition nodes instead of the
crappy metafunc hack
"""
def runtest(self):
raise RuntimeError("function definitions are not supposed to be used")
setup = runtest

View File

@@ -2,7 +2,9 @@ import math
import sys
import py
from six.moves import zip
from six import binary_type, text_type
from six.moves import zip, filterfalse
from more_itertools.more import always_iterable
from _pytest.compat import isclass
from _pytest.outcomes import fail
@@ -30,6 +32,10 @@ class ApproxBase(object):
or sequences of numbers.
"""
# Tell numpy to use our `__eq__` operator instead of its
__array_ufunc__ = None
__array_priority__ = 100
def __init__(self, expected, rel=None, abs=None, nan_ok=False):
self.expected = expected
self.abs = abs
@@ -68,14 +74,13 @@ class ApproxNumpy(ApproxBase):
Perform approximate comparisons for numpy arrays.
"""
# Tell numpy to use our `__eq__` operator instead of its.
__array_priority__ = 100
def __repr__(self):
# It might be nice to rewrite this function to account for the
# shape of the array...
import numpy as np
return "approx({0!r})".format(list(
self._approx_scalar(x) for x in self.expected))
self._approx_scalar(x) for x in np.asarray(self.expected)))
if sys.version_info[0] == 2:
__cmp__ = _cmp_raises_type_error
@@ -83,12 +88,15 @@ class ApproxNumpy(ApproxBase):
def __eq__(self, actual):
import numpy as np
try:
actual = np.asarray(actual)
except: # noqa
raise TypeError("cannot compare '{0}' to numpy.ndarray".format(actual))
# self.expected is supposed to always be an array here
if actual.shape != self.expected.shape:
if not np.isscalar(actual):
try:
actual = np.asarray(actual)
except: # noqa
raise TypeError("cannot compare '{0}' to numpy.ndarray".format(actual))
if not np.isscalar(actual) and actual.shape != self.expected.shape:
return False
return ApproxBase.__eq__(self, actual)
@@ -96,11 +104,16 @@ class ApproxNumpy(ApproxBase):
def _yield_comparisons(self, actual):
import numpy as np
# We can be sure that `actual` is a numpy array, because it's
# casted in `__eq__` before being passed to `ApproxBase.__eq__`,
# which is the only method that calls this one.
for i in np.ndindex(self.expected.shape):
yield actual[i], self.expected[i]
# `actual` can either be a numpy array or a scalar, it is treated in
# `__eq__` before being passed to `ApproxBase.__eq__`, which is the
# only method that calls this one.
if np.isscalar(actual):
for i in np.ndindex(self.expected.shape):
yield actual, np.asscalar(self.expected[i])
else:
for i in np.ndindex(self.expected.shape):
yield np.asscalar(actual[i]), np.asscalar(self.expected[i])
class ApproxMapping(ApproxBase):
@@ -130,9 +143,6 @@ class ApproxSequence(ApproxBase):
Perform approximate comparisons for sequences of numbers.
"""
# Tell numpy to use our `__eq__` operator instead of its.
__array_priority__ = 100
def __repr__(self):
seq_type = type(self.expected)
if seq_type not in (tuple, list, set):
@@ -153,6 +163,8 @@ class ApproxScalar(ApproxBase):
"""
Perform approximate comparisons for single numbers only.
"""
DEFAULT_ABSOLUTE_TOLERANCE = 1e-12
DEFAULT_RELATIVE_TOLERANCE = 1e-6
def __repr__(self):
"""
@@ -186,6 +198,8 @@ class ApproxScalar(ApproxBase):
Return true if the given value is equal to the expected value within
the pre-specified tolerance.
"""
if _is_numpy_array(actual):
return ApproxNumpy(actual, self.abs, self.rel, self.nan_ok) == self.expected
# Short-circuit exact equality.
if actual == self.expected:
@@ -223,7 +237,7 @@ class ApproxScalar(ApproxBase):
# Figure out what the absolute tolerance should be. ``self.abs`` is
# either None or a value specified by the user.
absolute_tolerance = set_default(self.abs, 1e-12)
absolute_tolerance = set_default(self.abs, self.DEFAULT_ABSOLUTE_TOLERANCE)
if absolute_tolerance < 0:
raise ValueError("absolute tolerance can't be negative: {}".format(absolute_tolerance))
@@ -241,7 +255,7 @@ class ApproxScalar(ApproxBase):
# we've made sure the user didn't ask for an absolute tolerance only,
# because we don't want to raise errors about the relative tolerance if
# we aren't even going to use it.
relative_tolerance = set_default(self.rel, 1e-6) * abs(self.expected)
relative_tolerance = set_default(self.rel, self.DEFAULT_RELATIVE_TOLERANCE) * abs(self.expected)
if relative_tolerance < 0:
raise ValueError("relative tolerance can't be negative: {}".format(absolute_tolerance))
@@ -252,6 +266,13 @@ class ApproxScalar(ApproxBase):
return max(relative_tolerance, absolute_tolerance)
class ApproxDecimal(ApproxScalar):
from decimal import Decimal
DEFAULT_ABSOLUTE_TOLERANCE = Decimal('1e-12')
DEFAULT_RELATIVE_TOLERANCE = Decimal('1e-6')
def approx(expected, rel=None, abs=None, nan_ok=False):
"""
Assert that two numbers (or two sets of numbers) are equal to each other
@@ -298,12 +319,18 @@ def approx(expected, rel=None, abs=None, nan_ok=False):
>>> {'a': 0.1 + 0.2, 'b': 0.2 + 0.4} == approx({'a': 0.3, 'b': 0.6})
True
And ``numpy`` arrays::
``numpy`` arrays::
>>> import numpy as np # doctest: +SKIP
>>> np.array([0.1, 0.2]) + np.array([0.2, 0.4]) == approx(np.array([0.3, 0.6])) # doctest: +SKIP
True
And for a ``numpy`` array against a scalar::
>>> import numpy as np # doctest: +SKIP
>>> np.array([0.1, 0.2]) + np.array([0.2, 0.1]) == approx(0.3) # doctest: +SKIP
True
By default, ``approx`` considers numbers within a relative tolerance of
``1e-6`` (i.e. one part in a million) of its expected value to be equal.
This treatment would lead to surprising results if the expected value was
@@ -399,8 +426,9 @@ def approx(expected, rel=None, abs=None, nan_ok=False):
__ https://docs.python.org/3/reference/datamodel.html#object.__ge__
"""
from collections import Mapping, Sequence
from _pytest.compat import Mapping, Sequence
from _pytest.compat import STRING_TYPES as String
from decimal import Decimal
# Delegate the comparison to a class that knows how to deal with the type
# of the expected value (e.g. int, float, list, dict, numpy.array, etc).
@@ -422,6 +450,8 @@ def approx(expected, rel=None, abs=None, nan_ok=False):
cls = ApproxMapping
elif isinstance(expected, Sequence) and not isinstance(expected, String):
cls = ApproxSequence
elif isinstance(expected, Decimal):
cls = ApproxDecimal
else:
cls = ApproxScalar
@@ -535,8 +565,9 @@ def raises(expected_exception, *args, **kwargs):
The string will be evaluated using the same ``locals()`` and ``globals()``
at the moment of the ``raises`` call.
.. autoclass:: _pytest._code.ExceptionInfo
:members:
.. currentmodule:: _pytest._code
Consult the API of ``excinfo`` objects: :class:`ExceptionInfo`.
.. note::
Similar to caught exception objects in Python, explicitly clearing
@@ -554,14 +585,11 @@ def raises(expected_exception, *args, **kwargs):
"""
__tracebackhide__ = True
msg = ("exceptions must be old-style classes or"
" derived from BaseException, not %s")
if isinstance(expected_exception, tuple):
for exc in expected_exception:
if not isclass(exc):
raise TypeError(msg % type(exc))
elif not isclass(expected_exception):
raise TypeError(msg % type(expected_exception))
base_type = (type, text_type, binary_type)
for exc in filterfalse(isclass, always_iterable(expected_exception, base_type)):
msg = ("exceptions must be old-style classes or"
" derived from BaseException, not %s")
raise TypeError(msg % type(exc))
message = "DID NOT RAISE {0}".format(expected_exception)
match_expr = None
@@ -571,6 +599,10 @@ def raises(expected_exception, *args, **kwargs):
message = kwargs.pop("message")
if "match" in kwargs:
match_expr = kwargs.pop("match")
if kwargs:
msg = 'Unexpected keyword arguments passed to pytest.raises: '
msg += ', '.join(kwargs.keys())
raise TypeError(msg)
return RaisesContext(expected_exception, message, match_expr)
elif isinstance(args[0], str):
code, = args

View File

@@ -16,10 +16,7 @@ from _pytest.outcomes import fail
@yield_fixture
def recwarn():
"""Return a WarningsRecorder instance that provides these methods:
* ``pop(category=None)``: return last warning matching the category.
* ``clear()``: clear list of warnings
"""Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions.
See http://docs.python.org/library/warnings.html for information
on warning categories.
@@ -88,11 +85,11 @@ class _DeprecatedCallContext(object):
def warns(expected_warning, *args, **kwargs):
"""Assert that code raises a particular class of warning.
Specifically, the input @expected_warning can be a warning class or
tuple of warning classes, and the code must return that warning
(if a single class) or one of those warnings (if a tuple).
Specifically, the parameter ``expected_warning`` can be a warning class or
sequence of warning classes, and the inside the ``with`` block must issue a warning of that class or
classes.
This helper produces a list of ``warnings.WarningMessage`` objects,
This helper produces a list of :class:`warnings.WarningMessage` objects,
one for each warning raised.
This function can be used as a context manager, or any of the other ways

View File

@@ -105,6 +105,7 @@ def pytest_runtest_setup(item):
def pytest_runtest_call(item):
_update_current_test_var(item, 'call')
sys.last_type, sys.last_value, sys.last_traceback = (None, None, None)
try:
item.runtest()
except Exception:
@@ -114,7 +115,7 @@ def pytest_runtest_call(item):
sys.last_type = type
sys.last_value = value
sys.last_traceback = tb
del tb # Get rid of it in this namespace
del type, value, tb # Get rid of these in this frame
raise
@@ -175,7 +176,8 @@ def check_interactive_exception(call, report):
def call_runtest_hook(item, when, **kwds):
hookname = "pytest_runtest_" + when
ihook = getattr(item.ihook, hookname)
return CallInfo(lambda: ihook(item=item, **kwds), when=when)
return CallInfo(lambda: ihook(item=item, **kwds), when=when,
treat_keyboard_interrupt_as_exception=item.config.getvalue("usepdb"))
class CallInfo(object):
@@ -183,7 +185,7 @@ class CallInfo(object):
#: None or ExceptionInfo object.
excinfo = None
def __init__(self, func, when):
def __init__(self, func, when, treat_keyboard_interrupt_as_exception=False):
#: context of invocation: one of "setup", "call",
#: "teardown", "memocollect"
self.when = when
@@ -191,8 +193,11 @@ class CallInfo(object):
try:
self.result = func()
except KeyboardInterrupt:
self.stop = time()
raise
if treat_keyboard_interrupt_as_exception:
self.excinfo = ExceptionInfo()
else:
self.stop = time()
raise
except: # noqa
self.excinfo = ExceptionInfo()
self.stop = time()
@@ -256,6 +261,14 @@ class BaseReport(object):
exc = tw.stringio.getvalue()
return exc.strip()
@property
def caplog(self):
"""Return captured log lines, if log capturing is enabled
.. versionadded:: 3.5
"""
return '\n'.join(content for (prefix, content) in self.get_sections('Captured log'))
@property
def capstdout(self):
"""Return captured text from stdout, if capturing is enabled
@@ -309,7 +322,7 @@ def pytest_runtest_makereport(item, call):
sections.append(("Captured %s %s" % (key, rwhen), content))
return TestReport(item.nodeid, item.location,
keywords, outcome, longrepr, when,
sections, duration)
sections, duration, user_properties=item.user_properties)
class TestReport(BaseReport):
@@ -318,7 +331,7 @@ class TestReport(BaseReport):
"""
def __init__(self, nodeid, location, keywords, outcome,
longrepr, when, sections=(), duration=0, **extra):
longrepr, when, sections=(), duration=0, user_properties=(), **extra):
#: normalized collection node id
self.nodeid = nodeid
@@ -340,6 +353,10 @@ class TestReport(BaseReport):
#: one of 'setup', 'call', 'teardown' to indicate runtest phase.
self.when = when
#: user properties is a list of tuples (name, value) that holds user
#: defined properties of the test
self.user_properties = user_properties
#: list of pairs ``(str, str)`` of extra information which needs to
#: marshallable. Used by pytest to add captured text
#: from ``stdout`` and ``stderr``, but may be used by other plugins

View File

@@ -1,14 +1,9 @@
""" support for skip/xfail functions and markers. """
from __future__ import absolute_import, division, print_function
import os
import six
import sys
import traceback
from _pytest.config import hookimpl
from _pytest.mark import MarkInfo, MarkDecorator
from _pytest.outcomes import fail, skip, xfail, TEST_OUTCOME
from _pytest.mark.evaluate import MarkEvaluator
from _pytest.outcomes import fail, skip, xfail
def pytest_addoption(parser):
@@ -17,11 +12,11 @@ def pytest_addoption(parser):
action="store_true", dest="runxfail", default=False,
help="run tests even if they are marked xfail")
parser.addini("xfail_strict", "default for the strict parameter of xfail "
"markers when not given explicitly (default: "
"False)",
default=False,
type="bool")
parser.addini("xfail_strict",
"default for the strict parameter of xfail "
"markers when not given explicitly (default: False)",
default=False,
type="bool")
def pytest_configure(config):
@@ -60,125 +55,16 @@ def pytest_configure(config):
)
class MarkEvaluator(object):
def __init__(self, item, name):
self.item = item
self._marks = None
self._mark = None
self._mark_name = name
def __bool__(self):
self._marks = self._get_marks()
return bool(self._marks)
__nonzero__ = __bool__
def wasvalid(self):
return not hasattr(self, 'exc')
def _get_marks(self):
keyword = self.item.keywords.get(self._mark_name)
if isinstance(keyword, MarkDecorator):
return [keyword.mark]
elif isinstance(keyword, MarkInfo):
return [x.combined for x in keyword]
else:
return []
def invalidraise(self, exc):
raises = self.get('raises')
if not raises:
return
return not isinstance(exc, raises)
def istrue(self):
try:
return self._istrue()
except TEST_OUTCOME:
self.exc = sys.exc_info()
if isinstance(self.exc[1], SyntaxError):
msg = [" " * (self.exc[1].offset + 4) + "^", ]
msg.append("SyntaxError: invalid syntax")
else:
msg = traceback.format_exception_only(*self.exc[:2])
fail("Error evaluating %r expression\n"
" %s\n"
"%s"
% (self._mark_name, self.expr, "\n".join(msg)),
pytrace=False)
def _getglobals(self):
d = {'os': os, 'sys': sys, 'config': self.item.config}
if hasattr(self.item, 'obj'):
d.update(self.item.obj.__globals__)
return d
def _istrue(self):
if hasattr(self, 'result'):
return self.result
self._marks = self._get_marks()
if self._marks:
self.result = False
for mark in self._marks:
self._mark = mark
if 'condition' in mark.kwargs:
args = (mark.kwargs['condition'],)
else:
args = mark.args
for expr in args:
self.expr = expr
if isinstance(expr, six.string_types):
d = self._getglobals()
result = cached_eval(self.item.config, expr, d)
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."
fail(msg)
result = bool(expr)
if result:
self.result = True
self.reason = mark.kwargs.get('reason', None)
self.expr = expr
return self.result
if not args:
self.result = True
self.reason = mark.kwargs.get('reason', None)
return self.result
return False
def get(self, attr, default=None):
if self._mark is None:
return default
return self._mark.kwargs.get(attr, default)
def getexplanation(self):
expl = getattr(self, 'reason', None) or self.get('reason', None)
if not expl:
if not hasattr(self, 'expr'):
return ""
else:
return "condition: " + str(self.expr)
return expl
@hookimpl(tryfirst=True)
def pytest_runtest_setup(item):
# Check if skip or skipif are specified as pytest marks
item._skipped_by_mark = False
skipif_info = item.keywords.get('skipif')
if isinstance(skipif_info, (MarkInfo, MarkDecorator)):
eval_skipif = MarkEvaluator(item, 'skipif')
if eval_skipif.istrue():
item._skipped_by_mark = True
skip(eval_skipif.getexplanation())
eval_skipif = MarkEvaluator(item, 'skipif')
if eval_skipif.istrue():
item._skipped_by_mark = True
skip(eval_skipif.getexplanation())
skip_info = item.keywords.get('skip')
if isinstance(skip_info, (MarkInfo, MarkDecorator)):
for skip_info in item.iter_markers(name='skip'):
item._skipped_by_mark = True
if 'reason' in skip_info.kwargs:
skip(skip_info.kwargs['reason'])
@@ -239,7 +125,7 @@ def pytest_runtest_makereport(item, call):
rep.outcome = "passed"
rep.wasxfail = rep.longrepr
elif item.config.option.runxfail:
pass # don't interefere
pass # don't interefere
elif call.excinfo and call.excinfo.errisinstance(xfail.Exception):
rep.wasxfail = "reason: " + call.excinfo.value.msg
rep.outcome = "skipped"
@@ -269,6 +155,7 @@ def pytest_runtest_makereport(item, call):
filename, line = item.location[:2]
rep.longrepr = filename, line, reason
# called by terminalreporter progress reporting
@@ -279,6 +166,7 @@ def pytest_report_teststatus(report):
elif report.passed:
return "xpassed", "X", ("XPASS", {'yellow': True})
# called by the terminalreporter instance/plugin
@@ -294,18 +182,8 @@ def pytest_terminal_summary(terminalreporter):
lines = []
for char in tr.reportchars:
if char == "x":
show_xfailed(terminalreporter, lines)
elif char == "X":
show_xpassed(terminalreporter, lines)
elif char in "fF":
show_simple(terminalreporter, lines, 'failed', "FAIL %s")
elif char in "sS":
show_skipped(terminalreporter, lines)
elif char == "E":
show_simple(terminalreporter, lines, 'error', "ERROR %s")
elif char == 'p':
show_simple(terminalreporter, lines, 'passed', "PASSED %s")
action = REPORTCHAR_ACTIONS.get(char, lambda tr, lines: None)
action(terminalreporter, lines)
if lines:
tr._tw.sep("=", "short test summary info")
@@ -341,18 +219,6 @@ def show_xpassed(terminalreporter, lines):
lines.append("XPASS %s %s" % (pos, reason))
def cached_eval(config, expr, d):
if not hasattr(config, '_evalcache'):
config._evalcache = {}
try:
return config._evalcache[expr]
except KeyError:
import _pytest._code
exprcode = _pytest._code.compile(expr, mode="eval")
config._evalcache[expr] = x = eval(exprcode, d)
return x
def folded_skips(skipped):
d = {}
for event in skipped:
@@ -364,7 +230,7 @@ def folded_skips(skipped):
# TODO: revisit after marks scope would be fixed
when = getattr(event, 'when', None)
if when == 'setup' and 'skip' in keywords and 'pytestmark' not in keywords:
key = (key[0], None, key[2], )
key = (key[0], None, key[2])
d.setdefault(key, []).append(event)
values = []
for key, events in d.items():
@@ -395,3 +261,23 @@ def show_skipped(terminalreporter, lines):
lines.append(
"SKIP [%d] %s: %s" %
(num, fspath, reason))
def shower(stat, format):
def show_(terminalreporter, lines):
return show_simple(terminalreporter, lines, stat, format)
return show_
REPORTCHAR_ACTIONS = {
'x': show_xfailed,
'X': show_xpassed,
'f': shower('failed', "FAIL %s"),
'F': shower('failed', "FAIL %s"),
's': show_skipped,
'S': show_skipped,
'p': shower('passed', "PASSED %s"),
'E': shower('error', "ERROR %s")
}

View File

@@ -12,6 +12,7 @@ import time
import pluggy
import py
import six
from more_itertools import collapse
import pytest
from _pytest import nodes
@@ -19,12 +20,45 @@ from _pytest.main import EXIT_OK, EXIT_TESTSFAILED, EXIT_INTERRUPTED, \
EXIT_USAGEERROR, EXIT_NOTESTSCOLLECTED
import argparse
class MoreQuietAction(argparse.Action):
"""
a modified copy of the argparse count action which counts down and updates
the legacy quiet attribute at the same time
used to unify verbosity handling
"""
def __init__(self,
option_strings,
dest,
default=None,
required=False,
help=None):
super(MoreQuietAction, self).__init__(
option_strings=option_strings,
dest=dest,
nargs=0,
default=default,
required=required,
help=help)
def __call__(self, parser, namespace, values, option_string=None):
new_count = getattr(namespace, self.dest, 0) - 1
setattr(namespace, self.dest, new_count)
# todo Deprecate config.quiet
namespace.quiet = getattr(namespace, 'quiet', 0) + 1
def pytest_addoption(parser):
group = parser.getgroup("terminal reporting", "reporting", after="general")
group._addoption('-v', '--verbose', action="count",
dest="verbose", default=0, help="increase verbosity."),
group._addoption('-q', '--quiet', action="count",
dest="quiet", default=0, help="decrease verbosity."),
group._addoption('-v', '--verbose', action="count", default=0,
dest="verbose", help="increase verbosity."),
group._addoption('-q', '--quiet', action=MoreQuietAction, default=0,
dest="verbose", help="decrease verbosity."),
group._addoption("--verbosity", dest='verbose', type=int, default=0,
help="set verbosity")
group._addoption('-r',
action="store", dest="reportchars", default='', metavar="chars",
help="show extra test summary info as specified by chars (f)ailed, "
@@ -42,6 +76,11 @@ def pytest_addoption(parser):
action="store", dest="tbstyle", default='auto',
choices=['auto', 'long', 'short', 'no', 'line', 'native'],
help="traceback print mode (auto/long/short/line/native/no).")
group._addoption('--show-capture',
action="store", dest="showcapture",
choices=['no', 'stdout', 'stderr', 'log', 'all'], default='all',
help="Controls how captured stdout/stderr/log is shown on failed tests. "
"Default is 'all'.")
group._addoption('--fulltrace', '--full-trace',
action="store_true", default=False,
help="don't cut any tracebacks (default is to cut).")
@@ -56,7 +95,6 @@ def pytest_addoption(parser):
def pytest_configure(config):
config.option.verbose -= config.option.quiet
reporter = TerminalReporter(config, sys.stdout)
config.pluginmanager.register(reporter, 'terminalreporter')
if config.option.debug or config.option.traceconfig:
@@ -324,6 +362,8 @@ class TerminalReporter(object):
_PROGRESS_LENGTH = len(' [100%]')
def _get_progress_information_message(self):
if self.config.getoption('capture') == 'no':
return ''
collected = self._session.testscollected
if collected:
progress = len(self._progress_nodeids_reported) * 100 // collected
@@ -356,6 +396,7 @@ class TerminalReporter(object):
errors = len(self.stats.get('error', []))
skipped = len(self.stats.get('skipped', []))
deselected = len(self.stats.get('deselected', []))
if final:
line = "collected "
else:
@@ -363,6 +404,8 @@ class TerminalReporter(object):
line += str(self._numcollected) + " item" + ('' if self._numcollected == 1 else 's')
if errors:
line += " / %d errors" % errors
if deselected:
line += " / %d deselected" % deselected
if skipped:
line += " / %d skipped" % skipped
if self.isatty:
@@ -372,6 +415,7 @@ class TerminalReporter(object):
else:
self.write_line(line)
@pytest.hookimpl(trylast=True)
def pytest_collection_modifyitems(self):
self.report_collect(True)
@@ -399,7 +443,7 @@ class TerminalReporter(object):
def _write_report_lines_from_hooks(self, lines):
lines.reverse()
for line in flatten(lines):
for line in collapse(lines):
self.write_line(line)
def pytest_report_header(self, config):
@@ -472,16 +516,19 @@ class TerminalReporter(object):
if exitstatus in summary_exit_codes:
self.config.hook.pytest_terminal_summary(terminalreporter=self,
exitstatus=exitstatus)
self.summary_errors()
self.summary_failures()
self.summary_warnings()
self.summary_passes()
if exitstatus == EXIT_INTERRUPTED:
self._report_keyboardinterrupt()
del self._keyboardinterrupt_memo
self.summary_deselected()
self.summary_stats()
@pytest.hookimpl(hookwrapper=True)
def pytest_terminal_summary(self):
self.summary_errors()
self.summary_failures()
yield
self.summary_warnings()
self.summary_passes()
def pytest_keyboard_interrupt(self, excinfo):
self._keyboardinterrupt_memo = excinfo.getrepr(funcargs=True)
@@ -622,7 +669,12 @@ class TerminalReporter(object):
def _outrep_summary(self, rep):
rep.toterminal(self._tw)
showcapture = self.config.option.showcapture
if showcapture == 'no':
return
for secname, content in rep.sections:
if showcapture != 'all' and showcapture not in secname:
continue
self._tw.sep("-", secname)
if content[-1:] == "\n":
content = content[:-1]
@@ -639,11 +691,6 @@ class TerminalReporter(object):
if self.verbosity == -1:
self.write_line(msg, **markup)
def summary_deselected(self):
if 'deselected' in self.stats:
self.write_sep("=", "%d tests deselected" % (
len(self.stats['deselected'])), bold=True)
def repr_pythonversion(v=None):
if v is None:
@@ -654,15 +701,6 @@ def repr_pythonversion(v=None):
return str(v)
def flatten(values):
for x in values:
if isinstance(x, (list, tuple)):
for y in flatten(x):
yield y
else:
yield x
def build_summary_stats_line(stats):
keys = ("failed passed skipped deselected "
"xfailed xpassed warnings error").split()

View File

@@ -116,6 +116,8 @@ def tmpdir(request, tmpdir_factory):
created as a sub directory of the base temporary
directory. The returned object is a `py.path.local`_
path object.
.. _`py.path.local`: https://py.readthedocs.io/en/latest/path.html
"""
name = request.node.name
name = re.sub(r"[\W]", "_", name)

View File

@@ -60,8 +60,7 @@ def catch_warnings_for_item(item):
for arg in inifilters:
_setoption(warnings, arg)
mark = item.get_marker('filterwarnings')
if mark:
for mark in item.iter_markers(name='filterwarnings'):
for arg in mark.args:
warnings._setoption(arg)

View File

@@ -1 +0,0 @@
Move import of ``doctest.UnexpectedException`` to top-level to avoid possible errors when using ``--pdb``.

View File

@@ -1 +0,0 @@
Added printing of captured stdout/stderr before entering pdb, and improved a test which was giving false negatives about output capturing.

View File

@@ -1 +0,0 @@
pytest has changed the publication procedure and is now being published to PyPI directly from Travis.

View File

@@ -1 +0,0 @@
Fix ordering of tests using parametrized fixtures which can lead to fixtures being created more than necessary.

View File

@@ -1 +0,0 @@
Rename ``ParameterSet._for_parameterize()`` to ``_for_parametrize()`` in order to comply with the naming convention.

View File

@@ -1 +0,0 @@
Fix bug where logging happening at hooks outside of "test run" hooks would cause an internal error.

View File

@@ -1 +0,0 @@
Add Sphinx parameter docs for ``match`` and ``message`` args to ``pytest.raises``.

View File

@@ -1 +0,0 @@
Detect arguments injected by ``unittest.mock.patch`` decorator correctly when pypi ``mock.patch`` is installed and imported.

View File

@@ -1 +0,0 @@
Errors shown when a ``pytest.raises()`` with ``match=`` fails are now cleaner on what happened: When no exception was raised, the "matching '...'" part got removed as it falsely implies that an exception was raised but it didn't match. When a wrong exception was raised, it's now thrown (like ``pytest.raised()`` without ``match=`` would) instead of complaining about the unmatched text.

View File

@@ -1 +0,0 @@
Fixed output capture handling in doctests on macOS.

View File

@@ -1 +0,0 @@
Skip failing pdb/doctest test on mac.

View File

@@ -2,15 +2,16 @@
<ul>
<li><a href="{{ pathto('index') }}">Home</a></li>
<li><a href="{{ pathto('contents') }}">Contents</a></li>
<li><a href="{{ pathto('getting-started') }}">Install</a></li>
<li><a href="{{ pathto('contents') }}">Contents</a></li>
<li><a href="{{ pathto('reference') }}">Reference</a></li>
<li><a href="{{ pathto('example/index') }}">Examples</a></li>
<li><a href="{{ pathto('customize') }}">Customize</a></li>
<li><a href="{{ pathto('contact') }}">Contact</a></li>
<li><a href="{{ pathto('talks') }}">Talks/Posts</a></li>
<li><a href="{{ pathto('changelog') }}">Changelog</a></li>
<li><a href="{{ pathto('contributing') }}">Contributing</a></li>
<li><a href="{{ pathto('backwards-compatibility') }}">Backwards Compatibility</a></li>
<li><a href="{{ pathto('license') }}">License</a></li>
<li><a href="{{ pathto('contact') }}">Contact Channels</a></li>
</ul>
{%- if display_toc %}

View File

@@ -1,8 +1,6 @@
<h3>Useful Links</h3>
<ul>
<li><a href="{{ pathto('index') }}">The pytest Website</a></li>
<li><a href="{{ pathto('contributing') }}">Contribution Guide</a></li>
<li><a href="https://pypi.python.org/pypi/pytest">pytest @ PyPI</a></li>
<li><a href="https://pypi.org/project/pytest/">pytest @ PyPI</a></li>
<li><a href="https://github.com/pytest-dev/pytest/">pytest @ GitHub</a></li>
<li><a href="http://plugincompat.herokuapp.com/">3rd party plugins</a></li>
<li><a href="https://github.com/pytest-dev/pytest/issues">Issue Tracker</a></li>

View File

@@ -6,6 +6,10 @@ Release announcements
:maxdepth: 2
release-3.6.0
release-3.5.1
release-3.5.0
release-3.4.2
release-3.4.1
release-3.4.0
release-3.3.2

View File

@@ -18,7 +18,7 @@ comes with the following fixes and features:
rather use the post-2.0 parametrize features instead of yield, see:
http://pytest.org/latest/example/parametrize.html
- fix autouse-issue where autouse-fixtures would not be discovered
if defined in a a/conftest.py file and tests in a/tests/test_some.py
if defined in an a/conftest.py file and tests in a/tests/test_some.py
- fix issue226 - LIFO ordering for fixture teardowns
- fix issue224 - invocations with >256 char arguments now work
- fix issue91 - add/discuss package/directory level setups in example

View File

@@ -14,7 +14,7 @@ few interesting new plugins saw the light last month:
And several others like pytest-django saw maintenance releases.
For a more complete list, check out
https://pypi.python.org/pypi?%3Aaction=search&term=pytest&submit=search.
https://pypi.org/search/?q=pytest
For general information see:

View File

@@ -23,14 +23,14 @@ a full list of details. A few feature highlights:
called if the corresponding setup method succeeded.
- integrate tab-completion on command line options if you
have `argcomplete <http://pypi.python.org/pypi/argcomplete>`_
have `argcomplete <https://pypi.org/project/argcomplete/>`_
configured.
- allow boolean expression directly with skipif/xfail
if a "reason" is also specified.
- a new hook ``pytest_load_initial_conftests`` allows plugins like
`pytest-django <http://pypi.python.org/pypi/pytest-django>`_ to
`pytest-django <https://pypi.org/project/pytest-django/>`_ to
influence the environment before conftest files import ``django``.
- reporting: color the last line red or green depending if

View File

@@ -52,7 +52,7 @@ Changes 2.6.1
"::" node id specifications (copy pasted from "-v" output)
- fix issue544 by only removing "@NUM" at the end of "::" separated parts
and if the part has an ".py" extension
and if the part has a ".py" extension
- don't use py.std import helper, rather import things directly.
Thanks Bruno Oliveira.

View File

@@ -0,0 +1,28 @@
pytest-3.4.2
=======================================
pytest 3.4.2 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at http://doc.pytest.org/en/latest/changelog.html.
Thanks to all who contributed to this release, among them:
* Allan Feldman
* Bruno Oliveira
* Florian Bruhin
* Jason R. Coombs
* Kyle Altendorf
* Maik Figura
* Ronny Pfannschmidt
* codetriage-readme-bot
* feuillemorte
* joshm91
* mike
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,51 @@
pytest-3.5.0
=======================================
The pytest team is proud to announce the 3.5.0 release!
pytest is a mature Python testing tool with more than a 1600 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:
http://doc.pytest.org/en/latest/changelog.html
For complete documentation, please visit:
http://docs.pytest.org
As usual, you can upgrade from pypi via:
pip install -U pytest
Thanks to all who contributed to this release, among them:
* Allan Feldman
* Brian Maissy
* Bruno Oliveira
* Carlos Jenkins
* Daniel Hahler
* Florian Bruhin
* Jason R. Coombs
* Jeffrey Rackauckas
* Jordan Speicher
* Julien Palard
* Kale Kundert
* Kostis Anagnostopoulos
* Kyle Altendorf
* Maik Figura
* Pedro Algarvio
* Ronny Pfannschmidt
* Tadeu Manoel
* Tareq Alayan
* Thomas Hisch
* William Lee
* codetriage-readme-bot
* feuillemorte
* joshm91
* mike
Happy testing,
The Pytest Development Team

View File

@@ -0,0 +1,30 @@
pytest-3.5.1
=======================================
pytest 3.5.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 http://doc.pytest.org/en/latest/changelog.html.
Thanks to all who contributed to this release, among them:
* Brian Maissy
* Bruno Oliveira
* Darren Burns
* David Chudzicki
* Floris Bruynooghe
* Holger Kohr
* Irmen de Jong
* Jeffrey Rackauckas
* Rachel Kogan
* Ronny Pfannschmidt
* Stefan Scherfke
* Tim Strazny
* Семён Марьясин
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,41 @@
pytest-3.6.0
=======================================
The pytest team is proud to announce the 3.6.0 release!
pytest is a mature Python testing tool with more than a 1600 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:
http://doc.pytest.org/en/latest/changelog.html
For complete documentation, please visit:
http://docs.pytest.org
As usual, you can upgrade from pypi via:
pip install -U pytest
Thanks to all who contributed to this release, among them:
* Anthony Shaw
* ApaDoctor
* Brian Maissy
* Bruno Oliveira
* Jon Dufresne
* Katerina Koukiou
* Miro Hrončok
* Rachel Kogan
* Ronny Pfannschmidt
* Tim Hughes
* Tyler Goodlet
* Ville Skyttä
* aviral1701
* feuillemorte
Happy testing,
The Pytest Development Team

View File

@@ -1,81 +1,18 @@
:orphan:
.. _`pytest helpers`:
Pytest API and builtin fixtures
================================================
This is a list of ``pytest.*`` API functions and fixtures.
Most of the information of this page has been moved over to :ref:`reference`.
For information on plugin hooks and objects, see :ref:`plugins`.
For information on the ``pytest.mark`` mechanism, see :ref:`mark`.
For the below objects, you can also interactively ask for help, e.g. by
typing on the Python interactive prompt something like::
import pytest
help(pytest)
.. currentmodule:: pytest
Invoking pytest interactively
---------------------------------------------------
.. autofunction:: main
More examples at :ref:`pytest.main-usage`
Helpers for assertions about Exceptions/Warnings
--------------------------------------------------------
.. autofunction:: raises
Examples at :ref:`assertraises`.
.. autofunction:: deprecated_call
Comparing floating point numbers
--------------------------------
.. autofunction:: approx
Raising a specific test outcome
--------------------------------------
You can use the following functions in your test, fixture or setup
functions to force a certain test outcome. Note that most often
you can rather use declarative marks, see :ref:`skipping`.
.. autofunction:: _pytest.outcomes.fail
.. autofunction:: _pytest.outcomes.skip
.. autofunction:: _pytest.outcomes.importorskip
.. autofunction:: _pytest.outcomes.xfail
.. autofunction:: _pytest.outcomes.exit
Fixtures and requests
-----------------------------------------------------
To mark a fixture function:
.. autofunction:: _pytest.fixtures.fixture
Tutorial at :ref:`fixtures`.
The ``request`` object that can be used from fixture functions.
.. autoclass:: _pytest.fixtures.FixtureRequest()
:members:
.. _builtinfixtures:
.. _builtinfuncargs:
Builtin fixtures/function arguments
-----------------------------------------
You can ask for available builtin or project-custom
:ref:`fixtures <fixtures>` by typing::
For information about fixtures, see :ref:`fixtures`. To see a complete list of available fixtures (add ``-v`` to also see fixtures with leading ``_``), type ::
$ pytest -q --fixtures
cache
@@ -89,17 +26,17 @@ You can ask for available builtin or project-custom
Values can be any object handled by the json stdlib module.
capsys
Enable capturing of writes to sys.stdout/sys.stderr and make
Enable capturing of writes to ``sys.stdout`` and ``sys.stderr`` and make
captured output available via ``capsys.readouterr()`` method calls
which return a ``(out, err)`` tuple. ``out`` and ``err`` will be ``text``
which return a ``(out, err)`` namedtuple. ``out`` and ``err`` will be ``text``
objects.
capsysbinary
Enable capturing of writes to sys.stdout/sys.stderr and make
Enable capturing of writes to ``sys.stdout`` and ``sys.stderr`` and make
captured output available via ``capsys.readouterr()`` method calls
which return a ``(out, err)`` tuple. ``out`` and ``err`` will be ``bytes``
objects.
capfd
Enable capturing of writes to file descriptors 1 and 2 and make
Enable capturing of writes to file descriptors ``1`` and ``2`` and make
captured output available via ``capfd.readouterr()`` method calls
which return a ``(out, err)`` tuple. ``out`` and ``err`` will be ``text``
objects.
@@ -109,25 +46,41 @@ You can ask for available builtin or project-custom
which return a ``(out, err)`` tuple. ``out`` and ``err`` will be
``bytes`` objects.
doctest_namespace
Inject names into the doctest namespace.
Fixture that returns a :py:class:`dict` that will be injected into the namespace of doctests.
pytestconfig
the pytest config object with access to command line opts.
record_xml_property
Add extra xml properties to the tag for the calling test.
Session-scoped fixture that returns the :class:`_pytest.config.Config` object.
Example::
def test_foo(pytestconfig):
if pytestconfig.getoption("verbose"):
...
record_property
Add an extra properties the calling test.
User properties become part of the test report and are available to the
configured reporters, like JUnit XML.
The fixture is callable with ``(name, value)``, with value being automatically
xml-encoded.
Example::
def test_function(record_property):
record_property("example_key", 1)
record_xml_property
(Deprecated) use record_property.
record_xml_attribute
Add extra xml attributes to the tag for the calling test.
The fixture is callable with ``(name, value)``, with value being automatically
xml-encoded
The fixture is callable with ``(name, value)``, with value being
automatically xml-encoded
caplog
Access and control log capturing.
Captured logs are available through the following methods::
* caplog.text() -> string containing formatted log output
* caplog.records() -> list of logging.LogRecord instances
* caplog.record_tuples() -> list of (logger_name, level, message) tuples
* caplog.text -> string containing formatted log output
* caplog.records -> list of logging.LogRecord instances
* caplog.record_tuples -> list of (logger_name, level, message) tuples
* caplog.clear() -> clear captured records and formatted log output string
monkeypatch
The returned ``monkeypatch`` fixture provides these
helper methods to modify objects, dictionaries or os.environ::
@@ -146,10 +99,7 @@ You can ask for available builtin or project-custom
parameter determines if a KeyError or AttributeError
will be raised if the set/deletion operation has no target.
recwarn
Return a WarningsRecorder instance that provides these methods:
* ``pop(category=None)``: return last warning matching the category.
* ``clear()``: clear list of warnings
Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions.
See http://docs.python.org/library/warnings.html for information
on warning categories.
@@ -161,5 +111,13 @@ You can ask for available builtin or project-custom
created as a sub directory of the base temporary
directory. The returned object is a `py.path.local`_
path object.
.. _`py.path.local`: https://py.readthedocs.io/en/latest/path.html
no tests ran in 0.12 seconds
You can also interactively ask for help, e.g. by typing on the Python interactive prompt something like::
import pytest
help(pytest)

View File

@@ -78,7 +78,7 @@ If you then run it with ``--lf``::
=========================== 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 50 items
collected 50 items / 48 deselected
run-last-failure: rerun previous 2 failures
test_50.py FF [100%]
@@ -106,7 +106,6 @@ If you then run it with ``--lf``::
E Failed: bad luck
test_50.py:6: Failed
=========================== 48 tests deselected ============================
================= 2 failed, 48 deselected in 0.12 seconds ==================
You have run only the two failing test from the last run, while 48 tests have
@@ -152,6 +151,20 @@ of ``FF`` and dots)::
.. _`config.cache`:
New ``--nf``, ``--new-first`` options: run new tests first followed by the rest
of the tests, in both cases tests are also sorted by the file modified time,
with more recent files coming first.
Behavior when no tests failed in the last run
---------------------------------------------
When no tests failed in the last run, or when no cached ``lastfailed`` data was
found, ``pytest`` can be configured either to run all of the tests or no tests,
using the ``--last-failed-no-failures`` option, which takes one of the following values::
pytest --last-failed-no-failures all # run all tests (default behavior)
pytest --last-failed-no-failures none # run no tests and exit
The new config.cache object
--------------------------------
@@ -212,7 +225,7 @@ the cache and this will be quick::
test_caching.py:14: AssertionError
1 failed in 0.12 seconds
See the `cache-api`_ for more details.
See the :ref:`cache-api` for more details.
Inspecting Cache content
@@ -221,7 +234,7 @@ Inspecting Cache content
You can always peek at the content of the cache using the
``--cache-show`` command line option::
$ py.test --cache-show
$ pytest --cache-show
=========================== 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:
@@ -229,6 +242,8 @@ You can always peek at the content of the cache using the
------------------------------- cache values -------------------------------
cache/lastfailed contains:
{'test_caching.py::test_function': True}
cache/nodeids contains:
['test_caching.py::test_function']
example/value contains:
42
@@ -247,22 +262,3 @@ servers where isolation and correctness is more important
than speed.
.. _`cache-api`:
config.cache API
------------------
The ``config.cache`` object allows other plugins,
including ``conftest.py`` files,
to safely and flexibly store and retrieve values across
test runs because the ``config`` object is available
in many places.
Under the hood, the cache plugin uses the simple
dumps/loads API of the json stdlib module
.. currentmodule:: _pytest.cacheprovider
.. automethod:: Cache.get
.. automethod:: Cache.set
.. automethod:: Cache.makedir

View File

@@ -9,7 +9,8 @@ Default stdout/stderr/stdin capturing behaviour
During test execution any output sent to ``stdout`` and ``stderr`` is
captured. If a test or a setup method fails its according captured
output will usually be shown along with the failure traceback.
output will usually be shown along with the failure traceback. (this
behavior can be configured by the ``--show-capture`` command-line option).
In addition, ``stdin`` is set to a "null" object which will
fail on attempts to read from it because it is rarely desired

View File

@@ -18,8 +18,12 @@
# The full version, including alpha/beta/rc tags.
# The short X.Y version.
import os, sys
import os
import sys
import datetime
from _pytest import __version__ as version
release = ".".join(version.split(".")[:2])
# If extensions (or modules to document with autodoc) are in another directory,
@@ -38,7 +42,7 @@ todo_include_todos = 1
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.autosummary',
'sphinx.ext.intersphinx', 'sphinx.ext.viewcode']
'sphinx.ext.intersphinx', 'sphinx.ext.viewcode', 'sphinxcontrib_trio']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -54,7 +58,8 @@ master_doc = 'contents'
# General information about the project.
project = u'pytest'
copyright = u'2015, holger krekel and pytest-dev team'
year = datetime.datetime.utcnow().year
copyright = u'2015{} , holger krekel and pytest-dev team'.format(year)
@@ -310,9 +315,7 @@ texinfo_documents = [
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'python': ('http://docs.python.org/', None),
# 'lib': ("http://docs.python.org/2.7library/", None),
}
intersphinx_mapping = {'python': ('http://docs.python.org/3', None)}
def setup(app):

View File

@@ -14,14 +14,13 @@ Full pytest documentation
usage
existingtestsuite
assert
builtin
fixture
mark
monkeypatch
tmpdir
capture
warnings
doctest
mark
skipping
parametrize
cache
@@ -31,6 +30,7 @@ Full pytest documentation
plugins
writing_plugins
logging
reference
goodpractices
pythonpath

View File

@@ -38,6 +38,10 @@ Here's a summary what ``pytest`` uses ``rootdir`` for:
Important to emphasize that ``rootdir`` is **NOT** used to modify ``sys.path``/``PYTHONPATH`` or
influence how modules are imported. See :ref:`pythonpath` for more details.
``--rootdir=path`` command-line option can be used to force a specific directory.
The directory passed may contain environment variables when it is used in conjunction
with ``addopts`` in a ``pytest.ini`` file.
Finding the ``rootdir``
~~~~~~~~~~~~~~~~~~~~~~~
@@ -152,222 +156,4 @@ above will show verbose output because ``-v`` overwrites ``-q``.
Builtin configuration file options
----------------------------------------------
Here is a list of builtin configuration options that may be written in a ``pytest.ini``, ``tox.ini`` or ``setup.cfg``
file, usually located at the root of your repository. All options must be under a ``[pytest]`` section
(``[tool:pytest]`` for ``setup.cfg`` files).
Configuration file options may be overwritten in the command-line by using ``-o/--override``, which can also be
passed multiple times. The expected format is ``name=value``. For example::
pytest -o console_output_style=classic -o cache_dir=/tmp/mycache
.. confval:: minversion
Specifies a minimal pytest version required for running tests.
.. code-block:: ini
# content of pytest.ini
[pytest]
minversion = 3.0 # will fail if we run with pytest-2.8
.. confval:: addopts
Add the specified ``OPTS`` to the set of command line arguments as if they
had been specified by the user. Example: if you have this ini file content:
.. code-block:: ini
# content of pytest.ini
[pytest]
addopts = --maxfail=2 -rf # exit after 2 failures, report fail info
issuing ``pytest test_hello.py`` actually means::
pytest --maxfail=2 -rf test_hello.py
Default is to add no options.
.. confval:: norecursedirs
Set the directory basename patterns to avoid when recursing
for test discovery. The individual (fnmatch-style) patterns are
applied to the basename of a directory to decide if to recurse into it.
Pattern matching characters::
* matches everything
? matches any single character
[seq] matches any character in seq
[!seq] matches any char not in seq
Default patterns are ``'.*', 'build', 'dist', 'CVS', '_darcs', '{arch}', '*.egg', 'venv'``.
Setting a ``norecursedirs`` replaces the default. Here is an example of
how to avoid certain directories:
.. code-block:: ini
# content of pytest.ini
[pytest]
norecursedirs = .svn _build tmp*
This would tell ``pytest`` to not look into typical subversion or
sphinx-build directories or into any ``tmp`` prefixed directory.
Additionally, ``pytest`` will attempt to intelligently identify and ignore a
virtualenv by the presence of an activation script. Any directory deemed to
be the root of a virtual environment will not be considered during test
collection unless ``collectinvirtualenv`` is given. Note also that
``norecursedirs`` takes precedence over ``collectinvirtualenv``; e.g. if
you intend to run tests in a virtualenv with a base directory that matches
``'.*'`` you *must* override ``norecursedirs`` in addition to using the
``collectinvirtualenv`` flag.
.. confval:: testpaths
.. versionadded:: 2.8
Sets list of directories that should be searched for tests when
no specific directories, files or test ids are given in the command line when
executing pytest from the :ref:`rootdir <rootdir>` directory.
Useful when all project tests are in a known location to speed up
test collection and to avoid picking up undesired tests by accident.
.. code-block:: ini
# content of pytest.ini
[pytest]
testpaths = testing doc
This tells pytest to only look for tests in ``testing`` and ``doc``
directories when executing from the root directory.
.. 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.
.. 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
class prefixed with ``Test`` as a test collection. Here is an example of how
to collect tests from classes that end in ``Suite``:
.. code-block:: ini
# content of pytest.ini
[pytest]
python_classes = *Suite
Note that ``unittest.TestCase`` derived classes are always collected
regardless of this option, as ``unittest``'s own collection framework is used
to collect those tests.
.. 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
function prefixed with ``test`` as a test. Here is an example of how
to collect test functions and methods that end in ``_test``:
.. code-block:: ini
# content of pytest.ini
[pytest]
python_functions = *_test
Note that this has no effect on methods that live on a ``unittest
.TestCase`` derived class, as ``unittest``'s own collection framework is used
to collect those tests.
See :ref:`change naming conventions` for more detailed examples.
.. confval:: doctest_optionflags
One or more doctest flag names from the standard ``doctest`` module.
:doc:`See how pytest handles doctests <doctest>`.
.. confval:: confcutdir
Sets a directory where search upwards for ``conftest.py`` files stops.
By default, pytest will stop searching for ``conftest.py`` files upwards
from ``pytest.ini``/``tox.ini``/``setup.cfg`` of the project if any,
or up to the file-system root.
.. confval:: filterwarnings
.. versionadded:: 3.1
Sets a list of filters and actions that should be taken for matched
warnings. By default all warnings emitted during the test session
will be displayed in a summary at the end of the test session.
.. code-block:: ini
# content of pytest.ini
[pytest]
filterwarnings =
error
ignore::DeprecationWarning
This tells pytest to ignore deprecation warnings and turn all other warnings
into errors. For more information please refer to :ref:`warnings`.
.. confval:: cache_dir
.. versionadded:: 3.2
Sets a directory where stores content of cache plugin. Default directory is
``.cache`` which is created in :ref:`rootdir <rootdir>`. Directory may be
relative or absolute path. If setting relative path, then directory is created
relative to :ref:`rootdir <rootdir>`. Additionally path may contain environment
variables, that will be expanded. For more information about cache plugin
please refer to :ref:`cache_provider`.
.. confval:: console_output_style
.. versionadded:: 3.3
Sets the console output style while running tests:
* ``classic``: classic pytest output.
* ``progress``: like classic pytest output, but with a progress indicator.
The default is ``progress``, but you can fallback to ``classic`` if you prefer or
the new mode is causing unexpected problems:
.. code-block:: ini
# content of pytest.ini
[pytest]
console_output_style = classic
.. confval:: empty_parameter_set_mark
.. versionadded:: 3.4
Allows to pick the action for empty parametersets in parameterization
* ``skip`` skips tests with a empty parameterset (default)
* ``xfail`` marks tests with a empty parameterset as xfail(run=False)
.. code-block:: ini
# content of pytest.ini
[pytest]
empty_parameter_set_mark = xfail
.. note::
The default value of this option is planned to change to ``xfail`` in future releases
as this is considered less error prone, see `#3155`_ for more details.
.. _`#3155`: https://github.com/pytest-dev/pytest/issues/3155
For the full list of options consult the :ref:`reference documentation <ini options ref>`.

View File

@@ -2,7 +2,7 @@
Development Guide
=================
Some general guidelines regarding development in pytest for core maintainers and general contributors. Nothing here
Some general guidelines regarding development in pytest for maintainers and contributors. Nothing here
is set in stone and can't be changed, feel free to suggest improvements or changes in the workflow.
@@ -10,7 +10,7 @@ Code Style
----------
* `PEP-8 <https://www.python.org/dev/peps/pep-0008>`_
* `flake8 <https://pypi.python.org/pypi/flake8>`_ for quality checks
* `flake8 <https://pypi.org/project/flake8/>`_ for quality checks
* `invoke <http://www.pyinvoke.org/>`_ to automate development tasks
@@ -37,72 +37,19 @@ Any question, feature, bug or proposal is welcome as an issue. Users are encoura
GitHub issues should use labels to categorize them. Labels should be created sporadically, to fill a niche; we should
avoid creating labels just for the sake of creating them.
Here is a list of labels and a brief description mentioning their intent.
Each label should include a description in the GitHub's interface stating its purpose.
Temporary labels
~~~~~~~~~~~~~~~~
**Type**
To classify issues for a special event it is encouraged to create a temporary label. This helps those involved to find
the relevant issues to work on. Examples of that are sprints in Python events or global hacking events.
* ``type: backward compatibility``: issue that will cause problems with old pytest versions.
* ``type: bug``: problem that needs to be addressed.
* ``type: deprecation``: feature that will be deprecated in the future.
* ``type: docs``: documentation missing or needing clarification.
* ``type: enhancement``: new feature or API change, should be merged into ``features``.
* ``type: feature-branch``: new feature or API change, should be merged into ``features``.
* ``type: infrastructure``: improvement to development/releases/CI structure.
* ``type: performance``: performance or memory problem/improvement.
* ``type: proposal``: proposal for a new feature, often to gather opinions or design the API around the new feature.
* ``type: question``: question regarding usage, installation, internals or how to test something.
* ``type: refactoring``: internal improvements to the code.
* ``type: regression``: indicates a problem that was introduced in a release which was working previously.
* ``temporary: EP2017 sprint``: candidate issues or PRs tackled during the EuroPython 2017
**Status**
Issues created at those events should have other relevant labels added as well.
* ``status: critical``: grave problem or usability issue that affects lots of users.
* ``status: easy``: easy issue that is friendly to new contributors.
* ``status: help wanted``: core developers need help from experts on this topic.
* ``status: needs information``: reporter needs to provide more information; can be closed after 2 or more weeks of inactivity.
**Topic**
* ``topic: collection``
* ``topic: fixtures``
* ``topic: parametrize``
* ``topic: reporting``
* ``topic: selection``
* ``topic: tracebacks``
**Plugin (internal or external)**
* ``plugin: cache``
* ``plugin: capture``
* ``plugin: doctests``
* ``plugin: junitxml``
* ``plugin: monkeypatch``
* ``plugin: nose``
* ``plugin: pastebin``
* ``plugin: pytester``
* ``plugin: tmpdir``
* ``plugin: unittest``
* ``plugin: warnings``
* ``plugin: xdist``
**OS**
Issues specific to a single operating system. Do not use as a means to indicate where an issue originated from, only
for problems that happen **only** in that system.
* ``os: linux``
* ``os: mac``
* ``os: windows``
**Temporary**
Used to classify issues for limited time, to help find issues related in events for example.
They should be removed after they are no longer relevant.
* ``temporary: EP2017 sprint``:
* ``temporary: sprint-candidate``:
Those labels should be removed after they are no longer relevant.
.. include:: ../../HOWTORELEASE.rst

View File

@@ -115,6 +115,13 @@ itself::
>>> get_unicode_greeting() # doctest: +ALLOW_UNICODE
'Hello'
By default, pytest would report only the first failure for a given doctest. If
you want to continue the test even when you have failures, do::
pytest --doctest-modules --doctest-continue-on-failure
.. _`doctest_namespace`:
The 'doctest_namespace' fixture
-------------------------------

View File

@@ -34,11 +34,10 @@ You can then restrict a test run to only run tests marked with ``webtest``::
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.5
cachedir: .pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 4 items
collecting ... collected 4 items / 3 deselected
test_server.py::test_send_http PASSED [100%]
============================ 3 tests deselected ============================
================== 1 passed, 3 deselected in 0.12 seconds ==================
Or the inverse, running all tests except the webtest ones::
@@ -48,13 +47,12 @@ Or the inverse, running all tests except the webtest ones::
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.5
cachedir: .pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 4 items
collecting ... collected 4 items / 1 deselected
test_server.py::test_something_quick PASSED [ 33%]
test_server.py::test_another PASSED [ 66%]
test_server.py::TestClass::test_method PASSED [100%]
============================ 1 tests deselected ============================
================== 3 passed, 1 deselected in 0.12 seconds ==================
Selecting tests based on their node ID
@@ -133,11 +131,10 @@ select tests based on their names::
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.5
cachedir: .pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 4 items
collecting ... collected 4 items / 3 deselected
test_server.py::test_send_http PASSED [100%]
============================ 3 tests deselected ============================
================== 1 passed, 3 deselected in 0.12 seconds ==================
And you can also run all tests except the ones that match the keyword::
@@ -147,13 +144,12 @@ And you can also run all tests except the ones that match the keyword::
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.5
cachedir: .pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 4 items
collecting ... collected 4 items / 1 deselected
test_server.py::test_something_quick PASSED [ 33%]
test_server.py::test_another PASSED [ 66%]
test_server.py::TestClass::test_method PASSED [100%]
============================ 1 tests deselected ============================
================== 3 passed, 1 deselected in 0.12 seconds ==================
Or to select "http" and "quick" tests::
@@ -163,12 +159,11 @@ Or to select "http" and "quick" tests::
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.5
cachedir: .pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 4 items
collecting ... collected 4 items / 2 deselected
test_server.py::test_send_http PASSED [ 50%]
test_server.py::test_something_quick PASSED [100%]
============================ 2 tests deselected ============================
================== 2 passed, 2 deselected in 0.12 seconds ==================
.. note::
@@ -335,11 +330,10 @@ specifies via named environments::
"env(name): mark test to run only on named environment")
def pytest_runtest_setup(item):
envmarker = item.get_marker("env")
if envmarker is not None:
envname = envmarker.args[0]
if envname != item.config.getoption("-E"):
pytest.skip("test requires env %r" % envname)
envnames = [mark.args[0] for mark in item.iter_markers(name='env')]
if envnames:
if item.config.getoption("-E") not in envnames:
pytest.skip("test requires env in %r" % envnames)
A test file using this local plugin::
@@ -408,11 +402,9 @@ Below is the config file that will be used in the next examples::
import sys
def pytest_runtest_setup(item):
marker = item.get_marker('my_marker')
if marker is not None:
for info in marker:
print('Marker info name={} args={} kwars={}'.format(info.name, info.args, info.kwargs))
sys.stdout.flush()
for marker in item.iter_markers(name='my_marker'):
print(marker)
sys.stdout.flush()
A custom marker can have its argument set, i.e. ``args`` and ``kwargs`` properties, defined by either invoking it as a callable or using ``pytest.mark.MARKER_NAME.with_args``. These two methods achieve the same effect most of the time.
@@ -431,7 +423,7 @@ However, if there is a callable as the single positional argument with no keywor
The output is as follows::
$ pytest -q -s
Marker info name=my_marker args=(<function hello_world at 0xdeadbeef>,) kwars={}
Mark(name='my_marker', args=(<function hello_world at 0xdeadbeef>,), kwargs={})
.
1 passed in 0.12 seconds
@@ -465,11 +457,9 @@ test function. From a conftest file we can read it like this::
import sys
def pytest_runtest_setup(item):
g = item.get_marker("glob")
if g is not None:
for info in g:
print ("glob args=%s kwargs=%s" %(info.args, info.kwargs))
sys.stdout.flush()
for mark in item.iter_markers(name='glob'):
print ("glob args=%s kwargs=%s" %(mark.args, mark.kwargs))
sys.stdout.flush()
Let's run this without capturing output and see what we get::
@@ -499,11 +489,10 @@ for your particular platform, you could use the following plugin::
ALL = set("darwin linux win32".split())
def pytest_runtest_setup(item):
if isinstance(item, item.Function):
plat = sys.platform
if not item.get_marker(plat):
if ALL.intersection(item.keywords):
pytest.skip("cannot run on platform %s" %(plat))
supported_platforms = ALL.intersection(mark.name for mark in item.iter_markers())
plat = sys.platform
if supported_platforms and plat not in supported_platforms:
pytest.skip("cannot run on platform %s" % (plat))
then tests will be skipped if they were specified for a different platform.
Let's do a little test file to show how this looks like::
@@ -537,7 +526,7 @@ then you will see two tests skipped and two executed tests as expected::
test_plat.py s.s. [100%]
========================= short test summary info ==========================
SKIP [2] $REGENDOC_TMPDIR/conftest.py:13: cannot run on platform linux
SKIP [2] $REGENDOC_TMPDIR/conftest.py:12: cannot run on platform linux
=================== 2 passed, 2 skipped in 0.12 seconds ====================
@@ -547,11 +536,10 @@ Note that if you specify a platform via the marker-command line option like this
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
rootdir: $REGENDOC_TMPDIR, inifile:
collected 4 items
collected 4 items / 3 deselected
test_plat.py . [100%]
============================ 3 tests deselected ============================
================== 1 passed, 3 deselected in 0.12 seconds ==================
then the unmarked-tests will not be run. It is thus a way to restrict the run to the specific tests.
@@ -599,7 +587,7 @@ We can now use the ``-m option`` to select one set::
=========================== 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 4 items
collected 4 items / 2 deselected
test_module.py FF [100%]
@@ -612,7 +600,6 @@ We can now use the ``-m option`` to select one set::
test_module.py:6: in test_interface_complex
assert 0
E assert 0
============================ 2 tests deselected ============================
================== 2 failed, 2 deselected in 0.12 seconds ==================
or to select both "event" and "interface" tests::
@@ -621,7 +608,7 @@ or to select both "event" and "interface" tests::
=========================== 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 4 items
collected 4 items / 1 deselected
test_module.py FFF [100%]
@@ -638,5 +625,4 @@ or to select both "event" and "interface" tests::
test_module.py:9: in test_event_simple
assert 0
E assert 0
============================ 1 tests deselected ============================
================== 3 failed, 1 deselected in 0.12 seconds ==================

View File

@@ -10,7 +10,7 @@ A basic example for specifying tests in Yaml files
--------------------------------------------------------------
.. _`pytest-yamlwsgi`: http://bitbucket.org/aafshar/pytest-yamlwsgi/src/tip/pytest_yamlwsgi.py
.. _`PyYAML`: http://pypi.python.org/pypi/PyYAML/
.. _`PyYAML`: https://pypi.org/project/PyYAML/
Here is an example ``conftest.py`` (extracted from Ali Afshnars special purpose `pytest-yamlwsgi`_ plugin). This ``conftest.py`` will collect ``test*.yml`` files and will execute the yaml-formatted content as custom tests:

View File

@@ -160,7 +160,7 @@ together with the actual data, instead of listing them separately.
A quick port of "testscenarios"
------------------------------------
.. _`test scenarios`: http://pypi.python.org/pypi/testscenarios/
.. _`test scenarios`: https://pypi.org/project/testscenarios/
Here is a quick port to run tests configured with `test scenarios`_,
an add-on from Robert Collins for the standard unittest framework. We
@@ -469,7 +469,7 @@ If you run this with reporting for skips enabled::
=================== 1 passed, 1 skipped in 0.12 seconds ====================
You'll see that we don't have a ``opt2`` module and thus the second test run
You'll see that we don't have an ``opt2`` module and thus the second test run
of our ``test_func1`` was skipped. A few notes:
- the fixture functions in the ``conftest.py`` file are "session-scoped" because we

View File

@@ -39,6 +39,14 @@ you will see that ``pytest`` only collects test-modules, which do not match the
======= 5 passed in 0.02 seconds =======
Deselect tests during test collection
-------------------------------------
Tests can individually be deselected during collection by passing the ``--deselect=item`` option.
For example, say ``tests/foobar/test_foobar_01.py`` contains ``test_a`` and ``test_b``.
You can run all of the tests within ``tests/`` *except* for ``tests/foobar/test_foobar_01.py::test_a``
by invoking ``pytest`` with ``--deselect tests/foobar/test_foobar_01.py::test_a``.
``pytest`` allows multiple ``--deselect`` options.
Keeping duplicate paths specified from command line
----------------------------------------------------
@@ -46,7 +54,7 @@ Keeping duplicate paths specified from command line
Default behavior of ``pytest`` is to ignore duplicate paths specified from the command line.
Example::
py.test path_a path_a
pytest path_a path_a
...
collected 1 item
@@ -57,7 +65,7 @@ Just collect tests once.
To collect duplicate tests, use the ``--keep-duplicates`` option on the cli.
Example::
py.test --keep-duplicates path_a path_a
pytest --keep-duplicates path_a path_a
...
collected 2 items
@@ -67,7 +75,7 @@ As the collector just works on directories, if you specify twice a single test f
still collect it twice, no matter if the ``--keep-duplicates`` is not specified.
Example::
py.test test_a.py test_a.py
pytest test_a.py test_a.py
...
collected 2 items

View File

@@ -358,7 +358,7 @@ get on the terminal - we are working on that)::
> int(s)
E ValueError: invalid literal for int() with base 10: 'qwe'
<0-codegen $PYTHON_PREFIX/lib/python3.5/site-packages/_pytest/python_api.py:583>:1: ValueError
<0-codegen $PYTHON_PREFIX/lib/python3.5/site-packages/_pytest/python_api.py:615>:1: ValueError
______________________ TestRaises.test_raises_doesnt _______________________
self = <failure_demo.TestRaises object at 0xdeadbeef>

View File

@@ -3,6 +3,8 @@
Basic patterns and examples
==========================================================
.. _request example:
Pass different values to a test function, depending on command line options
----------------------------------------------------------------------------
@@ -100,13 +102,13 @@ the command line arguments before they get processed:
# content of conftest.py
import sys
def pytest_cmdline_preparse(args):
def pytest_load_initial_conftests(args):
if 'xdist' in sys.modules: # pytest-xdist plugin
import multiprocessing
num = max(multiprocessing.cpu_count() / 2, 1)
args[:] = ["-n", str(num)] + args
If you have the `xdist plugin <https://pypi.python.org/pypi/pytest-xdist>`_ installed
If you have the `xdist plugin <https://pypi.org/project/pytest-xdist/>`_ installed
you will now always perform test runs using a number
of subprocesses close to your CPU. Running in an empty
directory with the above conftest.py::
@@ -387,7 +389,7 @@ Now we can profile which test functions execute the slowest::
========================= slowest 3 test durations =========================
0.30s call test_some_are_slow.py::test_funcslow2
0.20s call test_some_are_slow.py::test_funcslow1
0.10s call test_some_are_slow.py::test_funcfast
0.11s call test_some_are_slow.py::test_funcfast
========================= 3 passed in 0.12 seconds =========================
incremental testing - test steps
@@ -449,9 +451,6 @@ If we run this::
collected 4 items
test_step.py .Fx. [100%]
========================= short test summary info ==========================
XFAIL test_step.py::TestUserHandling::()::test_deletion
reason: previous test failed (test_modification)
================================= FAILURES =================================
____________________ TestUserHandling.test_modification ____________________
@@ -463,6 +462,9 @@ If we run this::
E assert 0
test_step.py:9: AssertionError
========================= short test summary info ==========================
XFAIL test_step.py::TestUserHandling::()::test_deletion
reason: previous test failed (test_modification)
============== 1 failed, 2 passed, 1 xfailed in 0.12 seconds ===============
We'll see that ``test_deletion`` was not executed because ``test_modification``
@@ -537,7 +539,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_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, tmpdir, tmpdir_factory
> use 'pytest --fixtures [testpath]' for help on them.
$REGENDOC_TMPDIR/b/test_error.py:1
@@ -764,6 +766,8 @@ and run it::
You'll see that the fixture finalizers could use the precise reporting
information.
.. _pytest current test env:
``PYTEST_CURRENT_TEST`` environment variable
--------------------------------------------
@@ -774,7 +778,7 @@ which test got stuck, for example if pytest was run in quiet mode (``-q``) or yo
output. This is particularly a problem if the problem helps only sporadically, the famous "flaky" kind of tests.
``pytest`` sets a ``PYTEST_CURRENT_TEST`` environment variable when running tests, which can be inspected
by process monitoring utilities or libraries like `psutil <https://pypi.python.org/pypi/psutil>`_ to discover which
by process monitoring utilities or libraries like `psutil <https://pypi.org/project/psutil/>`_ to discover which
test got stuck if necessary:
.. code-block:: python

View File

@@ -30,14 +30,14 @@ and does not handle Deferreds returned from a test in pytest style.
If you are using trial's unittest.TestCase chances are that you can
just run your tests even if you return Deferreds. In addition,
there also is a dedicated `pytest-twisted
<http://pypi.python.org/pypi/pytest-twisted>`_ plugin which allows you to
<https://pypi.org/project/pytest-twisted/>`_ plugin which allows you to
return deferreds from pytest-style tests, allowing the use of
:ref:`fixtures` and other features.
how does pytest work with Django?
++++++++++++++++++++++++++++++++++++++++++++++
In 2012, some work is going into the `pytest-django plugin <http://pypi.python.org/pypi/pytest-django>`_. It substitutes the usage of Django's
In 2012, some work is going into the `pytest-django plugin <https://pypi.org/project/pytest-django/>`_. It substitutes the usage of Django's
``manage.py test`` and allows the use of all pytest features_ most of which
are not available from Django directly.

View File

@@ -111,11 +111,11 @@ with a list of available function arguments.
.. note::
You can always issue::
You can always issue ::
pytest --fixtures test_simplefactory.py
to see available fixtures.
to see available fixtures (fixtures with leading ``_`` are only shown if you add the ``-v`` option).
Fixtures: a prime example of dependency injection
---------------------------------------------------
@@ -141,7 +141,7 @@ automatically gets discovered by pytest. The discovery of
fixture functions starts at test classes, then test modules, then
``conftest.py`` files and finally builtin and third party plugins.
You can also use the ``conftest.py`` file to implement
You can also use the ``conftest.py`` file to implement
:ref:`local per-directory plugins <conftest.py plugins>`.
Sharing test data
@@ -154,7 +154,7 @@ This makes use of the automatic caching mechanisms of pytest.
Another good approach is by adding the data files in the ``tests`` folder.
There are also community plugins available to help managing this aspect of
testing, e.g. `pytest-datadir <https://github.com/gabrielcnr/pytest-datadir>`__
and `pytest-datafiles <https://pypi.python.org/pypi/pytest-datafiles>`__.
and `pytest-datafiles <https://pypi.org/project/pytest-datafiles/>`__.
.. _smtpshared:
@@ -256,6 +256,50 @@ instance, you can simply declare it:
Finally, the ``class`` scope will invoke the fixture once per test *class*.
Higher-scoped fixtures are instantiated first
---------------------------------------------
.. versionadded:: 3.5
Within a function request for features, fixture of higher-scopes (such as ``session``) are instantiated first than
lower-scoped fixtures (such as ``function`` or ``class``). The relative order of fixtures of same scope follows
the declared order in the test function and honours dependencies between fixtures.
Consider the code below:
.. code-block:: python
@pytest.fixture(scope="session")
def s1():
pass
@pytest.fixture(scope="module")
def m1():
pass
@pytest.fixture
def f1(tmpdir):
pass
@pytest.fixture
def f2():
pass
def test_foo(f1, m1, f2, s1):
...
The fixtures requested by ``test_foo`` will be instantiated in the following order:
1. ``s1``: is the highest-scoped fixture (``session``).
2. ``m1``: is the second highest-scoped fixture (``module``).
3. ``tmpdir``: is a ``function``-scoped fixture, required by ``f1``: it needs to be instantiated at this point
because it is a dependency of ``f1``.
4. ``f1``: is the first ``function``-scoped fixture in ``test_foo`` parameter list.
5. ``f2``: is the last ``function``-scoped fixture in ``test_foo`` parameter list.
.. _`finalization`:
Fixture finalization / executing teardown code
@@ -369,7 +413,7 @@ ends, but ``addfinalizer`` has two key differences over ``yield``:
Fixtures can introspect the requesting test context
-------------------------------------------------------------
Fixture function can accept the :py:class:`request <FixtureRequest>` object
Fixture functions can accept the :py:class:`request <FixtureRequest>` object
to introspect the "requesting" test function, class or module context.
Further extending the previous ``smtp`` fixture example, let's
read an optional server URL from the test module which uses our fixture::
@@ -579,6 +623,40 @@ Running the above tests results in the following test IDs being used::
======================= no tests ran in 0.12 seconds =======================
.. _`fixture-parametrize-marks`:
Using marks with parametrized fixtures
--------------------------------------
:func:`pytest.param` can be used to apply marks in values sets of parametrized fixtures in the same way
that they can be used with :ref:`@pytest.mark.parametrize <@pytest.mark.parametrize>`.
Example::
# content of test_fixture_marks.py
import pytest
@pytest.fixture(params=[0, 1, pytest.param(2, marks=pytest.mark.skip)])
def data_set(request):
return request.param
def test_data(data_set):
pass
Running this test will *skip* the invocation of ``data_set`` with value ``2``::
$ pytest test_fixture_marks.py -v
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.5
cachedir: .pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 3 items
test_fixture_marks.py::test_data[0] PASSED [ 33%]
test_fixture_marks.py::test_data[1] PASSED [ 66%]
test_fixture_marks.py::test_data[2] SKIPPED [100%]
=================== 2 passed, 1 skipped in 0.12 seconds ====================
.. _`interdependent fixtures`:
Modularity: using fixtures from a fixture function
@@ -696,11 +774,11 @@ Let's run the tests in verbose mode and with looking at the print-output::
test_module.py::test_1[mod1] SETUP modarg mod1
RUN test1 with modarg mod1
PASSED
test_module.py::test_2[1-mod1] SETUP otherarg 1
test_module.py::test_2[mod1-1] SETUP otherarg 1
RUN test2 with otherarg 1 and modarg mod1
PASSED TEARDOWN otherarg 1
test_module.py::test_2[2-mod1] SETUP otherarg 2
test_module.py::test_2[mod1-2] SETUP otherarg 2
RUN test2 with otherarg 2 and modarg mod1
PASSED TEARDOWN otherarg 2
@@ -708,11 +786,11 @@ Let's run the tests in verbose mode and with looking at the print-output::
SETUP modarg mod2
RUN test1 with modarg mod2
PASSED
test_module.py::test_2[1-mod2] SETUP otherarg 1
test_module.py::test_2[mod2-1] SETUP otherarg 1
RUN test2 with otherarg 1 and modarg mod2
PASSED TEARDOWN otherarg 1
test_module.py::test_2[2-mod2] SETUP otherarg 2
test_module.py::test_2[mod2-2] SETUP otherarg 2
RUN test2 with otherarg 2 and modarg mod2
PASSED TEARDOWN otherarg 2
TEARDOWN modarg mod2

View File

@@ -5,10 +5,10 @@ Installation and Getting Started
**Platforms**: Unix/Posix and Windows
**PyPI package name**: `pytest <http://pypi.python.org/pypi/pytest>`_
**PyPI package name**: `pytest <https://pypi.org/project/pytest/>`_
**Dependencies**: `py <http://pypi.python.org/pypi/py>`_,
`colorama (Windows) <http://pypi.python.org/pypi/colorama>`_,
**Dependencies**: `py <https://pypi.org/project/py/>`_,
`colorama (Windows) <https://pypi.org/project/colorama/>`_,
**Documentation as PDF**: `download latest <https://media.readthedocs.org/pdf/pytest/latest/pytest.pdf>`_
@@ -160,21 +160,24 @@ List the name ``tmpdir`` in the test function signature and ``pytest`` will look
PYTEST_TMPDIR/test_needsfiles0
1 failed in 0.12 seconds
More info on tmpdir handling is available at `Temporary directories and files <tmpdir handling>`_.
More info on tmpdir handling is available at :ref:`Temporary directories and files <tmpdir handling>`.
Find out what kind of builtin ```pytest`` fixtures <fixtures>`_ exist with the command::
Find out what kind of builtin :ref:`pytest fixtures <fixtures>` exist with the command::
pytest --fixtures # shows builtin and custom fixtures
Note that this command omits fixtures with leading ``_`` unless the ``-v`` option is added.
Continue reading
-------------------------------------
Check out additional pytest resources to help you customize tests for your unique workflow:
* ":ref:`cmdline`" for command line invocation examples
* ":ref:`goodpractices`" for virtualenv and test layouts
* ":ref:`existingtestsuite`" for working with pre-existing tests
* ":ref:`mark`" for information on the ``pytest.mark`` mechanism
* ":ref:`fixtures`" for providing a functional baseline to your tests
* ":ref:`plugins`" for managing and writing plugins
* ":ref:`goodpractices`" for virtualenv and test layouts
.. include:: links.inc

View File

@@ -145,7 +145,7 @@ Note that this layout also works in conjunction with the ``src`` layout mentione
.. note::
If ``pytest`` finds a "a/b/test_module.py" test file while
If ``pytest`` finds an "a/b/test_module.py" test file while
recursing into the filesystem it determines the import name
as follows:
@@ -168,9 +168,9 @@ Note that this layout also works in conjunction with the ``src`` layout mentione
to avoid surprises such as a test module getting imported twice.
.. _`virtualenv`: http://pypi.python.org/pypi/virtualenv
.. _`virtualenv`: https://pypi.org/project/virtualenv/
.. _`buildout`: http://www.buildout.org/
.. _pip: http://pypi.python.org/pypi/pip
.. _pip: https://pypi.org/project/pip/
.. _`use tox`:
@@ -205,7 +205,7 @@ Integrating with setuptools / ``python setup.py test`` / ``pytest-runner``
--------------------------------------------------------------------------
You can integrate test runs into your setuptools based project
with the `pytest-runner <https://pypi.python.org/pypi/pytest-runner>`_ plugin.
with the `pytest-runner <https://pypi.org/project/pytest-runner/>`_ plugin.
Add this to ``setup.py`` file:

View File

@@ -7,14 +7,14 @@
.. _`reStructured Text`: http://docutils.sourceforge.net
.. _`Python debugger`: http://docs.python.org/lib/module-pdb.html
.. _nose: https://nose.readthedocs.io/en/latest/
.. _pytest: http://pypi.python.org/pypi/pytest
.. _pytest: https://pypi.org/project/pytest/
.. _mercurial: http://mercurial.selenic.com/wiki/
.. _`setuptools`: http://pypi.python.org/pypi/setuptools
.. _`setuptools`: https://pypi.org/project/setuptools/
.. _`easy_install`:
.. _`distribute docs`:
.. _`distribute`: http://pypi.python.org/pypi/distribute
.. _`pip`: http://pypi.python.org/pypi/pip
.. _`virtualenv`: http://pypi.python.org/pypi/virtualenv
.. _`distribute`: https://pypi.org/project/distribute/
.. _`pip`: https://pypi.org/project/pip/
.. _`virtualenv`: https://pypi.org/project/virtualenv/
.. _hudson: http://hudson-ci.org/
.. _jenkins: http://jenkins-ci.org/
.. _tox: http://testrun.org/tox

View File

@@ -50,26 +50,10 @@ These options can also be customized through ``pytest.ini`` file:
log_format = %(asctime)s %(levelname)s %(message)s
log_date_format = %Y-%m-%d %H:%M:%S
Further it is possible to disable reporting logs on failed tests completely
with::
Further it is possible to disable reporting of captured content (stdout,
stderr and logs) on failed tests completely with::
pytest --no-print-logs
Or in the ``pytest.ini`` file:
.. code-block:: ini
[pytest]
log_print = False
Shows failed tests in the normal manner as no logs were captured::
----------------------- Captured stdout call ----------------------
text going to stdout
----------------------- Captured stderr call ----------------------
text going to stderr
==================== 2 failed in 0.02 seconds =====================
pytest --show-capture=no
caplog fixture
@@ -139,7 +123,7 @@ You can call ``caplog.clear()`` to reset the captured log records in a test::
assert ['Foo'] == [rec.message for rec in caplog.records]
The ``caplop.records`` attribute contains records from the current stage only, so
The ``caplog.records`` attribute contains records from the current stage only, so
inside the ``setup`` phase it contains only setup logs, same with the ``call`` and
``teardown`` phases.
@@ -160,11 +144,9 @@ the records for the ``setup`` and ``call`` stages during teardown like so:
pytest.fail('warning messages encountered during testing: {}'.format(messages))
caplog fixture API
~~~~~~~~~~~~~~~~~~
.. autoclass:: _pytest.logging.LogCaptureFixture
:members:
The full API is available at :class:`_pytest.logging.LogCaptureFixture`.
.. _live_logs:

View File

@@ -4,7 +4,6 @@
Marking test functions with attributes
=================================================================
.. currentmodule:: _pytest.mark
By using the ``pytest.mark`` helper you can easily set
metadata on your test functions. There are
@@ -27,15 +26,114 @@ which also serve as documentation.
:ref:`fixtures <fixtures>`.
API reference for mark related objects
------------------------------------------------
.. autoclass:: MarkGenerator
.. currentmodule:: _pytest.mark.structures
.. autoclass:: Mark
:members:
:noindex:
.. autoclass:: MarkDecorator
:members:
.. autoclass:: MarkInfo
:members:
.. `marker-iteration`
Marker revamp and iteration
---------------------------
.. versionadded:: 3.6
pytest's marker implementation traditionally worked by simply updating the ``__dict__`` attribute of functions to add markers, in a cumulative manner. As a result of the this, markers would unintendely be passed along class hierarchies in surprising ways plus the API for retriving them was inconsistent, as markers from parameterization would be stored differently than markers applied using the ``@pytest.mark`` decorator and markers added via ``node.add_marker``.
This state of things made it technically next to impossible to use data from markers correctly without having a deep understanding of the internals, leading to subtle and hard to understand bugs in more advanced usages.
Depending on how a marker got declared/changed one would get either a ``MarkerInfo`` which might contain markers from sibling classes,
``MarkDecorators`` when marks came from parameterization or from a ``node.add_marker`` call, discarding prior marks. Also ``MarkerInfo`` acts like a single mark, when it in fact repressents a merged view on multiple marks with the same name.
On top of that markers where not accessible the same way for modules, classes, and functions/methods,
in fact, markers where only accessible in functions, even if they where declared on classes/modules.
A new API to access markers has been introduced in pytest 3.6 in order to solve the problems with the initial design, providing :func:`_pytest.nodes.Node.iter_markers` method to iterate over markers in a consistent manner and reworking the internals, which solved great deal of problems with the initial design.
.. _update marker code:
Updating code
~~~~~~~~~~~~~
The old ``Node.get_marker(name)`` function is considered deprecated because it returns an internal ``MarkerInfo`` object
which contains the merged name, ``*args`` and ``**kwargs**`` of all the markers which apply to that node.
In general there are two scenarios on how markers should be handled:
1. Marks overwrite each other. Order matters but you only want to think of your mark as a single item. E.g.
``log_level('info')`` at a module level can be overwritten by ``log_level('debug')`` for a specific test.
In this case replace use ``Node.get_closest_marker(name)``:
.. code-block:: python
# replace this:
marker = item.get_marker('log_level')
if marker:
level = marker.args[0]
# by this:
marker = item.get_closest_marker('log_level')
if marker:
level = marker.args[0]
2. Marks compose additive. E.g. ``skipif(condition)`` marks means you just want to evaluate all of them,
order doesn't even matter. You probably want to think of your marks as a set here.
In this case iterate over each mark and handle their ``*args`` and ``**kwargs`` individually.
.. code-block:: python
# replace this
skipif = item.get_marker('skipif')
if skipif:
for condition in skipif.args:
# eval condition
# by this:
for skipif in item.iter_markers('skipif'):
condition = skipif.args[0]
# eval condition
If you are unsure or have any questions, please consider opening
`an issue <https://github.com/pytest-dev/pytest/issues>`_.
Related issues
~~~~~~~~~~~~~~
Here is a non-exhaustive list of issues fixed by the new implementation:
* Marks don't pick up nested classes (`#199 <https://github.com/pytest-dev/pytest/issues/199>`_).
* markers stains on all related classes (`#568 <https://github.com/pytest-dev/pytest/issues/568>`_).
* combining marks - args and kwargs calculation (`#2897 <https://github.com/pytest-dev/pytest/issues/2897>`_).
* ``request.node.get_marker('name')`` returns ``None`` for markers applied in classes (`#902 <https://github.com/pytest-dev/pytest/issues/902>`_).
* marks applied in parametrize are stored as markdecorator (`#2400 <https://github.com/pytest-dev/pytest/issues/2400>`_).
* fix marker interaction in a backward incompatible way (`#1670 <https://github.com/pytest-dev/pytest/issues/1670>`_).
* Refactor marks to get rid of the current "marks transfer" mechanism (`#2363 <https://github.com/pytest-dev/pytest/issues/2363>`_).
* Introduce FunctionDefinition node, use it in generate_tests (`#2522 <https://github.com/pytest-dev/pytest/issues/2522>`_).
* remove named marker attributes and collect markers in items (`#891 <https://github.com/pytest-dev/pytest/issues/891>`_).
* skipif mark from parametrize hides module level skipif mark (`#1540 <https://github.com/pytest-dev/pytest/issues/1540>`_).
* skipif + parametrize not skipping tests (`#1296 <https://github.com/pytest-dev/pytest/issues/1296>`_).
* marker transfer incompatible with inheritance (`#535 <https://github.com/pytest-dev/pytest/issues/535>`_).
More details can be found in the `original PR <https://github.com/pytest-dev/pytest/pull/3317>`_.
.. note::
in a future major relase of pytest we will introduce class based markers,
at which points markers will no longer be limited to instances of :py:class:`Mark`

View File

@@ -61,15 +61,27 @@ so that any attempts within tests to create http requests will fail.
``compile``, etc., because it might break pytest's internals. If that's
unavoidable, passing ``--tb=native``, ``--assert=plain`` and ``--capture=no`` might
help although there's no guarantee.
.. note::
Mind that patching ``stdlib`` functions and some third-party libraries used by pytest
might break pytest itself, therefore in those cases it is recommended to use
:meth:`MonkeyPatch.context` to limit the patching to the block you want tested:
.. code-block:: python
import functools
def test_partial(monkeypatch):
with monkeypatch.context() as m:
m.setattr(functools, "partial", 3)
assert functools.partial == 3
See issue `#3290 <https://github.com/pytest-dev/pytest/issues/3290>`_ for details.
Method reference of the monkeypatch fixture
-------------------------------------------
.. currentmodule:: _pytest.monkeypatch
.. autoclass:: MonkeyPatch
:members:
``monkeypatch.setattr/delattr/delitem/delenv()`` all
by default raise an Exception if the target does not exist.
Pass ``raising=False`` if you want to skip this check.
API Reference
-------------
Consult the docs for the :class:`MonkeyPatch` class.

View File

@@ -33,7 +33,7 @@ pytest enables test parametrization at several levels:
.. versionchanged:: 2.4
Several improvements.
The builtin ``pytest.mark.parametrize`` decorator enables
The builtin :ref:`pytest.mark.parametrize ref` decorator enables
parametrization of arguments for a test function. Here is a typical example
of a test function that implements checking that a certain input leads
to an expected output::
@@ -206,12 +206,3 @@ More examples
For further examples, you might want to look at :ref:`more
parametrization examples <paramexamples>`.
.. _`metafunc object`:
The **metafunc** object
-------------------------------------------
.. currentmodule:: _pytest.python
.. autoclass:: Metafunc
:members:

View File

@@ -20,39 +20,39 @@ Here is a little annotated list for some popular plugins:
.. _`django`: https://www.djangoproject.com/
* `pytest-django <http://pypi.python.org/pypi/pytest-django>`_: write tests
* `pytest-django <https://pypi.org/project/pytest-django/>`_: write tests
for `django`_ apps, using pytest integration.
* `pytest-twisted <http://pypi.python.org/pypi/pytest-twisted>`_: write tests
* `pytest-twisted <https://pypi.org/project/pytest-twisted/>`_: write tests
for `twisted <http://twistedmatrix.com>`_ apps, starting a reactor and
processing deferreds from test functions.
* `pytest-cov <http://pypi.python.org/pypi/pytest-cov>`_:
* `pytest-cov <https://pypi.org/project/pytest-cov/>`_:
coverage reporting, compatible with distributed testing
* `pytest-xdist <http://pypi.python.org/pypi/pytest-xdist>`_:
* `pytest-xdist <https://pypi.org/project/pytest-xdist/>`_:
to distribute tests to CPUs and remote hosts, to run in boxed
mode which allows to survive segmentation faults, to run in
looponfailing mode, automatically re-running failing tests
on file changes.
* `pytest-instafail <http://pypi.python.org/pypi/pytest-instafail>`_:
* `pytest-instafail <https://pypi.org/project/pytest-instafail/>`_:
to report failures while the test run is happening.
* `pytest-bdd <http://pypi.python.org/pypi/pytest-bdd>`_ and
`pytest-konira <http://pypi.python.org/pypi/pytest-konira>`_
* `pytest-bdd <https://pypi.org/project/pytest-bdd/>`_ and
`pytest-konira <https://pypi.org/project/pytest-konira/>`_
to write tests using behaviour-driven testing.
* `pytest-timeout <http://pypi.python.org/pypi/pytest-timeout>`_:
* `pytest-timeout <https://pypi.org/project/pytest-timeout/>`_:
to timeout tests based on function marks or global definitions.
* `pytest-pep8 <http://pypi.python.org/pypi/pytest-pep8>`_:
* `pytest-pep8 <https://pypi.org/project/pytest-pep8/>`_:
a ``--pep8`` option to enable PEP8 compliance checking.
* `pytest-flakes <https://pypi.python.org/pypi/pytest-flakes>`_:
* `pytest-flakes <https://pypi.org/project/pytest-flakes/>`_:
check source code with pyflakes.
* `oejskit <http://pypi.python.org/pypi/oejskit>`_:
* `oejskit <https://pypi.org/project/oejskit/>`_:
a plugin to run javascript unittests in live browsers.
To see a complete list of all plugins with their latest testing
@@ -61,10 +61,11 @@ status against different pytest and Python versions, please visit
You may also discover more plugins through a `pytest- pypi.python.org search`_.
.. _`available installable plugins`:
.. _`pytest- pypi.python.org search`: http://pypi.python.org/pypi?%3Aaction=search&term=pytest-&submit=search
.. _`pytest- pypi.python.org search`: https://pypi.org/search/?q=pytest-
.. _`available installable plugins`:
Requiring/Loading plugins in a test module or conftest file
-----------------------------------------------------------
@@ -79,6 +80,12 @@ will be loaded as well.
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
:ref:`full explanation <requiring plugins in non-root conftests>`
in the Writing plugins section.
.. _`findpluginname`:
Finding out which plugins are active
@@ -120,35 +127,3 @@ CI server), you can set ``PYTEST_ADDOPTS`` environment variable to
See :ref:`findpluginname` for how to obtain the name of a plugin.
.. _`builtin plugins`:
Pytest default plugin reference
-------------------------------
You can find the source code for the following plugins
in the `pytest repository <https://github.com/pytest-dev/pytest>`_.
.. autosummary::
_pytest.assertion
_pytest.cacheprovider
_pytest.capture
_pytest.config
_pytest.doctest
_pytest.helpconfig
_pytest.junitxml
_pytest.mark
_pytest.monkeypatch
_pytest.nose
_pytest.pastebin
_pytest.debugging
_pytest.pytester
_pytest.python
_pytest.recwarn
_pytest.resultlog
_pytest.runner
_pytest.main
_pytest.skipping
_pytest.terminal
_pytest.tmpdir
_pytest.unittest

View File

@@ -32,40 +32,40 @@ Here are some examples of projects using ``pytest`` (please send notes via :ref:
* `PyPM <http://code.activestate.com/pypm/>`_ ActiveState's package manager
* `Fom <http://packages.python.org/Fom/>`_ a fluid object mapper for FluidDB
* `applib <https://github.com/ActiveState/applib>`_ cross-platform utilities
* `six <http://pypi.python.org/pypi/six/>`_ Python 2 and 3 compatibility utilities
* `six <https://pypi.org/project/six/>`_ Python 2 and 3 compatibility utilities
* `pediapress <http://code.pediapress.com/wiki/wiki>`_ MediaWiki articles
* `mwlib <http://pypi.python.org/pypi/mwlib>`_ mediawiki parser and utility library
* `mwlib <https://pypi.org/project/mwlib/>`_ mediawiki parser and utility library
* `The Translate Toolkit <http://translate.sourceforge.net/wiki/toolkit/index>`_ for localization and conversion
* `execnet <http://codespeak.net/execnet>`_ rapid multi-Python deployment
* `pylib <https://py.readthedocs.io>`_ cross-platform path, IO, dynamic code library
* `Pacha <http://pacha.cafepais.com/>`_ configuration management in five minutes
* `bbfreeze <http://pypi.python.org/pypi/bbfreeze>`_ create standalone executables from Python scripts
* `bbfreeze <https://pypi.org/project/bbfreeze/>`_ create standalone executables from Python scripts
* `pdb++ <http://bitbucket.org/antocuni/pdb>`_ a fancier version of PDB
* `py-s3fuse <http://code.google.com/p/py-s3fuse/>`_ Amazon S3 FUSE based filesystem
* `waskr <http://code.google.com/p/waskr/>`_ WSGI Stats Middleware
* `guachi <http://code.google.com/p/guachi/>`_ global persistent configs for Python modules
* `Circuits <http://pypi.python.org/pypi/circuits>`_ lightweight Event Driven Framework
* `Circuits <https://pypi.org/project/circuits/>`_ lightweight Event Driven Framework
* `pygtk-helpers <http://bitbucket.org/aafshar/pygtkhelpers-main/>`_ easy interaction with PyGTK
* `QuantumCore <http://quantumcore.org/>`_ statusmessage and repoze openid plugin
* `pydataportability <http://pydataportability.net/>`_ libraries for managing the open web
* `XIST <http://www.livinglogic.de/Python/xist/>`_ extensible HTML/XML generator
* `tiddlyweb <http://pypi.python.org/pypi/tiddlyweb>`_ optionally headless, extensible RESTful datastore
* `tiddlyweb <https://pypi.org/project/tiddlyweb/>`_ optionally headless, extensible RESTful datastore
* `fancycompleter <http://bitbucket.org/antocuni/fancycompleter/src>`_ for colorful tab-completion
* `Paludis <http://paludis.exherbo.org/>`_ tools for Gentoo Paludis package manager
* `Gerald <http://halfcooked.com/code/gerald/>`_ schema comparison tool
* `abjad <http://code.google.com/p/abjad/>`_ Python API for Formalized Score control
* `bu <http://packages.python.org/bu/>`_ a microscopic build system
* `katcp <https://bitbucket.org/hodgestar/katcp>`_ Telescope communication protocol over Twisted
* `kss plugin timer <http://pypi.python.org/pypi/kss.plugin.timer>`_
* `kss plugin timer <https://pypi.org/project/kss.plugin.timer/>`_
* `pyudev <https://pyudev.readthedocs.io/en/latest/tests/plugins.html>`_ a pure Python binding to the Linux library libudev
* `pytest-localserver <https://bitbucket.org/pytest-dev/pytest-localserver/>`_ a plugin for pytest that provides an httpserver and smtpserver
* `pytest-monkeyplus <http://pypi.python.org/pypi/pytest-monkeyplus/>`_ a plugin that extends monkeypatch
* `pytest-monkeyplus <https://pypi.org/project/pytest-monkeyplus/>`_ a plugin that extends monkeypatch
These projects help integrate ``pytest`` into other Python frameworks:
* `pytest-django <http://pypi.python.org/pypi/pytest-django/>`_ for Django
* `pytest-django <https://pypi.org/project/pytest-django/>`_ for Django
* `zope.pytest <http://packages.python.org/zope.pytest/>`_ for Zope and Grok
* `pytest_gae <http://pypi.python.org/pypi/pytest_gae/0.2.1>`_ for Google App Engine
* `pytest_gae <https://pypi.org/project/pytest_gae/0.2.1/>`_ for Google App Engine
* There is `some work <https://github.com/Kotti/Kotti/blob/master/kotti/testing.py>`_ underway for Kotti, a CMS built in Pyramid/Pylons

View File

@@ -118,7 +118,7 @@ all parameters marked as a fixture.
.. note::
The `pytest-lazy-fixture <https://pypi.python.org/pypi/pytest-lazy-fixture>`_ plugin implements a very
The `pytest-lazy-fixture <https://pypi.org/project/pytest-lazy-fixture/>`_ plugin implements a very
similar solution to the proposal below, make sure to check it out.
.. code-block:: python

1294
doc/en/reference.rst Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,4 @@
# pinning sphinx to 1.4.* due to search issues with rtd:
# https://github.com/rtfd/readthedocs-sphinx-ext/issues/25
sphinx ==1.4.*
sphinxcontrib-trio

View File

@@ -71,6 +71,8 @@ It is also possible to skip the whole module using
The imperative method is useful when it is not possible to evaluate the skip condition
during import time.
**Reference**: :ref:`pytest.mark.skip ref`
``skipif``
~~~~~~~~~~
@@ -116,6 +118,8 @@ Alternatively, you can use :ref:`condition strings
<string conditions>` instead of booleans, but they can't be shared between modules easily
so they are supported mainly for backward compatibility reasons.
**Reference**: :ref:`pytest.mark.skipif ref`
Skip all test functions of a class or module
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -232,15 +236,10 @@ This will unconditionally make ``test_function`` ``XFAIL``. Note that no other c
after ``pytest.xfail`` call, differently from the marker. That's because it is implemented
internally by raising a known exception.
Here's the signature of the ``xfail`` **marker** (not the function), using Python 3 keyword-only
arguments syntax:
.. code-block:: python
def xfail(condition=None, *, reason=None, raises=None, run=True, strict=False):
**Reference**: :ref:`pytest.mark.xfail ref`
.. _`xfail strict tutorial`:
``strict`` parameter
~~~~~~~~~~~~~~~~~~~~

View File

@@ -21,7 +21,7 @@ The `pytest-cov`_ package may be installed with pip or easy_install::
pip install pytest-cov
easy_install pytest-cov
.. _`pytest-cov`: http://pypi.python.org/pypi/pytest-cov/
.. _`pytest-cov`: https://pypi.org/project/pytest-cov/
Uninstallation

View File

@@ -7,6 +7,6 @@ The approach enables to write integration tests such that the JavaScript code is
For more info and download please visit the `oejskit PyPI`_ page.
.. _`oejskit`:
.. _`oejskit PyPI`: http://pypi.python.org/pypi/oejskit
.. _`oejskit PyPI`: https://pypi.org/project/oejskit/
.. source link 'http://bitbucket.org/pedronis/js-infrastructure/src/tip/pytest_jstests.py',

View File

@@ -23,7 +23,7 @@ command line options
``--full-trace``
don't cut any tracebacks (default is to cut).
``--fixtures``
show available function arguments, sorted by plugin
show available fixtures, sorted by plugin appearance (fixtures with leading ``_`` are only shown with '-v')
Start improving this plugin in 30 seconds
=========================================

View File

@@ -26,7 +26,7 @@ program source code to the remote place. All test results
are reported back and displayed to your local test session.
You may specify different Python versions and interpreters.
.. _`pytest-xdist`: http://pypi.python.org/pypi/pytest-xdist
.. _`pytest-xdist`: https://pypi.org/project/pytest-xdist/
Usage examples
---------------------

View File

@@ -51,6 +51,8 @@ Running this would result in a passed test except for the last
test_tmpdir.py:7: AssertionError
========================= 1 failed in 0.12 seconds =========================
.. _`tmpdir factory example`:
The 'tmpdir_factory' fixture
----------------------------
@@ -81,12 +83,8 @@ to save time:
img = load_image(image_file)
# compute and test histogram
``tmpdir_factory`` instances have the following methods:
See :ref:`tmpdir_factory API <tmpdir factory api>` for details.
.. currentmodule:: _pytest.tmpdir
.. automethod:: TempdirFactory.mktemp
.. automethod:: TempdirFactory.getbasetemp
.. _`base temporary directory`:

View File

@@ -46,9 +46,9 @@ in most cases without having to modify existing code:
* :ref:`maxfail`;
* :ref:`--pdb <pdb-option>` command-line option for debugging on test failures
(see :ref:`note <pdb-unittest-note>` below);
* Distribute tests to multiple CPUs using the `pytest-xdist <http://pypi.python.org/pypi/pytest-xdist>`_ plugin;
* Distribute tests to multiple CPUs using the `pytest-xdist <https://pypi.org/project/pytest-xdist/>`_ plugin;
* Use :ref:`plain assert-statements <assert>` instead of ``self.assert*`` functions (`unittest2pytest
<https://pypi.python.org/pypi/unittest2pytest/>`__ is immensely helpful in this);
<https://pypi.org/project/unittest2pytest/>`__ is immensely helpful in this);
pytest features in ``unittest.TestCase`` subclasses

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