Compare commits

...

569 Commits
2.8.1 ... 2.9.2

Author SHA1 Message Date
Ronny Pfannschmidt
978bb190a1 add release announcement 2016-05-31 19:06:29 +02:00
Ronny Pfannschmidt
a2a904466c alter changelog header 2016-05-31 19:05:36 +02:00
Ronny Pfannschmidt
77c28825df regendoc and comment out a python2.7 example as per #1573 2016-05-31 19:05:36 +02:00
Ronny Pfannschmidt
d3dcc2b8f1 bump regendoc python version to 3.5 2016-05-31 19:05:36 +02:00
Ronny Pfannschmidt
85541113eb update version 2016-05-31 19:05:36 +02:00
Ronny Pfannschmidt
7ef06822cb ignore hypothesis temp folder 2016-05-31 19:05:36 +02:00
Ronny Pfannschmidt
289e0091de Merge pull request #1577 from pytest-dev/docs-hookwrapper-experimental
Remove "experimental" status from hookwrapper
2016-05-31 17:56:08 +02:00
Bruno Oliveira
28efdebfcd Remove "experimental" status from hookwrapper 2016-05-31 12:17:55 -03:00
Ronny Pfannschmidt
fb2e7cc727 Merge pull request #1575 from hackebrot/fix-showfixtures-for-multiple-fixturedefs
Fix showfixtures for multiple fixturedefs
2016-05-31 13:53:13 +02:00
Raphael Pierzina
fb8ad714b1 Implement a test for showfixtures to show fixtures with same name 2016-05-31 11:47:16 +01:00
Raphael Pierzina
945072b89a Update changelog with --fixtures fix 2016-05-31 11:47:16 +01:00
Raphael Pierzina
0d80a9c729 Change _showfixtures_main to loop over fixturedefs 2016-05-31 11:47:16 +01:00
Ronny Pfannschmidt
357a7c79ef Merge branch 'tomviner-issue-1496-xfail-cond-kw'
fixes  #1496
2016-05-31 11:25:15 +02:00
Ronny Pfannschmidt
158f3cfaea merge master 2016-05-31 11:24:53 +02:00
Bruno Oliveira
82c74fe7e6 Merge pull request #1530 from RonnyPfannschmidt/fix-510
fix #510 by adding a describing skip marker on empty parameterize
2016-05-30 10:54:33 -03:00
Ronny Pfannschmidt
afc5c7e4f6 better message for empty argument skip
include the argument names to help determining the fixture/parametrization
2016-05-30 14:42:55 +02:00
Ronny Pfannschmidt
03eb9203fd remove the old empty argument triggers
unlike the marker based one its not composable
2016-05-30 14:41:00 +02:00
Ronny Pfannschmidt
ae4dff0e0a add missing link to changelog 2016-05-30 14:34:38 +02:00
Ronny Pfannschmidt
d217b52508 fix #510 by adding a describing skip marker on empty parameterize 2016-05-30 14:34:38 +02:00
Ronny Pfannschmidt
436e13ac25 Merge pull request #1566 from nicoddemus/fix-win32-path
Fix shell argument split in win32
2016-05-24 08:14:01 +02:00
Bruno Oliveira
9fb5ddf778 Fix shell argument split in win32
This fixes the bug inserted by accident in #1523
2016-05-23 20:41:47 -03:00
Ronny Pfannschmidt
6f98cd6faa Merge pull request #1560 from nicoddemus/fix-py26-tox-comment
Move comment in tox.ini due to recent bug in pip
2016-05-18 21:31:56 +02:00
Bruno Oliveira
561a5fb558 Move comment in tox.ini due to recent bug in pip
As discussed in #1554

* https://bitbucket.org/hpk42/tox/issues/332/
* https://github.com/pypa/pip/issues/3667
2016-05-18 16:15:29 -03:00
Bruno Oliveira
47739291cf Merge pull request #1557 from adamchainz/readthedocs.io
Convert readthedocs link for their .org -> .io migration for hosted projects
2016-05-18 13:24:03 -03:00
Adam Chainz
8a39869347 Convert readthedocs link for their .org -> .io migration for hosted projects
As per their email ‘Changes to project subdomains’:

> Starting today, Read the Docs will start hosting projects from subdomains on the domain readthedocs.io, instead of on readthedocs.org. This change addresses some security concerns around site cookies while hosting user generated data on the same domain as our dashboard.

Test Plan: Manually visited all the links I’ve modified. One was not modified - `http://media.readthedocs.org/epub/pytest/latest/pytest.epub` - since it doesn't work on `readthedocs.io`.
2016-05-18 17:12:39 +01:00
Ronny Pfannschmidt
8a94c66e68 Merge pull request #1551 from fushi/master
Fix #1549 - count skips in junit-xml
2016-05-05 22:03:30 +02:00
John Towler
0d07b64571 Fixes Issue 1549 2016-05-05 11:29:05 -07:00
Florian Bruhin
60212e8831 Merge pull request #1537 from ben4ever/patch-1
Fix typo in doc
2016-04-27 22:13:51 -07:00
Benjamin Dopplinger
75abfbe8d4 Fix typo in doc 2016-04-28 14:40:17 +10:00
Ronny Pfannschmidt
6d661ace0a Merge pull request #1523 from MengJueM/master 2016-04-20 16:55:31 +02:00
MengJueM
f51c34ef31 Add changelog 2016-04-20 22:32:35 +08:00
Ronny Pfannschmidt
a986b8f945 Merge pull request #1525 from The-Compiler/traceback-item
Fix TracebackItem documentation in pytest.code
2016-04-20 09:38:03 +02:00
Florian Bruhin
c24e8e01b4 Fix TracebackItem documentation in pytest.code
The TracebackItem class does not exist, but it seems the docstrings refer to
TracebackEntry.
2016-04-20 08:35:17 +02:00
MengJueM
1a37035d71 Add test test_absolute_win32_path 2016-04-20 01:27:37 +08:00
TomV
99c4b6fdc3 issue 1496 - xfail with condition keyword 2016-04-19 18:12:37 +01:00
Meng Jue
dd2425675b Fix a small issue about shlex.split not working well with win32 path 2016-04-19 13:08:08 +08:00
Bruno Oliveira
6a3c943ce2 Merge pull request #1514 from The-Compiler/docfix
doc: Use ascii chars for file tree
2016-04-13 19:13:53 -03:00
Florian Bruhin
98430a17f2 doc: Use ascii chars for file tree
LaTeX doesn't like those particular unicode chars, so let's avoid them so the
PDF builds easily.
2016-04-13 23:38:58 +02:00
Bruno Oliveira
fe6e1b2059 Merge pull request #1506 from prusse-martin/fix-repr-tb-with-unicode
Fix `repr_traceback` to work with unicode errors
2016-04-09 13:03:55 -03:00
Martin Prusse
7ce5873da2 Perform a "unicode aware" check for maximum recursion depth error
Avoid errors `UnicodeErrosr`s due non maximum recursion depth errors
when checking for those errors.
2016-04-08 23:32:18 -03:00
Ronny Pfannschmidt
0eb80bcb5a Merge pull request #1504 from guettli/patch-1
Hudson -> Jenkins
2016-04-05 14:57:03 +02:00
Thomas Güttler
fb45f82840 Hudson -> Jenkins 2016-04-05 14:08:30 +02:00
Bruno Oliveira
5af5ba11d3 Merge pull request #1500 from tgoodlet/lstrip-keywordexpr
Always lstrip() keyword expression
2016-04-01 17:48:58 -03:00
Tyler Goodlet
053c052190 Always lstrip() keyword expression 2016-04-01 13:10:05 -04:00
Bruno Oliveira
56156bb119 Merge pull request #1490 from omarkohl/get_issues
Adapt get_issues.py script for GitHub (instead of Bitbucket)
2016-03-28 20:15:29 -03:00
Omar Kohl
e048315d9b Adapt get_issues.py script for GitHub (instead of Bitbucket) 2016-03-28 20:00:57 +02:00
Florian Bruhin
3155d0ca9c Merge pull request #1475 from nicoddemus/classifier-docs
Mention Pytest::Framework PyPI classifier on docs
2016-03-23 06:12:34 +01:00
Bruno Oliveira
53d319144d Mention Pytest::Framework PyPI classifier on docs 2016-03-22 20:42:52 -03:00
Ronny Pfannschmidt
6ddfd60ce7 Merge pull request #1472 from pquentin/master
Fix pytest.mark.skip mark when used in strict mode
2016-03-22 13:49:57 +01:00
Quentin Pradet
653a53226a Add strict parameter to xfail marker doc 2016-03-22 16:01:51 +04:00
Quentin Pradet
1fbd19b8cb Fix pytest.mark.skip mark when used in strict mode 2016-03-22 15:40:34 +04:00
Ronny Pfannschmidt
890c2fa555 Merge pull request #1469 from omarkohl/doc-fixes
Doc fixes
2016-03-20 20:48:36 +01:00
Omar Kohl
725290a8ab Add 'Minor doc fixes' to CHANGELOG 2016-03-20 20:39:31 +01:00
Omar Kohl
98c707561c Document --full-trace option and KeyboardInterrupt
fix #513
2016-03-20 20:07:16 +01:00
Omar Kohl
a341dddc74 Replace --fulltrace with --full-trace in doc
Since 'py.test --help' shows --full-trace as an option (and not --fulltrace)
even though both forms are accepted I assume --full-trace is the preferred form
and should therefore be used in the documentation.
2016-03-20 19:51:23 +01:00
Omar Kohl
16e49d96d1 Replace --traceconfig with --trace-config in doc
Since 'py.test --help' shows --trace-config as an option (and not
--traceconfig) even though both forms are accepted I assume --trace-config is
the preferred form and should therefore be used in the documentation.
2016-03-20 17:12:50 +01:00
Omar Kohl
ec62a3c9e4 Fix minor typo in 'writing plugins' doc 2016-03-20 17:01:04 +01:00
Omar Kohl
f70ed83479 Fix 'test grouping by fixture instances' doc example
The fin() function was never added as a finalizer and did therefore not print
anything in the captured output.

In general improve the output by making it more verbose/explicit and extend the
final explanation.
2016-03-20 15:53:32 +01:00
Omar Kohl
dff914cadd Add short explanation and link to yield fixtures in fixture doc 2016-03-20 14:10:20 +01:00
Omar Kohl
3135463573 No longer refer to the 'yield fixture mechanism' as experimental (doc)
The feature has been there for a long time and in the 2.7.1 release notes it
says:

> fixed docs to remove the notion that yield-fixtures are experimental.

Therefore this one place was probably just missed.
2016-03-20 13:54:56 +01:00
Omar Kohl
266b53dfc2 Add Jenkins xUnit Plugin link to doc 2016-03-20 13:39:10 +01:00
Omar Kohl
bdb3581a52 Fix minor mistake in test discovery doc
The example output shown was for Python3 not Python2.

Add Python2 output and rephrase for clarity.
2016-03-20 13:24:43 +01:00
Omar Kohl
27b62740e3 Fix minor mistake in usage doc (pkg instead of pypkg) 2016-03-20 12:47:17 +01:00
Bruno Oliveira
a70e92777f Bump version to 2.9.2.dev1 2016-03-18 18:24:24 -03:00
Bruno Oliveira
3c011c05db Merge pull request #1460 from nicoddemus/release-2.9.1
Release 2.9.1
2016-03-18 18:22:21 -03:00
nicoddemus
168daaa71f Regendocs 2016-03-17 17:13:41 -04:00
nicoddemus
43fc1b47c0 Add announce doc 2016-03-17 17:10:02 -04:00
nicoddemus
699f094b0c Finalize CHANGELOG for 2.9.1 2016-03-17 17:04:26 -04:00
nicoddemus
9bdf51fcc5 Bump version to 2.9.1 2016-03-17 17:03:46 -04:00
Ronny Pfannschmidt
1d23999033 Merge pull request #1458 from The-Compiler/talks
Update talks page
2016-03-17 09:09:35 +01:00
Florian Bruhin
1d35a03812 Update talks page 2016-03-17 06:37:49 +01:00
Ronny Pfannschmidt
d70596da91 Merge pull request #1452 from palaviv/specify-paramterized-node-in-command-line
Specify paramterized node in command line - fixes #649
2016-03-16 11:16:54 +01:00
palaviv
a1277aaf0e added TODO comment to remove the parametrized workaround 2016-03-15 23:42:25 +02:00
Ronny Pfannschmidt
19cec79363 Merge pull request #1451 from blueyed/fix-doc-pytest_load_initial_conftests
docs: sort arguments for pytest_load_initial_conftests
the change is purely cosmetic, but its nice to have them ordered by name
2016-03-14 14:14:22 +01:00
palaviv
981fcb2798 added CHANGELOG and AUTHORS entry 2016-03-13 23:47:41 +02:00
palaviv
1ee3d40dbe allow parametrized nodes to be specified from command line 2016-03-13 23:37:21 +02:00
Daniel Hahler
bdddc9c38b doc: fix argument order for pytest_load_initial_conftests 2016-03-12 17:35:27 +01:00
Bruno Oliveira
b46c7dddaa Fix 'check' in markdown for github templates 2016-03-11 18:22:15 -03:00
Ronny Pfannschmidt
4636bf6160 Merge pull request #1391 from malinoff/issue1342
LastFailed now creates .cache only when needed. Fixes #1342
2016-03-11 11:18:50 +01:00
Dmitry Malinovsky
52a5acda92 Use testscollected to make xdist happy 2016-03-11 14:48:17 +06:00
Florian Bruhin
2b0ad4630d Merge pull request #1443 from nicoddemus/improve-import-error-msg
Improve error message when a plugin fails to import
2016-03-11 06:10:58 +01:00
Bruno Oliveira
3ea987ef9d Copy over name and path attributes to the re-raised exception 2016-03-10 18:13:59 -03:00
Bruno Oliveira
7a186df271 Improve error message when a plugin fails to import 2016-03-08 19:18:13 -03:00
Ronny Pfannschmidt
6d4b14d7ee Merge pull request #1438 from Bachmann1234/issue-1437
Make a good faith effort to display a bytestring when one is provided…
2016-03-06 06:48:32 +01:00
Ronny Pfannschmidt
fd0010e6e9 Merge pull request #1439 from pytest-dev/fix-1178
Support pytest.fail with non-ascii characters

Fixes #1178
2016-03-06 06:42:35 +01:00
Matt Bachmann
8ce32b0795 When a regex pattern contains bytes instead of a string use escape_encode to turn it into a string before further processing. Thanks @nicoddemus for the review and tips! 2016-03-05 21:04:34 -05:00
Bruno Oliveira
5d4703852e Add test when using native strings with non-ascii chars 2016-03-05 19:34:15 -03:00
Ronny Pfannschmidt
9b51536a18 Merge pull request #1440 from nicoddemus/issue-578
Fix decoding issue while formatting SyntaxErrors during collection
2016-03-05 21:47:44 +01:00
Bruno Oliveira
d8403d793f Fix decoding issue while formatting SyntaxErrors during collection
This happens only in Python 2, as in Python 3 we receive
the "badline" in the exception is already properly encoded

Fix #578
2016-03-05 16:58:50 -03:00
Bruno Oliveira
24d3e01548 pytest.fail with non-ascii characters raises an internal pytest error
Fix #1178
2016-03-05 16:09:01 -03:00
Ronny Pfannschmidt
3884398055 Merge pull request #1433 from nicoddemus/issue-1427
Mention that pytest_addoption may be used from conftest files

fixes #1427
2016-03-04 10:48:43 +01:00
Bruno Oliveira
819e0ead44 Mention that pytest_addoption may be used from conftest files
fix #1427
2016-03-03 18:26:01 -03:00
Bruno Oliveira
c8ca1d12d7 Merge pull request #1432 from tomviner/issue469-junit-double-colon
issue469: junit parsing nodeid, add method test
2016-03-03 08:03:27 -03:00
TomV
3d2b7aeea5 issue469: junit parsing nodeid, add method test 2016-03-03 09:12:56 +00:00
Ronny Pfannschmidt
7aa7c6bbfd Merge pull request #1431 from tomviner/issue469-junit-double-colon
fix #469 - correctly mangle junit test names when double colons are in parameter id's
2016-03-03 08:45:30 +01:00
TomV
28530836c9 issue469: add fix junit double colon split issue 2016-03-02 22:18:57 +00:00
Bruno Oliveira
055f1dc9ea Merge pull request #1430 from vovanbo/patch-1
Reformat versions and header levels in CHANGELOG file
2016-03-02 16:23:55 -03:00
Vladimir Bolshakov
0cbd58e16a Reformat versions and header levels in CHANGELOG file 2016-03-02 22:07:30 +03:00
Bruno Oliveira
b7b863b7bf Merge pull request #1429 from Zearin/patch-1
Minor README tweaks (markup, phrasing)
2016-03-02 15:37:12 -03:00
Zearin
4bcc06362d Minor README tweaks (markup, phrasing) 2016-03-02 11:59:04 -05:00
Bruno Oliveira
316e39872a Add 2.9.1.dev1 to CHANGELOG 2016-03-01 18:55:53 -03:00
Bruno Oliveira
81af1c024a Bump version to 2.9.1.dev1 2016-03-01 18:46:17 -03:00
Bruno Oliveira
e656dbb602 Merge branch 'release-290' 2016-03-01 18:45:33 -03:00
Floris Bruynooghe
63b69326b8 Add company name to flub in sprint attendees 2016-03-01 10:17:31 +00:00
nicoddemus
19d05814d2 Fix reference to _CallOutcome in docs 2016-02-29 17:09:31 -05:00
nicoddemus
7d2b65813e Fix typo in CHANGELOG 2016-02-29 16:48:28 -05:00
nicoddemus
f82c03833f Regendocs 2016-02-29 15:52:56 -05:00
nicoddemus
486421fca2 Release announcement for 2.9.0 2016-02-29 15:47:23 -05:00
nicoddemus
2d7cfcd686 Finalize CHANGELOG 2016-02-29 15:42:15 -05:00
nicoddemus
623e786524 Bump version to 2.9.0 2016-02-29 15:39:00 -05:00
Bruno Oliveira
5c09b33150 Merge pull request #1423 from lukas-bednar/junit_properties
junit: allow multiple properties with same name
2016-02-29 15:35:03 -03:00
Lukas Bednar
7e758a9dc6 junit: allow multiple properties with same name
It might happen that test can be affected by two or more bugs.
I need to be able to track them all.
2016-02-29 16:00:26 +01:00
Bruno Oliveira
3445bdaca2 Merge pull request #1418 from hackebrot/update-sprint-info
Add company name sprint doc
2016-02-27 19:48:36 -03:00
Raphael Pierzina
89151b8c63 Add company name to @hackebrot in sprint attendees 2016-02-27 22:15:07 +00:00
Bruno Oliveira
4194ddd5b4 Merge pull request #1417 from hackebrot/fix-docs-on-traceback-styles
Fix docs on traceback styles
2016-02-27 18:28:57 -03:00
Raphael Pierzina
ab90043adc Update changelog with traceback style docs fix 2016-02-27 21:07:13 +00:00
Raphael Pierzina
a95fe3693b Add auto to tb styles (new default since v2.6) 2016-02-27 20:54:44 +00:00
Bruno Oliveira
b64aaac7ec Merge pull request #1416 from blubber/fix-mark-documentation
Fix a typo in the docstring for mark.MarkGenerator
2016-02-27 11:58:43 -03:00
Tiemo Kieft
424b46de1b Fix a typo in the docstring for mark.MarkGenerator 2016-02-27 14:15:42 +01:00
Florian Bruhin
c9927bb66f Merge pull request #1414 from rygwdn/indexerror
catch IndexError exceptions when getting exception source location
2016-02-26 18:53:10 +01:00
Florian Bruhin
cbb5d48fdd Merge pull request #1413 from nicoddemus/skip-if-parametrize
Fix skip/xfail markers in parametrized arguments
2016-02-26 18:41:37 +01:00
Ryan Wooden
d98d655094 Simplify IndexError test. 2016-02-26 08:25:49 -04:00
Anatoly Bubenkov
cf9a09e988 catch IndexError exceptions when getting exception source location 2016-02-26 08:18:12 -04:00
Bruno Oliveira
d9ede1bac2 Add test for unconditional skip with reason 2016-02-25 20:02:34 -03:00
Bruno Oliveira
5f90907509 Fix skip/xfail markers in parametrized arguments
Fix #1412
2016-02-25 19:13:09 -03:00
Bruno Oliveira
310bada6f5 Merge pull request #1409 from nicoddemus/builtin-fixtures-docs
Add reference to some builtin fixtures in "contents"
2016-02-25 15:46:33 -03:00
Bruno Oliveira
08b40396c9 Merge pull request #1400 from nicoddemus/contributing-improvements
Contributions to CONTRIBUTING.rst
2016-02-25 15:46:04 -03:00
holger krekel
a50209b29e add some more people, and preliminary company affiliations 2016-02-25 06:50:19 +01:00
Bruno Oliveira
a7b907d325 Add Raphael Pierzina to AUTHORS 2016-02-23 20:29:08 -03:00
Bruno Oliveira
c78a8b28dc Add reference to some builtin fixtures in "contents"
Fix #1408
2016-02-23 17:26:24 -03:00
Bruno Oliveira
3a6a0f1220 Merge pull request #1410 from milliams/master
Correct JUnit test invocation example
2016-02-23 08:06:21 -03:00
Matt Williams
fc4e240596 Correct JUnit test invocation example 2016-02-23 10:05:51 +00:00
holger krekel
5507a4d239 Merge pull request #1405 from nicoddemus/collection-atty
Display collect progress only when in a terminal
2016-02-21 08:17:35 +01:00
Bruno Oliveira
01479189a5 Merge pull request #1406 from lwm/fix-typo
fix typo
2016-02-20 20:49:58 -02:00
Luke Murphy
ddb7060535 fix typo 2016-02-20 23:44:55 +01:00
Bruno Oliveira
96a331e32f Display collect progress only when in a terminal
Fix #1397
2016-02-20 14:38:30 -02:00
Bruno Oliveira
688622f5cf Code review suggestions 2016-02-19 18:27:33 -02:00
Bruno Oliveira
fa7b0086a2 Move sections around (from easier to hardest) and added howto for repo transfers
#1282
2016-02-18 19:17:06 -02:00
Ronny Pfannschmidt
3874d53ee1 Merge pull request #1399 from nicoddemus/master
Add issue and PR GitHub templates
2016-02-18 21:38:50 +01:00
Bruno Oliveira
b76de91474 Add issue and PR GitHub templates 2016-02-18 18:26:38 -02:00
Bruno Oliveira
6940f82235 Merge pull request #1389 from nicoddemus/fix-strict-xfail
Fix bug in strict xfail: test was not being actually called
2016-02-17 17:58:49 -02:00
Bruno Oliveira
f6a2f779ae Merge pull request #1393 from hackebrot/add-license-section-to-homepage
Add license section to homepage
2016-02-17 17:53:49 -02:00
Raphael Pierzina
19536c9f05 Add full MIT license text to the docs section 2016-02-17 00:54:54 +00:00
Raphael Pierzina
e4c1b9c1c4 Add link to license page to index.rst 2016-02-17 00:47:49 +00:00
Raphael Pierzina
25aed0dca8 Add a license section to the docs toc 2016-02-16 19:42:16 +00:00
Raphael Pierzina
b2f837acd8 Add a reference to LICENSE file to README 2016-02-16 19:40:06 +00:00
Raphael Pierzina
3ef003f0ff Wrap lines at 79 characters 2016-02-16 19:14:55 +00:00
Raphael Pierzina
baabde76ae Insert copyright holders and year as mentioned in README 2016-02-16 19:13:39 +00:00
Raphael Pierzina
c805412d47 Grab official MIT license text from opensource.org 2016-02-16 19:12:22 +00:00
Dmitry Malinovsky
e4d361b093 LastFailed now creates .cache only when needed. Fixes #1342 2016-02-16 11:42:04 +06:00
Bruno Oliveira
bed56e504a Merge pull request #1390 from tomviner/bugfix/docs-typo-plugins
Fix usage of backticks in ReStructured Text files
2016-02-15 22:14:05 -02:00
TomV
3dd50d039d Fix rst syntax 2016-02-15 23:07:31 +00:00
Bruno Oliveira
0eeb466f11 Fix strict xfail: it should behave exactly like xfail when a test fails 2016-02-15 19:18:48 -02:00
Bruno Oliveira
ee88679c54 Fix bug in strict xfail: test was not being actually called 2016-02-15 18:43:45 -02:00
Bruno Oliveira
9af1f63ab6 Small typo in CHANGELOG 2016-02-14 23:36:17 -02:00
Bruno Oliveira
fb7bbf816c Merge branch 'strict-xpass' 2016-02-14 23:35:03 -02:00
Bruno Oliveira
9aae4b782b Add CHANGELOG entry for #1355 2016-02-14 23:31:31 -02:00
Ronny Pfannschmidt
1d190dc618 Merge pull request #1386 from nicoddemus/strict-xpass
Add a "strict" parameter to xfail
2016-02-15 02:29:43 +01:00
Bruno Oliveira
7823838e69 Add strict option to xfail, making tests which XPASS to actually fail the suite
Fix #1355
2016-02-14 20:52:27 -02:00
Bruno Oliveira
a965386b9e Add bool type to addini 2016-02-14 20:52:27 -02:00
Bruno Oliveira
6f0d90cd5a Merge pull request #1385 from RonnyPfannschmidt/release-process-update
update release process to ensure correct usage of base branches and p…
2016-02-14 00:54:25 -02:00
Ronny Pfannschmidt
48424a6bf6 update release process to ensure correct usage of base branches and propper finalization 2016-02-13 20:30:58 +01:00
holger krekel
2a8d58814a Merge pull request #1384 from hpk42/master
trying to settle the new layout
2016-02-13 16:56:44 +01:00
holger krekel
fa601de5c4 update participants 2016-02-13 16:55:16 +01:00
holger krekel
8284d14ec4 re-introduce topics 2016-02-13 16:54:06 +01:00
holger krekel
238dcd8bae some layout refinements 2016-02-13 14:30:10 +01:00
holger krekel
b95ff7104c Merge branch 'master' of https://github.com/pytest-dev/pytest
removed doc/en/announce/sprint-funding-2016.txt
because /doc/en/announnce/sprint2016.txt is the one which
we published.
2016-02-13 14:18:05 +01:00
holger krekel
03f8b50c8a Merge pull request #1383 from RonnyPfannschmidt/founding-header
give the founding header contrast
2016-02-13 14:10:44 +01:00
Ronny Pfannschmidt
dc7f76c276 adapt the header text based on a discussion with bruno 2016-02-13 13:40:59 +01:00
Ronny Pfannschmidt
20bd56f4b2 give the header contrast 2016-02-13 13:24:28 +01:00
Bruno Oliveira
89c75b2c91 Fix tox doc task 2016-02-13 02:09:25 -02:00
Bruno Oliveira
341bc33ff3 Merge pull request #1380 from biern/fix-utf8-explanation
Fix formatting utf-8 error explanation
2016-02-12 19:14:50 -02:00
Bruno Oliveira
fe10057c15 Merge pull request #1382 from RonnyPfannschmidt/founding-header
add links for the funding campaign in the header and sidebar
2016-02-12 18:45:32 -02:00
Ronny Pfannschmidt
48b62e4d89 add links for the funding campaign in the header and sidebar 2016-02-12 21:14:06 +01:00
Marcin Biernat
1b431d6644 fix formatting utf-8 error explanation 2016-02-12 20:28:06 +01:00
holger krekel
b1955c7f84 Merge pull request #1374 from RonnyPfannschmidt/fix-1366
fix issue #1366 by showing a note on the --fulltrace option
2016-02-11 10:45:12 +01:00
Ronny Pfannschmidt
bfa2fadac1 fix issue #1366 by showing a note on the --fulltrace option 2016-02-10 20:27:50 +01:00
holger krekel
48109b0e60 remove superflous file 2016-02-09 15:45:51 +01:00
holger krekel
fdce2306a7 fix names once again 2016-02-09 15:44:32 +01:00
Florian Bruhin
f00577f7c4 Also fix Brianna's name. 2016-02-09 15:37:45 +01:00
Florian Bruhin
569dbeb087 Fix names properly. 2016-02-09 15:36:59 +01:00
Bruno Oliveira
c6938221ab Merge branch 'issue1290-at-operator' 2016-02-06 09:38:45 -02:00
Bruno Oliveira
2b764a0e73 Fix CHANGELOG entry to new format 2016-02-06 09:37:13 -02:00
Bruno Oliveira
51694b8295 Merge branch 'master' into issue1290-at-operator 2016-02-06 09:31:42 -02:00
Bruno Oliveira
2e04771893 Merge pull request #1358 from RonnyPfannschmidt/features
merge features into master to begin the 2.9 line
2016-02-06 00:26:30 -02:00
TomV
7d107018e8 Fix #1290: Py3.5's @ operator/assertion rewriting. 2016-02-05 23:09:57 +00:00
holger krekel
05aad5c381 fix brianna's name once again 2016-02-05 14:12:35 +01:00
holger krekel
1e0088a949 fix EUR->USD 2016-02-05 14:11:30 +01:00
holger krekel
ba3b29e831 add a link to lieve indiegogo campaign in the pytest sprint page 2016-02-05 14:04:32 +01:00
holger krekel
6218e20e88 - preliminary sprint page, not yet linked through the header
- also removed plugins_index call from tox.ini "doc" env because it's not there
2016-02-05 11:34:40 +01:00
Ronny Pfannschmidt
190a52badb correct merge misstake in appveyor.yml 2016-02-05 01:18:11 +01:00
Ronny Pfannschmidt
7b2956e10b merge latest master into features as well 2016-02-05 00:13:48 +01:00
Ronny Pfannschmidt
de1a9f574c merge from master 2016-02-05 00:10:28 +01:00
Ronny Pfannschmidt
05a26d995b Merge pull request #1354 from gdyuldin/no_raise_message
Add expected exceptions to pytest.raises fail message
2016-02-03 13:54:24 +01:00
Georgy Dyuldin
79722ae89b Add expected exceptions to 'DID NOT RAISE' msg 2016-02-03 14:12:41 +03:00
Bruno Oliveira
b5dc7d9be1 Merge pull request #1350 from pytest-dev/appveyor-pypy
Test with pypy and enable coveralls in AppVeyor
2016-02-01 19:31:48 -02:00
Bruno Oliveira
30e61f2777 Test with pypy and enable coveralls in AppVeyor
* Install pypy using chocolatey
* Enable coveralls test environment in AppVeyor
* Suggest maintaining build matrix in .travis.yml by using "tox --listenvs"

 Fix #1254
2016-02-01 17:40:30 -02:00
holger krekel
58af604f82 Merge pull request #1346 from hpk42/master
merge sprint funding draft -- please feel free to commit directly.
2016-01-30 19:13:17 +01:00
holger krekel
c0024a723d fixed name 2016-01-30 19:12:41 +01:00
holger krekel
545bf0d5a1 a few refinements 2016-01-29 15:43:15 +01:00
holger krekel
70b5d5aee9 sprint funding draft 2016-01-29 14:52:07 +01:00
Bruno Oliveira
fde44f4f30 Merge branch 'better_flag_names' of https://github.com/MichaelAquilina/pytest into features 2016-01-27 20:02:50 -02:00
Bruno Oliveira
fa4d832507 Merge branch 'better_flag_names' into features 2016-01-27 19:57:23 -02:00
Bruno Oliveira
74a68b5ec6 Add CHANGELOG and docs for #1345 2016-01-27 19:57:11 -02:00
Michael Aquilina
e35ce98f89 Add explicit flag names for failed first and last failed 2016-01-27 21:28:38 +00:00
Bruno Oliveira
dd0062c177 Dummy change to test readthedocs hook 2016-01-27 08:59:26 -02:00
Bruno Oliveira
6c37a51f95 Fix version at the top of the CHANGELOG 2016-01-27 08:28:18 -02:00
Ronny Pfannschmidt
52ac6cd7a9 Merge pull request #1199 from nicoddemus/move-pycode-to-pytest
Copy py.code code to py.test
2016-01-26 23:29:09 +01:00
Bruno Oliveira
4825678e1a Add CHANGELOG entry for py.code merge 2016-01-26 20:24:08 -02:00
Bruno Oliveira
e43eaffd93 Remove unused import 2016-01-25 23:30:53 -02:00
Bruno Oliveira
9f85d4c952 mark test_comments as xfail on pypy
while migrating this code it was noticed that this test was failing even
on the original py repository, so it was decided to xfail it and investigate
later
2016-01-25 23:18:04 -02:00
Bruno Oliveira
7a6f902f6f Drop assertionnew and assertionold from _pytest._code 2016-01-25 23:18:04 -02:00
Bruno Oliveira
a912d3745b Moved py.code code over to py.test
Fix #103
2016-01-25 23:18:04 -02:00
Ronny Pfannschmidt
f23307b06d Merge release branch into mater 2016-01-25 00:01:43 +01:00
Ronny Pfannschmidt
6c3e6401d4 begin 2.8.8 cycle 2016-01-24 23:56:56 +01:00
Ronny Pfannschmidt
3315b3a12f finalize changelog for 2.8.7 2016-01-24 23:23:15 +01:00
Ronny Pfannschmidt
64d7d00218 Prepare 2.8.7 release 2016-01-24 17:59:48 +01:00
Ronny Pfannschmidt
7c747c97ec Merge pull request #1339 from RonnyPfannschmidt/fix-1338
Fix 1338
2016-01-24 17:51:00 +01:00
Ronny Pfannschmidt
56c5db6e12 add requests dependency to tox.ini to ensure all monkeypatch tests run 2016-01-24 12:30:38 +01:00
Ronny Pfannschmidt
2d05f831fe monkeypatch: unnest handling code
this avoid python3 nested exceptions
2016-01-24 12:28:14 +01:00
Ronny Pfannschmidt
cb6181255e changelog 2016-01-24 00:45:59 +01:00
Ronny Pfannschmidt
cd9e30b221 work around python 2/3 difference by using str(exception) 2016-01-24 00:40:27 +01:00
Ronny Pfannschmidt
d028fe1e66 remove unused import 2016-01-23 20:29:54 +01:00
Ronny Pfannschmidt
b825af2e66 pass trough annotated exceptions 2016-01-23 19:31:17 +01:00
Ronny Pfannschmidt
60e9698530 fix issue 1338 2016-01-23 19:12:51 +01:00
Ronny Pfannschmidt
9e6bb74d71 reformat monkeypatch core plugin/its tests 2016-01-23 19:12:51 +01:00
Bruno Oliveira
ed3c96ee58 Merge pull request #1336 from nicoddemus/merge-master-in-features
Merge master in features
2016-01-22 19:05:34 -02:00
Bruno Oliveira
199fcf93d4 Merge branch 'master' into 'features' 2016-01-22 18:32:45 -02:00
Bruno Oliveira
c8caa87759 Merge branch 'release-2.8.6' 2016-01-22 18:00:01 -02:00
Bruno Oliveira
b7de0401b8 Bump version to 2.8.7.dev1 2016-01-22 17:59:25 -02:00
Bruno Oliveira
01793ed8bc Use a full url to pytest logo on README
So it appers correctly in PyPI
2016-01-22 17:46:28 -02:00
Bruno Oliveira
82d00efa8d 2.8.6 release: version, CHANGELOG
Remove note about expected failing envs in tox, as tox now supports
skipping certain environments based on the platform.
2016-01-21 19:17:53 -02:00
Bruno Oliveira
61c569f960 Merge branch 'junit_stdout_err_on_error' 2016-01-20 19:06:50 -02:00
Bruno Oliveira
dd56d7b7fc Add CHANGELOG and AUTHORS entry for #1334 2016-01-20 19:00:52 -02:00
Bruno Oliveira
4de3d595c9 Merge pull request #1333 from lesteve/fix-practise-typo
Fix practise -> practice typo in documentation
2016-01-20 18:03:15 -02:00
Georgy Dyuldin
b28b3cc271 Add captured stdout to jUnit report on setup error 2016-01-20 20:13:01 +03:00
Loïc Estève
99072ea8c9 Fix practise -> practice typo in documentation 2016-01-20 16:35:27 +01:00
Ronny Pfannschmidt
11a7bcaaa5 Merge pull request #1322 from nicoddemus/missing-hooks-docs
Add a few missing hooks to the docs
2016-01-19 08:12:58 +01:00
Bruno Oliveira
0caee1a673 Add a few missing hooks to the docs 2016-01-18 19:27:35 -02:00
Bruno Oliveira
4c87a6aa09 Merge pull request #1330 from nicoddemus/fix-flakes
Fix flakes
2016-01-18 15:36:20 -02:00
Bruno Oliveira
7b13c4bec0 Fix flakes 2016-01-14 21:01:57 -02:00
Bruno Oliveira
aa8c352c10 Merge pull request #1323 from jeffwidman/patch-1
Change `input` to `test_input` in docs for clarity
2016-01-12 23:28:52 -02:00
Jeff Widman
ee75ecbda0 Change input to test_input in docs for clarity
Using `input` is confusing because it's also the name of a Python built-in function. So we use `test_input` instead. Fix #1321
2016-01-12 17:01:34 -08:00
Ronny Pfannschmidt
bc32e45bb6 Merge pull request #1314 from The-Compiler/ci-verbose
Always show full comparison output if on CI.
2016-01-11 10:05:15 +01:00
Florian Bruhin
3e5c9038ec Always show full comparison output if on CI.
When you don't get enough information with a test running on a CI, it's quite
frustrating, for various reasons:

- It's more likely to be a flaky test, so you might not be able to reproduce
  the failure.
- Passing -vv is quite bothersome (creating a temporary commit and reverting
  it)

For those reasons, if something goes wrong on CI, it's good to have as much
information as possible.
2016-01-11 08:45:04 +01:00
Ronny Pfannschmidt
b2c0864fbf Merge pull request #1318 from nicoddemus/doctest-unicode-error
Fix decode error in Python 2.7 when docstrings contain a non-ascii character
2016-01-11 08:37:24 +01:00
Florian Bruhin
a80efb038a Merge pull request #1320 from bionikspoon/patch-1
update docs plugin.rst typo
2016-01-11 07:37:46 +01:00
Manu Phatak
5b29f579c5 update docs plugin.rst typo 2016-01-11 00:11:46 -06:00
Bruno Oliveira
808cb8e3ad Merge branch 'stdout-pdb-capture' 2016-01-09 11:46:13 -02:00
Bruno Oliveira
8727503dd4 Add CHANGELOG entry for #1223 2016-01-09 11:45:56 -02:00
foxx
f46de68804 Fixes bug with stdout/stderr capture on pdb 2016-01-09 12:04:26 +00:00
Bruno Oliveira
3c19cfcd9a Fix decode error in Python 2.7 when docstrings contain a non-ascii character
Fix #628
2016-01-08 23:10:02 -02:00
Bruno Oliveira
b8784c28c9 Merge branch 'master' into 'features' 2016-01-08 21:51:34 -02:00
Bruno Oliveira
29b05c8391 Merge pull request #1309 from k4rtik/patch-1
Correct platform name osx -> darwin
2016-01-05 23:29:33 -02:00
Kartik Singhal
26c835eea5 Correct platform name osx -> darwin 2016-01-05 18:43:03 -05:00
Florian Bruhin
eebf5c1d2c Merge pull request #1304 from nicoddemus/rst-changelog
Changelog now in rst format (2.9.0 and onward) and add rst-lint check
2016-01-05 14:05:53 -08:00
Bruno Oliveira
3daa0756eb Add CHANGELOG.rst to MANIFEST and small format fix 2016-01-05 20:01:41 -02:00
Bruno Oliveira
3e34db50fb Rename "flakes" testenv to "linting" as requested in review 2016-01-05 18:18:29 -02:00
Ronny Pfannschmidt
e2603d7050 Merge pull request #1306 from nicoddemus/summary-warnings
pytest warnings emitted during ``pytest_terminal_summary`` are now properly displayed.
2016-01-04 11:16:33 +01:00
Bruno Oliveira
369d9ecaa5 pytest warnings emitted during `pytest_terminal_summary` are now properly displayed.
Fix #1305
2016-01-04 00:07:45 -02:00
Bruno Oliveira
02dd6df6e6 Changelog now in rst format (2.9.0 and onward) and add rst-lint check
Fix #1274
2016-01-03 23:09:24 -02:00
Ronny Pfannschmidt
6c170201d6 Merge branch 'master' into features 2016-01-02 23:56:01 +01:00
Ronny Pfannschmidt
bf4de4bd68 Merge pull request #1294 from nicoddemus/doctest-bytes-literals
Doctest bytes literals
2016-01-01 18:18:08 +01:00
Bruno Oliveira
80d6d94635 Merge pull request #1298 from aktech/patch-1
Update copyright in README
2016-01-01 12:06:21 -02:00
AMiT Kumar
63cba1ed0d Update copyright in README 2016-01-01 18:52:56 +05:30
Ronny Pfannschmidt
71ab6b8b05 Merge pull request #1295 from nicoddemus/monkeypatch-perf
Make monkeypatch calls O(1)
2015-12-30 22:21:16 +01:00
Ronny Pfannschmidt
c367180ab2 Merge pull request #1297 from nicoddemus/multi-doctest-glob
Multiple --doctest-glob arguments
2015-12-30 22:18:46 +01:00
Bruno Oliveira
1bdf71730a Complement #1255 by adding tests and docs
Fix #1242
2015-12-30 18:24:59 -02:00
jab
0ea8dc0d40 make --doctest-glob multi-allowed 2015-12-30 17:32:14 -02:00
Bruno Oliveira
638b3f5e39 Make monkeypatch calls O(1)
Fix #1292
2015-12-29 22:04:35 -02:00
Bruno Oliveira
309ecf7ab3 Rename "BugFixes/Adjustments" to just "Bug Fixes" as commented elsewhere 2015-12-29 21:09:15 -02:00
Bruno Oliveira
719d63085d Add CHANGELOG entry for ALLOW_BYTES doctest option 2015-12-29 21:08:25 -02:00
Bruno Oliveira
5a5b732fe1 Add docs for ALLOW_BYTES doctest option
Fix #1287
2015-12-29 21:05:11 -02:00
Bruno Oliveira
a0edbb75a4 Implement ALLOW_BYTES doctest option
Fix #1287
2015-12-29 20:55:19 -02:00
Bruno Oliveira
0ef73ed3e0 Small typo on README 2015-12-28 18:56:14 -02:00
Bruno Oliveira
7cfb750d7f Small wording on README 2015-12-28 18:55:20 -02:00
Bruno Oliveira
70f72229c6 Merge pull request #1291 from ulope/master
Make monkeypatch differentiate ImportError sources
2015-12-28 13:18:25 -02:00
Ulrich Petri
2e02579437 Add CHANGELOG entry for #900 fix 2015-12-28 10:53:38 +01:00
Ulrich Petri
8d49abb0d1 Make monkeypatch differentiate ImportError sources
Previously `monkeypatch` assumed that any `ImportError` was caused by
a mistake in the specified import path. However this assumption is false 
in case the import target itself causes an `ImportError`.

Fixes: #900
2015-12-27 22:44:23 +01:00
Ronny Pfannschmidt
c5631b6567 Merge pull request #1284 from nicoddemus/pyversions-badge
Add badge of supported python versions to README
2015-12-27 16:05:43 +01:00
Ronny Pfannschmidt
522224ee7c Merge pull request #1285 from nicoddemus/readme-changes-logo
Add logo and some readme changes
2015-12-27 15:47:29 +01:00
Bruno Oliveira
015e8e574a Merge pull request #1289 from WoLpH/patch-1
Updated out of date link
2015-12-27 07:22:41 -02:00
Rick van Hattem
87ff7ee232 Updated out of date link 2015-12-27 02:35:02 +01:00
Bruno Oliveira
b5490b289d Add logo and update readme
* Show pytest logo
* Show assertion introspection in the example
* Use separate sections for change-log, docs, license, etc.
2015-12-26 12:47:22 -02:00
Ronny Pfannschmidt
46039f8687 Merge pull request #1286 from nicoddemus/changelog-sections
Separate 2.9.0 CHANGELOG into sections
2015-12-26 11:09:45 +01:00
Bruno Oliveira
6b25fb4d64 Separate 2.9.0 CHANGELOG into sections
Fix #1275
2015-12-25 20:44:03 -02:00
Bruno Oliveira
99a5067edb Merge branch 'pyreadline-workaround' 2015-12-25 18:03:25 -02:00
Bruno Oliveira
5afb61ad26 Fix trailing white-space 2015-12-25 17:51:55 -02:00
Bruno Oliveira
fbfab6778c Give proper thanks for the PR in the CHANGELOG 2015-12-25 17:48:03 -02:00
Bruno Oliveira
57bc14caa0 Apply readline workaround during initial conftest loading 2015-12-25 17:46:19 -02:00
Bruno Oliveira
df3f21afb6 Add badge of supported python versions to README 2015-12-25 16:28:50 -02:00
Ronny Pfannschmidt
1a87bb2416 Merge pull request #1280 from nicoddemus/del-index-from-overview
Remove "index" entry from overview.rst
2015-12-25 07:33:15 +01:00
Erik M. Bray
6e170a4a1c * Moved workaround to its own function, mostly for the sake of adding
a more descriptive docstring (the workaround itself is just to import
  readline earlier).  I removed the conditional on targetfd from the
  workaround since it doesn't really matter.

* Added # noqa marker.

* Added changelog entry, and self to authors.
2015-12-24 16:43:34 -05:00
Erik M. Bray
924a9667e1 Make sure readline has been imported before duping any stdio handles--otherwise pyreadline fails to connect to the correct handle for the console's stdout/in. 2015-12-24 14:56:57 -05:00
Bruno Oliveira
319f6310f8 Remove "index" entry from overview.rst
"index" is already displayed in the main docs, repeating it here breaks the reading flow
2015-12-24 16:08:43 -02:00
Bruno Oliveira
cd3a441304 Merge pull request #1277 from peterdemin/patch-1
doc typo
2015-12-24 15:57:01 -02:00
Peter Demin
8180165229 doc typo 2015-12-24 08:56:54 -05:00
Ronny Pfannschmidt
ec5a429c77 junitxml tests: extend with extra items 2015-12-17 22:30:27 +01:00
Ronny Pfannschmidt
713069ebd4 implement review comments for #1266 2015-12-17 22:27:01 +01:00
Ronny Pfannschmidt
f7af08d309 Merge pull request #1268 from nicoddemus/merge-newinterpret
Merge newinterpret.py into reinterpret.py
2015-12-17 07:10:47 +01:00
Bruno Oliveira
943099ddd1 Merge newinterpret into reinterpret.py 2015-12-16 18:31:43 -02:00
Bruno Oliveira
379562107e Merge pull request #1267 from nicoddemus/remove-pre-2.6
Remove pre-2.6 support code and docs (including obsolete oldinterpret module)
2015-12-16 18:30:53 -02:00
Bruno Oliveira
25c392196f Mention #1226 in the CHANGELOG 2015-12-16 16:48:49 -02:00
Bruno Oliveira
ec597e81a4 Remove references to python pre-2.6 from docs 2015-12-16 16:16:22 -02:00
Bruno Oliveira
81588d7f63 Remove obsolete "oldinterpret" module 2015-12-16 16:16:22 -02:00
Bruno Oliveira
af893aab26 Remove code related to support python <= 2.5
Fix #1226
2015-12-16 16:16:22 -02:00
Ronny Pfannschmidt
8bf7e7cc4b fixes #1259 - take finalized nodes out of the mapping
this allows double node id usage for file based items
2015-12-16 19:09:44 +01:00
Ronny Pfannschmidt
7b20288c2b Merge pull request #1264 from nicoddemus/exception-interact-deprecated
Remove experimental status from pytest_exception_interact
2015-12-16 18:32:58 +01:00
Bruno Oliveira
2b2bec6b97 Remove experimental status from pytest_exception_interact 2015-12-16 14:29:39 -02:00
Ronny Pfannschmidt
fcc20d4181 Merge pull request #1257 from nicoddemus/merge-master-into-features
Merge master into features after 2.8.5
2015-12-12 08:15:55 +01:00
Bruno Oliveira
6ac31088c5 Merge branch 'master' into merge-master-into-features 2015-12-11 23:13:50 -02:00
Bruno Oliveira
7eea6b3b02 Add 2.8.6.dev1 to CHANGELOG 2015-12-11 23:11:36 -02:00
Bruno Oliveira
e87facfb22 Bump master to 2.8.6.dev1 2015-12-11 23:07:30 -02:00
Bruno Oliveira
855b115dab Remove plugins_index generation from HOWTORELEASE 2015-12-11 22:35:44 -02:00
Bruno Oliveira
4263b8b407 Restore regen-doc result 2015-12-11 22:34:16 -02:00
Florian Bruhin
7d150c20cf Regenerate 2.8.5 docs. 2015-12-11 22:28:23 -02:00
Bruno Oliveira
a124163425 Prepare for 2.8.5: bump version, CHANGELOG, announce 2015-12-11 19:20:07 -02:00
Ronny Pfannschmidt
2382546112 Merge pull request #1252 from nicoddemus/optimize-app-veyor
Optimize appveyor build
2015-12-11 16:10:45 +01:00
Bruno Oliveira
946bb08da5 Tyding up the CHANGELOG 2015-12-11 00:36:26 -02:00
Bruno Oliveira
85d1f0404a Merge pull request #1243 from aselus-hub/master
Update python.py
2015-12-11 00:33:46 -02:00
Bruno Oliveira
1d60f61ba8 Optimize appveyor build
- AppVeyor does not run matrix builds in parallel, so creating a matrix with the intent to
  speed up the build will actually result in longer build times, as each matrix will execute
  in a brand new VM.
- tox does not detect 64 bit installations in AppVeyor, it always installs interpreters
  from C:\PythonX.Y, so there's no point to have 64bit builds
- No need for the auxiliary script "install.ps1" because all python versions we are interested
  in are already pre-installed in AppVeyor
2015-12-11 00:20:20 -02:00
aselus-hub
ad05cbe6da fixed meesage. 2015-12-10 15:39:31 -08:00
aselus-hub
1216a27b44 added docstrign to inection collection test. 2015-12-10 15:19:08 -08:00
aselus-hub
2b2240e904 added myself to authors, added changelog entry. 2015-12-10 15:15:09 -08:00
aselus-hub
74f7efd2a3 added line comparison that is pytest-sugar agnostic. 2015-12-10 15:10:55 -08:00
aselus-hub
34db8aed34 added verification that test actually passed. 2015-12-10 15:02:57 -08:00
Bruno Oliveira
926c6028bb Merge pull request #1250 from nicoddemus/merge-master-into-features
Merge master into features
2015-12-10 20:56:10 -02:00
aselus-hub
af54e09759 nit: fixed newline 2015-12-10 14:46:51 -08:00
aselus-hub
dfaeefd692 added test to verify injection. 2015-12-10 14:45:36 -08:00
Bruno Oliveira
86b6ce5042 Merge remote-tracking branch 'upstream/master' into merge-master-into-features 2015-12-10 19:41:14 -02:00
Bruno Oliveira
8f880e1625 Fix CHANGELOG merge 2015-12-10 19:40:45 -02:00
Bruno Oliveira
46c85bc352 Merge pull request #1249 from nicoddemus/remove-plugins-index
Remove plugins_index from the docs
2015-12-10 19:11:32 -02:00
Bruno Oliveira
8b61a332ba Merge remote-tracking branch 'bukzor/features-merge-master' into features 2015-12-10 19:03:55 -02:00
Bruno Oliveira
139c97930b Remove plugins_index from the docs
Fix #1229
2015-12-10 19:00:01 -02:00
Ronny Pfannschmidt
9cfee82f9b Merge pull request #1247 from alex/patch-1
Use https for images in readme
2015-12-10 00:02:47 +01:00
Alex Gaynor
c0a5f3df10 Use https for images in readme 2015-12-09 18:01:36 -05:00
Bruno Oliveira
8220c05b01 Merge pull request #1245 from RonnyPfannschmidt/howtorelease
add a reminder of the features branch merge to HOWTORELEASE.rst
2015-12-09 18:34:41 -02:00
Ronny Pfannschmidt
d0f5f6676b add a reminder of the features branch merge to HOWTORELEASE.rst 2015-12-09 21:20:54 +01:00
aselus-hub
ec02f694ef Update python.py
updated dictionary itteration to create a list for generation, so that tests can be added in the generator functions under python3. This works fine as-is in python2 because python 2 already creates a list, whereas python3 returns an itterator. Forcing a list format for the return fixes python3 to work the same way as python2
2015-12-09 11:32:19 -08:00
Ronny Pfannschmidt
6351e2846c Merge pull request #1232 from codewarrior0/report-passing-option
Add -rp and -rP options to report passing tests.
2015-12-09 06:57:42 +01:00
Ronny Pfannschmidt
1c70827f33 Merge pull request #1241 from nicoddemus/fix-deprecated-call-args
Fix deprecated_call regression introduced in 2.8.4
2015-12-09 06:48:43 +01:00
David Vierra
ccfd962170 Add "no -rP" case to test_pass_output_reporting 2015-12-08 17:33:03 -10:00
David Vierra
b417d7cb79 Add tests to test_terminal.py for -rp and -rP 2015-12-08 15:54:23 -10:00
Bruno Oliveira
1c46462991 Fix deprecated_call regression introduced in 2.8.4
Fix #1238
2015-12-08 22:40:05 -02:00
Buck Golemon
5ccb7b1ced update test_recwarn to new style 2015-12-08 11:08:33 -08:00
Buck Golemon
eabf2f9091 Merge branch 'master' into features
Conflicts:
	AUTHORS
	_pytest/__init__.py
	_pytest/hookspec.py
	_pytest/recwarn.py
	testing/test_recwarn.py
2015-12-07 14:28:59 -08:00
David Vierra
1db4cbcc9f Update AUTHORS and CHANGELOG 2015-12-07 12:17:30 -10:00
David Vierra
fbac936596 Add -rp and -rP options to report passing tests.
-rP is an alternative to `-s` for viewing the output of passing tests.
This causes the captured stdout/stderr of passing tests to be output in
the same way as that of failing tests.

-rp adds a simple one-line-per-test summary for passing tests.

Neither option is included by -ra.

Additional changes to `pytest_capturelog` and `pytest_catchlog` are
needed for this option to also output captured logs: They must be
changed to use `rep.sections.add` instead of `rep.longrepr.addsection`,
and to add these additional sections even if the test passes, since
passing tests don't seem to have a `longrepr` at report time.
2015-12-07 11:32:56 -10:00
Ronny Pfannschmidt
ffa572531a Merge pull request #1091 from RonnyPfannschmidt/fix-1074
fix issue 1074 and clean up junitxml
2015-12-07 22:13:16 +01:00
Ronny Pfannschmidt
fde2a6f5fd add changelog entry 2015-12-07 21:58:34 +01:00
Ronny Pfannschmidt
7b7737bf96 handle duplicate test ids via collection and xdist each reporting 2015-12-07 21:54:25 +01:00
Ronny Pfannschmidt
04e9ae75c8 add xfailing test for double test id failure 2015-12-07 21:54:25 +01:00
Ronny Pfannschmidt
9ea7826427 Junitxml: correct node reporter attribute names 2015-12-07 21:54:25 +01:00
Ronny Pfannschmidt
09cc45b0c5 junitxml: correct docstring of make_properties_node 2015-12-07 21:54:25 +01:00
Ronny Pfannschmidt
0aa54101c9 junitxml: follow Bruno's attribute/method naming hint from the review 2015-12-07 21:54:25 +01:00
Ronny Pfannschmidt
5eef6a2821 junitxml: fix python3 compat of the tests 2015-12-07 21:54:25 +01:00
Ronny Pfannschmidt
518c88f149 finalize nodereporters by throwing away the intermediate xml nodes, fixes issue #1074 2015-12-07 21:54:25 +01:00
Ronny Pfannschmidt
5f5a7995b9 reintroduce junitxml report order and debug cleanups 2015-12-07 21:54:24 +01:00
Ronny Pfannschmidt
0528e5b45f junitxml: intermediate, move testcase generation to NodeReporter 2015-12-07 21:54:24 +01:00
Ronny Pfannschmidt
9b04958303 junitxml: keep track of custom property insert order
+ review: should we allow the same key multile times
2015-12-07 21:54:24 +01:00
Ronny Pfannschmidt
faed54d6c7 junitxml: use node.warn to ensure fslocations 2015-12-07 21:54:24 +01:00
Ronny Pfannschmidt
1f609f96e6 junitxml: introduce nodereporter and track durations/properties there 2015-12-07 21:54:24 +01:00
Ronny Pfannschmidt
0664ae137c junitxml: remove debug print 2015-12-07 21:54:24 +01:00
Ronny Pfannschmidt
d0107c898e junitxml restrucutre stat generation - use node tags 2015-12-07 21:54:24 +01:00
Ronny Pfannschmidt
9128fec4c4 junitxml: simplify the api used for testing junitml 2015-12-07 21:54:24 +01:00
Ronny Pfannschmidt
80bcf8d624 junitxml: simplify tests by creating a api wrapper 2015-12-07 21:54:24 +01:00
Ronny Pfannschmidt
b8df5446c0 junitxml: yapf clean the tests 2015-12-07 21:54:24 +01:00
Ronny Pfannschmidt
2a31df072b junitxml: reverse the if/else logic for failure appending 2015-12-07 21:54:24 +01:00
Ronny Pfannschmidt
02f5defd89 yapf junitxml 2015-12-07 21:54:24 +01:00
Bruno Oliveira
efb5332023 Merge pull request #1186 from RonnyPfannschmidt/fix-manifest
fix #1185 - ensure MANIFEST.in exactly matches what should go to a sdist
2015-12-07 13:48:39 -02:00
Ronny Pfannschmidt
b9908cc036 Merge pull request #1228 from pytest-dev/pytest-2.8.4
finish the release process of pytest 2.8.4
2015-12-06 20:32:52 +01:00
Ronny Pfannschmidt
c727860241 bump to 2.8.5.dev 2015-12-06 20:31:14 +01:00
Ronny Pfannschmidt
8c17c7cd12 correct copy&paste error in the release announcement version number 2015-12-06 17:44:08 +01:00
Ronny Pfannschmidt
b7459b8a64 finish release announcement 2015-12-06 16:46:44 +01:00
Ronny Pfannschmidt
b920f09a95 doc regen for release 2.8.4 2015-12-06 16:14:23 +01:00
Ronny Pfannschmidt
a3353c49fd prepare release 2.8.4 - changelog updates + version bump 2015-12-06 16:13:55 +01:00
Ronny Pfannschmidt
a4a12b8356 Merge pull request #1227 from nicoddemus/warn-deprecated-call
Add a note about how DeprecationWarning and PendingDeprecationWarning are treated differently
2015-12-06 10:52:58 +01:00
Ronny Pfannschmidt
13ae2fe28b adopt review comment of #1186 2015-12-06 10:24:24 +01:00
Ronny Pfannschmidt
141a463fed fix #1185 - ensure MANIFEST.in exactly matches what should go to a sdist 2015-12-06 10:24:24 +01:00
Ronny Pfannschmidt
f508a52ca9 Merge pull request #1225 from pytest-dev/skipping-docs-inconsistency
Fix inconsistency in skipif example
2015-12-05 20:44:01 +01:00
Bruno Oliveira
b48a02fdb1 Add a note about how DeprecationWarning and PendingDeprecationWarning are treated differently
Fix #1026
2015-12-05 13:53:58 -02:00
Bruno Oliveira
382efc6363 Fix same inconsistency in next example 2015-12-05 00:43:06 -02:00
Bruno Oliveira
1bed514eb6 Fix inconsistency in skipif example
Fix #1224
2015-12-05 00:35:31 -02:00
Bruno Oliveira
41f19796e8 Merge pull request #1212 from nicoddemus/goodpractices
Goodpractises docs reorganization/review
2015-12-05 00:27:28 -02:00
Ronny Pfannschmidt
427e6c3b4d Merge pull request #1222 from nicoddemus/pastebin-unicode
Fix #1222 - pastebin when captured output contains non-ascii characters
2015-12-04 07:10:49 +01:00
Bruno Oliveira
14bc3c4009 Fix pastebin when captured output contains non-ascii characters
Fix #1219
2015-12-03 20:07:18 -02:00
Florian Bruhin
7e063eec08 Merge pull request #1221 from jeffwidman/patch-1
Fix typo: previosuly --> previously
2015-12-03 21:25:40 +01:00
Jeff Widman
61934ae82d Fix typo: previosuly --> previously 2015-12-03 11:56:18 -08:00
Bruno Oliveira
8c74bb0d25 Improve description on how pytest starts test collection in goodpractises 2015-12-03 01:01:34 -02:00
Bruno Oliveira
bb4771cedf Remove promise about documenting how to create a zipped pytest 2015-12-03 00:13:16 -02:00
Bruno Oliveira
9475cd3fb8 Replace "--assertmode=off" by "--assert=plain"
Fix #1214
2015-12-02 18:39:32 -02:00
Bruno Oliveira
464e16deca Removed incorrect note about genscript requiring wheels 2015-12-02 18:33:53 -02:00
Bruno Oliveira
d9b78f2a95 Remove reference to distutils 2015-12-02 18:30:12 -02:00
Ronny Pfannschmidt
7232b45f25 Merge pull request #1213 from nicoddemus/pastebin-py3
merge Pastebin py3 support

 also closes #1202 and fixes #1198
2015-12-02 09:08:50 +01:00
Bruno Oliveira
a54e4e64cd Merge remote-tracking branch 'upstream/master' into pastebin-py3 2015-12-01 23:51:14 -02:00
Bruno Oliveira
edfb567091 Add #1198 fix to CHANGELOG 2015-12-01 23:37:16 -02:00
Bruno Oliveira
6a2ebddc7c Decode urlopen response in pastebin
Fix #1198
2015-12-01 23:33:37 -02:00
Bruno Oliveira
5040dde0c5 Fix genscript deprecation version and document reasons for such 2015-12-01 23:09:15 -02:00
Bruno Oliveira
095abfd035 Fix formatting errors 2015-12-01 22:52:22 -02:00
Bruno Oliveira
69ef0ab189 Merged virtual env into the Tox section
Nowadays virtualenv use is widespread so we don't need to
devote a how-to section in pytest's docs
2015-12-01 22:47:36 -02:00
Bruno Oliveira
d851a8fd07 Fixed some formatting 2015-12-01 22:43:11 -02:00
Bruno Oliveira
0704fcacd7 Removed setuptools/genscript session 2015-12-01 22:32:07 -02:00
Bruno Oliveira
4f17d56ecb Move deprecated genscript method to the bottom of the document 2015-12-01 22:30:08 -02:00
Bruno Oliveira
b1f6dc23da Moved "conventions for Python test discovery to the top" 2015-12-01 22:28:20 -02:00
Bruno Oliveira
c6f90c25e3 Remove finalize_options override from goodpractices
This is not required in latest versions of `setuptools`, and
`self.test_args` is a read-only attribute in some of the
versions of the 18.X series.

Fix #1134
2015-12-01 22:20:40 -02:00
Florian Bruhin
f0e5cb362e Merge pull request #1211 from nchammas/patch-1
Update xdist newhooks.py link; BitBucket -> GitHub
2015-12-01 20:15:40 +01:00
Nicholas Chammas
c7cf4adfd0 Update xdist link; BitBucket -> GitHub
xdist is now hosted on GitHub.
2015-12-01 13:20:55 -05:00
Ronny Pfannschmidt
def543924b Merge pull request #1209 from jeffwidman/master
Fix spelling: explicitely --> explicitly
2015-12-01 10:51:26 +01:00
Jeff Widman
6be6798cdf Fix spelling: explicitely --> explicitly 2015-12-01 01:41:47 -08:00
Ronny Pfannschmidt
ce4eb51ee0 Merge pull request #1208 from The-Compiler/no-tests-run-spelling
Fix spelling mistake in #1207.
2015-11-30 17:40:26 +01:00
Florian Bruhin
0d2668017d Fix spelling mistake in #1207. 2015-11-30 17:33:34 +01:00
Ronny Pfannschmidt
e7e4860ded Merge pull request #1207 from The-Compiler/no-tests-run
Fix terminal output if no tests were run.
2015-11-30 17:26:47 +01:00
Florian Bruhin
aba55a0fb2 Fix terminal output if no tests were run.
Before:
====  in 0.00 seconds ====

After:
==== no tests run in 0.00 seconds ====
2015-11-30 17:24:40 +01:00
Ronny Pfannschmidt
b5d65e5139 Merge pull request #1206 from The-Compiler/collect-getattr
Don't collect classes with truthy __getattr__.
2015-11-30 17:23:47 +01:00
Ronny Pfannschmidt
3a3f0f5c56 Merge pull request #1205 from The-Compiler/reportinfo-getattr
Fix getting line number with nasty __getattr__. fixes #1204
2015-11-30 17:23:05 +01:00
Florian Bruhin
ba9146c131 Don't collect classes with truthy __getattr__.
When we have a metaclass which returns something truthy (like a method) in its
__getattr__, we collected the class because pytest thought its __test__
attribute was set to True.

We can work around this to some degree by assuming __test__ will always be set
to an explicit True if that's what the user has intended, and if it's something
other than that, this is probably a mistake.

Fixes #1204.
2015-11-30 16:41:13 +01:00
Florian Bruhin
c790f7475e Fix getting line number with nasty __getattr__.
When an object has a custom __getattr__ which always returns a non-int, we
tried to get compat_co_firstlineno from it and checked it was a integer, which
caused an exception if such a class is mistakenly collected.

If we still mistakenly collect such a class (which is likely to be something
other than a test), we now skip it with a warning (because it probably has an
__init__) instead of producing an error.

See #1204.
2015-11-30 16:13:15 +01:00
mehdy
f9b1e39b8a fix #1198 - decoding monkeypatched data to unicode 2015-11-29 19:42:50 +03:30
mehdy
81ad1689b9 fix #1198 - removed docoding the result 2015-11-29 19:20:37 +03:30
mehdy
44f60ba141 fixed #1198 issue by encoding the unicode parameters to bytes and decoding the
bytes response to unicode
2015-11-29 18:27:05 +03:30
Ronny Pfannschmidt
bced5a3f81 Merge pull request #1200 from macrotim/master
Merge #1200 - correct various documentation spelling misstakes
2015-11-28 08:00:24 +01:00
Tim Chan
a8d7e513f4 Fixed docs 2015-11-27 22:46:45 -08:00
Ronny Pfannschmidt
604a021a2a Merge pull request #1196 from nicoddemus/deprecated-call-1190
Make deprecated_call() use monkey-patching again , fixes #1190
2015-11-27 22:09:09 +01:00
Bruno Oliveira
603d81ef2f deprecated_call now uses monkey patching strategy to capture warnings
similar to what we had in 2.7, with a few enhancements

Fix #1190
2015-11-26 16:48:58 -02:00
Bruno Oliveira
6378cdf7a9 Restored 2.7 implentation of deprecated_call 2015-11-26 15:54:57 -02:00
Bruno Oliveira
84eacf3e3c Small changelog formatting fix 2015-11-26 14:37:55 -02:00
Bruno Oliveira
320c95ca43 Fix formatting in HOWTORELEASE.rst 2015-11-24 12:05:41 -02:00
Bruno Oliveira
b20803f0a6 Mention pytest_enter_pdb in the docs 2015-11-23 18:00:02 -02:00
Ronny Pfannschmidt
df767cca8f Merge pull request #1188 from nicoddemus/pytest_enter_pdb
Pass pytest config object to pytest_enter_pdb
2015-11-23 20:55:33 +01:00
Bruno Oliveira
b3166a538c Pass pytest's config object to pytest_enter_pdb 2015-11-23 14:42:21 -02:00
Bruno Oliveira
1f148a93ec Mention pytest_enter_pdb in the docs 2015-11-23 13:02:15 -02:00
Ronny Pfannschmidt
af46ffe021 bump to 2.8.4.dev 2015-11-19 22:01:34 +01:00
Ronny Pfannschmidt
e4a000bb00 refer more explicit to osx el cap in the changelogs 2015-11-18 17:57:41 +01:00
Ronny Pfannschmidt
d04505553e update authors list for 2.8.3 and add a log command to the howto 2015-11-18 17:55:18 +01:00
Ronny Pfannschmidt
2b5c46b2ab correct drop in version in announcement 2015-11-18 17:48:35 +01:00
Ronny Pfannschmidt
063f90f0d2 run regendoc 2015-11-18 17:47:17 +01:00
Ronny Pfannschmidt
6096cae3dd release announcements for 2.8.3 2015-11-18 17:38:31 +01:00
Ronny Pfannschmidt
3dc57d99f2 Changelog groming and release preps for 2.8.3 2015-11-18 17:34:30 +01:00
Florian Bruhin
3cc5a4ecb0 Merge pull request #1175 from gabehollombe/add-pyconsg2015-talk
Add 'Improve your testing with Pytest and Mock' video from PyCon SG 2015
2015-11-11 06:16:46 +01:00
Gabe Hollombe
ac0b7b9803 Add 'Improve your testing with Pytest and Mock' video from PyCon SG 2015 2015-11-11 11:28:59 +08:00
Bruno Oliveira
a1d226b751 Merge pull request #1168 from hackebrot/write-docs-on-ignore-cli-option
Write docs on ignore cli option
2015-11-07 12:38:21 -02:00
Raphael Pierzina
d667259e31 Change pytest ignore invocation as suggested by @nicoddemus 2015-11-04 23:45:29 +01:00
Bruno Oliveira
98668c943d Merge pull request #1171 from nicoddemus/fix-1169
Allow @unittest.skip decorators in Python 2.7
2015-11-04 20:27:00 -02:00
Bruno Oliveira
a7a470b56f Give credit where it is due 2015-11-04 19:55:05 -02:00
Bruno Oliveira
7d6edb9ca5 Fix unittest.skip decorator test and separate the fix into a different, self-doc function 2015-11-04 19:54:18 -02:00
Lee Kamentsky
313050b15b Suggested edits by Bruno.
Moved fix to TestCaseFunction.setup. Added myself to AUTHORS and added entry to CHANGELOG
2015-11-04 15:30:16 -05:00
Lee Kamentsky
1833547936 Added test for issue #1169
(I undid my fix, checked for failure, redid my fix and it passes)
2015-11-04 14:24:22 -05:00
LeeKamentsky
8de2c035e2 Fixes #1169
The unittest.skip decorator runs @functools.wraps on self._testcase. functools.wraps expects a "__name__" attribute and this patch adds one. It might not be the correct fix, but it works for me.
2015-11-04 13:52:40 -05:00
Raphael Pierzina
a2d07bfa93 Add pytest invocation along with result 2015-11-04 15:34:18 +01:00
Raphael Pierzina
586fdbcbbd Add example tests directory structure 2015-11-04 15:00:37 +01:00
Raphael Pierzina
04079f9ae5 Start doc section on `--ignore` in pytestcollection.rst 2015-11-04 14:49:02 +01:00
Bruno Oliveira
af2d391903 Merge pull request #1166 from Toilal/pytest-runner
Use pytest-runner for setuptools integration
2015-11-02 12:50:21 -02:00
Toilal
8abf85e96c Use pytest-runner for setuptools integration 2015-11-02 12:22:40 +01:00
Ronny Pfannschmidt
9013cb1b4c Merge pull request #1144 from The-Compiler/elcapitan-sip
Handle EPERM when writing rewritten bytecode.
2015-11-01 08:20:00 +01:00
Florian Bruhin
2eb345f9b7 Merge pull request #1159 from zoidbergwill/patch-1
Fix typo in parametrize.rst
2015-10-30 09:02:29 +01:00
William Martin Stewart
1376d75fbe Fix typo in parametrize.rst 2015-10-30 09:58:41 +02:00
Bruno Oliveira
4fe7cca44e Merge pull request #1157 from gabrielcnr/master
Color highlight on the name of the failed tests
2015-10-28 17:36:47 -02:00
gabriel.reis
a83d5c0f08 Updated entries on CHANGELOG and AUTHORS. Ref: #1157 2015-10-28 19:35:04 +00:00
Gabriel Reis
f3f61fb5d1 Color highlight on the name of the failed tests 2015-10-28 08:12:57 +00:00
Bruno Oliveira
8e54d07c40 Fix link in FAQ about parametrizing tests
Fix #1154
2015-10-26 22:46:35 -02:00
Florian Bruhin
bd36e00174 Merge pull request #1153 from b-jazz/patch-2
Update usage.rst
2015-10-24 15:41:24 +02:00
Ronny Pfannschmidt
da6b1557f0 Merge pull request #1152 from b-jazz/patch-1
Update CONTRIBUTING.rst
2015-10-24 12:00:33 +02:00
b-jazz
f174026f53 Update usage.rst
Remove double "the" in usage comments
2015-10-24 02:36:02 -07:00
b-jazz
390e1c93ed Update CONTRIBUTING.rst
Clean up the grammar of one of the lines.
2015-10-24 02:21:22 -07:00
Bruno Oliveira
8f31a1a64b Merge pull request #1147 from jayvdb/remove-extrainit
Remove commented out extrainit
2015-10-20 10:25:54 -02:00
John Vandenberg
9a04879129 Remove commented out extrainit
Unused since 2012, and the docstring syntax is incorrect with
four quotes.
2015-10-20 21:08:21 +11:00
Bruno Oliveira
7469b5591e Update plugins_index.rst 2015-10-17 13:37:38 -03:00
Bruno Oliveira
75932a92d0 Make pytest-version mandatory
Fix #1118
2015-10-17 13:37:01 -03:00
Bruno Oliveira
cf56f59f58 Merge pull request #1145 from htgoebel/patch-1
Add link to unittest2pytest
2015-10-17 13:16:55 -03:00
Hartmut Goebel
4fba20e544 Add link to unittest2pytest
Closes #991.
2015-10-17 18:14:52 +02:00
Florian Bruhin
2956627b8b Handle EPERM when writing rewritten bytecode.
Fixes #1143.
2015-10-17 11:55:55 +02:00
Ronny Pfannschmidt
3404d2a99b Merge pull request #1142 from nicoddemus/issue-1133-str-relto
Fix situation where a traceback entry "path" returns a str object
2015-10-17 08:58:48 +02:00
Bruno Oliveira
311b0a9683 Fix situation where a traceback entry "path" returns a str object
Fix #1133
2015-10-16 20:18:14 -03:00
Ronny Pfannschmidt
3b11995dbe Merge pull request #1053 from mbirtwell/issue_331_failure_for_test_with_no_name
Prevent non-function callables from being collected
2015-10-13 23:35:32 +02:00
Michael Birtwell
a3bda59a30 collection: Prevent non-function callables from being collected
Fixes issue 331
previously to this change the collection code would issue a warning for
when ever it encountered a variable that looked like a test but wasn't a
function saying that it wouldn't collect it because it wasn't a function.
This fixes the logic so that if that warning is issued it really isn't
collected.
However previously special cases existed to support tests that were
created using functools.wraps and functools.partial. So the condition for
issuing that warning has been updated to take that in to account

Also try the old way of detecting functions just for proper integration
with mock.path in python 2.7 the get_real_func returned the unbound method
2015-10-12 22:23:18 +01:00
Bruno Oliveira
0c21533cc5 Merge pull request #1130 from The-Compiler/pytest_doc_fix
Use a string for pytest_plugins in docs.
2015-10-12 10:58:39 -03:00
Florian Bruhin
dadf03baea Use a string for pytest_plugins in docs. 2015-10-12 06:57:43 +02:00
Ronny Pfannschmidt
1525cc78f6 Merge pull request #1127 from michael-k/indentation
[doc] Fixed indentation
2015-10-10 14:22:18 +02:00
Ronny Pfannschmidt
db077555f6 Merge pull request #1102 from nicoddemus/doctest-fixtures-fix
Fix autouse fixtures and doctest modules
2015-10-10 14:21:53 +02:00
Michael K
37e96c9335 [doc] Fixed indentation 2015-10-10 13:29:22 +02:00
Bruno Oliveira
b052becba9 Merge pull request #1121 from tomviner/issue1035-getattr
Issue1035 getattr
2015-10-09 16:45:52 -03:00
TomV
88c8dd96f9 issue1035 Override inspect.isclass for python 2.6 2015-10-09 19:44:47 +01:00
Bruno Oliveira
cee828130c Merge pull request #1124 from bukzor/unit-test-config-fromdictargs
tests of (and fixes for) Config.fromdictargs (2)
2015-10-09 15:10:40 -03:00
Buck Golemon
67236d6de3 strengthen the ini assertion 2015-10-09 09:58:12 -07:00
Buck Golemon
470e4f9e91 changelog entry 2015-10-09 09:58:12 -07:00
Buck Golemon
0e55a8793f all tests pass 2015-10-09 09:58:12 -07:00
Buck Golemon
49d46a0059 an ugly patch to fix all but the most important part =/ 2015-10-08 10:44:58 -07:00
Buck Golemon
616d8251f3 unit tests of Config.fromdictargs. currently failing 2015-10-08 10:44:58 -07:00
TomV
707226298a issue1035 add test for classes setting __getattr__ 2015-10-08 09:08:32 +01:00
Florian Bruhin
1f6988bdec Add empty 2.8.3.dev section to CHANGELOG. 2015-10-07 22:47:22 +02:00
Florian Bruhin
ec74a8deb8 Fix contents.rst.
This was broken in 787c866191.
2015-10-07 18:10:13 +02:00
Florian Bruhin
b5b53b6aec start 2.8.3 development 2015-10-07 17:56:05 +02:00
Florian Bruhin
7c529e0afe Merge branch '2.8.2-release' 2015-10-07 17:51:34 +02:00
Florian Bruhin
b955473533 Add 2.8.2 release annoucement. 2015-10-06 22:37:59 +02:00
Florian Bruhin
2026ce0ed2 Update plugin_index. 2015-10-06 22:35:16 +02:00
Florian Bruhin
42937d4bb6 Prepare 2.8.2 release. 2015-10-06 21:06:25 +02:00
Bruno Oliveira
cfd259ae6f Merge pull request #1113 from demianbrecht/typo_fix
typo fix, changed position of context manager notes
2015-10-05 17:21:59 -03:00
Demian Brecht
493530ec6d typo fix, changed position of context manager notes 2015-10-05 12:13:25 -07:00
Florian Bruhin
4dea0892cb Merge pull request #1108 from nicoddemus/plugins_index
plugins_index was still generating ".txt" files, so it was effectively being ignored
2015-10-05 09:37:31 -07:00
Bruno Oliveira
a14c77aeba Fix problems when mixing autouse fixtures and doctest modules
The main problem was that previously DoctestModule was setting
up its fixtures during collection, instead of letting
each DoctestItem make its own fixture setup

Fix #1100
Fix #1057
2015-10-03 16:07:14 -03:00
Bruno Oliveira
a24126effb Add credit for pytest.mark.skip to the CHANGELOG 2015-10-03 14:15:22 -03:00
Michael Aquilina
8984177448 TestXFail also shouldnt explicitly inherit from object 2015-10-03 17:12:44 +01:00
Michael Aquilina
750442909c Add unconditional skip entry to CHANGELOG 2015-10-03 17:04:06 +01:00
Michael Aquilina
df874db817 Update default reason to "unconditional skip" 2015-10-03 17:02:18 +01:00
Michael Aquilina
00d0c74657 Update reason in test to prevent confusing with test_no_reason 2015-10-03 17:01:21 +01:00
Michael Aquilina
122980ecad Add myself to AUTHORS 2015-10-03 17:01:11 +01:00
Michael Aquilina
fc0bd9412c Test that "unconditional skip" is the default reason if none given 2015-10-03 17:00:16 +01:00
Michael Aquilina
5ff9a0ff54 Remove redundant comments 2015-10-03 16:59:27 +01:00
Michael Aquilina
25d74a5919 Dont explicitly inherit from object 2015-10-03 16:55:04 +01:00
Michael Aquilina
213dbe7a5f newlines 2015-10-03 16:42:15 +01:00
Michael Aquilina
9e57954b03 First argument in pytest.mark.skip is a reason 2015-10-03 16:42:15 +01:00
Michael Aquilina
1b5aa2868d Check no reason displayed if none specified 2015-10-03 16:42:15 +01:00
Michael Aquilina
eee24138b0 Fix failing test 2015-10-03 16:42:15 +01:00
Michael Aquilina
04545f8a54 classes inherit from object 2015-10-03 16:42:15 +01:00
Michael Aquilina
d1628944a6 Update skippings tests for better coverage 2015-10-03 16:42:15 +01:00
Michael Aquilina
abc27f56fc Update skipping.rst with correct version marker 2015-10-03 16:42:15 +01:00
Michael Aquilina
771aef9ddb Add a test_skip_class test 2015-10-03 16:42:15 +01:00
Michael Aquilina
dc7153e33c Spelling and grammar fixes 2015-10-03 16:42:15 +01:00
Michael Aquilina
5ec08d3081 Delete trailing whitespace 2015-10-03 16:42:15 +01:00
Michael Aquilina
61b8443723 Update docs with new skip marker 2015-10-03 16:42:14 +01:00
Michael Aquilina
ad0b8e31b8 Fix case where skip is assigned to as an attribute directly 2015-10-03 16:42:14 +01:00
Michael Aquilina
f144666f8b Work towards test coverage of mark.skip 2015-10-03 16:42:14 +01:00
Michael Aquilina
4e94135d36 Remove incorrect use of pytest.mark.skip 2015-10-03 16:42:14 +01:00
Michael Aquilina
b71add27da Add MarkEvaluator for skip 2015-10-03 16:42:14 +01:00
Bruno Oliveira
9deab6c0fd Add a reminder on HOWTORELEASE to update plugins_index 2015-10-02 19:44:00 -03:00
Bruno Oliveira
f9e9413f52 plugins_index was still generating ".txt" files, so it was effectively being ignored
Fix #1105
2015-10-02 19:21:55 -03:00
Bruno Oliveira
5171d167ce Move "note" to the end of the main text
Fix #1097
2015-10-02 18:14:17 -03:00
Demian Brecht
2981bece55 pyflakes fix 2015-10-02 13:03:43 -07:00
Demian Brecht
fd211bf490 Add a note about usage of the context manager
Addesses #1097
2015-10-02 12:40:58 -07:00
Ronny Pfannschmidt
9263b3a051 Merge pull request #1096 from hackebrot/update-docs-skip-with-cli-option
Update docs skip with cli option
2015-10-01 18:31:55 +02:00
holger krekel
88209c6ac6 Merge pull request #1092 from nicoddemus/param-ids-fix
Param ids fix
2015-10-01 15:09:11 +02:00
Ronny Pfannschmidt
688f955f5e Merge pull request #1058 from nicoddemus/doc-how-disable-cache
Add docs on how to disable cache provider
2015-10-01 13:06:02 +02:00
Bruno Oliveira
f3cee8f0b5 Merge remote-tracking branch 'upstream/master' into param-ids-fix 2015-10-01 07:58:55 -03:00
Ronny Pfannschmidt
b7fd3f0031 Merge pull request #1093 from nicoddemus/relto-bug
Fix internal error when filtering tracebacks where one entry was generated by an exec() statement
Fixes #995
2015-10-01 06:00:40 +02:00
Bruno Oliveira
d1e00f6e19 Detect dynamic code explicitly in filter_traceback 2015-09-30 17:32:49 -03:00
Bruno Oliveira
c9480c5b8b Move imports outside _escape_bytes as suggested in review 2015-09-30 17:02:19 -03:00
Raphael Pierzina
4829eac1e1 Use a variable for the skipif marker as suggested by @nicoddemus 2015-09-30 21:53:34 +02:00
Raphael Pierzina
a10da0e540 Implement skipif marker for slow test based on pytest.config 2015-09-30 19:41:47 +02:00
Raphael Pierzina
7ac8a88a05 Remove pytest_runtest_setup from conftest.py 2015-09-30 19:25:57 +02:00
Bruno Oliveira
639ae0cfe0 Merge pull request #1095 from ionelmc/fix-hook-docs
Correct hook examples in docs.
2015-09-30 11:28:43 -03:00
Ionel Cristian Mărieș
c8f5a40fd9 Edit examples again to use hookimpl. 2015-09-30 16:58:12 +03:00
Ionel Cristian Mărieș
f4f23e8e09 Correct hook examples in docs. 2015-09-30 16:12:33 +03:00
Florian Bruhin
dc9ad12182 Merge pull request #1094 from nicoddemus/cache-fixture-docstring-fix
fix docstring for cache fixture regarding ``/`` on keys
2015-09-30 06:02:38 +02:00
Bruno Oliveira
a808e09204 fix docstring for cache fixture regarding `/` on keys 2015-09-30 00:27:52 -03:00
Bruno Oliveira
6ae16eba36 add entries for #1085 and #1087 to the CHANGELOG 2015-09-29 22:37:11 -03:00
Bruno Oliveira
11f100813e Fix internal error when filtering tracebacks where one entry was generated by an exec() statement
Fix #995
2015-09-29 22:29:43 -03:00
Bruno Oliveira
b64470443f Fix SystemError when using unicode_escape on Python 3
Fix #1087
2015-09-29 18:20:30 -03:00
Bruno Oliveira
8633c4cefd Fix encoding errors for parametrized tests with unicode parameters in py2
Fix #1085
2015-09-29 17:57:49 -03:00
Florian Bruhin
e9240f7eee Merge pull request #1090 from RonnyPfannschmidt/fix-flaky-getusertest
fix flaky get_user fallback tests
2015-09-29 21:35:45 +02:00
Ronny Pfannschmidt
04deea3c6d fix flaky get_user fallback tests 2015-09-29 21:00:12 +02:00
Bruno Oliveira
6ca8923ec7 Merge pull request #1088 from hpk42/docfix
remove unclear todo that irritates in the online docs
2015-09-29 13:26:54 -03:00
holger krekel
e3562be530 remove unclear todo that irritates in the online docs 2015-09-29 18:23:44 +02:00
holger krekel
cb58eaa611 Merge remote-tracking branch 'upstream/master' into features
Conflicts:
	_pytest/__init__.py
	testing/test_recwarn.py
2015-09-29 15:56:41 +02:00
holger krekel
c5fd42b699 start 2.8.2 development 2015-09-29 15:32:33 +02:00
Bruno Oliveira
b184f391c6 Show known environment variables in py.test --help 2015-09-28 18:34:16 -03:00
Bruno Oliveira
5f860181b6 Remove print left by accident 2015-09-28 18:25:20 -03:00
Bruno Oliveira
6caa7083db Moved more detailed options on how to disable a plugin to plugins.rst 2015-09-28 18:23:08 -03:00
holger krekel
0c05ca1fd5 Merge branch 'master' into features 2015-09-26 10:03:42 +02:00
Bruno Oliveira
b09d3724a0 Add docs on how to disable cache provider 2015-09-23 19:15:44 -03:00
holger krekel
4867554eec Merge branch 'master' into features 2015-09-23 16:42:42 +02:00
Ronny Pfannschmidt
36924b59bd Merge branch '653-deprecated-context-manager' of https://github.com/chiller/pytest into features 2015-09-22 15:14:06 +02:00
holger krekel
7c088d1104 remove nonsense line 2015-09-22 11:23:24 +02:00
holger krekel
48df2f6842 Merge branch 'master' into features 2015-09-22 11:23:05 +02:00
holger krekel
79587d4b70 Merge branch 'master' into features
Conflicts:
	_pytest/__init__.py
2015-09-22 11:06:21 +02:00
holger krekel
8a4517fd17 re-add 2.8.x changelog so that MASTER can be merged into features wrt
to the changelog
2015-09-22 11:05:19 +02:00
holger krekel
97f7815feb also change pytest version to target 2.9.0 2015-09-22 11:01:33 +02:00
holger krekel
d8fbb0b8e3 start features branch 2015-09-22 11:00:55 +02:00
Galaczi Endre
9f77a8507e removed mutation of global state, changed filter addition in WarningsRecorder 2015-09-21 19:01:23 +01:00
Galaczi Endre
beaa8e55bd Fixes #653 use deprecated_call as context_manager 2015-09-21 19:01:23 +01:00
143 changed files with 8345 additions and 3466 deletions

8
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

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

8
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,8 @@
Thanks for submitting a PR, your contribution is really appreciated!
Here's a quick checklist that should be present in PRs:
- [ ] Target: for bug or doc fixes, target `master`; for new features, target `features`
- [ ] Make sure to include one or more tests for your change
- [ ] Add yourself to `AUTHORS`
- [ ] Add a new entry to the `CHANGELOG` (choose any open position to avoid merge conflicts with other PRs)

1
.gitignore vendored
View File

@@ -16,6 +16,7 @@ include/
*.class
*.orig
*~
.hypothesis/
.eggs/

View File

@@ -7,25 +7,25 @@ install: "pip install -U tox"
# # command to run tests
env:
matrix:
# coveralls is not listed in tox's envlist, but should run in travis
- TESTENV=coveralls
- TESTENV=doctesting
- TESTENV=flakes
# note: please use "tox --listenvs" to populate the build matrix below
- TESTENV=linting
- TESTENV=py26
- TESTENV=py27
- TESTENV=py27-cxfreeze
- TESTENV=py27-nobyte
- TESTENV=py27-pexpect
- TESTENV=py27-subprocess
- TESTENV=py27-trial
- TESTENV=py27-xdist
- TESTENV=py33
- TESTENV=py33
- TESTENV=py34
- TESTENV=py35-pexpect
- TESTENV=py35-trial
- TESTENV=py35-xdist
- TESTENV=py35
- TESTENV=pypy
- TESTENV=py27-pexpect
- TESTENV=py27-xdist
- TESTENV=py27-trial
- TESTENV=py35-pexpect
- TESTENV=py35-xdist
- TESTENV=py35-trial
- TESTENV=py27-nobyte
- TESTENV=doctesting
- TESTENV=py27-cxfreeze
script: tox --recreate -e $TESTENV

21
AUTHORS
View File

@@ -10,6 +10,7 @@ Andy Freeland
Anthon van der Neut
Armin Rigo
Aron Curzon
Aviv Palivoda
Benjamin Peterson
Bob Ippolito
Brian Dorsey
@@ -23,16 +24,22 @@ Christian Theunert
Christian Tismer
Christopher Gilling
Daniel Grana
Daniel Hahler
Daniel Nuri
Dave Hunt
David Mohr
David Vierra
Edison Gustavo Muenz
Eduardo Schettino
Endre Galaczi
Elizaveta Shashkova
Eric Hunsberger
Eric Siegerman
Erik M. Bray
Florian Bruhin
Floris Bruynooghe
Gabriel Reis
Georgy Dyuldin
Graham Horler
Grig Gheorghiu
Guido Wesdorp
@@ -42,23 +49,34 @@ Jaap Broekhuizen
Jan Balster
Janne Vanhala
Jason R. Coombs
John Towler
Joshua Bronson
Jurko Gospodnetić
Katarzyna Jachim
Kevin Cox
Lee Kamentsky
Lukas Bednar
Maciek Fijalkowski
Maho
Marc Schlaich
Mark Abramowitz
Markus Unterwaditzer
Martijn Faassen
Martin Prusse
Matt Bachmann
Michael Aquilina
Michael Birtwell
Michael Droettboom
Nicolas Delaby
Pieter Mulder
Piotr Banaszkiewicz
Punyashloka Biswal
Quentin Pradet
Ralf Schmitt
Raphael Pierzina
Ronny Pfannschmidt
Ross Lawley
Ryan Wooden
Samuele Pedroni
Tom Viner
Trevor Bekolay
@@ -68,3 +86,6 @@ Eric Hunsberger
Simon Gomizelj
Russel Winder
Ben Webb
Alexei Kozlenok
Cal Leeming
Feng Ma

View File

@@ -1,5 +1,321 @@
2.9.2
=====
**Bug Fixes**
* fix `#510`_: skip tests where one parameterize dimension was empty
thanks Alex Stapleton for the Report and `@RonnyPfannschmidt`_ for the PR
* Fix Xfail does not work with condition keyword argument.
Thanks `@astraw38`_ for reporting the issue (`#1496`_) and `@tomviner`_
for PR the (`#1524`_).
* Fix win32 path issue when puttinging custom config file with absolute path
in ``pytest.main("-c your_absolute_path")``.
* Fix maximum recursion depth detection when raised error class is not aware
of unicode/encoded bytes.
Thanks `@prusse-martin`_ for the PR (`#1506`_).
* Fix ``pytest.mark.skip`` mark when used in strict mode.
Thanks `@pquentin`_ for the PR and `@RonnyPfannschmidt`_ for
showing how to fix the bug.
* Minor improvements and fixes to the documentation.
Thanks `@omarkohl`_ for the PR.
* Fix ``--fixtures`` to show all fixture definitions as opposed to just
one per fixture name.
Thanks to `@hackebrot`_ for the PR.
.. _#510: https://github.com/pytest-dev/pytest/issues/510
.. _#1506: https://github.com/pytest-dev/pytest/pull/1506
.. _#1496: https://github.com/pytest-dev/pytest/issue/1496
.. _#1524: https://github.com/pytest-dev/pytest/issue/1524
.. _@prusse-martin: https://github.com/prusse-martin
.. _@astraw38: https://github.com/astraw38
2.9.1
=====
**Bug Fixes**
* Improve error message when a plugin fails to load.
Thanks `@nicoddemus`_ for the PR.
* Fix (`#1178 <https://github.com/pytest-dev/pytest/issues/1178>`_):
``pytest.fail`` with non-ascii characters raises an internal pytest error.
Thanks `@nicoddemus`_ for the PR.
* Fix (`#469`_): junit parses report.nodeid incorrectly, when params IDs
contain ``::``. Thanks `@tomviner`_ for the PR (`#1431`_).
* Fix (`#578 <https://github.com/pytest-dev/pytest/issues/578>`_): SyntaxErrors
containing non-ascii lines at the point of failure generated an internal
py.test error.
Thanks `@asottile`_ for the report and `@nicoddemus`_ for the PR.
* Fix (`#1437`_): When passing in a bytestring regex pattern to parameterize
attempt to decode it as utf-8 ignoring errors.
* Fix (`#649`_): parametrized test nodes cannot be specified to run on the command line.
.. _#1437: https://github.com/pytest-dev/pytest/issues/1437
.. _#469: https://github.com/pytest-dev/pytest/issues/469
.. _#1431: https://github.com/pytest-dev/pytest/pull/1431
.. _#649: https://github.com/pytest-dev/pytest/issues/649
.. _@asottile: https://github.com/asottile
2.9.0
=====
**New Features**
* New ``pytest.mark.skip`` mark, which unconditionally skips marked tests.
Thanks `@MichaelAquilina`_ for the complete PR (`#1040`_).
* ``--doctest-glob`` may now be passed multiple times in the command-line.
Thanks `@jab`_ and `@nicoddemus`_ for the PR.
* New ``-rp`` and ``-rP`` reporting options give the summary and full output
of passing tests, respectively. Thanks to `@codewarrior0`_ for the PR.
* ``pytest.mark.xfail`` now has a ``strict`` option, which makes ``XPASS``
tests to fail the test suite (defaulting to ``False``). There's also a
``xfail_strict`` ini option that can be used to configure it project-wise.
Thanks `@rabbbit`_ for the request and `@nicoddemus`_ for the PR (`#1355`_).
* ``Parser.addini`` now supports options of type ``bool``.
Thanks `@nicoddemus`_ for the PR.
* New ``ALLOW_BYTES`` doctest option. This strips ``b`` prefixes from byte strings
in doctest output (similar to ``ALLOW_UNICODE``).
Thanks `@jaraco`_ for the request and `@nicoddemus`_ for the PR (`#1287`_).
* Give a hint on ``KeyboardInterrupt`` to use the ``--fulltrace`` option to show the errors.
Fixes `#1366`_.
Thanks to `@hpk42`_ for the report and `@RonnyPfannschmidt`_ for the PR.
* Catch ``IndexError`` exceptions when getting exception source location.
Fixes a pytest internal error for dynamically generated code (fixtures and tests)
where source lines are fake by intention.
**Changes**
* **Important**: `py.code <https://pylib.readthedocs.io/en/latest/code.html>`_ has been
merged into the ``pytest`` repository as ``pytest._code``. This decision
was made because ``py.code`` had very few uses outside ``pytest`` and the
fact that it was in a different repository made it difficult to fix bugs on
its code in a timely manner. The team hopes with this to be able to better
refactor out and improve that code.
This change shouldn't affect users, but it is useful to let users aware
if they encounter any strange behavior.
Keep in mind that the code for ``pytest._code`` is **private** and
**experimental**, so you definitely should not import it explicitly!
Please note that the original ``py.code`` is still available in
`pylib <https://pylib.readthedocs.io>`_.
* ``pytest_enter_pdb`` now optionally receives the pytest config object.
Thanks `@nicoddemus`_ for the PR.
* Removed code and documentation for Python 2.5 or lower versions,
including removal of the obsolete ``_pytest.assertion.oldinterpret`` module.
Thanks `@nicoddemus`_ for the PR (`#1226`_).
* Comparisons now always show up in full when ``CI`` or ``BUILD_NUMBER`` is
found in the environment, even when ``-vv`` isn't used.
Thanks `@The-Compiler`_ for the PR.
* ``--lf`` and ``--ff`` now support long names: ``--last-failed`` and
``--failed-first`` respectively.
Thanks `@MichaelAquilina`_ for the PR.
* Added expected exceptions to ``pytest.raises`` fail message.
* Collection only displays progress ("collecting X items") when in a terminal.
This avoids cluttering the output when using ``--color=yes`` to obtain
colors in CI integrations systems (`#1397`_).
**Bug Fixes**
* The ``-s`` and ``-c`` options should now work under ``xdist``;
``Config.fromdictargs`` now represents its input much more faithfully.
Thanks to `@bukzor`_ for the complete PR (`#680`_).
* Fix (`#1290`_): support Python 3.5's ``@`` operator in assertion rewriting.
Thanks `@Shinkenjoe`_ for report with test case and `@tomviner`_ for the PR.
* Fix formatting utf-8 explanation messages (`#1379`_).
Thanks `@biern`_ for the PR.
* Fix `traceback style docs`_ to describe all of the available options
(auto/long/short/line/native/no), with `auto` being the default since v2.6.
Thanks `@hackebrot`_ for the PR.
* Fix (`#1422`_): junit record_xml_property doesn't allow multiple records
with same name.
.. _`traceback style docs`: https://pytest.org/latest/usage.html#modifying-python-traceback-printing
.. _#1422: https://github.com/pytest-dev/pytest/issues/1422
.. _#1379: https://github.com/pytest-dev/pytest/issues/1379
.. _#1366: https://github.com/pytest-dev/pytest/issues/1366
.. _#1040: https://github.com/pytest-dev/pytest/pull/1040
.. _#680: https://github.com/pytest-dev/pytest/issues/680
.. _#1287: https://github.com/pytest-dev/pytest/pull/1287
.. _#1226: https://github.com/pytest-dev/pytest/pull/1226
.. _#1290: https://github.com/pytest-dev/pytest/pull/1290
.. _#1355: https://github.com/pytest-dev/pytest/pull/1355
.. _#1397: https://github.com/pytest-dev/pytest/issues/1397
.. _@biern: https://github.com/biern
.. _@MichaelAquilina: https://github.com/MichaelAquilina
.. _@bukzor: https://github.com/bukzor
.. _@hpk42: https://github.com/hpk42
.. _@nicoddemus: https://github.com/nicoddemus
.. _@jab: https://github.com/jab
.. _@codewarrior0: https://github.com/codewarrior0
.. _@jaraco: https://github.com/jaraco
.. _@The-Compiler: https://github.com/The-Compiler
.. _@Shinkenjoe: https://github.com/Shinkenjoe
.. _@tomviner: https://github.com/tomviner
.. _@RonnyPfannschmidt: https://github.com/RonnyPfannschmidt
.. _@rabbbit: https://github.com/rabbbit
.. _@hackebrot: https://github.com/hackebrot
.. _@omarkohl: https://github.com/omarkohl
.. _@pquentin: https://github.com/pquentin
2.8.7
=====
- fix #1338: use predictable object resolution for monkeypatch
2.8.6
=====
- fix #1259: allow for double nodeids in junitxml,
this was a regression failing plugins combinations
like pytest-pep8 + pytest-flakes
- Workaround for exception that occurs in pyreadline when using
``--pdb`` with standard I/O capture enabled.
Thanks Erik M. Bray for the PR.
- fix #900: Better error message in case the target of a ``monkeypatch`` call
raises an ``ImportError``.
- fix #1292: monkeypatch calls (setattr, setenv, etc.) are now O(1).
Thanks David R. MacIver for the report and Bruno Oliveira for the PR.
- fix #1223: captured stdout and stderr are now properly displayed before
entering pdb when ``--pdb`` is used instead of being thrown away.
Thanks Cal Leeming for the PR.
- fix #1305: pytest warnings emitted during ``pytest_terminal_summary`` are now
properly displayed.
Thanks Ionel Maries Cristian for the report and Bruno Oliveira for the PR.
- fix #628: fixed internal UnicodeDecodeError when doctests contain unicode.
Thanks Jason R. Coombs for the report and Bruno Oliveira for the PR.
- fix #1334: Add captured stdout to jUnit XML report on setup error.
Thanks Georgy Dyuldin for the PR.
2.8.5
=====
- fix #1243: fixed issue where class attributes injected during collection could break pytest.
PR by Alexei Kozlenok, thanks Ronny Pfannschmidt and Bruno Oliveira for the review and help.
- fix #1074: precompute junitxml chunks instead of storing the whole tree in objects
Thanks Bruno Oliveira for the report and Ronny Pfannschmidt for the PR
- fix #1238: fix ``pytest.deprecated_call()`` receiving multiple arguments
(Regression introduced in 2.8.4). Thanks Alex Gaynor for the report and
Bruno Oliveira for the PR.
2.8.4
=====
- fix #1190: ``deprecated_call()`` now works when the deprecated
function has been already called by another test in the same
module. Thanks Mikhail Chernykh for the report and Bruno Oliveira for the
PR.
- fix #1198: ``--pastebin`` option now works on Python 3. Thanks
Mehdy Khoshnoody for the PR.
- fix #1219: ``--pastebin`` now works correctly when captured output contains
non-ascii characters. Thanks Bruno Oliveira for the PR.
- fix #1204: another error when collecting with a nasty __getattr__().
Thanks Florian Bruhin for the PR.
- fix the summary printed when no tests did run.
Thanks Florian Bruhin for the PR.
- fix #1185 - ensure MANIFEST.in exactly matches what should go to a sdist
- a number of documentation modernizations wrt good practices.
Thanks Bruno Oliveira for the PR.
2.8.3
=====
- fix #1169: add __name__ attribute to testcases in TestCaseFunction to
support the @unittest.skip decorator on functions and methods.
Thanks Lee Kamentsky for the PR.
- fix #1035: collecting tests if test module level obj has __getattr__().
Thanks Suor for the report and Bruno Oliveira / Tom Viner for the PR.
- fix #331: don't collect tests if their failure cannot be reported correctly
e.g. they are a callable instance of a class.
- fix #1133: fixed internal error when filtering tracebacks where one entry
belongs to a file which is no longer available.
Thanks Bruno Oliveira for the PR.
- enhancement made to highlight in red the name of the failing tests so
they stand out in the output.
Thanks Gabriel Reis for the PR.
- add more talks to the documentation
- extend documentation on the --ignore cli option
- use pytest-runner for setuptools integration
- minor fixes for interaction with OS X El Capitan
system integrity protection (thanks Florian)
2.8.2
=====
- fix #1085: proper handling of encoding errors when passing encoded byte
strings to pytest.parametrize in Python 2.
Thanks Themanwithoutaplan for the report and Bruno Oliveira for the PR.
- fix #1087: handling SystemError when passing empty byte strings to
pytest.parametrize in Python 3.
Thanks Paul Kehrer for the report and Bruno Oliveira for the PR.
- fix #995: fixed internal error when filtering tracebacks where one entry
was generated by an exec() statement.
Thanks Daniel Hahler, Ashley C Straw, Philippe Gauthier and Pavel Savchenko
for contributing and Bruno Oliveira for the PR.
- fix #1100 and #1057: errors when using autouse fixtures and doctest modules.
Thanks Sergey B Kirpichev and Vital Kudzelka for contributing and Bruno
Oliveira for the PR.
2.8.1
-----
=====
- fix #1034: Add missing nodeid on pytest_logwarning call in
addhook. Thanks Simon Gomizelj for the PR.
@@ -21,9 +337,9 @@
"pytest-xdist" plugin, with test reports being assigned to the wrong tests.
Thanks Daniel Grunwald for the report and Bruno Oliveira for the PR.
- (experimental) adapt more SEMVER style versioning and change meaning of
master branch in git repo: "master" branch now keeps the bugfixes, changes
aimed for micro releases. "features" branch will only be be released
- (experimental) adapt more SEMVER style versioning and change meaning of
master branch in git repo: "master" branch now keeps the bugfixes, changes
aimed for micro releases. "features" branch will only be be released
with minor or major pytest releases.
- Fix issue #766 by removing documentation references to distutils.
@@ -38,6 +354,7 @@
- Fix issue #411: Add __eq__ method to assertion comparison example.
Thanks Ben Webb.
- Fix issue #653: deprecated_call can be used as context manager.
- fix issue 877: properly handle assertion explanations with non-ascii repr
Thanks Mathieu Agopian for the report and Ronny Pfannschmidt for the PR.
@@ -45,10 +362,10 @@
- fix issue 1029: transform errors when writing cache values into pytest-warnings
2.8.0
-----------------------------
=====
- new ``--lf`` and ``-ff`` options to run only the last failing tests or
"failing tests first" from the last run. This functionality is provided
"failing tests first" from the last run. This functionality is provided
through porting the formerly external pytest-cache plugin into pytest core.
BACKWARD INCOMPAT: if you used pytest-cache's functionality to persist
data between test runs be aware that we don't serialize sets anymore.
@@ -102,10 +419,10 @@
- fix issue768: docstrings found in python modules were not setting up session
fixtures. Thanks Jason R. Coombs for reporting and Bruno Oliveira for the PR.
- added `tmpdir_factory`, a session-scoped fixture that can be used to create
- added ``tmpdir_factory``, a session-scoped fixture that can be used to create
directories under the base temporary directory. Previously this object was
installed as a `_tmpdirhandler` attribute of the `config` object, but now it
is part of the official API and using `config._tmpdirhandler` is
installed as a ``_tmpdirhandler`` attribute of the ``config`` object, but now it
is part of the official API and using ``config._tmpdirhandler`` is
deprecated.
Thanks Bruno Oliveira for the PR.
@@ -134,7 +451,7 @@
or no tests were run at all (related to issue500).
Thanks Eric Siegerman.
- New `testpaths` ini option: list of directories to search for tests
- New ``testpaths`` ini option: list of directories to search for tests
when executing pytest from the root directory. This can be used
to speed up test collection when a project has well specified directories
for tests, being usually more practical than configuring norecursedirs for
@@ -154,9 +471,9 @@
- fix issue735: assertion failures on debug versions of Python 3.4+
- new option ``--import-mode`` to allow to change test module importing
behaviour to append to sys.path instead of prepending. This better allows
to run test modules against installated versions of a package even if the
- new option ``--import-mode`` to allow to change test module importing
behaviour to append to sys.path instead of prepending. This better allows
to run test modules against installated versions of a package even if the
package under test has the same import root. In this example::
testing/__init__.py
@@ -164,7 +481,7 @@
pkg_under_test/
the tests will run against the installed version
of pkg_under_test when ``--import-mode=append`` is used whereas
of pkg_under_test when ``--import-mode=append`` is used whereas
by default they would always pick up the local version. Thanks Holger Krekel.
- pytester: add method ``TmpTestdir.delete_loaded_modules()``, and call it
@@ -230,20 +547,20 @@
- issue951: add new record_xml_property fixture, that supports logging
additional information on xml output. Thanks David Diaz for the PR.
- issue949: paths after normal options (for example `-s`, `-v`, etc) are now
properly used to discover `rootdir` and `ini` files.
- issue949: paths after normal options (for example ``-s``, ``-v``, etc) are now
properly used to discover ``rootdir`` and ``ini`` files.
Thanks Peter Lauri for the report and Bruno Oliveira for the PR.
2.7.3 (compared to 2.7.2)
-----------------------------
=============================
- Allow 'dev', 'rc', or other non-integer version strings in `importorskip`.
- Allow 'dev', 'rc', or other non-integer version strings in ``importorskip``.
Thanks to Eric Hunsberger for the PR.
- fix issue856: consider --color parameter in all outputs (for example
--fixtures). Thanks Barney Gale for the report and Bruno Oliveira for the PR.
- fix issue855: passing str objects as `plugins` argument to pytest.main
- fix issue855: passing str objects as ``plugins`` argument to pytest.main
is now interpreted as a module name to be imported and registered as a
plugin, instead of silently having no effect.
Thanks xmo-odoo for the report and Bruno Oliveira for the PR.
@@ -278,7 +595,7 @@
Thanks Bruno Oliveira for the PR.
2.7.2 (compared to 2.7.1)
-----------------------------
=============================
- fix issue767: pytest.raises value attribute does not contain the exception
instance on Python 2.6. Thanks Eric Siegerman for providing the test
@@ -307,7 +624,7 @@
2.7.1 (compared to 2.7.0)
-----------------------------
=============================
- fix issue731: do not get confused by the braces which may be present
and unbalanced in an object's repr while collapsing False
@@ -340,7 +657,7 @@
at least by pytest-xdist.
2.7.0 (compared to 2.6.4)
-----------------------------
=============================
- fix issue435: make reload() work when assert rewriting is active.
Thanks Daniel Hahler.
@@ -399,7 +716,7 @@
it from the "decorator" case. Thanks Tom Viner.
- "python_classes" and "python_functions" options now support glob-patterns
for test discovery, as discussed in issue600. Thanks Ldiary Translations.
for test discovery, as discussed in issue600. Thanks Ldiary Translations.
- allow to override parametrized fixtures with non-parametrized ones and vice versa (bubenkoff).
@@ -410,7 +727,7 @@
via postmortem debugging (almarklein).
2.6.4
----------
=====
- Improve assertion failure reporting on iterables, by using ndiff and
pprint.
@@ -439,7 +756,7 @@
- fix issue614: fixed pastebin support.
2.6.3
-----------
=====
- fix issue575: xunit-xml was reporting collection errors as failures
instead of errors, thanks Oleg Sinyavskiy.
@@ -466,7 +783,7 @@
Floris Bruynooghe.
2.6.2
-----------
=====
- Added function pytest.freeze_includes(), which makes it easy to embed
pytest into executables using tools like cx_freeze.
@@ -495,7 +812,7 @@
to them.
2.6.1
-----------------------------------
=====
- No longer show line numbers in the --verbose output, the output is now
purely the nodeid. The line number is still shown in failure reports.
@@ -527,7 +844,7 @@
Thanks Bruno Oliveira.
2.6
-----------------------------------
===
- Cache exceptions from fixtures according to their scope (issue 467).
@@ -632,7 +949,7 @@
2.5.2
-----------------------------------
=====
- fix issue409 -- better interoperate with cx_freeze by not
trying to import from collections.abc which causes problems
@@ -660,7 +977,7 @@
2.5.1
-----------------------------------
=====
- merge new documentation styling PR from Tobias Bieniek.
@@ -681,7 +998,7 @@
2.5.0
-----------------------------------
=====
- dropped python2.5 from automated release testing of pytest itself
which means it's probably going to break soon (but still works
@@ -816,8 +1133,8 @@
- fix verbose reporting for @mock'd test functions
v2.4.2
-----------------------------------
2.4.2
=====
- on Windows require colorama and a newer py lib so that py.io.TerminalWriter()
now uses colorama instead of its own ctypes hacks. (fixes issue365)
@@ -847,8 +1164,8 @@ v2.4.2
- add pluginmanager.do_configure(config) as a link to
config.do_configure() for plugin-compatibility
v2.4.1
-----------------------------------
2.4.1
=====
- When using parser.addoption() unicode arguments to the
"type" keyword should also be converted to the respective types.
@@ -863,8 +1180,8 @@ v2.4.1
- merge doc typo fixes, thanks Andy Dirnberger
v2.4
-----------------------------------
2.4
===
known incompatibilities:
@@ -1032,8 +1349,8 @@ Bug fixes:
".section(title)" and ".line(msg)" methods to print extra
information at the end of a test run.
v2.3.5
-----------------------------------
2.3.5
=====
- fix issue169: respect --tb=style with setup/teardown errors as well.
@@ -1097,8 +1414,8 @@ v2.3.5
- fix issue266 - accept unicode in MarkEvaluator expressions
v2.3.4
-----------------------------------
2.3.4
=====
- yielded test functions will now have autouse-fixtures active but
cannot accept fixtures as funcargs - it's anyway recommended to
@@ -1117,8 +1434,8 @@ v2.3.4
need to write as -k "TestClass and test_method" to match a certain
method in a certain test class.
v2.3.3
-----------------------------------
2.3.3
=====
- fix issue214 - parse modules that contain special objects like e. g.
flask's request object which blows up on getattr access if no request
@@ -1149,8 +1466,8 @@ v2.3.3
- fix issue127 - improve documentation for pytest_addoption() and
add a ``config.getoption(name)`` helper function for consistency.
v2.3.2
-----------------------------------
2.3.2
=====
- fix issue208 and fix issue29 use new py version to avoid long pauses
when printing tracebacks in long modules
@@ -1182,8 +1499,8 @@ v2.3.2
- add tox.ini to pytest distribution so that ignore-dirs and others config
bits are properly distributed for maintainers who run pytest-own tests
v2.3.1
-----------------------------------
2.3.1
=====
- fix issue202 - fix regression: using "self" from fixture functions now
works as expected (it's the same "self" instance that a test method
@@ -1195,8 +1512,8 @@ v2.3.1
- link to web pages from --markers output which provides help for
pytest.mark.* usage.
v2.3.0
-----------------------------------
2.3.0
=====
- fix issue202 - better automatic names for parametrized test functions
- fix issue139 - introduce @pytest.fixture which allows direct scoping
@@ -1274,8 +1591,8 @@ v2.3.0
- py.test -vv will show all of assert comparisations instead of truncating
v2.2.4
-----------------------------------
2.2.4
=====
- fix error message for rewritten assertions involving the % operator
- fix issue 126: correctly match all invalid xml characters for junitxml
@@ -1291,13 +1608,13 @@ v2.2.4
- fix issue #144: better mangle test ids to junitxml classnames
- upgrade distribute_setup.py to 0.6.27
v2.2.3
----------------------------------------
2.2.3
=====
- fix uploaded package to only include neccesary files
v2.2.2
----------------------------------------
2.2.2
=====
- fix issue101: wrong args to unittest.TestCase test function now
produce better output
@@ -1316,8 +1633,8 @@ v2.2.2
- allow adding of attributes to test reports such that it also works
with distributed testing (no upgrade of pytest-xdist needed)
v2.2.1
----------------------------------------
2.2.1
=====
- fix issue99 (in pytest and py) internallerrors with resultlog now
produce better output - fixed by normalizing pytest_internalerror
@@ -1333,8 +1650,8 @@ v2.2.1
- fix collection crash due to unknown-source collected items, thanks
to Ralf Schmitt (fixed by depending on a more recent pylib)
v2.2.0
----------------------------------------
2.2.0
=====
- fix issue90: introduce eager tearing down of test items so that
teardown function are called earlier.
@@ -1368,8 +1685,8 @@ v2.2.0
- simplify junitxml output code by relying on py.xml
- add support for skip properties on unittest classes and functions
v2.1.3
----------------------------------------
2.1.3
=====
- fix issue79: assertion rewriting failed on some comparisons in boolops
- correctly handle zero length arguments (a la pytest '')
@@ -1377,8 +1694,8 @@ v2.1.3
- fix issue75 / skipping test failure on jython
- fix issue77 / Allow assertrepr_compare hook to apply to a subset of tests
v2.1.2
----------------------------------------
2.1.2
=====
- fix assertion rewriting on files with windows newlines on some Python versions
- refine test discovery by package/module name (--pyargs), thanks Florian Mayer
@@ -1387,8 +1704,8 @@ v2.1.2
- fix issue66: use different assertion rewriting caches when the -O option is passed
- don't try assertion rewriting on Jython, use reinterp
v2.1.1
----------------------------------------------
2.1.1
=====
- fix issue64 / pytest.set_trace now works within pytest_generate_tests hooks
- fix issue60 / fix error conditions involving the creation of __pycache__
@@ -1400,8 +1717,8 @@ v2.1.1
- fix issue61: assertion rewriting on boolean operations with 3 or more operands
- you can now build a man page with "cd doc ; make man"
v2.1.0
----------------------------------------------
2.1.0
=====
- fix issue53 call nosestyle setup functions with correct ordering
- fix issue58 and issue59: new assertion code fixes
@@ -1420,8 +1737,8 @@ v2.1.0
- report KeyboardInterrupt even if interrupted during session startup
- fix issue 35 - provide PDF doc version and download link from index page
v2.0.3
----------------------------------------------
2.0.3
=====
- fix issue38: nicer tracebacks on calls to hooks, particularly early
configure/sessionstart ones
@@ -1440,8 +1757,8 @@ v2.0.3
- fix issue37: avoid invalid characters in junitxml's output
v2.0.2
----------------------------------------------
2.0.2
=====
- tackle issue32 - speed up test runs of very quick test functions
by reducing the relative overhead
@@ -1492,8 +1809,8 @@ v2.0.2
- avoid std unittest assertion helper code in tracebacks (thanks Ronny)
v2.0.1
----------------------------------------------
2.0.1
=====
- refine and unify initial capturing so that it works nicely
even if the logging module is used on an early-loaded conftest.py
@@ -1541,8 +1858,8 @@ v2.0.1
parametraization remains the "pytest_generate_tests"
mechanism, see the docs.
v2.0.0
----------------------------------------------
2.0.0
=====
- pytest-2.0 is now its own package and depends on pylib-2.0
- new ability: python -m pytest / python -m pytest.main ability
@@ -1586,8 +1903,8 @@ v2.0.0
- add ability to use "class" level for cached_setup helper
- fix strangeness: mark.* objects are now immutable, create new instances
v1.3.4
----------------------------------------------
1.3.4
=====
- fix issue111: improve install documentation for windows
- fix issue119: fix custom collectability of __init__.py as a module
@@ -1595,8 +1912,8 @@ v1.3.4
- fix issue115: unify internal exception passthrough/catching/GeneratorExit
- fix issue118: new --tb=native for presenting cpython-standard exceptions
v1.3.3
----------------------------------------------
1.3.3
=====
- fix issue113: assertion representation problem with triple-quoted strings
(and possibly other cases)
@@ -1610,11 +1927,10 @@ v1.3.3
(thanks Armin Ronacher for reporting)
- remove trailing whitespace in all py/text distribution files
v1.3.2
----------------------------------------------
1.3.2
=====
New features
++++++++++++++++++
**New features**
- fix issue103: introduce py.test.raises as context manager, examples::
@@ -1649,8 +1965,7 @@ New features
- introduce '--junitprefix=STR' option to prepend a prefix
to all reports in the junitxml file.
Bug fixes / Maintenance
++++++++++++++++++++++++++
**Bug fixes**
- make tests and the ``pytest_recwarn`` plugin in particular fully compatible
to Python2.7 (if you use the ``recwarn`` funcarg warnings will be enabled so that
@@ -1685,11 +2000,10 @@ Bug fixes / Maintenance
- fix homedir detection on Windows
- ship distribute_setup.py version 0.6.13
v1.3.1
---------------------------------------------
1.3.1
=====
New features
++++++++++++++++++
**New features**
- issue91: introduce new py.test.xfail(reason) helper
to imperatively mark a test as expected to fail. Can
@@ -1727,8 +2041,7 @@ New features
course requires that your application and tests are properly teared
down and don't have global state.
Fixes / Maintenance
++++++++++++++++++++++
**Bug Fixes**
- improved traceback presentation:
- improved and unified reporting for "--tb=short" option
@@ -1757,8 +2070,8 @@ Fixes / Maintenance
(and internally be more careful when presenting unexpected byte sequences)
v1.3.0
---------------------------------------------
1.3.0
=====
- deprecate --report option in favour of a new shorter and easier to
remember -r option: it takes a string argument consisting of any
@@ -1822,8 +2135,8 @@ v1.3.0
- added links to the new capturelog and coverage plugins
v1.2.0
---------------------------------------------
1.2.0
=====
- refined usage and options for "py.cleanup"::
@@ -1861,8 +2174,8 @@ v1.2.0
- fix plugin links
v1.1.1
---------------------------------------------
1.1.1
=====
- moved dist/looponfailing from py.test core into a new
separately released pytest-xdist plugin.
@@ -1882,7 +2195,7 @@ v1.1.1
- new funcarg: "pytestconfig" is the pytest config object for access
to command line args and can now be easily used in a test.
- install 'py.test' and `py.which` with a ``-$VERSION`` suffix to
- install ``py.test`` and ``py.which`` with a ``-$VERSION`` suffix to
disambiguate between Python3, python2.X, Jython and PyPy installed versions.
- new "pytestconfig" funcarg allows access to test config object
@@ -1945,8 +2258,8 @@ v1.1.1
- fix docs, fix internal bin/ script generation
v1.1.0
---------------------------------------------
1.1.0
=====
- introduce automatic plugin registration via 'pytest11'
entrypoints via setuptools' pkg_resources.iter_entry_points
@@ -1964,8 +2277,8 @@ v1.1.0
- try harder to have deprecation warnings for py.compat.* accesses
report a correct location
v1.0.2
---------------------------------------------
1.0.3
=====
* adjust and improve docs
@@ -2049,16 +2362,16 @@ v1.0.2
* simplified internal localpath implementation
v1.0.2
-------------------------------------------
1.0.2
=====
* fixing packaging issues, triggered by fedora redhat packaging,
also added doc, examples and contrib dirs to the tarball.
* added a documentation link to the new django plugin.
v1.0.1
-------------------------------------------
1.0.1
=====
* added a 'pytest_nose' plugin which handles nose.SkipTest,
nose-style function/method/generator setup/teardown and
@@ -2091,14 +2404,14 @@ v1.0.1
* simplified multicall mechanism and plugin architecture,
renamed some internal methods and argnames
v1.0.0
-------------------------------------------
1.0.0
=====
* more terse reporting try to show filesystem path relatively to current dir
* improve xfail output a bit
v1.0.0b9
-------------------------------------------
1.0.0b9
=======
* cleanly handle and report final teardown of test setup
@@ -2131,8 +2444,8 @@ v1.0.0b9
* item.repr_failure(excinfo) instead of item.repr_failure(excinfo, outerr)
v1.0.0b8
-------------------------------------------
1.0.0b8
=======
* pytest_unittest-plugin is now enabled by default
@@ -2160,8 +2473,8 @@ v1.0.0b8
* tweaked doctest output for docstrings in py modules,
thanks Radomir.
v1.0.0b7
-------------------------------------------
1.0.0b7
=======
* renamed py.test.xfail back to py.test.mark.xfail to avoid
two ways to decorate for xfail
@@ -2185,8 +2498,8 @@ v1.0.0b7
* make __name__ == "__channelexec__" for remote_exec code
v1.0.0b3
-------------------------------------------
1.0.0b3
=======
* plugin classes are removed: one now defines
hooks directly in conftest.py or global pytest_*.py
@@ -2202,8 +2515,8 @@ v1.0.0b3
well with function arguments.
v1.0.0b1
-------------------------------------------
1.0.0b1
=======
* introduced new "funcarg" setup method,
see doc/test/funcarg.txt
@@ -2226,8 +2539,8 @@ v1.0.0b1
XXX lots of things missing here XXX
v0.9.2
-------------------------------------------
0.9.2
=====
* refined installation and metadata, created new setup.py,
now based on setuptools/ez_setup (thanks to Ralf Schmitt
@@ -2259,8 +2572,8 @@ v0.9.2
* there now is a py.__version__ attribute
v0.9.1
-------------------------------------------
0.9.1
=====
This is a fairly complete list of v0.9.1, which can
serve as a reference for developers.

View File

@@ -9,46 +9,18 @@ so do not hesitate!
:depth: 2
.. _submitplugin:
.. _submitfeedback:
Submit a plugin, co-develop pytest
----------------------------------
Feature requests and feedback
-----------------------------
Pytest development of the core, some plugins and support code happens
in repositories living under:
Do you like pytest? Share some love on Twitter or in your blog posts!
- `the pytest-dev github organisation <https://github.com/pytest-dev>`_
We'd also like to hear about your propositions and suggestions. Feel free to
`submit them as issues <https://github.com/pytest-dev/pytest/issues>`_ and:
- `the pytest-dev bitbucket team <https://bitbucket.org/pytest-dev>`_
All pytest-dev Contributors team members have write access to all contained
repositories. pytest core and plugins are generally developed
using `pull requests`_ to respective repositories.
You can submit your plugin by subscribing to the `pytest-dev mail list
<https://mail.python.org/mailman/listinfo/pytest-dev>`_ and writing a
mail pointing to your existing pytest plugin repository which must have
the following:
- PyPI presence with a ``setup.py`` that contains a license, ``pytest-``
prefixed, version number, authors, short and long description.
- a ``tox.ini`` for running tests using `tox <http://tox.testrun.org>`_.
- a ``README.txt`` describing how to use the plugin and on which
platforms it runs.
- a ``LICENSE.txt`` file or equivalent containing the licensing
information, with matching info in ``setup.py``.
- an issue tracker unless you rather want to use the core ``pytest``
issue tracker.
If no contributor strongly objects and two agree, the repo will be
transferred to the ``pytest-dev`` organisation and you'll become a
member of the ``pytest-dev Contributors`` team, with commit rights
to all projects. We recommend that each plugin has at least three
people who have the right to release to pypi.
* Explain in detail how they should work.
* Keep the scope as narrow as possible. This will make it easier to implement.
.. _reportbugs:
@@ -56,7 +28,7 @@ people who have the right to release to pypi.
Report bugs
-----------
Report bugs for pytest at https://github.com/pytest-dev/pytest/issues
Report bugs for pytest in the `issue tracker <https://github.com/pytest-dev/pytest/issues>`_.
If you are reporting a bug, please include:
@@ -70,22 +42,6 @@ If you can write a demonstration test that currently fails but should pass (xfai
that is a very useful commit to make as well, even if you can't find how
to fix the bug yet.
.. _submitfeedback:
Submit feedback for developers
------------------------------
Do you like pytest? Share some love on Twitter or in your blog posts!
We'd also like to hear about your propositions and suggestions. Feel free to
`submit them as issues <https://github.com/pytest-dev/pytest/issues>`__ and:
* Set the "kind" to "enhancement" or "proposal" so that we can quickly find
about them.
* Explain in detail how they should work.
* Keep the scope as narrow as possible. This will make it easier to implement.
* If you have required skills and/or knowledge, we are very happy for
:ref:`pull requests <pull-requests>`.
.. _fixbugs:
@@ -118,19 +74,95 @@ pytest could always use more documentation. What exactly is needed?
* More complementary documentation. Have you perhaps found something unclear?
* Documentation translations. We currently have only English.
* Docstrings. There's never too much of them.
* Docstrings. There can never be too many of them.
* Blog posts, articles and such -- they're all very appreciated.
You can also edit documentation files directly in the Github web interface
without needing to make a fork and local copy. This can be convenient for
small fixes.
.. _submitplugin:
Submitting Plugins to pytest-dev
--------------------------------
Pytest development of the core, some plugins and support code happens
in repositories living under the ``pytest-dev`` organisations:
- `pytest-dev on GitHub <https://github.com/pytest-dev>`_
- `pytest-dev on Bitbucket <https://bitbucket.org/pytest-dev>`_
All pytest-dev Contributors team members have write access to all contained
repositories. pytest core and plugins are generally developed
using `pull requests`_ to respective repositories.
The objectives of the ``pytest-dev`` organisation are:
* Having a central location for popular pytest plugins
* Sharing some of the maintenance responsibility (in case a maintainer no longer whishes to maintain a plugin)
You can submit your plugin by subscribing to the `pytest-dev mail list
<https://mail.python.org/mailman/listinfo/pytest-dev>`_ and writing a
mail pointing to your existing pytest plugin repository which must have
the following:
- PyPI presence with a ``setup.py`` that contains a license, ``pytest-``
prefixed name, version number, authors, short and long description.
- a ``tox.ini`` for running tests using `tox <http://tox.testrun.org>`_.
- a ``README.txt`` describing how to use the plugin and on which
platforms it runs.
- a ``LICENSE.txt`` file or equivalent containing the licensing
information, with matching info in ``setup.py``.
- an issue tracker for bug reports and enhancement requests.
If no contributor strongly objects and two agree, the repository can then be
transferred to the ``pytest-dev`` organisation.
Here's a rundown of how a repository transfer usually proceeds
(using a repository named ``joedoe/pytest-xyz`` as example):
* One of the ``pytest-dev`` administrators creates:
- ``pytest-xyz-admin`` team, with full administration rights to
``pytest-dev/pytest-xyz``.
- ``pytest-xyz-developers`` team, with write access to
``pytest-dev/pytest-xyz``.
* ``joedoe`` is invited to the ``pytest-xyz-admin`` team;
* After accepting the invitation, ``joedoe`` transfers the repository from its
original location to ``pytest-dev/pytest-xyz`` (A nice feature is that GitHub handles URL redirection from
the old to the new location automatically).
* ``joedoe`` is free to add any other collaborators to the
``pytest-xyz-admin`` or ``pytest-xyz-developers`` team as desired.
The ``pytest-dev/Contributors`` team has write access to all projects, and
every project administrator is in it. We recommend that each plugin has at least three
people who have the right to release to PyPI.
Repository owners can be assured that no ``pytest-dev`` administrator will ever make
releases of your repository or take ownership in any way, except in rare cases
where someone becomes unresponsive after months of contact attempts.
As stated, the objective is to share maintenance and avoid "plugin-abandon".
.. _`pull requests`:
.. _pull-requests:
Preparing Pull Requests on GitHub
---------------------------------
There's an excellent tutorial on how Pull Requests work in the
`GitHub Help Center <https://help.github.com/articles/using-pull-requests/>`_
.. note::
What is a "pull request"? It informs project's core developers about the
changes you want to review and merge. Pull requests are stored on
@@ -179,10 +211,10 @@ but here is a simple overview:
You need to have Python 2.7 and 3.5 available in your system. Now
running tests is as simple as issuing this command::
$ python runtox.py -e py27,py35,flakes
$ python runtox.py -e linting,py27,py35
This command will run tests via the "tox" tool against Python 2.7 and 3.5
and also perform "flakes" coding-style checks. ``runtox.py`` is
and also perform "lint" coding-style checks. ``runtox.py`` is
a thin wrapper around ``tox`` which installs from a development package
index where newer (not yet released to pypi) versions of dependencies
(especially ``py``) might be present.

View File

@@ -3,12 +3,18 @@ How to release pytest
Note: this assumes you have already registered on pypi.
0. create the branch release-VERSION
use features as base for minor/major releases
and master as base for bugfix releases
1. Bump version numbers in _pytest/__init__.py (setup.py reads it)
2. Check and finalize CHANGELOG
3. Write doc/en/announce/release-VERSION.txt and include
it in doc/en/announce/index.txt
it in doc/en/announce/index.txt::
git log 2.8.2..HEAD --format='%aN' | sort -u # lists the names of authors involved
4. Use devpi for uploading a release tarball to a staging area::
@@ -25,10 +31,6 @@ Note: this assumes you have already registered on pypi.
devpi list pytest
or look at failures with "devpi list -f pytest".
There will be some failed environments like e.g. the py33-trial
or py27-pexpect tox environments on Win32 platforms
which is ok (tox does not support skipping on
per-platform basis yet).
7. Regenerate the docs examples using tox, and check for regressions::
@@ -39,7 +41,7 @@ Note: this assumes you have already registered on pypi.
8. Build the docs, you need a virtualenv with py and sphinx
installed::
cd doc/en
cd doc/en
make html
Commit any changes before tagging the release.
@@ -54,25 +56,25 @@ Note: this assumes you have already registered on pypi.
cd doc/en
make install # or "installall" if you have LaTeX installed for PDF
This requires ssh-login permission on pytest.org because it uses
rsync.
Note that the ``install`` target of ``doc/en/Makefile`` defines where the
rsync goes to, typically to the "latest" section of pytest.org.
This requires ssh-login permission on pytest.org because it uses
rsync.
Note that the ``install`` target of ``doc/en/Makefile`` defines where the
rsync goes to, typically to the "latest" section of pytest.org.
If you are making a minor release (e.g. 5.4), you also need to manually
create a symlink for "latest"::
If you are making a minor release (e.g. 5.4), you also need to manually
create a symlink for "latest"::
ssh pytest-dev@pytest.org
ln -s 5.4 latest
ssh pytest-dev@pytest.org
ln -s 5.4 latest
Browse to pytest.org to verify.
Browse to pytest.org to verify.
11. Publish to pypi::
devpi push pytest-VERSION pypi:NAME
where NAME is the name of pypi.python.org as configured in your ``~/.pypirc``
file `for devpi <http://doc.devpi.net/latest/quickstart-releaseprocess.html?highlight=pypirc#devpi-push-releasing-to-an-external-index>`_.
where NAME is the name of pypi.python.org as configured in your ``~/.pypirc``
file `for devpi <http://doc.devpi.net/latest/quickstart-releaseprocess.html?highlight=pypirc#devpi-push-releasing-to-an-external-index>`_.
12. Send release announcement to mailing lists:
@@ -83,5 +85,8 @@ Note: this assumes you have already registered on pypi.
13. **after the release** Bump the version number in ``_pytest/__init__.py``,
to the next Minor release version (i.e. if you released ``pytest-2.8.0``,
set it to ``pytest-2.9.0.dev1``).
to the next Minor release version (i.e. if you released ``pytest-2.8.0``,
set it to ``pytest-2.9.0.dev1``).
14. merge the actual release into the master branch and do a pull request against it
15. merge from master to features

View File

@@ -18,6 +18,7 @@ Unlike mock, "args.path" acts on the parsed auto-spec'ed ``os.path.abspath``
so it's independent from if the client side called "os.path.abspath(path=...)"
or "os.path.abspath('positional')".
refine parametrize API
-------------------------------------------------------------
tags: critical feature
@@ -115,7 +116,7 @@ tags: feature
- introduce pytest.mark.nocollect for not considering a function for
test collection at all. maybe also introduce a pytest.mark.test to
explicitely mark a function to become a tested one. Lookup JUnit ways
explicitly mark a function to become a tested one. Lookup JUnit ways
of tagging tests.
introduce pytest.mark.importorskip

36
LICENSE
View File

@@ -1,19 +1,21 @@
The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Copyright (c) 2004-2016 Holger Krekel and others
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,7 +1,34 @@
include CHANGELOG
include README.rst
include setup.py
include tox.ini
include CHANGELOG.rst
include LICENSE
graft doc
include AUTHORS
include README.rst
include CONTRIBUTING.rst
include tox.ini
include setup.py
include .coveragerc
include plugin-test.sh
include requirements-docs.txt
include runtox.py
recursive-include bench *.py
recursive-include extra *.py
graft testing
graft doc
exclude _pytest/impl
graft _pytest/vendored_packages
recursive-exclude * *.pyc *.pyo
exclude appveyor/install.ps1
exclude appveyor.yml
exclude appveyor
exclude ISSUES.txt
exclude HOWTORELEASE.rst

View File

@@ -1,66 +1,102 @@
======
pytest
======
.. image:: http://pytest.org/latest/_static/pytest1.png
:target: http://pytest.org
:align: center
:alt: pytest
The ``pytest`` testing tool makes it easy to write small tests, yet
scales to support complex functional testing.
------
.. image:: http://img.shields.io/pypi/v/pytest.svg
.. image:: https://img.shields.io/pypi/v/pytest.svg
:target: https://pypi.python.org/pypi/pytest
.. image:: http://img.shields.io/coveralls/pytest-dev/pytest/master.svg
.. image:: https://img.shields.io/pypi/pyversions/pytest.svg
:target: https://pypi.python.org/pypi/pytest
.. image:: https://img.shields.io/coveralls/pytest-dev/pytest/master.svg
:target: https://coveralls.io/r/pytest-dev/pytest
.. image:: https://travis-ci.org/pytest-dev/pytest.svg?branch=master
:target: https://travis-ci.org/pytest-dev/pytest
.. image:: https://ci.appveyor.com/api/projects/status/mrgbjaua7t33pg6b?svg=true
:target: https://ci.appveyor.com/project/pytestbot/pytest
Documentation: http://pytest.org/latest/
The ``pytest`` framework makes it easy to write small tests, yet
scales to support complex functional testing for applications and libraries.
Changelog: http://pytest.org/latest/changelog.html
An example of a simple test:
Issues: https://github.com/pytest-dev/pytest/issues
.. code-block:: python
# content of test_sample.py
def func(x):
return x + 1
def test_answer():
assert func(3) == 5
To execute it::
$ py.test
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
collected 1 items
test_sample.py F
======= FAILURES ========
_______ test_answer ________
def test_answer():
> assert func(3) == 5
E assert 4 == 5
E + where 4 = func(3)
test_sample.py:5: AssertionError
======= 1 failed in 0.12 seconds ========
Due to ``py.test``'s detailed assertion introspection, only plain ``assert`` statements are used. See `getting-started <http://pytest.org/latest/getting-started.html#our-first-test-run>`_ for more examples.
Features
--------
- `auto-discovery
<http://pytest.org/latest/goodpractises.html#python-test-discovery>`_
of test modules and functions,
- detailed info on failing `assert statements <http://pytest.org/latest/assert.html>`_ (no need to remember ``self.assert*`` names)
- `modular fixtures <http://pytest.org/latest/fixture.html>`_ for
managing small or parametrized long-lived test resources.
- multi-paradigm support: you can use ``pytest`` to run test suites based
on `unittest <http://pytest.org/latest/unittest.html>`_ (or trial),
`nose <http://pytest.org/latest/nose.html>`_
- single-source compatibility from Python2.6 all the way up to
Python3.5, PyPy-2.3, (jython-2.5 untested)
- Detailed info on failing `assert statements <http://pytest.org/latest/assert.html>`_ (no need to remember ``self.assert*`` names);
- `Auto-discovery
<http://pytest.org/latest/goodpractices.html#python-test-discovery>`_
of test modules and functions;
- `Modular fixtures <http://pytest.org/latest/fixture.html>`_ for
managing small or parametrized long-lived test resources;
- Can run `unittest <http://pytest.org/latest/unittest.html>`_ (or trial),
`nose <http://pytest.org/latest/nose.html>`_ test suites out of the box;
- Python2.6+, Python3.2+, PyPy-2.3, Jython-2.5 (untested);
- Rich plugin architecture, with over 150+ `external plugins <http://pytest.org/latest/plugins.html#installing-external-plugins-searching>`_ and thriving community;
- many `external plugins <http://pytest.org/latest/plugins.html#installing-external-plugins-searching>`_.
Documentation
-------------
A simple example for a test:
.. code-block:: python
# content of test_module.py
def test_function():
i = 4
assert i == 3
which can be run with ``py.test test_module.py``. See `getting-started <http://pytest.org/latest/getting-started.html#our-first-test-run>`_ for more examples.
For much more info, including PDF docs, see
http://pytest.org
and report bugs at:
https://github.com/pytest-dev/pytest/issues
and checkout or fork repo at:
https://github.com/pytest-dev/pytest
For full documentation, including installation, tutorials and PDF documents, please see http://pytest.org.
Copyright Holger Krekel and others, 2004-2015
Licensed under the MIT license.
Bugs/Requests
-------------
Please use the `GitHub issue tracker <https://github.com/pytest-dev/pytest/issues>`_ to submit bugs or request features.
Changelog
---------
Consult the `Changelog <http://pytest.org/latest/changelog.html>`_ page for fixes and enhancements of each version.
License
-------
Copyright Holger Krekel and others, 2004-2016.
Distributed under the terms of the `MIT`_ license, pytest is free and open source software.
.. _`MIT`: https://github.com/pytest-dev/pytest/blob/master/LICENSE

View File

@@ -1,2 +1,2 @@
#
__version__ = '2.8.1'
__version__ = '2.9.2'

View File

@@ -88,9 +88,6 @@ class FastFilesCompleter:
return completion
if os.environ.get('_ARGCOMPLETE'):
# argcomplete 0.5.6 is not compatible with python 2.5.6: print/with/format
if sys.version_info[:2] < (2, 6):
sys.exit(1)
try:
import argcomplete.completers
except ImportError:

12
_pytest/_code/__init__.py Normal file
View File

@@ -0,0 +1,12 @@
""" python inspection/code generation API """
from .code import Code # noqa
from .code import ExceptionInfo # noqa
from .code import Frame # noqa
from .code import Traceback # noqa
from .code import getrawcode # noqa
from .code import patch_builtins # noqa
from .code import unpatch_builtins # noqa
from .source import Source # noqa
from .source import compile_ as compile # noqa
from .source import getfslineno # noqa

View File

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

805
_pytest/_code/code.py Normal file
View File

@@ -0,0 +1,805 @@
import sys
from inspect import CO_VARARGS, CO_VARKEYWORDS
import py
builtin_repr = repr
reprlib = py.builtin._tryimport('repr', 'reprlib')
if sys.version_info[0] >= 3:
from traceback import format_exception_only
else:
from ._py2traceback import format_exception_only
class Code(object):
""" wrapper around Python code objects """
def __init__(self, rawcode):
if not hasattr(rawcode, "co_filename"):
rawcode = getrawcode(rawcode)
try:
self.filename = rawcode.co_filename
self.firstlineno = rawcode.co_firstlineno - 1
self.name = rawcode.co_name
except AttributeError:
raise TypeError("not a code object: %r" %(rawcode,))
self.raw = rawcode
def __eq__(self, other):
return self.raw == other.raw
def __ne__(self, other):
return not self == other
@property
def path(self):
""" return a path object pointing to source code (note that it
might not point to an actually existing file). """
p = py.path.local(self.raw.co_filename)
# maybe don't try this checking
if not p.check():
# XXX maybe try harder like the weird logic
# in the standard lib [linecache.updatecache] does?
p = self.raw.co_filename
return p
@property
def fullsource(self):
""" return a _pytest._code.Source object for the full source file of the code
"""
from _pytest._code import source
full, _ = source.findsource(self.raw)
return full
def source(self):
""" return a _pytest._code.Source object for the code object's source only
"""
# return source only for that part of code
import _pytest._code
return _pytest._code.Source(self.raw)
def getargs(self, var=False):
""" return a tuple with the argument names for the code object
if 'var' is set True also return the names of the variable and
keyword arguments when present
"""
# handfull shortcut for getting args
raw = self.raw
argcount = raw.co_argcount
if var:
argcount += raw.co_flags & CO_VARARGS
argcount += raw.co_flags & CO_VARKEYWORDS
return raw.co_varnames[:argcount]
class Frame(object):
"""Wrapper around a Python frame holding f_locals and f_globals
in which expressions can be evaluated."""
def __init__(self, frame):
self.lineno = frame.f_lineno - 1
self.f_globals = frame.f_globals
self.f_locals = frame.f_locals
self.raw = frame
self.code = Code(frame.f_code)
@property
def statement(self):
""" statement this frame is at """
import _pytest._code
if self.code.fullsource is None:
return _pytest._code.Source("")
return self.code.fullsource.getstatement(self.lineno)
def eval(self, code, **vars):
""" evaluate 'code' in the frame
'vars' are optional additional local variables
returns the result of the evaluation
"""
f_locals = self.f_locals.copy()
f_locals.update(vars)
return eval(code, self.f_globals, f_locals)
def exec_(self, code, **vars):
""" exec 'code' in the frame
'vars' are optiona; additional local variables
"""
f_locals = self.f_locals.copy()
f_locals.update(vars)
py.builtin.exec_(code, self.f_globals, f_locals )
def repr(self, object):
""" return a 'safe' (non-recursive, one-line) string repr for 'object'
"""
return py.io.saferepr(object)
def is_true(self, object):
return object
def getargs(self, var=False):
""" return a list of tuples (name, value) for all arguments
if 'var' is set True also include the variable and keyword
arguments when present
"""
retval = []
for arg in self.code.getargs(var):
try:
retval.append((arg, self.f_locals[arg]))
except KeyError:
pass # this can occur when using Psyco
return retval
class TracebackEntry(object):
""" a single entry in a traceback """
_repr_style = None
exprinfo = None
def __init__(self, rawentry):
self._rawentry = rawentry
self.lineno = rawentry.tb_lineno - 1
def set_repr_style(self, mode):
assert mode in ("short", "long")
self._repr_style = mode
@property
def frame(self):
import _pytest._code
return _pytest._code.Frame(self._rawentry.tb_frame)
@property
def relline(self):
return self.lineno - self.frame.code.firstlineno
def __repr__(self):
return "<TracebackEntry %s:%d>" %(self.frame.code.path, self.lineno+1)
@property
def statement(self):
""" _pytest._code.Source object for the current statement """
source = self.frame.code.fullsource
return source.getstatement(self.lineno)
@property
def path(self):
""" path to the source code """
return self.frame.code.path
def getlocals(self):
return self.frame.f_locals
locals = property(getlocals, None, None, "locals of underlaying frame")
def reinterpret(self):
"""Reinterpret the failing statement and returns a detailed information
about what operations are performed."""
from _pytest.assertion.reinterpret import reinterpret
if self.exprinfo is None:
source = py.builtin._totext(self.statement).strip()
x = reinterpret(source, self.frame, should_fail=True)
if not py.builtin._istext(x):
raise TypeError("interpret returned non-string %r" % (x,))
self.exprinfo = x
return self.exprinfo
def getfirstlinesource(self):
# on Jython this firstlineno can be -1 apparently
return max(self.frame.code.firstlineno, 0)
def getsource(self, astcache=None):
""" return failing source code. """
# we use the passed in astcache to not reparse asttrees
# within exception info printing
from _pytest._code.source import getstatementrange_ast
source = self.frame.code.fullsource
if source is None:
return None
key = astnode = None
if astcache is not None:
key = self.frame.code.path
if key is not None:
astnode = astcache.get(key, None)
start = self.getfirstlinesource()
try:
astnode, _, end = getstatementrange_ast(self.lineno, source,
astnode=astnode)
except SyntaxError:
end = self.lineno + 1
else:
if key is not None:
astcache[key] = astnode
return source[start:end]
source = property(getsource)
def ishidden(self):
""" return True if the current frame has a var __tracebackhide__
resolving to True
mostly for internal use
"""
try:
return self.frame.f_locals['__tracebackhide__']
except KeyError:
try:
return self.frame.f_globals['__tracebackhide__']
except KeyError:
return False
def __str__(self):
try:
fn = str(self.path)
except py.error.Error:
fn = '???'
name = self.frame.code.name
try:
line = str(self.statement).lstrip()
except KeyboardInterrupt:
raise
except:
line = "???"
return " File %r:%d in %s\n %s\n" %(fn, self.lineno+1, name, line)
def name(self):
return self.frame.code.raw.co_name
name = property(name, None, None, "co_name of underlaying code")
class Traceback(list):
""" Traceback objects encapsulate and offer higher level
access to Traceback entries.
"""
Entry = TracebackEntry
def __init__(self, tb):
""" initialize from given python traceback object. """
if hasattr(tb, 'tb_next'):
def f(cur):
while cur is not None:
yield self.Entry(cur)
cur = cur.tb_next
list.__init__(self, f(tb))
else:
list.__init__(self, tb)
def cut(self, path=None, lineno=None, firstlineno=None, excludepath=None):
""" return a Traceback instance wrapping part of this Traceback
by provding any combination of path, lineno and firstlineno, the
first frame to start the to-be-returned traceback is determined
this allows cutting the first part of a Traceback instance e.g.
for formatting reasons (removing some uninteresting bits that deal
with handling of the exception/traceback)
"""
for x in self:
code = x.frame.code
codepath = code.path
if ((path is None or codepath == path) and
(excludepath is None or not hasattr(codepath, 'relto') or
not codepath.relto(excludepath)) and
(lineno is None or x.lineno == lineno) and
(firstlineno is None or x.frame.code.firstlineno == firstlineno)):
return Traceback(x._rawentry)
return self
def __getitem__(self, key):
val = super(Traceback, self).__getitem__(key)
if isinstance(key, type(slice(0))):
val = self.__class__(val)
return val
def filter(self, fn=lambda x: not x.ishidden()):
""" return a Traceback instance with certain items removed
fn is a function that gets a single argument, a TracebackEntry
instance, and should return True when the item should be added
to the Traceback, False when not
by default this removes all the TracebackEntries which are hidden
(see ishidden() above)
"""
return Traceback(filter(fn, self))
def getcrashentry(self):
""" return last non-hidden traceback entry that lead
to the exception of a traceback.
"""
for i in range(-1, -len(self)-1, -1):
entry = self[i]
if not entry.ishidden():
return entry
return self[-1]
def recursionindex(self):
""" return the index of the frame/TracebackEntry where recursion
originates if appropriate, None if no recursion occurred
"""
cache = {}
for i, entry in enumerate(self):
# id for the code.raw is needed to work around
# the strange metaprogramming in the decorator lib from pypi
# which generates code objects that have hash/value equality
#XXX needs a test
key = entry.frame.code.path, id(entry.frame.code.raw), entry.lineno
#print "checking for recursion at", key
l = cache.setdefault(key, [])
if l:
f = entry.frame
loc = f.f_locals
for otherloc in l:
if f.is_true(f.eval(co_equal,
__recursioncache_locals_1=loc,
__recursioncache_locals_2=otherloc)):
return i
l.append(entry.frame.f_locals)
return None
co_equal = compile('__recursioncache_locals_1 == __recursioncache_locals_2',
'?', 'eval')
class ExceptionInfo(object):
""" wraps sys.exc_info() objects and offers
help for navigating the traceback.
"""
_striptext = ''
def __init__(self, tup=None, exprinfo=None):
import _pytest._code
if tup is None:
tup = sys.exc_info()
if exprinfo is None and isinstance(tup[1], AssertionError):
exprinfo = getattr(tup[1], 'msg', None)
if exprinfo is None:
exprinfo = str(tup[1])
if exprinfo and exprinfo.startswith('assert '):
self._striptext = 'AssertionError: '
self._excinfo = tup
#: the exception class
self.type = tup[0]
#: the exception instance
self.value = tup[1]
#: the exception raw traceback
self.tb = tup[2]
#: the exception type name
self.typename = self.type.__name__
#: the exception traceback (_pytest._code.Traceback instance)
self.traceback = _pytest._code.Traceback(self.tb)
def __repr__(self):
return "<ExceptionInfo %s tblen=%d>" % (self.typename, len(self.traceback))
def exconly(self, tryshort=False):
""" return the exception as a string
when 'tryshort' resolves to True, and the exception is a
_pytest._code._AssertionError, only the actual exception part of
the exception representation is returned (so 'AssertionError: ' is
removed from the beginning)
"""
lines = format_exception_only(self.type, self.value)
text = ''.join(lines)
text = text.rstrip()
if tryshort:
if text.startswith(self._striptext):
text = text[len(self._striptext):]
return text
def errisinstance(self, exc):
""" return True if the exception is an instance of exc """
return isinstance(self.value, exc)
def _getreprcrash(self):
exconly = self.exconly(tryshort=True)
entry = self.traceback.getcrashentry()
path, lineno = entry.frame.code.raw.co_filename, entry.lineno
return ReprFileLocation(path, lineno+1, exconly)
def getrepr(self, showlocals=False, style="long",
abspath=False, tbfilter=True, funcargs=False):
""" return str()able representation of this exception info.
showlocals: show locals per traceback entry
style: long|short|no|native traceback style
tbfilter: hide entries (where __tracebackhide__ is true)
in case of style==native, tbfilter and showlocals is ignored.
"""
if style == 'native':
return ReprExceptionInfo(ReprTracebackNative(
py.std.traceback.format_exception(
self.type,
self.value,
self.traceback[0]._rawentry,
)), self._getreprcrash())
fmt = FormattedExcinfo(showlocals=showlocals, style=style,
abspath=abspath, tbfilter=tbfilter, funcargs=funcargs)
return fmt.repr_excinfo(self)
def __str__(self):
entry = self.traceback[-1]
loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly())
return str(loc)
def __unicode__(self):
entry = self.traceback[-1]
loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly())
return unicode(loc)
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 = {}
def _getindent(self, source):
# figure out indent for given source
try:
s = str(source.getstatement(len(source)-1))
except KeyboardInterrupt:
raise
except:
try:
s = str(source[-1])
except KeyboardInterrupt:
raise
except:
return 0
return 4 + (len(s) - len(s.lstrip()))
def _getentrysource(self, entry):
source = entry.getsource(self.astcache)
if source is not None:
source = source.deindent()
return source
def _saferepr(self, obj):
return py.io.saferepr(obj)
def repr_args(self, entry):
if self.funcargs:
args = []
for argname, argvalue in entry.frame.getargs(var=True):
args.append((argname, self._saferepr(argvalue)))
return ReprFuncArgs(args)
def get_source(self, source, line_index=-1, excinfo=None, short=False):
""" return formatted and marked up source lines. """
import _pytest._code
lines = []
if source is None or line_index >= len(source.lines):
source = _pytest._code.Source("???")
line_index = 0
if line_index < 0:
line_index += len(source)
space_prefix = " "
if short:
lines.append(space_prefix + source.lines[line_index].strip())
else:
for line in source.lines[:line_index]:
lines.append(space_prefix + line)
lines.append(self.flow_marker + " " + source.lines[line_index])
for line in source.lines[line_index+1:]:
lines.append(space_prefix + line)
if excinfo is not None:
indent = 4 if short else self._getindent(source)
lines.extend(self.get_exconly(excinfo, indent=indent, markall=True))
return lines
def get_exconly(self, excinfo, indent=4, markall=False):
lines = []
indent = " " * indent
# get the real exception information out
exlines = excinfo.exconly(tryshort=True).split('\n')
failindent = self.fail_marker + indent[1:]
for line in exlines:
lines.append(failindent + line)
if not markall:
failindent = indent
return lines
def repr_locals(self, locals):
if self.showlocals:
lines = []
keys = [loc for loc in locals if loc[0] != "@"]
keys.sort()
for name in keys:
value = locals[name]
if name == '__builtins__':
lines.append("__builtins__ = <builtins>")
else:
# This formatting could all be handled by the
# _repr() function, which is only reprlib.Repr in
# disguise, so is very configurable.
str_repr = self._saferepr(value)
#if len(str_repr) < 70 or not isinstance(value,
# (list, tuple, dict)):
lines.append("%-10s = %s" %(name, str_repr))
#else:
# self._line("%-10s =\\" % (name,))
# # XXX
# py.std.pprint.pprint(value, stream=self.excinfowriter)
return ReprLocals(lines)
def repr_traceback_entry(self, entry, excinfo=None):
import _pytest._code
source = self._getentrysource(entry)
if source is None:
source = _pytest._code.Source("???")
line_index = 0
else:
# entry.getfirstlinesource() can be -1, should be 0 on jython
line_index = entry.lineno - max(entry.getfirstlinesource(), 0)
lines = []
style = entry._repr_style
if style is None:
style = self.style
if style in ("short", "long"):
short = style == "short"
reprargs = self.repr_args(entry) if not short else None
s = self.get_source(source, line_index, excinfo, short=short)
lines.extend(s)
if short:
message = "in %s" %(entry.name)
else:
message = excinfo and excinfo.typename or ""
path = self._makepath(entry.path)
filelocrepr = ReprFileLocation(path, entry.lineno+1, message)
localsrepr = None
if not short:
localsrepr = self.repr_locals(entry.locals)
return ReprEntry(lines, reprargs, localsrepr, filelocrepr, style)
if excinfo:
lines.extend(self.get_exconly(excinfo, indent=4))
return ReprEntry(lines, None, None, None, style)
def _makepath(self, path):
if not self.abspath:
try:
np = py.path.local().bestrelpath(path)
except OSError:
return path
if len(np) < len(str(path)):
path = np
return path
def repr_traceback(self, excinfo):
traceback = excinfo.traceback
if self.tbfilter:
traceback = traceback.filter()
recursionindex = None
if is_recursion_error(excinfo):
recursionindex = traceback.recursionindex()
last = traceback[-1]
entries = []
extraline = None
for index, entry in enumerate(traceback):
einfo = (last == entry) and excinfo or None
reprentry = self.repr_traceback_entry(entry, einfo)
entries.append(reprentry)
if index == recursionindex:
extraline = "!!! Recursion detected (same locals & position)"
break
return ReprTraceback(entries, extraline, style=self.style)
def repr_excinfo(self, excinfo):
reprtraceback = self.repr_traceback(excinfo)
reprcrash = excinfo._getreprcrash()
return ReprExceptionInfo(reprtraceback, reprcrash)
class TerminalRepr:
def __str__(self):
s = self.__unicode__()
if sys.version_info[0] < 3:
s = s.encode('utf-8')
return s
def __unicode__(self):
# FYI this is called from pytest-xdist's serialization of exception
# information.
io = py.io.TextIO()
tw = py.io.TerminalWriter(file=io)
self.toterminal(tw)
return io.getvalue().strip()
def __repr__(self):
return "<%s instance at %0x>" %(self.__class__, id(self))
class ReprExceptionInfo(TerminalRepr):
def __init__(self, reprtraceback, reprcrash):
self.reprtraceback = reprtraceback
self.reprcrash = reprcrash
self.sections = []
def addsection(self, name, content, sep="-"):
self.sections.append((name, content, sep))
def toterminal(self, tw):
self.reprtraceback.toterminal(tw)
for name, content, sep in self.sections:
tw.sep(sep, name)
tw.line(content)
class ReprTraceback(TerminalRepr):
entrysep = "_ "
def __init__(self, reprentries, extraline, style):
self.reprentries = reprentries
self.extraline = extraline
self.style = style
def toterminal(self, tw):
# the entries might have different styles
for i, entry in enumerate(self.reprentries):
if entry.style == "long":
tw.line("")
entry.toterminal(tw)
if i < len(self.reprentries) - 1:
next_entry = self.reprentries[i+1]
if entry.style == "long" or \
entry.style == "short" and next_entry.style == "long":
tw.sep(self.entrysep)
if self.extraline:
tw.line(self.extraline)
class ReprTracebackNative(ReprTraceback):
def __init__(self, tblines):
self.style = "native"
self.reprentries = [ReprEntryNative(tblines)]
self.extraline = None
class ReprEntryNative(TerminalRepr):
style = "native"
def __init__(self, tblines):
self.lines = tblines
def toterminal(self, tw):
tw.write("".join(self.lines))
class ReprEntry(TerminalRepr):
localssep = "_ "
def __init__(self, lines, reprfuncargs, reprlocals, filelocrepr, style):
self.lines = lines
self.reprfuncargs = reprfuncargs
self.reprlocals = reprlocals
self.reprfileloc = filelocrepr
self.style = style
def toterminal(self, tw):
if self.style == "short":
self.reprfileloc.toterminal(tw)
for line in self.lines:
red = line.startswith("E ")
tw.line(line, bold=True, red=red)
#tw.line("")
return
if self.reprfuncargs:
self.reprfuncargs.toterminal(tw)
for line in self.lines:
red = line.startswith("E ")
tw.line(line, bold=True, red=red)
if self.reprlocals:
#tw.sep(self.localssep, "Locals")
tw.line("")
self.reprlocals.toterminal(tw)
if self.reprfileloc:
if self.lines:
tw.line("")
self.reprfileloc.toterminal(tw)
def __str__(self):
return "%s\n%s\n%s" % ("\n".join(self.lines),
self.reprlocals,
self.reprfileloc)
class ReprFileLocation(TerminalRepr):
def __init__(self, path, lineno, message):
self.path = str(path)
self.lineno = lineno
self.message = message
def toterminal(self, tw):
# filename and lineno output for each entry,
# using an output format that most editors unterstand
msg = self.message
i = msg.find("\n")
if i != -1:
msg = msg[:i]
tw.line("%s:%s: %s" %(self.path, self.lineno, msg))
class ReprLocals(TerminalRepr):
def __init__(self, lines):
self.lines = lines
def toterminal(self, tw):
for line in self.lines:
tw.line(line)
class ReprFuncArgs(TerminalRepr):
def __init__(self, args):
self.args = args
def toterminal(self, tw):
if self.args:
linesofar = ""
for name, value in self.args:
ns = "%s = %s" %(name, value)
if len(ns) + len(linesofar) + 2 > tw.fullwidth:
if linesofar:
tw.line(linesofar)
linesofar = ns
else:
if linesofar:
linesofar += ", " + ns
else:
linesofar = ns
if linesofar:
tw.line(linesofar)
tw.line("")
oldbuiltins = {}
def patch_builtins(assertion=True, compile=True):
""" put compile and AssertionError builtins to Python's builtins. """
if assertion:
from _pytest.assertion import reinterpret
l = oldbuiltins.setdefault('AssertionError', [])
l.append(py.builtin.builtins.AssertionError)
py.builtin.builtins.AssertionError = reinterpret.AssertionError
if compile:
import _pytest._code
l = oldbuiltins.setdefault('compile', [])
l.append(py.builtin.builtins.compile)
py.builtin.builtins.compile = _pytest._code.compile
def unpatch_builtins(assertion=True, compile=True):
""" remove compile and AssertionError builtins from Python builtins. """
if assertion:
py.builtin.builtins.AssertionError = oldbuiltins['AssertionError'].pop()
if compile:
py.builtin.builtins.compile = oldbuiltins['compile'].pop()
def getrawcode(obj, trycall=True):
""" return code object for given function. """
try:
return obj.__code__
except AttributeError:
obj = getattr(obj, 'im_func', obj)
obj = getattr(obj, 'func_code', obj)
obj = getattr(obj, 'f_code', obj)
obj = getattr(obj, '__code__', obj)
if trycall and not hasattr(obj, 'co_firstlineno'):
if hasattr(obj, '__call__') and not py.std.inspect.isclass(obj):
x = getrawcode(obj.__call__, trycall=False)
if hasattr(x, 'co_firstlineno'):
return x
return obj
if sys.version_info[:2] >= (3, 5): # RecursionError introduced in 3.5
def is_recursion_error(excinfo):
return excinfo.errisinstance(RecursionError) # noqa
else:
def is_recursion_error(excinfo):
if not excinfo.errisinstance(RuntimeError):
return False
try:
return "maximum recursion depth exceeded" in str(excinfo.value)
except UnicodeError:
return False

421
_pytest/_code/source.py Normal file
View File

@@ -0,0 +1,421 @@
from __future__ import generators
from bisect import bisect_right
import sys
import inspect, tokenize
import py
from types import ModuleType
cpy_compile = compile
try:
import _ast
from _ast import PyCF_ONLY_AST as _AST_FLAG
except ImportError:
_AST_FLAG = 0
_ast = None
class Source(object):
""" a immutable object holding a source code fragment,
possibly deindenting it.
"""
_compilecounter = 0
def __init__(self, *parts, **kwargs):
self.lines = lines = []
de = kwargs.get('deindent', True)
rstrip = kwargs.get('rstrip', True)
for part in parts:
if not part:
partlines = []
if isinstance(part, Source):
partlines = part.lines
elif isinstance(part, (tuple, list)):
partlines = [x.rstrip("\n") for x in part]
elif isinstance(part, py.builtin._basestring):
partlines = part.split('\n')
if rstrip:
while partlines:
if partlines[-1].strip():
break
partlines.pop()
else:
partlines = getsource(part, deindent=de).lines
if de:
partlines = deindent(partlines)
lines.extend(partlines)
def __eq__(self, other):
try:
return self.lines == other.lines
except AttributeError:
if isinstance(other, str):
return str(self) == other
return False
def __getitem__(self, key):
if isinstance(key, int):
return self.lines[key]
else:
if key.step not in (None, 1):
raise IndexError("cannot slice a Source with a step")
return self.__getslice__(key.start, key.stop)
def __len__(self):
return len(self.lines)
def __getslice__(self, start, end):
newsource = Source()
newsource.lines = self.lines[start:end]
return newsource
def strip(self):
""" return new source object with trailing
and leading blank lines removed.
"""
start, end = 0, len(self)
while start < end and not self.lines[start].strip():
start += 1
while end > start and not self.lines[end-1].strip():
end -= 1
source = Source()
source.lines[:] = self.lines[start:end]
return source
def putaround(self, before='', after='', indent=' ' * 4):
""" return a copy of the source object with
'before' and 'after' wrapped around it.
"""
before = Source(before)
after = Source(after)
newsource = Source()
lines = [ (indent + line) for line in self.lines]
newsource.lines = before.lines + lines + after.lines
return newsource
def indent(self, indent=' ' * 4):
""" return a copy of the source object with
all lines indented by the given indent-string.
"""
newsource = Source()
newsource.lines = [(indent+line) for line in self.lines]
return newsource
def getstatement(self, lineno, assertion=False):
""" return Source statement which contains the
given linenumber (counted from 0).
"""
start, end = self.getstatementrange(lineno, assertion)
return self[start:end]
def getstatementrange(self, lineno, assertion=False):
""" return (start, end) tuple which spans the minimal
statement region which containing the given lineno.
"""
if not (0 <= lineno < len(self)):
raise IndexError("lineno out of range")
ast, start, end = getstatementrange_ast(lineno, self)
return start, end
def deindent(self, offset=None):
""" return a new source object deindented by offset.
If offset is None then guess an indentation offset from
the first non-blank line. Subsequent lines which have a
lower indentation offset will be copied verbatim as
they are assumed to be part of multilines.
"""
# XXX maybe use the tokenizer to properly handle multiline
# strings etc.pp?
newsource = Source()
newsource.lines[:] = deindent(self.lines, offset)
return newsource
def isparseable(self, deindent=True):
""" return True if source is parseable, heuristically
deindenting it by default.
"""
try:
import parser
except ImportError:
syntax_checker = lambda x: compile(x, 'asd', 'exec')
else:
syntax_checker = parser.suite
if deindent:
source = str(self.deindent())
else:
source = str(self)
try:
#compile(source+'\n', "x", "exec")
syntax_checker(source+'\n')
except KeyboardInterrupt:
raise
except Exception:
return False
else:
return True
def __str__(self):
return "\n".join(self.lines)
def compile(self, filename=None, mode='exec',
flag=generators.compiler_flag,
dont_inherit=0, _genframe=None):
""" return compiled code object. if filename is None
invent an artificial filename which displays
the source/line position of the caller frame.
"""
if not filename or py.path.local(filename).check(file=0):
if _genframe is None:
_genframe = sys._getframe(1) # the caller
fn,lineno = _genframe.f_code.co_filename, _genframe.f_lineno
base = "<%d-codegen " % self._compilecounter
self.__class__._compilecounter += 1
if not filename:
filename = base + '%s:%d>' % (fn, lineno)
else:
filename = base + '%r %s:%d>' % (filename, fn, lineno)
source = "\n".join(self.lines) + '\n'
try:
co = cpy_compile(source, filename, mode, flag)
except SyntaxError:
ex = sys.exc_info()[1]
# re-represent syntax errors from parsing python strings
msglines = self.lines[:ex.lineno]
if ex.offset:
msglines.append(" "*ex.offset + '^')
msglines.append("(code was compiled probably from here: %s)" % filename)
newex = SyntaxError('\n'.join(msglines))
newex.offset = ex.offset
newex.lineno = ex.lineno
newex.text = ex.text
raise newex
else:
if flag & _AST_FLAG:
return co
lines = [(x + "\n") for x in self.lines]
if sys.version_info[0] >= 3:
# XXX py3's inspect.getsourcefile() checks for a module
# and a pep302 __loader__ ... we don't have a module
# at code compile-time so we need to fake it here
m = ModuleType("_pycodecompile_pseudo_module")
py.std.inspect.modulesbyfile[filename] = None
py.std.sys.modules[None] = m
m.__loader__ = 1
py.std.linecache.cache[filename] = (1, None, lines, filename)
return co
#
# public API shortcut functions
#
def compile_(source, filename=None, mode='exec', flags=
generators.compiler_flag, dont_inherit=0):
""" compile the given source to a raw code object,
and maintain an internal cache which allows later
retrieval of the source code for the code object
and any recursively created code objects.
"""
if _ast is not None and isinstance(source, _ast.AST):
# XXX should Source support having AST?
return cpy_compile(source, filename, mode, flags, dont_inherit)
_genframe = sys._getframe(1) # the caller
s = Source(source)
co = s.compile(filename, mode, flags, _genframe=_genframe)
return co
def getfslineno(obj):
""" Return source location (path, lineno) for the given object.
If the source cannot be determined return ("", -1)
"""
import _pytest._code
try:
code = _pytest._code.Code(obj)
except TypeError:
try:
fn = (py.std.inspect.getsourcefile(obj) or
py.std.inspect.getfile(obj))
except TypeError:
return "", -1
fspath = fn and py.path.local(fn) or None
lineno = -1
if fspath:
try:
_, lineno = findsource(obj)
except IOError:
pass
else:
fspath = code.path
lineno = code.firstlineno
assert isinstance(lineno, int)
return fspath, lineno
#
# helper functions
#
def findsource(obj):
try:
sourcelines, lineno = py.std.inspect.findsource(obj)
except py.builtin._sysex:
raise
except:
return None, -1
source = Source()
source.lines = [line.rstrip() for line in sourcelines]
return source, lineno
def getsource(obj, **kwargs):
import _pytest._code
obj = _pytest._code.getrawcode(obj)
try:
strsrc = inspect.getsource(obj)
except IndentationError:
strsrc = "\"Buggy python version consider upgrading, cannot get source\""
assert isinstance(strsrc, str)
return Source(strsrc, **kwargs)
def deindent(lines, offset=None):
if offset is None:
for line in lines:
line = line.expandtabs()
s = line.lstrip()
if s:
offset = len(line)-len(s)
break
else:
offset = 0
if offset == 0:
return list(lines)
newlines = []
def readline_generator(lines):
for line in lines:
yield line + '\n'
while True:
yield ''
it = readline_generator(lines)
try:
for _, _, (sline, _), (eline, _), _ in tokenize.generate_tokens(lambda: next(it)):
if sline > len(lines):
break # End of input reached
if sline > len(newlines):
line = lines[sline - 1].expandtabs()
if line.lstrip() and line[:offset].isspace():
line = line[offset:] # Deindent
newlines.append(line)
for i in range(sline, eline):
# Don't deindent continuing lines of
# multiline tokens (i.e. multiline strings)
newlines.append(lines[i])
except (IndentationError, tokenize.TokenError):
pass
# Add any lines we didn't see. E.g. if an exception was raised.
newlines.extend(lines[len(newlines):])
return newlines
def get_statement_startend2(lineno, node):
import ast
# flatten all statements and except handlers into one lineno-list
# AST's line numbers start indexing at 1
l = []
for x in ast.walk(node):
if isinstance(x, _ast.stmt) or isinstance(x, _ast.ExceptHandler):
l.append(x.lineno - 1)
for name in "finalbody", "orelse":
val = getattr(x, name, None)
if val:
# treat the finally/orelse part as its own statement
l.append(val[0].lineno - 1 - 1)
l.sort()
insert_index = bisect_right(l, lineno)
start = l[insert_index - 1]
if insert_index >= len(l):
end = None
else:
end = l[insert_index]
return start, end
def getstatementrange_ast(lineno, source, assertion=False, astnode=None):
if astnode is None:
content = str(source)
if sys.version_info < (2,7):
content += "\n"
try:
astnode = compile(content, "source", "exec", 1024) # 1024 for AST
except ValueError:
start, end = getstatementrange_old(lineno, source, assertion)
return None, start, end
start, end = get_statement_startend2(lineno, astnode)
# we need to correct the end:
# - ast-parsing strips comments
# - there might be empty lines
# - we might have lesser indented code blocks at the end
if end is None:
end = len(source.lines)
if end > start + 1:
# make sure we don't span differently indented code blocks
# by using the BlockFinder helper used which inspect.getsource() uses itself
block_finder = inspect.BlockFinder()
# if we start with an indented line, put blockfinder to "started" mode
block_finder.started = source.lines[start][0].isspace()
it = ((x + "\n") for x in source.lines[start:end])
try:
for tok in tokenize.generate_tokens(lambda: next(it)):
block_finder.tokeneater(*tok)
except (inspect.EndOfBlock, IndentationError):
end = block_finder.last + start
except Exception:
pass
# the end might still point to a comment or empty line, correct it
while end:
line = source.lines[end - 1].lstrip()
if line.startswith("#") or not line:
end -= 1
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

@@ -2,6 +2,7 @@
support for presenting detailed information in failing assertions.
"""
import py
import os
import sys
from _pytest.monkeypatch import monkeypatch
from _pytest.assertion import util
@@ -86,6 +87,12 @@ def pytest_collection(session):
hook.set_session(session)
def _running_on_ci():
"""Check if we're currently running on a CI system."""
env_vars = ['CI', 'BUILD_NUMBER']
return any(var in os.environ for var in env_vars)
def pytest_runtest_setup(item):
"""Setup the pytest_assertrepr_compare hook
@@ -99,7 +106,8 @@ def pytest_runtest_setup(item):
This uses the first result from the hook and then ensures the
following:
* Overly verbose explanations are dropped unles -vv was used.
* Overly verbose explanations are dropped unless -vv was used or
running on a CI.
* Embedded newlines are escaped to help util.format_explanation()
later.
* If the rewrite mode is used embedded %-characters are replaced
@@ -112,8 +120,9 @@ def pytest_runtest_setup(item):
config=item.config, op=op, left=left, right=right)
for new_expl in hook_result:
if new_expl:
if (sum(len(p) for p in new_expl[1:]) > 80*8
and item.config.option.verbose < 2):
if (sum(len(p) for p in new_expl[1:]) > 80*8 and
item.config.option.verbose < 2 and
not _running_on_ci()):
show_max = 10
truncated_lines = len(new_expl) - show_max
new_expl[show_max:] = [py.builtin._totext(

View File

@@ -1,365 +0,0 @@
"""
Find intermediate evalutation results in assert statements through builtin AST.
This should replace oldinterpret.py eventually.
"""
import sys
import ast
import py
from _pytest.assertion import util
from _pytest.assertion.reinterpret import BuiltinAssertionError
if sys.platform.startswith("java"):
# See http://bugs.jython.org/issue1497
_exprs = ("BoolOp", "BinOp", "UnaryOp", "Lambda", "IfExp", "Dict",
"ListComp", "GeneratorExp", "Yield", "Compare", "Call",
"Repr", "Num", "Str", "Attribute", "Subscript", "Name",
"List", "Tuple")
_stmts = ("FunctionDef", "ClassDef", "Return", "Delete", "Assign",
"AugAssign", "Print", "For", "While", "If", "With", "Raise",
"TryExcept", "TryFinally", "Assert", "Import", "ImportFrom",
"Exec", "Global", "Expr", "Pass", "Break", "Continue")
_expr_nodes = set(getattr(ast, name) for name in _exprs)
_stmt_nodes = set(getattr(ast, name) for name in _stmts)
def _is_ast_expr(node):
return node.__class__ in _expr_nodes
def _is_ast_stmt(node):
return node.__class__ in _stmt_nodes
else:
def _is_ast_expr(node):
return isinstance(node, ast.expr)
def _is_ast_stmt(node):
return isinstance(node, ast.stmt)
try:
_Starred = ast.Starred
except AttributeError:
# Python 2. Define a dummy class so isinstance() will always be False.
class _Starred(object): pass
class Failure(Exception):
"""Error found while interpreting AST."""
def __init__(self, explanation=""):
self.cause = sys.exc_info()
self.explanation = explanation
def interpret(source, frame, should_fail=False):
mod = ast.parse(source)
visitor = DebugInterpreter(frame)
try:
visitor.visit(mod)
except Failure:
failure = sys.exc_info()[1]
return getfailure(failure)
if should_fail:
return ("(assertion failed, but when it was re-run for "
"printing intermediate values, it did not fail. Suggestions: "
"compute assert expression before the assert or use --assert=plain)")
def run(offending_line, frame=None):
if frame is None:
frame = py.code.Frame(sys._getframe(1))
return interpret(offending_line, frame)
def getfailure(e):
explanation = util.format_explanation(e.explanation)
value = e.cause[1]
if str(value):
lines = explanation.split('\n')
lines[0] += " << %s" % (value,)
explanation = '\n'.join(lines)
text = "%s: %s" % (e.cause[0].__name__, explanation)
if text.startswith('AssertionError: assert '):
text = text[16:]
return text
operator_map = {
ast.BitOr : "|",
ast.BitXor : "^",
ast.BitAnd : "&",
ast.LShift : "<<",
ast.RShift : ">>",
ast.Add : "+",
ast.Sub : "-",
ast.Mult : "*",
ast.Div : "/",
ast.FloorDiv : "//",
ast.Mod : "%",
ast.Eq : "==",
ast.NotEq : "!=",
ast.Lt : "<",
ast.LtE : "<=",
ast.Gt : ">",
ast.GtE : ">=",
ast.Pow : "**",
ast.Is : "is",
ast.IsNot : "is not",
ast.In : "in",
ast.NotIn : "not in"
}
unary_map = {
ast.Not : "not %s",
ast.Invert : "~%s",
ast.USub : "-%s",
ast.UAdd : "+%s"
}
class DebugInterpreter(ast.NodeVisitor):
"""Interpret AST nodes to gleam useful debugging information. """
def __init__(self, frame):
self.frame = frame
def generic_visit(self, node):
# Fallback when we don't have a special implementation.
if _is_ast_expr(node):
mod = ast.Expression(node)
co = self._compile(mod)
try:
result = self.frame.eval(co)
except Exception:
raise Failure()
explanation = self.frame.repr(result)
return explanation, result
elif _is_ast_stmt(node):
mod = ast.Module([node])
co = self._compile(mod, "exec")
try:
self.frame.exec_(co)
except Exception:
raise Failure()
return None, None
else:
raise AssertionError("can't handle %s" %(node,))
def _compile(self, source, mode="eval"):
return compile(source, "<assertion interpretation>", mode)
def visit_Expr(self, expr):
return self.visit(expr.value)
def visit_Module(self, mod):
for stmt in mod.body:
self.visit(stmt)
def visit_Name(self, name):
explanation, result = self.generic_visit(name)
# See if the name is local.
source = "%r in locals() is not globals()" % (name.id,)
co = self._compile(source)
try:
local = self.frame.eval(co)
except Exception:
# have to assume it isn't
local = None
if local is None or not self.frame.is_true(local):
return name.id, result
return explanation, result
def visit_Compare(self, comp):
left = comp.left
left_explanation, left_result = self.visit(left)
for op, next_op in zip(comp.ops, comp.comparators):
next_explanation, next_result = self.visit(next_op)
op_symbol = operator_map[op.__class__]
explanation = "%s %s %s" % (left_explanation, op_symbol,
next_explanation)
source = "__exprinfo_left %s __exprinfo_right" % (op_symbol,)
co = self._compile(source)
try:
result = self.frame.eval(co, __exprinfo_left=left_result,
__exprinfo_right=next_result)
except Exception:
raise Failure(explanation)
try:
if not self.frame.is_true(result):
break
except KeyboardInterrupt:
raise
except:
break
left_explanation, left_result = next_explanation, next_result
if util._reprcompare is not None:
res = util._reprcompare(op_symbol, left_result, next_result)
if res:
explanation = res
return explanation, result
def visit_BoolOp(self, boolop):
is_or = isinstance(boolop.op, ast.Or)
explanations = []
for operand in boolop.values:
explanation, result = self.visit(operand)
explanations.append(explanation)
if result == is_or:
break
name = is_or and " or " or " and "
explanation = "(" + name.join(explanations) + ")"
return explanation, result
def visit_UnaryOp(self, unary):
pattern = unary_map[unary.op.__class__]
operand_explanation, operand_result = self.visit(unary.operand)
explanation = pattern % (operand_explanation,)
co = self._compile(pattern % ("__exprinfo_expr",))
try:
result = self.frame.eval(co, __exprinfo_expr=operand_result)
except Exception:
raise Failure(explanation)
return explanation, result
def visit_BinOp(self, binop):
left_explanation, left_result = self.visit(binop.left)
right_explanation, right_result = self.visit(binop.right)
symbol = operator_map[binop.op.__class__]
explanation = "(%s %s %s)" % (left_explanation, symbol,
right_explanation)
source = "__exprinfo_left %s __exprinfo_right" % (symbol,)
co = self._compile(source)
try:
result = self.frame.eval(co, __exprinfo_left=left_result,
__exprinfo_right=right_result)
except Exception:
raise Failure(explanation)
return explanation, result
def visit_Call(self, call):
func_explanation, func = self.visit(call.func)
arg_explanations = []
ns = {"__exprinfo_func" : func}
arguments = []
for arg in call.args:
arg_explanation, arg_result = self.visit(arg)
if isinstance(arg, _Starred):
arg_name = "__exprinfo_star"
ns[arg_name] = arg_result
arguments.append("*%s" % (arg_name,))
arg_explanations.append("*%s" % (arg_explanation,))
else:
arg_name = "__exprinfo_%s" % (len(ns),)
ns[arg_name] = arg_result
arguments.append(arg_name)
arg_explanations.append(arg_explanation)
for keyword in call.keywords:
arg_explanation, arg_result = self.visit(keyword.value)
if keyword.arg:
arg_name = "__exprinfo_%s" % (len(ns),)
keyword_source = "%s=%%s" % (keyword.arg)
arguments.append(keyword_source % (arg_name,))
arg_explanations.append(keyword_source % (arg_explanation,))
else:
arg_name = "__exprinfo_kwds"
arguments.append("**%s" % (arg_name,))
arg_explanations.append("**%s" % (arg_explanation,))
ns[arg_name] = arg_result
if getattr(call, 'starargs', None):
arg_explanation, arg_result = self.visit(call.starargs)
arg_name = "__exprinfo_star"
ns[arg_name] = arg_result
arguments.append("*%s" % (arg_name,))
arg_explanations.append("*%s" % (arg_explanation,))
if getattr(call, 'kwargs', None):
arg_explanation, arg_result = self.visit(call.kwargs)
arg_name = "__exprinfo_kwds"
ns[arg_name] = arg_result
arguments.append("**%s" % (arg_name,))
arg_explanations.append("**%s" % (arg_explanation,))
args_explained = ", ".join(arg_explanations)
explanation = "%s(%s)" % (func_explanation, args_explained)
args = ", ".join(arguments)
source = "__exprinfo_func(%s)" % (args,)
co = self._compile(source)
try:
result = self.frame.eval(co, **ns)
except Exception:
raise Failure(explanation)
pattern = "%s\n{%s = %s\n}"
rep = self.frame.repr(result)
explanation = pattern % (rep, rep, explanation)
return explanation, result
def _is_builtin_name(self, name):
pattern = "%r not in globals() and %r not in locals()"
source = pattern % (name.id, name.id)
co = self._compile(source)
try:
return self.frame.eval(co)
except Exception:
return False
def visit_Attribute(self, attr):
if not isinstance(attr.ctx, ast.Load):
return self.generic_visit(attr)
source_explanation, source_result = self.visit(attr.value)
explanation = "%s.%s" % (source_explanation, attr.attr)
source = "__exprinfo_expr.%s" % (attr.attr,)
co = self._compile(source)
try:
try:
result = self.frame.eval(co, __exprinfo_expr=source_result)
except AttributeError:
# Maybe the attribute name needs to be mangled?
if not attr.attr.startswith("__") or attr.attr.endswith("__"):
raise
source = "getattr(__exprinfo_expr.__class__, '__name__', '')"
co = self._compile(source)
class_name = self.frame.eval(co, __exprinfo_expr=source_result)
mangled_attr = "_" + class_name + attr.attr
source = "__exprinfo_expr.%s" % (mangled_attr,)
co = self._compile(source)
result = self.frame.eval(co, __exprinfo_expr=source_result)
except Exception:
raise Failure(explanation)
explanation = "%s\n{%s = %s.%s\n}" % (self.frame.repr(result),
self.frame.repr(result),
source_explanation, attr.attr)
# Check if the attr is from an instance.
source = "%r in getattr(__exprinfo_expr, '__dict__', {})"
source = source % (attr.attr,)
co = self._compile(source)
try:
from_instance = self.frame.eval(co, __exprinfo_expr=source_result)
except Exception:
from_instance = None
if from_instance is None or self.frame.is_true(from_instance):
rep = self.frame.repr(result)
pattern = "%s\n{%s = %s\n}"
explanation = pattern % (rep, rep, explanation)
return explanation, result
def visit_Assert(self, assrt):
test_explanation, test_result = self.visit(assrt.test)
explanation = "assert %s" % (test_explanation,)
if not self.frame.is_true(test_result):
try:
raise BuiltinAssertionError
except Exception:
raise Failure(explanation)
return explanation, test_result
def visit_Assign(self, assign):
value_explanation, value_result = self.visit(assign.value)
explanation = "... = %s" % (value_explanation,)
name = ast.Name("__exprinfo_expr", ast.Load(),
lineno=assign.value.lineno,
col_offset=assign.value.col_offset)
new_assign = ast.Assign(assign.targets, name, lineno=assign.lineno,
col_offset=assign.col_offset)
mod = ast.Module([new_assign])
co = self._compile(mod, "exec")
try:
self.frame.exec_(co, __exprinfo_expr=value_result)
except Exception:
raise Failure(explanation)
return explanation, value_result

View File

@@ -1,566 +0,0 @@
import traceback
import types
import py
import sys, inspect
from compiler import parse, ast, pycodegen
from _pytest.assertion.util import format_explanation, BuiltinAssertionError
passthroughex = py.builtin._sysex
class Failure:
def __init__(self, node):
self.exc, self.value, self.tb = sys.exc_info()
self.node = node
class View(object):
"""View base class.
If C is a subclass of View, then C(x) creates a proxy object around
the object x. The actual class of the proxy is not C in general,
but a *subclass* of C determined by the rules below. To avoid confusion
we call view class the class of the proxy (a subclass of C, so of View)
and object class the class of x.
Attributes and methods not found in the proxy are automatically read on x.
Other operations like setting attributes are performed on the proxy, as
determined by its view class. The object x is available from the proxy
as its __obj__ attribute.
The view class selection is determined by the __view__ tuples and the
optional __viewkey__ method. By default, the selected view class is the
most specific subclass of C whose __view__ mentions the class of x.
If no such subclass is found, the search proceeds with the parent
object classes. For example, C(True) will first look for a subclass
of C with __view__ = (..., bool, ...) and only if it doesn't find any
look for one with __view__ = (..., int, ...), and then ..., object,...
If everything fails the class C itself is considered to be the default.
Alternatively, the view class selection can be driven by another aspect
of the object x, instead of the class of x, by overriding __viewkey__.
See last example at the end of this module.
"""
_viewcache = {}
__view__ = ()
def __new__(rootclass, obj, *args, **kwds):
self = object.__new__(rootclass)
self.__obj__ = obj
self.__rootclass__ = rootclass
key = self.__viewkey__()
try:
self.__class__ = self._viewcache[key]
except KeyError:
self.__class__ = self._selectsubclass(key)
return self
def __getattr__(self, attr):
# attributes not found in the normal hierarchy rooted on View
# are looked up in the object's real class
return getattr(object.__getattribute__(self, '__obj__'), attr)
def __viewkey__(self):
return self.__obj__.__class__
def __matchkey__(self, key, subclasses):
if inspect.isclass(key):
keys = inspect.getmro(key)
else:
keys = [key]
for key in keys:
result = [C for C in subclasses if key in C.__view__]
if result:
return result
return []
def _selectsubclass(self, key):
subclasses = list(enumsubclasses(self.__rootclass__))
for C in subclasses:
if not isinstance(C.__view__, tuple):
C.__view__ = (C.__view__,)
choices = self.__matchkey__(key, subclasses)
if not choices:
return self.__rootclass__
elif len(choices) == 1:
return choices[0]
else:
# combine the multiple choices
return type('?', tuple(choices), {})
def __repr__(self):
return '%s(%r)' % (self.__rootclass__.__name__, self.__obj__)
def enumsubclasses(cls):
for subcls in cls.__subclasses__():
for subsubclass in enumsubclasses(subcls):
yield subsubclass
yield cls
class Interpretable(View):
"""A parse tree node with a few extra methods."""
explanation = None
def is_builtin(self, frame):
return False
def eval(self, frame):
# fall-back for unknown expression nodes
try:
expr = ast.Expression(self.__obj__)
expr.filename = '<eval>'
self.__obj__.filename = '<eval>'
co = pycodegen.ExpressionCodeGenerator(expr).getCode()
result = frame.eval(co)
except passthroughex:
raise
except:
raise Failure(self)
self.result = result
self.explanation = self.explanation or frame.repr(self.result)
def run(self, frame):
# fall-back for unknown statement nodes
try:
expr = ast.Module(None, ast.Stmt([self.__obj__]))
expr.filename = '<run>'
co = pycodegen.ModuleCodeGenerator(expr).getCode()
frame.exec_(co)
except passthroughex:
raise
except:
raise Failure(self)
def nice_explanation(self):
return format_explanation(self.explanation)
class Name(Interpretable):
__view__ = ast.Name
def is_local(self, frame):
source = '%r in locals() is not globals()' % self.name
try:
return frame.is_true(frame.eval(source))
except passthroughex:
raise
except:
return False
def is_global(self, frame):
source = '%r in globals()' % self.name
try:
return frame.is_true(frame.eval(source))
except passthroughex:
raise
except:
return False
def is_builtin(self, frame):
source = '%r not in locals() and %r not in globals()' % (
self.name, self.name)
try:
return frame.is_true(frame.eval(source))
except passthroughex:
raise
except:
return False
def eval(self, frame):
super(Name, self).eval(frame)
if not self.is_local(frame):
self.explanation = self.name
class Compare(Interpretable):
__view__ = ast.Compare
def eval(self, frame):
expr = Interpretable(self.expr)
expr.eval(frame)
for operation, expr2 in self.ops:
if hasattr(self, 'result'):
# shortcutting in chained expressions
if not frame.is_true(self.result):
break
expr2 = Interpretable(expr2)
expr2.eval(frame)
self.explanation = "%s %s %s" % (
expr.explanation, operation, expr2.explanation)
source = "__exprinfo_left %s __exprinfo_right" % operation
try:
self.result = frame.eval(source,
__exprinfo_left=expr.result,
__exprinfo_right=expr2.result)
except passthroughex:
raise
except:
raise Failure(self)
expr = expr2
class And(Interpretable):
__view__ = ast.And
def eval(self, frame):
explanations = []
for expr in self.nodes:
expr = Interpretable(expr)
expr.eval(frame)
explanations.append(expr.explanation)
self.result = expr.result
if not frame.is_true(expr.result):
break
self.explanation = '(' + ' and '.join(explanations) + ')'
class Or(Interpretable):
__view__ = ast.Or
def eval(self, frame):
explanations = []
for expr in self.nodes:
expr = Interpretable(expr)
expr.eval(frame)
explanations.append(expr.explanation)
self.result = expr.result
if frame.is_true(expr.result):
break
self.explanation = '(' + ' or '.join(explanations) + ')'
# == Unary operations ==
keepalive = []
for astclass, astpattern in {
ast.Not : 'not __exprinfo_expr',
ast.Invert : '(~__exprinfo_expr)',
}.items():
class UnaryArith(Interpretable):
__view__ = astclass
def eval(self, frame, astpattern=astpattern):
expr = Interpretable(self.expr)
expr.eval(frame)
self.explanation = astpattern.replace('__exprinfo_expr',
expr.explanation)
try:
self.result = frame.eval(astpattern,
__exprinfo_expr=expr.result)
except passthroughex:
raise
except:
raise Failure(self)
keepalive.append(UnaryArith)
# == Binary operations ==
for astclass, astpattern in {
ast.Add : '(__exprinfo_left + __exprinfo_right)',
ast.Sub : '(__exprinfo_left - __exprinfo_right)',
ast.Mul : '(__exprinfo_left * __exprinfo_right)',
ast.Div : '(__exprinfo_left / __exprinfo_right)',
ast.Mod : '(__exprinfo_left % __exprinfo_right)',
ast.Power : '(__exprinfo_left ** __exprinfo_right)',
}.items():
class BinaryArith(Interpretable):
__view__ = astclass
def eval(self, frame, astpattern=astpattern):
left = Interpretable(self.left)
left.eval(frame)
right = Interpretable(self.right)
right.eval(frame)
self.explanation = (astpattern
.replace('__exprinfo_left', left .explanation)
.replace('__exprinfo_right', right.explanation))
try:
self.result = frame.eval(astpattern,
__exprinfo_left=left.result,
__exprinfo_right=right.result)
except passthroughex:
raise
except:
raise Failure(self)
keepalive.append(BinaryArith)
class CallFunc(Interpretable):
__view__ = ast.CallFunc
def is_bool(self, frame):
source = 'isinstance(__exprinfo_value, bool)'
try:
return frame.is_true(frame.eval(source,
__exprinfo_value=self.result))
except passthroughex:
raise
except:
return False
def eval(self, frame):
node = Interpretable(self.node)
node.eval(frame)
explanations = []
vars = {'__exprinfo_fn': node.result}
source = '__exprinfo_fn('
for a in self.args:
if isinstance(a, ast.Keyword):
keyword = a.name
a = a.expr
else:
keyword = None
a = Interpretable(a)
a.eval(frame)
argname = '__exprinfo_%d' % len(vars)
vars[argname] = a.result
if keyword is None:
source += argname + ','
explanations.append(a.explanation)
else:
source += '%s=%s,' % (keyword, argname)
explanations.append('%s=%s' % (keyword, a.explanation))
if self.star_args:
star_args = Interpretable(self.star_args)
star_args.eval(frame)
argname = '__exprinfo_star'
vars[argname] = star_args.result
source += '*' + argname + ','
explanations.append('*' + star_args.explanation)
if self.dstar_args:
dstar_args = Interpretable(self.dstar_args)
dstar_args.eval(frame)
argname = '__exprinfo_kwds'
vars[argname] = dstar_args.result
source += '**' + argname + ','
explanations.append('**' + dstar_args.explanation)
self.explanation = "%s(%s)" % (
node.explanation, ', '.join(explanations))
if source.endswith(','):
source = source[:-1]
source += ')'
try:
self.result = frame.eval(source, **vars)
except passthroughex:
raise
except:
raise Failure(self)
if not node.is_builtin(frame) or not self.is_bool(frame):
r = frame.repr(self.result)
self.explanation = '%s\n{%s = %s\n}' % (r, r, self.explanation)
class Getattr(Interpretable):
__view__ = ast.Getattr
def eval(self, frame):
expr = Interpretable(self.expr)
expr.eval(frame)
source = '__exprinfo_expr.%s' % self.attrname
try:
try:
self.result = frame.eval(source, __exprinfo_expr=expr.result)
except AttributeError:
# Maybe the attribute name needs to be mangled?
if (not self.attrname.startswith("__") or
self.attrname.endswith("__")):
raise
source = "getattr(__exprinfo_expr.__class__, '__name__', '')"
class_name = frame.eval(source, __exprinfo_expr=expr.result)
mangled_attr = "_" + class_name + self.attrname
source = "__exprinfo_expr.%s" % (mangled_attr,)
self.result = frame.eval(source, __exprinfo_expr=expr.result)
except passthroughex:
raise
except:
raise Failure(self)
self.explanation = '%s.%s' % (expr.explanation, self.attrname)
# if the attribute comes from the instance, its value is interesting
source = ('hasattr(__exprinfo_expr, "__dict__") and '
'%r in __exprinfo_expr.__dict__' % self.attrname)
try:
from_instance = frame.is_true(
frame.eval(source, __exprinfo_expr=expr.result))
except passthroughex:
raise
except:
from_instance = True
if from_instance:
r = frame.repr(self.result)
self.explanation = '%s\n{%s = %s\n}' % (r, r, self.explanation)
# == Re-interpretation of full statements ==
class Assert(Interpretable):
__view__ = ast.Assert
def run(self, frame):
test = Interpretable(self.test)
test.eval(frame)
# print the result as 'assert <explanation>'
self.result = test.result
self.explanation = 'assert ' + test.explanation
if not frame.is_true(test.result):
try:
raise BuiltinAssertionError
except passthroughex:
raise
except:
raise Failure(self)
class Assign(Interpretable):
__view__ = ast.Assign
def run(self, frame):
expr = Interpretable(self.expr)
expr.eval(frame)
self.result = expr.result
self.explanation = '... = ' + expr.explanation
# fall-back-run the rest of the assignment
ass = ast.Assign(self.nodes, ast.Name('__exprinfo_expr'))
mod = ast.Module(None, ast.Stmt([ass]))
mod.filename = '<run>'
co = pycodegen.ModuleCodeGenerator(mod).getCode()
try:
frame.exec_(co, __exprinfo_expr=expr.result)
except passthroughex:
raise
except:
raise Failure(self)
class Discard(Interpretable):
__view__ = ast.Discard
def run(self, frame):
expr = Interpretable(self.expr)
expr.eval(frame)
self.result = expr.result
self.explanation = expr.explanation
class Stmt(Interpretable):
__view__ = ast.Stmt
def run(self, frame):
for stmt in self.nodes:
stmt = Interpretable(stmt)
stmt.run(frame)
def report_failure(e):
explanation = e.node.nice_explanation()
if explanation:
explanation = ", in: " + explanation
else:
explanation = ""
sys.stdout.write("%s: %s%s\n" % (e.exc.__name__, e.value, explanation))
def check(s, frame=None):
if frame is None:
frame = sys._getframe(1)
frame = py.code.Frame(frame)
expr = parse(s, 'eval')
assert isinstance(expr, ast.Expression)
node = Interpretable(expr.node)
try:
node.eval(frame)
except passthroughex:
raise
except Failure:
e = sys.exc_info()[1]
report_failure(e)
else:
if not frame.is_true(node.result):
sys.stderr.write("assertion failed: %s\n" % node.nice_explanation())
###########################################################
# API / Entry points
# #########################################################
def interpret(source, frame, should_fail=False):
module = Interpretable(parse(source, 'exec').node)
#print "got module", module
if isinstance(frame, types.FrameType):
frame = py.code.Frame(frame)
try:
module.run(frame)
except Failure:
e = sys.exc_info()[1]
return getfailure(e)
except passthroughex:
raise
except:
traceback.print_exc()
if should_fail:
return ("(assertion failed, but when it was re-run for "
"printing intermediate values, it did not fail. Suggestions: "
"compute assert expression before the assert or use --assert=plain)")
else:
return None
def getmsg(excinfo):
if isinstance(excinfo, tuple):
excinfo = py.code.ExceptionInfo(excinfo)
#frame, line = gettbline(tb)
#frame = py.code.Frame(frame)
#return interpret(line, frame)
tb = excinfo.traceback[-1]
source = str(tb.statement).strip()
x = interpret(source, tb.frame, should_fail=True)
if not isinstance(x, str):
raise TypeError("interpret returned non-string %r" % (x,))
return x
def getfailure(e):
explanation = e.node.nice_explanation()
if str(e.value):
lines = explanation.split('\n')
lines[0] += " << %s" % (e.value,)
explanation = '\n'.join(lines)
text = "%s: %s" % (e.exc.__name__, explanation)
if text.startswith('AssertionError: assert '):
text = text[16:]
return text
def run(s, frame=None):
if frame is None:
frame = sys._getframe(1)
frame = py.code.Frame(frame)
module = Interpretable(parse(s, 'exec').node)
try:
module.run(frame)
except Failure:
e = sys.exc_info()[1]
report_failure(e)
if __name__ == '__main__':
# example:
def f():
return 5
def g():
return 3
def h(x):
return 'never'
check("f() * g() == 5")
check("not f()")
check("not (f() and g() or 0)")
check("f() == g()")
i = 4
check("i == f()")
check("len(f()) == 0")
check("isinstance(2+3+4, float)")
run("x = i")
check("x == 5")
run("assert not f(), 'oops'")
run("a, b, c = 1, 2")
run("a, b, c = f()")
check("max([f(),g()]) == 4")
check("'hello'[g()] == 'h'")
run("'guk%d' % h(f())")

View File

@@ -1,12 +1,18 @@
"""
Find intermediate evalutation results in assert statements through builtin AST.
"""
import ast
import sys
import _pytest._code
import py
from _pytest.assertion.util import BuiltinAssertionError
from _pytest.assertion import util
u = py.builtin._totext
class AssertionError(BuiltinAssertionError):
class AssertionError(util.BuiltinAssertionError):
def __init__(self, *args):
BuiltinAssertionError.__init__(self, *args)
util.BuiltinAssertionError.__init__(self, *args)
if args:
# on Python2.6 we get len(args)==2 for: assert 0, (x,y)
# on Python2.7 and above we always get len(args) == 1
@@ -22,7 +28,7 @@ class AssertionError(BuiltinAssertionError):
"<[broken __repr__] %s at %0xd>"
% (toprint.__class__, id(toprint)))
else:
f = py.code.Frame(sys._getframe(1))
f = _pytest._code.Frame(sys._getframe(1))
try:
source = f.code.fullsource
if source is not None:
@@ -46,7 +52,356 @@ class AssertionError(BuiltinAssertionError):
if sys.version_info > (3, 0):
AssertionError.__module__ = "builtins"
if sys.version_info >= (2, 6) or sys.platform.startswith("java"):
from _pytest.assertion.newinterpret import interpret as reinterpret
if sys.platform.startswith("java"):
# See http://bugs.jython.org/issue1497
_exprs = ("BoolOp", "BinOp", "UnaryOp", "Lambda", "IfExp", "Dict",
"ListComp", "GeneratorExp", "Yield", "Compare", "Call",
"Repr", "Num", "Str", "Attribute", "Subscript", "Name",
"List", "Tuple")
_stmts = ("FunctionDef", "ClassDef", "Return", "Delete", "Assign",
"AugAssign", "Print", "For", "While", "If", "With", "Raise",
"TryExcept", "TryFinally", "Assert", "Import", "ImportFrom",
"Exec", "Global", "Expr", "Pass", "Break", "Continue")
_expr_nodes = set(getattr(ast, name) for name in _exprs)
_stmt_nodes = set(getattr(ast, name) for name in _stmts)
def _is_ast_expr(node):
return node.__class__ in _expr_nodes
def _is_ast_stmt(node):
return node.__class__ in _stmt_nodes
else:
from _pytest.assertion.oldinterpret import interpret as reinterpret
def _is_ast_expr(node):
return isinstance(node, ast.expr)
def _is_ast_stmt(node):
return isinstance(node, ast.stmt)
try:
_Starred = ast.Starred
except AttributeError:
# Python 2. Define a dummy class so isinstance() will always be False.
class _Starred(object): pass
class Failure(Exception):
"""Error found while interpreting AST."""
def __init__(self, explanation=""):
self.cause = sys.exc_info()
self.explanation = explanation
def reinterpret(source, frame, should_fail=False):
mod = ast.parse(source)
visitor = DebugInterpreter(frame)
try:
visitor.visit(mod)
except Failure:
failure = sys.exc_info()[1]
return getfailure(failure)
if should_fail:
return ("(assertion failed, but when it was re-run for "
"printing intermediate values, it did not fail. Suggestions: "
"compute assert expression before the assert or use --assert=plain)")
def run(offending_line, frame=None):
if frame is None:
frame = _pytest._code.Frame(sys._getframe(1))
return reinterpret(offending_line, frame)
def getfailure(e):
explanation = util.format_explanation(e.explanation)
value = e.cause[1]
if str(value):
lines = explanation.split('\n')
lines[0] += " << %s" % (value,)
explanation = '\n'.join(lines)
text = "%s: %s" % (e.cause[0].__name__, explanation)
if text.startswith('AssertionError: assert '):
text = text[16:]
return text
operator_map = {
ast.BitOr : "|",
ast.BitXor : "^",
ast.BitAnd : "&",
ast.LShift : "<<",
ast.RShift : ">>",
ast.Add : "+",
ast.Sub : "-",
ast.Mult : "*",
ast.Div : "/",
ast.FloorDiv : "//",
ast.Mod : "%",
ast.Eq : "==",
ast.NotEq : "!=",
ast.Lt : "<",
ast.LtE : "<=",
ast.Gt : ">",
ast.GtE : ">=",
ast.Pow : "**",
ast.Is : "is",
ast.IsNot : "is not",
ast.In : "in",
ast.NotIn : "not in"
}
unary_map = {
ast.Not : "not %s",
ast.Invert : "~%s",
ast.USub : "-%s",
ast.UAdd : "+%s"
}
class DebugInterpreter(ast.NodeVisitor):
"""Interpret AST nodes to gleam useful debugging information. """
def __init__(self, frame):
self.frame = frame
def generic_visit(self, node):
# Fallback when we don't have a special implementation.
if _is_ast_expr(node):
mod = ast.Expression(node)
co = self._compile(mod)
try:
result = self.frame.eval(co)
except Exception:
raise Failure()
explanation = self.frame.repr(result)
return explanation, result
elif _is_ast_stmt(node):
mod = ast.Module([node])
co = self._compile(mod, "exec")
try:
self.frame.exec_(co)
except Exception:
raise Failure()
return None, None
else:
raise AssertionError("can't handle %s" %(node,))
def _compile(self, source, mode="eval"):
return compile(source, "<assertion interpretation>", mode)
def visit_Expr(self, expr):
return self.visit(expr.value)
def visit_Module(self, mod):
for stmt in mod.body:
self.visit(stmt)
def visit_Name(self, name):
explanation, result = self.generic_visit(name)
# See if the name is local.
source = "%r in locals() is not globals()" % (name.id,)
co = self._compile(source)
try:
local = self.frame.eval(co)
except Exception:
# have to assume it isn't
local = None
if local is None or not self.frame.is_true(local):
return name.id, result
return explanation, result
def visit_Compare(self, comp):
left = comp.left
left_explanation, left_result = self.visit(left)
for op, next_op in zip(comp.ops, comp.comparators):
next_explanation, next_result = self.visit(next_op)
op_symbol = operator_map[op.__class__]
explanation = "%s %s %s" % (left_explanation, op_symbol,
next_explanation)
source = "__exprinfo_left %s __exprinfo_right" % (op_symbol,)
co = self._compile(source)
try:
result = self.frame.eval(co, __exprinfo_left=left_result,
__exprinfo_right=next_result)
except Exception:
raise Failure(explanation)
try:
if not self.frame.is_true(result):
break
except KeyboardInterrupt:
raise
except:
break
left_explanation, left_result = next_explanation, next_result
if util._reprcompare is not None:
res = util._reprcompare(op_symbol, left_result, next_result)
if res:
explanation = res
return explanation, result
def visit_BoolOp(self, boolop):
is_or = isinstance(boolop.op, ast.Or)
explanations = []
for operand in boolop.values:
explanation, result = self.visit(operand)
explanations.append(explanation)
if result == is_or:
break
name = is_or and " or " or " and "
explanation = "(" + name.join(explanations) + ")"
return explanation, result
def visit_UnaryOp(self, unary):
pattern = unary_map[unary.op.__class__]
operand_explanation, operand_result = self.visit(unary.operand)
explanation = pattern % (operand_explanation,)
co = self._compile(pattern % ("__exprinfo_expr",))
try:
result = self.frame.eval(co, __exprinfo_expr=operand_result)
except Exception:
raise Failure(explanation)
return explanation, result
def visit_BinOp(self, binop):
left_explanation, left_result = self.visit(binop.left)
right_explanation, right_result = self.visit(binop.right)
symbol = operator_map[binop.op.__class__]
explanation = "(%s %s %s)" % (left_explanation, symbol,
right_explanation)
source = "__exprinfo_left %s __exprinfo_right" % (symbol,)
co = self._compile(source)
try:
result = self.frame.eval(co, __exprinfo_left=left_result,
__exprinfo_right=right_result)
except Exception:
raise Failure(explanation)
return explanation, result
def visit_Call(self, call):
func_explanation, func = self.visit(call.func)
arg_explanations = []
ns = {"__exprinfo_func" : func}
arguments = []
for arg in call.args:
arg_explanation, arg_result = self.visit(arg)
if isinstance(arg, _Starred):
arg_name = "__exprinfo_star"
ns[arg_name] = arg_result
arguments.append("*%s" % (arg_name,))
arg_explanations.append("*%s" % (arg_explanation,))
else:
arg_name = "__exprinfo_%s" % (len(ns),)
ns[arg_name] = arg_result
arguments.append(arg_name)
arg_explanations.append(arg_explanation)
for keyword in call.keywords:
arg_explanation, arg_result = self.visit(keyword.value)
if keyword.arg:
arg_name = "__exprinfo_%s" % (len(ns),)
keyword_source = "%s=%%s" % (keyword.arg)
arguments.append(keyword_source % (arg_name,))
arg_explanations.append(keyword_source % (arg_explanation,))
else:
arg_name = "__exprinfo_kwds"
arguments.append("**%s" % (arg_name,))
arg_explanations.append("**%s" % (arg_explanation,))
ns[arg_name] = arg_result
if getattr(call, 'starargs', None):
arg_explanation, arg_result = self.visit(call.starargs)
arg_name = "__exprinfo_star"
ns[arg_name] = arg_result
arguments.append("*%s" % (arg_name,))
arg_explanations.append("*%s" % (arg_explanation,))
if getattr(call, 'kwargs', None):
arg_explanation, arg_result = self.visit(call.kwargs)
arg_name = "__exprinfo_kwds"
ns[arg_name] = arg_result
arguments.append("**%s" % (arg_name,))
arg_explanations.append("**%s" % (arg_explanation,))
args_explained = ", ".join(arg_explanations)
explanation = "%s(%s)" % (func_explanation, args_explained)
args = ", ".join(arguments)
source = "__exprinfo_func(%s)" % (args,)
co = self._compile(source)
try:
result = self.frame.eval(co, **ns)
except Exception:
raise Failure(explanation)
pattern = "%s\n{%s = %s\n}"
rep = self.frame.repr(result)
explanation = pattern % (rep, rep, explanation)
return explanation, result
def _is_builtin_name(self, name):
pattern = "%r not in globals() and %r not in locals()"
source = pattern % (name.id, name.id)
co = self._compile(source)
try:
return self.frame.eval(co)
except Exception:
return False
def visit_Attribute(self, attr):
if not isinstance(attr.ctx, ast.Load):
return self.generic_visit(attr)
source_explanation, source_result = self.visit(attr.value)
explanation = "%s.%s" % (source_explanation, attr.attr)
source = "__exprinfo_expr.%s" % (attr.attr,)
co = self._compile(source)
try:
try:
result = self.frame.eval(co, __exprinfo_expr=source_result)
except AttributeError:
# Maybe the attribute name needs to be mangled?
if not attr.attr.startswith("__") or attr.attr.endswith("__"):
raise
source = "getattr(__exprinfo_expr.__class__, '__name__', '')"
co = self._compile(source)
class_name = self.frame.eval(co, __exprinfo_expr=source_result)
mangled_attr = "_" + class_name + attr.attr
source = "__exprinfo_expr.%s" % (mangled_attr,)
co = self._compile(source)
result = self.frame.eval(co, __exprinfo_expr=source_result)
except Exception:
raise Failure(explanation)
explanation = "%s\n{%s = %s.%s\n}" % (self.frame.repr(result),
self.frame.repr(result),
source_explanation, attr.attr)
# Check if the attr is from an instance.
source = "%r in getattr(__exprinfo_expr, '__dict__', {})"
source = source % (attr.attr,)
co = self._compile(source)
try:
from_instance = self.frame.eval(co, __exprinfo_expr=source_result)
except Exception:
from_instance = None
if from_instance is None or self.frame.is_true(from_instance):
rep = self.frame.repr(result)
pattern = "%s\n{%s = %s\n}"
explanation = pattern % (rep, rep, explanation)
return explanation, result
def visit_Assert(self, assrt):
test_explanation, test_result = self.visit(assrt.test)
explanation = "assert %s" % (test_explanation,)
if not self.frame.is_true(test_result):
try:
raise util.BuiltinAssertionError
except Exception:
raise Failure(explanation)
return explanation, test_result
def visit_Assign(self, assign):
value_explanation, value_result = self.visit(assign.value)
explanation = "... = %s" % (value_explanation,)
name = ast.Name("__exprinfo_expr", ast.Load(),
lineno=assign.value.lineno,
col_offset=assign.value.col_offset)
new_assign = ast.Assign(assign.targets, name, lineno=assign.lineno,
col_offset=assign.col_offset)
mod = ast.Module([new_assign])
co = self._compile(mod, "exec")
try:
self.frame.exec_(co, __exprinfo_expr=value_result)
except Exception:
raise Failure(explanation)
return explanation, value_result

View File

@@ -128,7 +128,7 @@ class AssertionRewritingHook(object):
# One of the path components was not a directory, likely
# because we're in a zip file.
write = False
elif e in [errno.EACCES, errno.EROFS]:
elif e in [errno.EACCES, errno.EROFS, errno.EPERM]:
state.trace("read only directory: %r" % fn_pypath.dirname)
write = False
else:
@@ -453,6 +453,11 @@ binop_map = {
ast.In: "in",
ast.NotIn: "not in"
}
# Python 3.5+ compatibility
try:
binop_map[ast.MatMult] = "@"
except AttributeError:
pass
# Python 3.4+ compatibility
if hasattr(ast, "NameConstant"):

View File

@@ -1,6 +1,7 @@
"""Utilities for assertion debugging"""
import pprint
import _pytest._code
import py
try:
from collections import Sequence
@@ -17,6 +18,15 @@ u = py.builtin._totext
_reprcompare = None
# the re-encoding is needed for python2 repr
# with non-ascii characters (see issue 877 and 1379)
def ecu(s):
try:
return u(s, 'utf-8', 'replace')
except TypeError:
return s
def format_explanation(explanation):
"""This formats an explanation
@@ -27,6 +37,7 @@ def format_explanation(explanation):
for when one explanation needs to span multiple lines, e.g. when
displaying diffs.
"""
explanation = ecu(explanation)
explanation = _collapse_false(explanation)
lines = _split_explanation(explanation)
result = _format_lines(lines)
@@ -130,18 +141,10 @@ def assertrepr_compare(config, op, left, right):
left_repr = py.io.saferepr(left, maxsize=int(width/2))
right_repr = py.io.saferepr(right, maxsize=width-len(left_repr))
# the re-encoding is needed for python2 repr
# with non-ascii characters (see issue 877)
def ecu(s):
try:
return u(s, 'utf-8', 'replace')
except TypeError:
return s
summary = u('%s %s %s') % (ecu(left_repr), op, ecu(right_repr))
issequence = lambda x: (isinstance(x, (list, tuple, Sequence))
and not isinstance(x, basestring))
issequence = lambda x: (isinstance(x, (list, tuple, Sequence)) and
not isinstance(x, basestring))
istext = lambda x: isinstance(x, basestring)
isdict = lambda x: isinstance(x, dict)
isset = lambda x: isinstance(x, (set, frozenset))
@@ -179,7 +182,7 @@ def assertrepr_compare(config, op, left, right):
explanation = [
u('(pytest_assertion plugin: representation of details failed. '
'Probably an object has a faulty __repr__.)'),
u(py.code.ExceptionInfo())]
u(_pytest._code.ExceptionInfo())]
if not explanation:
return None
@@ -263,8 +266,7 @@ def _compare_eq_sequence(left, right, verbose=False):
explanation += [
u('Right contains more items, first extra item: %s') %
py.io.saferepr(right[len(left)],)]
return explanation # + _diff_text(pprint.pformat(left),
# pprint.pformat(right))
return explanation
def _compare_eq_set(left, right, verbose=False):

View File

@@ -149,17 +149,19 @@ class LFPlugin:
config = self.config
if config.getvalue("cacheshow") or hasattr(config, "slaveinput"):
return
config.cache.set("cache/lastfailed", self.lastfailed)
prev_failed = config.cache.get("cache/lastfailed", None) is not None
if (session.testscollected and prev_failed) or self.lastfailed:
config.cache.set("cache/lastfailed", self.lastfailed)
def pytest_addoption(parser):
group = parser.getgroup("general")
group.addoption(
'--lf', action='store_true', dest="lf",
'--lf', '--last-failed', action='store_true', dest="lf",
help="rerun only the tests that failed "
"at the last run (or all if none failed)")
group.addoption(
'--ff', action='store_true', dest="failedfirst",
'--ff', '--failed-first', action='store_true', dest="failedfirst",
help="run all tests but run the last failures first. "
"This may re-order tests and thus lead to "
"repeated fixture setup/teardown")
@@ -192,8 +194,8 @@ def cache(request):
cache.get(key, default)
cache.set(key, value)
Keys must be strings not containing a "/" separator. Add a unique identifier
(such as plugin/app name) to avoid clashes with other cache users.
Keys must be a ``/`` separated value, where the first part is usually the
name of your plugin or application to avoid clashes with other cache users.
Values can be any object handled by the json stdlib module.
"""

View File

@@ -31,6 +31,7 @@ def pytest_addoption(parser):
@pytest.hookimpl(hookwrapper=True)
def pytest_load_initial_conftests(early_config, parser, args):
_readline_workaround()
ns = early_config.known_args_namespace
pluginmanager = early_config.pluginmanager
capman = CaptureManager(ns.capture)
@@ -442,3 +443,30 @@ class DontReadFromInput:
def close(self):
pass
def _readline_workaround():
"""
Ensure readline is imported so that it attaches to the correct stdio
handles on Windows.
Pdb uses readline support where available--when not running from the Python
prompt, the readline module is not imported until running the pdb REPL. If
running py.test with the --pdb option this means the readline module is not
imported until after I/O capture has been started.
This is a problem for pyreadline, which is often used to implement readline
support on Windows, as it does not attach to the correct handles for stdout
and/or stdin if they have been redirected by the FDCapture mechanism. This
workaround ensures that readline is imported before I/O capture is setup so
that it can attach to the actual stdin/out for the console.
See https://github.com/pytest-dev/pytest/pull/1281
"""
if not sys.platform.startswith('win32'):
return
try:
import readline # noqa
except ImportError:
pass

View File

@@ -8,6 +8,7 @@ import warnings
import py
# DON't import pytest here because it causes import cycle troubles
import sys, os
import _pytest._code
import _pytest.hookspec # the extension point definitions
from _pytest._pluggy import PluginManager, HookimplMarker, HookspecMarker
@@ -103,7 +104,7 @@ def _prepareconfig(args=None, plugins=None):
elif not isinstance(args, (tuple, list)):
if not isinstance(args, str):
raise ValueError("not a string or argument list: %r" % (args,))
args = shlex.split(args)
args = shlex.split(args, posix=sys.platform != "win32")
config = get_config()
pluginmanager = config.pluginmanager
try:
@@ -158,7 +159,7 @@ class PytestPluginManager(PluginManager):
Use :py:meth:`pluggy.PluginManager.add_hookspecs` instead.
"""
warning = dict(code="I2",
fslocation=py.code.getfslineno(sys._getframe(1)),
fslocation=_pytest._code.getfslineno(sys._getframe(1)),
nodeid=None,
message="use pluginmanager.add_hookspecs instead of "
"deprecated addhooks() method.")
@@ -195,7 +196,7 @@ class PytestPluginManager(PluginManager):
def _verify_hook(self, hook, hookmethod):
super(PytestPluginManager, self)._verify_hook(hook, hookmethod)
if "__multicall__" in hookmethod.argnames:
fslineno = py.code.getfslineno(hookmethod.function)
fslineno = _pytest._code.getfslineno(hookmethod.function)
warning = dict(code="I1",
fslocation=fslineno,
nodeid=None,
@@ -382,8 +383,13 @@ class PytestPluginManager(PluginManager):
importspec = modname
try:
__import__(importspec)
except ImportError:
raise
except ImportError as e:
new_exc = ImportError('Error importing plugin "%s": %s' % (modname, e))
# copy over name and path attributes
for attr in ('name', 'path'):
if hasattr(e, attr):
setattr(new_exc, attr, getattr(e, attr))
raise new_exc
except Exception as e:
import pytest
if not hasattr(pytest, 'skip') or not isinstance(e, pytest.skip.Exception):
@@ -455,11 +461,11 @@ class Parser:
"""
self._anonymous.addoption(*opts, **attrs)
def parse(self, args):
def parse(self, args, namespace=None):
from _pytest._argcomplete import try_argcomplete
self.optparser = self._getparser()
try_argcomplete(self.optparser)
return self.optparser.parse_args([str(x) for x in args])
return self.optparser.parse_args([str(x) for x in args], namespace=namespace)
def _getparser(self):
from _pytest._argcomplete import filescompleter
@@ -477,37 +483,38 @@ class Parser:
optparser.add_argument(FILE_OR_DIR, nargs='*').completer=filescompleter
return optparser
def parse_setoption(self, args, option):
parsedoption = self.parse(args)
def parse_setoption(self, args, option, namespace=None):
parsedoption = self.parse(args, namespace=namespace)
for name, value in parsedoption.__dict__.items():
setattr(option, name, value)
return getattr(parsedoption, FILE_OR_DIR)
def parse_known_args(self, args):
def parse_known_args(self, args, namespace=None):
"""parses and returns a namespace object with known arguments at this
point.
"""
return self.parse_known_and_unknown_args(args)[0]
return self.parse_known_and_unknown_args(args, namespace=namespace)[0]
def parse_known_and_unknown_args(self, args):
def parse_known_and_unknown_args(self, args, namespace=None):
"""parses and returns a namespace object with known arguments, and
the remaining arguments unknown at this point.
"""
optparser = self._getparser()
args = [str(x) for x in args]
return optparser.parse_known_args(args)
return optparser.parse_known_args(args, namespace=namespace)
def addini(self, name, help, type=None, default=None):
""" register an ini-file option.
:name: name of the ini-variable
:type: type of the variable, can be ``pathlist``, ``args`` or ``linelist``.
:type: type of the variable, can be ``pathlist``, ``args``, ``linelist``
or ``bool``.
:default: default value if no ini-file option exists but is queried.
The value of ini-variables can be retrieved via a call to
:py:func:`config.getini(name) <_pytest.config.Config.getini>`.
"""
assert type in (None, "pathlist", "args", "linelist")
assert type in (None, "pathlist", "args", "linelist", "bool")
self._inidict[name] = (help, type, default)
self._ininames.append(name)
@@ -779,10 +786,12 @@ def _ensure_removed_sysmodule(modname):
class CmdOptions(object):
""" holds cmdline options as attributes."""
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
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:
def __repr__(self):
@@ -879,8 +888,8 @@ class Config(object):
def fromdictargs(cls, option_dict, args):
""" constructor useable for subprocesses. """
config = get_config()
config._preparse(args, addopts=False)
config.option.__dict__.update(option_dict)
config.parse(args, addopts=False)
for x in config.option.plugins:
config.pluginmanager.consider_pluginarg(x)
return config
@@ -898,7 +907,7 @@ 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)
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)
self.rootdir, self.inifile, self.inicfg = r
self._parser.extra_info['rootdir'] = self.rootdir
@@ -919,7 +928,7 @@ class Config(object):
except ImportError as e:
self.warn("I2", "could not load setuptools entry import: %s" % (e,))
self.pluginmanager.consider_env()
self.known_args_namespace = ns = self._parser.parse_known_args(args)
self.known_args_namespace = ns = self._parser.parse_known_args(args, namespace=self.option.copy())
if self.known_args_namespace.confcutdir is None and self.inifile:
confcutdir = py.path.local(self.inifile).dirname
self.known_args_namespace.confcutdir = confcutdir
@@ -947,17 +956,17 @@ class Config(object):
self.inicfg.config.path, self.inicfg.lineof('minversion'),
minver, pytest.__version__))
def parse(self, args):
def parse(self, args, addopts=True):
# parse given cmdline arguments into this config object.
assert not hasattr(self, 'args'), (
"can only parse cmdline args at most once per Config object")
self._origargs = args
self.hook.pytest_addhooks.call_historic(
kwargs=dict(pluginmanager=self.pluginmanager))
self._preparse(args)
self._preparse(args, addopts=addopts)
# XXX deprecated hook:
self.hook.pytest_cmdline_preparse(config=self, args=args)
args = self._parser.parse_setoption(args, self.option)
args = self._parser.parse_setoption(args, self.option, namespace=self.option)
if not args:
cwd = os.getcwd()
if cwd == self.rootdir:
@@ -1008,6 +1017,8 @@ class Config(object):
return shlex.split(value)
elif type == "linelist":
return [t for t in map(lambda x: x.strip(), value.split("\n")) if t]
elif type == "bool":
return bool(_strtobool(value.strip()))
else:
assert type is None
return value
@@ -1161,3 +1172,21 @@ def create_terminal_writer(config, *args, **kwargs):
if config.option.color == 'no':
tw.hasmarkup = False
return tw
def _strtobool(val):
"""Convert a string representation of truth to true (1) or false (0).
True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if
'val' is anything else.
.. note:: copied from distutils.util
"""
val = val.lower()
if val in ('y', 'yes', 't', 'true', 'on', '1'):
return 1
elif val in ('n', 'no', 'f', 'false', 'off', '0'):
return 0
else:
raise ValueError("invalid truth value %r" % (val,))

View File

@@ -1,9 +1,13 @@
""" discover and run doctests in modules and test files."""
from __future__ import absolute_import
import traceback
import pytest, py
import pytest
from _pytest._code.code import TerminalRepr, ReprFileLocation, ExceptionInfo
from _pytest.python import FixtureRequest
from py._code.code import TerminalRepr, ReprFileLocation
def pytest_addoption(parser):
parser.addini('doctest_optionflags', 'option flags for doctests',
@@ -14,7 +18,7 @@ def pytest_addoption(parser):
help="run doctests in all .py modules",
dest="doctestmodules")
group.addoption("--doctest-glob",
action="store", default="test*.txt", metavar="pat",
action="append", default=[], metavar="pat",
help="doctests file matching pattern, default: test*.txt",
dest="doctestglob")
group.addoption("--doctest-ignore-import-errors",
@@ -22,29 +26,52 @@ def pytest_addoption(parser):
help="ignore doctest ImportErrors",
dest="doctest_ignore_import_errors")
def pytest_collect_file(path, parent):
config = parent.config
if path.ext == ".py":
if config.option.doctestmodules:
return DoctestModule(path, parent)
elif (path.ext in ('.txt', '.rst') and parent.session.isinitpath(path)) or \
path.check(fnmatch=config.getvalue("doctestglob")):
elif _is_doctest(config, path, parent):
return DoctestTextfile(path, parent)
def _is_doctest(config, path, parent):
if path.ext in ('.txt', '.rst') and parent.session.isinitpath(path):
return True
globs = config.getoption("doctestglob") or ['test*.txt']
for glob in globs:
if path.check(fnmatch=glob):
return True
return False
class ReprFailDoctest(TerminalRepr):
def __init__(self, reprlocation, lines):
self.reprlocation = reprlocation
self.lines = lines
def toterminal(self, tw):
for line in self.lines:
tw.line(line)
self.reprlocation.toterminal(tw)
class DoctestItem(pytest.Item):
def __init__(self, name, parent, runner=None, dtest=None):
super(DoctestItem, self).__init__(name, parent)
self.runner = runner
self.dtest = dtest
self.obj = None
self.fixture_request = None
def setup(self):
if self.dtest is not None:
self.fixture_request = _setup_fixtures(self)
globs = dict(getfixture=self.fixture_request.getfuncargvalue)
self.dtest.globs.update(globs)
def runtest(self):
_check_all_skipped(self.dtest)
@@ -64,17 +91,17 @@ class DoctestItem(pytest.Item):
lineno = test.lineno + example.lineno + 1
message = excinfo.type.__name__
reprlocation = ReprFileLocation(filename, lineno, message)
checker = _get_unicode_checker()
checker = _get_checker()
REPORT_UDIFF = doctest.REPORT_UDIFF
filelines = py.path.local(filename).readlines(cr=0)
lines = []
if lineno is not None:
i = max(test.lineno, max(0, lineno - 10)) # XXX?
for line in filelines[i:lineno]:
lines.append("%03d %s" % (i+1, line))
i += 1
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[example.lineno - 9:example.lineno + 1]
else:
lines.append('EXAMPLE LOCATION UNKNOWN, not showing all tests of that example')
lines = ['EXAMPLE LOCATION UNKNOWN, not showing all tests of that example']
indent = '>>>'
for line in example.source.splitlines():
lines.append('??? %s %s' % (indent, line))
@@ -83,7 +110,7 @@ class DoctestItem(pytest.Item):
lines += checker.output_difference(example,
doctestfailure.got, REPORT_UDIFF).split("\n")
else:
inner_excinfo = py.code.ExceptionInfo(excinfo.value.exc_info)
inner_excinfo = ExceptionInfo(excinfo.value.exc_info)
lines += ["UNEXPECTED EXCEPTION: %s" %
repr(inner_excinfo.value)]
lines += traceback.format_exception(*excinfo.value.exc_info)
@@ -94,6 +121,7 @@ class DoctestItem(pytest.Item):
def reportinfo(self):
return self.fspath, None, "[doctest] %s" % self.name
def _get_flag_lookup():
import doctest
return dict(DONT_ACCEPT_TRUE_FOR_1=doctest.DONT_ACCEPT_TRUE_FOR_1,
@@ -102,7 +130,10 @@ def _get_flag_lookup():
ELLIPSIS=doctest.ELLIPSIS,
IGNORE_EXCEPTION_DETAIL=doctest.IGNORE_EXCEPTION_DETAIL,
COMPARISON_FLAGS=doctest.COMPARISON_FLAGS,
ALLOW_UNICODE=_get_allow_unicode_flag())
ALLOW_UNICODE=_get_allow_unicode_flag(),
ALLOW_BYTES=_get_allow_bytes_flag(),
)
def get_optionflags(parent):
optionflags_str = parent.config.getini("doctest_optionflags")
@@ -113,7 +144,7 @@ def get_optionflags(parent):
return flag_acc
class DoctestTextfile(DoctestItem, pytest.File):
class DoctestTextfile(DoctestItem, pytest.Module):
def runtest(self):
import doctest
@@ -130,7 +161,7 @@ class DoctestTextfile(DoctestItem, pytest.File):
optionflags = get_optionflags(self)
runner = doctest.DebugRunner(verbose=0, optionflags=optionflags,
checker=_get_unicode_checker())
checker=_get_checker())
parser = doctest.DocTestParser()
test = parser.get_doctest(text, globs, name, filename, 0)
@@ -148,7 +179,7 @@ def _check_all_skipped(test):
pytest.skip('all tests skipped by +SKIP option')
class DoctestModule(pytest.File):
class DoctestModule(pytest.Module):
def collect(self):
import doctest
if self.fspath.basename == "conftest.py":
@@ -161,23 +192,19 @@ class DoctestModule(pytest.File):
pytest.skip('unable to import module %r' % self.fspath)
else:
raise
# satisfy `FixtureRequest` constructor...
fixture_request = _setup_fixtures(self)
doctest_globals = dict(getfixture=fixture_request.getfuncargvalue)
# uses internal doctest module parsing mechanism
finder = doctest.DocTestFinder()
optionflags = get_optionflags(self)
runner = doctest.DebugRunner(verbose=0, optionflags=optionflags,
checker=_get_unicode_checker())
for test in finder.find(module, module.__name__,
extraglobs=doctest_globals):
checker=_get_checker())
for test in finder.find(module, module.__name__):
if test.examples: # skip empty doctests
yield DoctestItem(test.name, self, runner, test)
def _setup_fixtures(doctest_item):
"""
Used by DoctestTextfile and DoctestModule to setup fixture information.
Used by DoctestTextfile and DoctestItem to setup fixture information.
"""
def func():
pass
@@ -191,28 +218,32 @@ def _setup_fixtures(doctest_item):
return fixture_request
def _get_unicode_checker():
def _get_checker():
"""
Returns a doctest.OutputChecker subclass that takes in account the
ALLOW_UNICODE option to ignore u'' prefixes in strings. Useful
when the same doctest should run in Python 2 and Python 3.
ALLOW_UNICODE option to ignore u'' prefixes in strings and ALLOW_BYTES
to strip b'' prefixes.
Useful when the same doctest should run in Python 2 and Python 3.
An inner class is used to avoid importing "doctest" at the module
level.
"""
if hasattr(_get_unicode_checker, 'UnicodeOutputChecker'):
return _get_unicode_checker.UnicodeOutputChecker()
if hasattr(_get_checker, 'LiteralsOutputChecker'):
return _get_checker.LiteralsOutputChecker()
import doctest
import re
class UnicodeOutputChecker(doctest.OutputChecker):
class LiteralsOutputChecker(doctest.OutputChecker):
"""
Copied from doctest_nose_plugin.py from the nltk project:
https://github.com/nltk/nltk
Further extended to also support byte literals.
"""
_literal_re = re.compile(r"(\W|^)[uU]([rR]?[\'\"])", re.UNICODE)
_unicode_literal_re = re.compile(r"(\W|^)[uU]([rR]?[\'\"])", re.UNICODE)
_bytes_literal_re = re.compile(r"(\W|^)[bB]([rR]?[\'\"])", re.UNICODE)
def check_output(self, want, got, optionflags):
res = doctest.OutputChecker.check_output(self, want, got,
@@ -220,23 +251,27 @@ def _get_unicode_checker():
if res:
return True
if not (optionflags & _get_allow_unicode_flag()):
allow_unicode = optionflags & _get_allow_unicode_flag()
allow_bytes = optionflags & _get_allow_bytes_flag()
if not allow_unicode and not allow_bytes:
return False
else: # pragma: no cover
# the code below will end up executed only in Python 2 in
# our tests, and our coverage check runs in Python 3 only
def remove_u_prefixes(txt):
return re.sub(self._literal_re, r'\1\2', txt)
def remove_prefixes(regex, txt):
return re.sub(regex, r'\1\2', txt)
want = remove_u_prefixes(want)
got = remove_u_prefixes(got)
if allow_unicode:
want = remove_prefixes(self._unicode_literal_re, want)
got = remove_prefixes(self._unicode_literal_re, got)
if allow_bytes:
want = remove_prefixes(self._bytes_literal_re, want)
got = remove_prefixes(self._bytes_literal_re, got)
res = doctest.OutputChecker.check_output(self, want, got,
optionflags)
return res
_get_unicode_checker.UnicodeOutputChecker = UnicodeOutputChecker
return _get_unicode_checker.UnicodeOutputChecker()
_get_checker.LiteralsOutputChecker = LiteralsOutputChecker
return _get_checker.LiteralsOutputChecker()
def _get_allow_unicode_flag():
@@ -245,3 +280,11 @@ def _get_allow_unicode_flag():
"""
import doctest
return doctest.register_optionflag('ALLOW_UNICODE')
def _get_allow_bytes_flag():
"""
Registers and returns the ALLOW_BYTES flag.
"""
import doctest
return doctest.register_optionflag('ALLOW_BYTES')

View File

@@ -80,13 +80,23 @@ def showhelp(config):
line = " %-24s %s" %(spec, help)
tw.line(line[:tw.fullwidth])
tw.line() ; tw.line()
#tw.sep("=")
tw.line()
tw.line("environment variables:")
vars = [
("PYTEST_ADDOPTS", "extra command line options"),
("PYTEST_PLUGINS", "comma-separated plugins to load during startup"),
("PYTEST_DEBUG", "set to enable debug tracing of pytest's internals")
]
for name, help in vars:
tw.line(" %-24s %s" % (name, help))
tw.line()
tw.line()
tw.line("to see available markers type: py.test --markers")
tw.line("to see available fixtures type: py.test --fixtures")
tw.line("(shown according to specified file_or_dir or current dir "
"if not specified)")
tw.line(str(reporter.stats))
for warningreport in reporter.stats.get('warnings', []):
tw.line("warning : " + warningreport.message, red=True)
return

View File

@@ -28,17 +28,14 @@ def pytest_plugin_registered(plugin, manager):
@hookspec(historic=True)
def pytest_addoption(parser):
"""register argparse-style options and ini-style config values.
"""register argparse-style options and ini-style config values,
called once at the beginning of a test run.
.. warning::
.. note::
This function must be implemented in a :ref:`plugin <pluginorder>`
and is called once at the beginning of a test run.
Implementing this hook from ``conftest.py`` files is **strongly**
discouraged because ``conftest.py`` files are lazily loaded and
may give strange *unknown option* errors depending on the directory
``py.test`` is invoked from.
This function should be implemented only in plugins or ``conftest.py``
files situated at the tests root directory due to how py.test
:ref:`discovers plugins during startup <pluginorder>`.
:arg parser: To add command line options, call
:py:func:`parser.addoption(...) <_pytest.config.Parser.addoption>`.
@@ -84,7 +81,7 @@ def pytest_cmdline_main(config):
""" called for performing the main command line action. The default
implementation will invoke the configure hooks and runtest_mainloop. """
def pytest_load_initial_conftests(args, early_config, parser):
def pytest_load_initial_conftests(early_config, parser, args):
""" implements the loading of initial conftest files ahead
of command line option parsing. """
@@ -282,13 +279,17 @@ def pytest_keyboard_interrupt(excinfo):
""" called for keyboard interrupt. """
def pytest_exception_interact(node, call, report):
""" (experimental, new in 2.4) called when
an exception was raised which can potentially be
"""called when an exception was raised which can potentially be
interactively handled.
This hook is only called if an exception was raised
that is not an internal exception like "skip.Exception".
that is not an internal exception like ``skip.Exception``.
"""
def pytest_enter_pdb():
""" called upon pdb.set_trace()"""
def pytest_enter_pdb(config):
""" called upon pdb.set_trace(), can be used by plugins to take special
action just before the python debugger enters in interactive mode.
:arg config: pytest config object
:type config: _pytest.config.Config
"""

View File

@@ -1,9 +1,13 @@
""" report test results in JUnit-XML format, for use with Hudson and build integration servers.
"""
report test results in JUnit-XML format,
for use with Jenkins and build integration servers.
Output conforms to https://github.com/jenkinsci/xunit-plugin/blob/master/src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd
Based on initial code from Ross Lawley.
"""
# Output conforms to https://github.com/jenkinsci/xunit-plugin/blob/master/
# src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd
import py
import os
import re
@@ -19,10 +23,10 @@ else:
unicode = str
long = int
class Junit(py.xml.Namespace):
pass
# We need to get the subset of the invalid unicode ranges according to
# XML 1.0 which are valid in this python build. Hence we calculate
# this dynamically instead of hardcoding it. The spec range of valid
@@ -30,21 +34,21 @@ class Junit(py.xml.Namespace):
# | [#x10000-#x10FFFF]
_legal_chars = (0x09, 0x0A, 0x0d)
_legal_ranges = (
(0x20, 0x7E),
(0x80, 0xD7FF),
(0xE000, 0xFFFD),
(0x10000, 0x10FFFF),
(0x20, 0x7E), (0x80, 0xD7FF), (0xE000, 0xFFFD), (0x10000, 0x10FFFF),
)
_legal_xml_re = [unicode("%s-%s") % (unichr(low), unichr(high))
for (low, high) in _legal_ranges
if low < sys.maxunicode]
_legal_xml_re = [
unicode("%s-%s") % (unichr(low), unichr(high))
for (low, high) in _legal_ranges if low < sys.maxunicode
]
_legal_xml_re = [unichr(x) for x in _legal_chars] + _legal_xml_re
illegal_xml_re = re.compile(unicode('[^%s]') %
unicode('').join(_legal_xml_re))
illegal_xml_re = re.compile(unicode('[^%s]') % unicode('').join(_legal_xml_re))
del _legal_chars
del _legal_ranges
del _legal_xml_re
_py_ext_re = re.compile(r"\.py$")
def bin_xml_escape(arg):
def repl(matchobj):
i = ord(matchobj.group())
@@ -52,122 +56,86 @@ def bin_xml_escape(arg):
return unicode('#x%02X') % i
else:
return unicode('#x%04X') % i
return py.xml.raw(illegal_xml_re.sub(repl, py.xml.escape(arg)))
@pytest.fixture
def record_xml_property(request):
"""Fixture that adds extra xml properties to the tag for the calling test.
The fixture is callable with (name, value), with value being automatically
xml-encoded.
"""
def inner(name, value):
if hasattr(request.config, "_xml"):
request.config._xml.add_custom_property(name, value)
msg = 'record_xml_property is an experimental feature'
request.config.warn(code='C3', message=msg,
fslocation=request.node.location[:2])
return inner
def pytest_addoption(parser):
group = parser.getgroup("terminal reporting")
group.addoption('--junitxml', '--junit-xml', action="store",
dest="xmlpath", metavar="path", default=None,
help="create junit-xml style report file at given path.")
group.addoption('--junitprefix', '--junit-prefix', action="store",
metavar="str", default=None,
help="prepend prefix to classnames in junit-xml output")
class _NodeReporter(object):
def __init__(self, nodeid, xml):
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.pluginmanager.register(config._xml)
self.id = nodeid
self.xml = xml
self.add_stats = self.xml.add_stats
self.duration = 0
self.properties = []
self.nodes = []
self.testcase = None
self.attrs = {}
def pytest_unconfigure(config):
xml = getattr(config, '_xml', None)
if xml:
del config._xml
config.pluginmanager.unregister(xml)
def append(self, node):
self.xml.add_stats(type(node).__name__)
self.nodes.append(node)
def mangle_testnames(names):
names = [x.replace(".py", "") for x in names if x != '()']
names[0] = names[0].replace("/", '.')
return names
def add_property(self, name, value):
self.properties.append((str(name), bin_xml_escape(value)))
class LogXML(object):
def __init__(self, logfile, prefix):
logfile = os.path.expanduser(os.path.expandvars(logfile))
self.logfile = os.path.normpath(os.path.abspath(logfile))
self.prefix = prefix
self.tests = []
self.tests_by_nodeid = {} # nodeid -> Junit.testcase
self.durations = {} # nodeid -> total duration (setup+call+teardown)
self.passed = self.skipped = 0
self.failed = self.errors = 0
self.custom_properties = {}
def make_properties_node(self):
"""Return a Junit node containing custom properties, if any.
"""
if self.properties:
return Junit.properties([
Junit.property(name=name, value=value)
for name, value in self.properties
])
return ''
def add_custom_property(self, name, value):
self.custom_properties[str(name)] = bin_xml_escape(str(value))
def _opentestcase(self, report):
names = mangle_testnames(report.nodeid.split("::"))
def record_testreport(self, testreport):
assert not self.testcase
names = mangle_test_address(testreport.nodeid)
classnames = names[:-1]
if self.prefix:
classnames.insert(0, self.prefix)
if self.xml.prefix:
classnames.insert(0, self.xml.prefix)
attrs = {
"classname": ".".join(classnames),
"name": bin_xml_escape(names[-1]),
"file": report.location[0],
"time": self.durations.get(report.nodeid, 0),
"file": testreport.location[0],
}
if report.location[1] is not None:
attrs["line"] = report.location[1]
testcase = Junit.testcase(**attrs)
custom_properties = self.pop_custom_properties()
if custom_properties:
testcase.append(custom_properties)
self.tests.append(testcase)
self.tests_by_nodeid[report.nodeid] = testcase
if testreport.location[1] is not None:
attrs["line"] = testreport.location[1]
self.attrs = attrs
def to_xml(self):
testcase = Junit.testcase(time=self.duration, **self.attrs)
testcase.append(self.make_properties_node())
for node in self.nodes:
testcase.append(node)
return testcase
def _add_simple(self, kind, message, data=None):
data = bin_xml_escape(data)
node = kind(data, message=message)
self.append(node)
def _write_captured_output(self, report):
for capname in ('out', 'err'):
allcontent = ""
for name, content in report.get_sections("Captured std%s" %
capname):
capname):
allcontent += content
if allcontent:
tag = getattr(Junit, 'system-'+capname)
tag = getattr(Junit, 'system-' + capname)
self.append(tag(bin_xml_escape(allcontent)))
def append(self, obj):
self.tests[-1].append(obj)
def pop_custom_properties(self):
"""Return a Junit node containing custom properties set for
the current test, if any, and reset the current custom properties.
"""
if self.custom_properties:
result = Junit.properties(
[
Junit.property(name=name, value=value)
for name, value in self.custom_properties.items()
]
)
self.custom_properties.clear()
return result
return None
def append_pass(self, report):
self.passed += 1
self.add_stats('passed')
self._write_captured_output(report)
def append_failure(self, report):
#msg = str(report.longrepr.reprtraceback.extraline)
# msg = str(report.longrepr.reprtraceback.extraline)
if hasattr(report, "wasxfail"):
self.append(
Junit.skipped(message="xfail-marked test passes unexpectedly"))
self.skipped += 1
self._add_simple(
Junit.skipped,
"xfail-marked test passes unexpectedly")
else:
if hasattr(report.longrepr, "reprcrash"):
message = report.longrepr.reprcrash.message
@@ -179,30 +147,27 @@ class LogXML(object):
fail = Junit.failure(message=message)
fail.append(bin_xml_escape(report.longrepr))
self.append(fail)
self.failed += 1
self._write_captured_output(report)
def append_collect_error(self, report):
#msg = str(report.longrepr.reprtraceback.extraline)
# msg = str(report.longrepr.reprtraceback.extraline)
self.append(Junit.error(bin_xml_escape(report.longrepr),
message="collection failure"))
self.errors += 1
def append_collect_skipped(self, report):
#msg = str(report.longrepr.reprtraceback.extraline)
self.append(Junit.skipped(bin_xml_escape(report.longrepr),
message="collection skipped"))
self.skipped += 1
self._add_simple(
Junit.skipped, "collection skipped", report.longrepr)
def append_error(self, report):
self.append(Junit.error(bin_xml_escape(report.longrepr),
message="test setup failure"))
self.errors += 1
self._add_simple(
Junit.error, "test setup failure", report.longrepr)
self._write_captured_output(report)
def append_skipped(self, report):
if hasattr(report, "wasxfail"):
self.append(Junit.skipped(bin_xml_escape(report.wasxfail),
message="expected test failure"))
self._add_simple(
Junit.skipped, "expected test failure", report.wasxfail
)
else:
filename, lineno, skipreason = report.longrepr
if skipreason.startswith("Skipped: "):
@@ -210,11 +175,129 @@ class LogXML(object):
self.append(
Junit.skipped("%s:%s: %s" % (filename, lineno, skipreason),
type="pytest.skip",
message=skipreason
))
self.skipped += 1
message=skipreason))
self._write_captured_output(report)
def finalize(self):
data = self.to_xml().unicode(indent=0)
self.__dict__.clear()
self.to_xml = lambda: py.xml.raw(data)
@pytest.fixture
def record_xml_property(request):
"""Fixture that adds extra xml properties to the tag for the calling test.
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
def pytest_addoption(parser):
group = parser.getgroup("terminal reporting")
group.addoption(
'--junitxml', '--junit-xml',
action="store",
dest="xmlpath",
metavar="path",
default=None,
help="create junit-xml style report file at given path.")
group.addoption(
'--junitprefix', '--junit-prefix',
action="store",
metavar="str",
default=None,
help="prepend prefix to classnames in junit-xml output")
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.pluginmanager.register(config._xml)
def pytest_unconfigure(config):
xml = getattr(config, '_xml', None)
if xml:
del config._xml
config.pluginmanager.unregister(xml)
def mangle_test_address(address):
path, possible_open_bracket, params = address.partition('[')
names = path.split("::")
try:
names.remove('()')
except ValueError:
pass
# convert file path to dotted path
names[0] = names[0].replace("/", '.')
names[0] = _py_ext_re.sub("", names[0])
# put any params back
names[-1] += possible_open_bracket + params
return names
class LogXML(object):
def __init__(self, logfile, prefix):
logfile = os.path.expanduser(os.path.expandvars(logfile))
self.logfile = os.path.normpath(os.path.abspath(logfile))
self.prefix = prefix
self.stats = dict.fromkeys([
'error',
'passed',
'failure',
'skipped',
], 0)
self.node_reporters = {} # nodeid -> _NodeReporter
self.node_reporters_ordered = []
def finalize(self, report):
nodeid = getattr(report, 'nodeid', report)
# local hack to handle xdist report order
slavenode = getattr(report, 'node', None)
reporter = self.node_reporters.pop((nodeid, slavenode))
if reporter is not None:
reporter.finalize()
def node_reporter(self, report):
nodeid = getattr(report, 'nodeid', report)
# local hack to handle xdist report order
slavenode = getattr(report, 'node', None)
key = nodeid, slavenode
if key in self.node_reporters:
# TODO: breasks for --dist=each
return self.node_reporters[key]
reporter = _NodeReporter(nodeid, self)
self.node_reporters[key] = reporter
self.node_reporters_ordered.append(reporter)
return reporter
def add_stats(self, key):
if key in self.stats:
self.stats[key] += 1
def _opentestcase(self, report):
reporter = self.node_reporter(report)
reporter.record_testreport(report)
return reporter
def pytest_runtest_logreport(self, report):
"""handle a setup/call/teardown report, generating the appropriate
xml tags as necessary.
@@ -240,47 +323,40 @@ class LogXML(object):
"""
if report.passed:
if report.when == "call": # ignore setup/teardown
self._opentestcase(report)
self.append_pass(report)
reporter = self._opentestcase(report)
reporter.append_pass(report)
elif report.failed:
self._opentestcase(report)
if report.when != "call":
self.append_error(report)
reporter = self._opentestcase(report)
if report.when == "call":
reporter.append_failure(report)
else:
self.append_failure(report)
reporter.append_error(report)
elif report.skipped:
self._opentestcase(report)
self.append_skipped(report)
reporter = self._opentestcase(report)
reporter.append_skipped(report)
self.update_testcase_duration(report)
if report.when == "teardown":
self.finalize(report)
def update_testcase_duration(self, report):
"""accumulates total duration for nodeid from given report and updates
the Junit.testcase with the new total if already created.
"""
total = self.durations.get(report.nodeid, 0.0)
total += getattr(report, 'duration', 0.0)
self.durations[report.nodeid] = total
testcase = self.tests_by_nodeid.get(report.nodeid)
if testcase is not None:
testcase.attr.time = total
reporter = self.node_reporter(report)
reporter.duration += getattr(report, 'duration', 0.0)
def pytest_collectreport(self, report):
if not report.passed:
self._opentestcase(report)
reporter = self._opentestcase(report)
if report.failed:
self.append_collect_error(report)
reporter.append_collect_error(report)
else:
self.append_collect_skipped(report)
reporter.append_collect_skipped(report)
def pytest_internalerror(self, excrepr):
self.errors += 1
data = bin_xml_escape(excrepr)
self.tests.append(
Junit.testcase(
Junit.error(data, message="internal error"),
classname="pytest",
name="internal"))
reporter = self.node_reporter('internal')
reporter.attrs.update(classname="pytest", name='internal')
reporter._add_simple(Junit.error, 'internal error', excrepr)
def pytest_sessionstart(self):
self.suite_start_time = time.time()
@@ -292,19 +368,20 @@ class LogXML(object):
logfile = open(self.logfile, 'w', encoding='utf-8')
suite_stop_time = time.time()
suite_time_delta = suite_stop_time - self.suite_start_time
numtests = self.passed + self.failed
numtests = self.stats['passed'] + self.stats['failure'] + self.stats['skipped']
logfile.write('<?xml version="1.0" encoding="utf-8"?>')
logfile.write(Junit.testsuite(
self.tests,
[x.to_xml() for x in self.node_reporters_ordered],
name="pytest",
errors=self.errors,
failures=self.failed,
skips=self.skipped,
errors=self.stats['error'],
failures=self.stats['failure'],
skips=self.stats['skipped'],
tests=numtests,
time="%.3f" % suite_time_delta,
).unicode(indent=0))
time="%.3f" % suite_time_delta, ).unicode(indent=0))
logfile.close()
def pytest_terminal_summary(self, terminalreporter):
terminalreporter.write_sep("-", "generated xml file: %s" % (self.logfile))
terminalreporter.write_sep("-",
"generated xml file: %s" % (self.logfile))

View File

@@ -1,9 +1,13 @@
""" core implementation of testing process: init, session, runtest loop. """
import imp
import os
import re
import sys
import _pytest
import _pytest._code
import py
import pytest, _pytest
import os, sys, imp
import pytest
try:
from collections import MutableMapping as MappingMixin
except ImportError:
@@ -91,11 +95,11 @@ def wrap_session(config, doit):
except pytest.UsageError:
raise
except KeyboardInterrupt:
excinfo = py.code.ExceptionInfo()
excinfo = _pytest._code.ExceptionInfo()
config.hook.pytest_keyboard_interrupt(excinfo=excinfo)
session.exitstatus = EXIT_INTERRUPTED
except:
excinfo = py.code.ExceptionInfo()
excinfo = _pytest._code.ExceptionInfo()
config.notify_exception(excinfo, config.option)
session.exitstatus = EXIT_INTERNALERROR
if excinfo.errisinstance(SystemExit):
@@ -240,17 +244,11 @@ class Node(object):
# used for storing artificial fixturedefs for direct parametrization
self._name2pseudofixturedef = {}
#self.extrainit()
@property
def ihook(self):
""" fspath sensitive hook proxy used to call pytest hooks"""
return self.session.gethookproxy(self.fspath)
#def extrainit(self):
# """"extra initialization after Node is initialized. Implemented
# by some subclasses. """
Module = compatproperty("Module")
Class = compatproperty("Class")
Instance = compatproperty("Instance")
@@ -720,7 +718,8 @@ class Session(FSCollector):
if rep.passed:
has_matched = False
for x in rep.result:
if x.name == name:
# TODO: remove parametrized workaround once collection structure contains parametrization
if x.name == name or x.name.split("[")[0] == name:
resultnodes.extend(self.matchnodes([x], nextnames))
has_matched = True
# XXX accept IDs that don't have "()" for class instances

View File

@@ -58,7 +58,7 @@ pytest_cmdline_main.tryfirst = True
def pytest_collection_modifyitems(items, config):
keywordexpr = config.option.keyword
keywordexpr = config.option.keyword.lstrip()
matchexpr = config.option.markexpr
if not keywordexpr and not matchexpr:
return
@@ -169,7 +169,7 @@ class MarkGenerator:
""" Factory for :class:`MarkDecorator` objects - exposed as
a ``pytest.mark`` singleton instance. Example::
import py
import pytest
@pytest.mark.slowtest
def test_function():
pass

View File

@@ -1,8 +1,13 @@
""" monkeypatching and mocking functionality. """
import os, sys
import re
from py.builtin import _basestring
RE_IMPORT_ERROR_NAME = re.compile("^No module named (.*)$")
def pytest_funcarg__monkeypatch(request):
"""The returned ``monkeypatch`` funcarg provides these
helper methods to modify objects, dictionaries or os.environ::
@@ -26,48 +31,71 @@ def pytest_funcarg__monkeypatch(request):
return mpatch
def resolve(name):
# simplified from zope.dottedname
parts = name.split('.')
used = parts.pop(0)
found = __import__(used)
for part in parts:
used += '.' + part
try:
found = getattr(found, part)
except AttributeError:
pass
else:
continue
# we use explicit un-nesting of the handling block in order
# to avoid nested exceptions on python 3
try:
__import__(used)
except ImportError as ex:
# str is used for py2 vs py3
expected = str(ex).split()[-1]
if expected == used:
raise
else:
raise ImportError(
'import error in %s: %s' % (used, ex)
)
found = annotated_getattr(found, part, used)
return found
def annotated_getattr(obj, name, ann):
try:
obj = getattr(obj, name)
except AttributeError:
raise AttributeError(
'%r object at %s has no attribute %r' % (
type(obj).__name__, ann, name
)
)
return obj
def derive_importpath(import_path, raising):
import pytest
if not isinstance(import_path, _basestring) or "." not in import_path:
raise TypeError("must be absolute import path string, not %r" %
(import_path,))
rest = []
target = import_path
while target:
try:
obj = __import__(target, None, None, "__doc__")
except ImportError:
if "." not in target:
__tracebackhide__ = True
pytest.fail("could not import any sub part: %s" %
import_path)
target, name = target.rsplit(".", 1)
rest.append(name)
else:
assert rest
try:
while len(rest) > 1:
attr = rest.pop()
obj = getattr(obj, attr)
attr = rest[0]
if raising:
getattr(obj, attr)
except AttributeError:
__tracebackhide__ = True
pytest.fail("object %r has no attribute %r" % (obj, attr))
return attr, obj
module, attr = import_path.rsplit('.', 1)
target = resolve(module)
if raising:
annotated_getattr(target, attr, ann=module)
return attr, target
class Notset:
def __repr__(self):
return "<notset>"
notset = Notset()
class monkeypatch:
""" Object keeping a record of setattr/item/env/syspath changes. """
def __init__(self):
self._setattr = []
self._setitem = []
@@ -94,19 +122,19 @@ class monkeypatch:
if value is notset:
if not isinstance(target, _basestring):
raise TypeError("use setattr(target, name, value) or "
"setattr(target, value) with target being a dotted "
"import string")
"setattr(target, value) with target being a dotted "
"import string")
value = name
name, target = derive_importpath(target, raising)
oldval = getattr(target, name, notset)
if raising and oldval is notset:
raise AttributeError("%r has no attribute %r" %(target, name))
raise AttributeError("%r has no attribute %r" % (target, name))
# avoid class descriptors like staticmethod/classmethod
if inspect.isclass(target):
oldval = target.__dict__.get(name, notset)
self._setattr.insert(0, (target, name, oldval))
self._setattr.append((target, name, oldval))
setattr(target, name, value)
def delattr(self, target, name=notset, raising=True):
@@ -132,13 +160,12 @@ class monkeypatch:
if raising:
raise AttributeError(name)
else:
self._setattr.insert(0, (target, name,
getattr(target, name, notset)))
self._setattr.append((target, name, getattr(target, name, notset)))
delattr(target, name)
def setitem(self, dic, name, value):
""" Set dictionary entry ``name`` to value. """
self._setitem.insert(0, (dic, name, dic.get(name, notset)))
self._setitem.append((dic, name, dic.get(name, notset)))
dic[name] = value
def delitem(self, dic, name, raising=True):
@@ -151,7 +178,7 @@ class monkeypatch:
if raising:
raise KeyError(name)
else:
self._setitem.insert(0, (dic, name, dic.get(name, notset)))
self._setitem.append((dic, name, dic.get(name, notset)))
del dic[name]
def setenv(self, name, value, prepend=None):
@@ -203,18 +230,18 @@ class monkeypatch:
calling `undo()` will undo all of the changes made in
both functions.
"""
for obj, name, value in self._setattr:
for obj, name, value in reversed(self._setattr):
if value is not notset:
setattr(obj, name, value)
else:
delattr(obj, name)
self._setattr[:] = []
for dictionary, name, value in self._setitem:
for dictionary, name, value in reversed(self._setitem):
if value is notset:
try:
del dictionary[name]
except KeyError:
pass # was already deleted, so we have the desired state
pass # was already deleted, so we have the desired state
else:
dictionary[name] = value
self._setitem[:] = []

View File

@@ -13,17 +13,21 @@ def pytest_addoption(parser):
@pytest.hookimpl(trylast=True)
def pytest_configure(config):
import py
if config.option.pastebin == "all":
tr = config.pluginmanager.getplugin('terminalreporter')
# if no terminal reporter plugin is present, nothing we can do here;
# this can happen when this function executes in a slave node
# when using pytest-xdist, for example
if tr is not None:
config._pastebinfile = tempfile.TemporaryFile('w+')
# pastebin file will be utf-8 encoded binary file
config._pastebinfile = tempfile.TemporaryFile('w+b')
oldwrite = tr._tw.write
def tee_write(s, **kwargs):
oldwrite(s, **kwargs)
config._pastebinfile.write(str(s))
if py.builtin._istext(s):
s = s.encode('utf-8')
config._pastebinfile.write(s)
tr._tw.write = tee_write
def pytest_unconfigure(config):
@@ -45,7 +49,7 @@ def create_new_paste(contents):
"""
Creates a new paste using bpaste.net service.
:contents: paste contents
:contents: paste contents as utf-8 encoded bytes
:returns: url to the pasted contents
"""
import re
@@ -61,8 +65,8 @@ def create_new_paste(contents):
'expiry': '1week',
}
url = 'https://bpaste.net'
response = urlopen(url, data=urlencode(params)).read()
m = re.search(r'href="/raw/(\w+)"', response)
response = urlopen(url, data=urlencode(params).encode('ascii')).read()
m = re.search(r'href="/raw/(\w+)"', response.decode('utf-8'))
if m:
return '%s/show/%s' % (url, m.group(1))
else:

View File

@@ -37,7 +37,6 @@ class pytestPDB:
""" invoke PDB set_trace debugging, dropping any IO capturing. """
import _pytest.config
frame = sys._getframe().f_back
capman = None
if self._pluginmanager is not None:
capman = self._pluginmanager.getplugin("capturemanager")
if capman:
@@ -45,7 +44,7 @@ class pytestPDB:
tw = _pytest.config.create_terminal_writer(self._config)
tw.line()
tw.sep(">", "PDB set_trace (IO-capturing turned off)")
self._pluginmanager.hook.pytest_enter_pdb()
self._pluginmanager.hook.pytest_enter_pdb(config=self._config)
pdb.Pdb().set_trace(frame)
@@ -53,7 +52,9 @@ class PdbInvoke:
def pytest_exception_interact(self, node, call, report):
capman = node.config.pluginmanager.getplugin("capturemanager")
if capman:
capman.suspendcapture(in_=True)
out, err = capman.suspendcapture(in_=True)
sys.stdout.write(out)
sys.stdout.write(err)
_enter_pdb(node, call.excinfo, report)
def pytest_internalerror(self, excrepr, excinfo):

View File

@@ -1,19 +1,20 @@
""" (disabled by default) support for testing pytest and pytest plugins. """
import gc
import sys
import traceback
import os
import codecs
import re
import time
import gc
import os
import platform
from fnmatch import fnmatch
import re
import subprocess
import sys
import time
import traceback
from fnmatch import fnmatch
import py
import pytest
from py.builtin import print_
from _pytest._code import Source
import py
import pytest
from _pytest.main import Session, EXIT_OK
@@ -472,7 +473,7 @@ class Testdir:
ret = None
for name, value in items:
p = self.tmpdir.join(name).new(ext=ext)
source = py.code.Source(value)
source = Source(value)
def my_totext(s, encoding="utf-8"):
if py.builtin._isbytes(s):
s = py.builtin._totext(s, encoding=encoding)
@@ -835,7 +836,7 @@ class Testdir:
to the temporarly directory to ensure it is a package.
"""
kw = {self.request.function.__name__: py.code.Source(source).strip()}
kw = {self.request.function.__name__: Source(source).strip()}
path = self.makepyfile(**kw)
if withinit:
self.makepyfile(__init__ = "#")
@@ -1041,8 +1042,8 @@ class LineMatcher:
def _getlines(self, lines2):
if isinstance(lines2, str):
lines2 = py.code.Source(lines2)
if isinstance(lines2, py.code.Source):
lines2 = Source(lines2)
if isinstance(lines2, Source):
lines2 = lines2.strip().lines
return lines2

View File

@@ -1,13 +1,15 @@
""" Python test discovery, setup and run of test functions. """
import re
import fnmatch
import functools
import py
import inspect
import re
import types
import sys
import py
import pytest
from _pytest._code.code import TerminalRepr
from _pytest.mark import MarkDecorator, MarkerError
from py._code.code import TerminalRepr
try:
import enum
@@ -43,13 +45,31 @@ else:
def _format_args(func):
return inspect.formatargspec(*inspect.getargspec(func))
if sys.version_info[:2] == (2, 6):
def isclass(object):
""" Return true if the object is a class. Overrides inspect.isclass for
python 2.6 because it will return True for objects which always return
something on __getattr__ calls (see #1035).
Backport of https://hg.python.org/cpython/rev/35bf8f7a8edc
"""
return isinstance(object, (type, types.ClassType))
def _has_positional_arg(func):
return func.__code__.co_argcount
def filter_traceback(entry):
return entry.path != cutdir1 and not entry.path.relto(cutdir2)
# entry.path might sometimes return a str object when the entry
# points to dynamically generated code
# see https://bitbucket.org/pytest-dev/py/issues/71
raw_filename = entry.frame.code.raw.co_filename
is_generated = '<' in raw_filename and '>' in raw_filename
if is_generated:
return False
# entry.path might point to an inexisting file, in which case it will
# alsso return a str object. see #1133
p = py.path.local(entry.path)
return p != cutdir1 and not p.relto(cutdir2)
def get_real_func(obj):
@@ -67,7 +87,7 @@ def getfslineno(obj):
obj = get_real_func(obj)
if hasattr(obj, 'place_as'):
obj = obj.place_as
fslineno = py.code.getfslineno(obj)
fslineno = _pytest._code.getfslineno(obj)
assert isinstance(fslineno[1], int), obj
return fslineno
@@ -296,11 +316,14 @@ def pytest_pycollect_makeitem(collector, name, obj):
elif collector.istestfunction(obj, name):
# mock seems to store unbound methods (issue473), normalize it
obj = getattr(obj, "__func__", obj)
if not isfunction(obj):
# We need to try and unwrap the function if it's a functools.partial
# or a funtools.wrapped.
# We musn't if it's been wrapped with mock.patch (python 2 only)
if not (isfunction(obj) or isfunction(get_real_func(obj))):
collector.warn(code="C2", message=
"cannot collect %r because it is not a function."
% name, )
if getattr(obj, "__test__", True):
elif getattr(obj, "__test__", True):
if is_generator(obj):
res = Generator(name, parent=collector)
else:
@@ -309,7 +332,7 @@ def pytest_pycollect_makeitem(collector, name, obj):
def is_generator(func):
try:
return py.code.getrawcode(func).co_flags & 32 # generator function
return _pytest._code.getrawcode(func).co_flags & 32 # generator function
except AttributeError: # builtin functions have no bytecode
# assume them to not be generators
return False
@@ -362,12 +385,13 @@ class PyobjMixin(PyobjContext):
def reportinfo(self):
# XXX caching?
obj = self.obj
if hasattr(obj, 'compat_co_firstlineno'):
compat_co_firstlineno = getattr(obj, 'compat_co_firstlineno', None)
if isinstance(compat_co_firstlineno, int):
# nose compatibility
fspath = sys.modules[obj.__module__].__file__
if fspath.endswith(".pyc"):
fspath = fspath[:-1]
lineno = obj.compat_co_firstlineno
lineno = compat_co_firstlineno
else:
fspath, lineno = getfslineno(obj)
modpath = self.getmodpath()
@@ -383,15 +407,18 @@ class PyCollector(PyobjMixin, pytest.Collector):
""" Look for the __test__ attribute, which is applied by the
@nose.tools.istest decorator
"""
return safe_getattr(obj, '__test__', False)
# We explicitly check for "is True" here to not mistakenly treat
# classes with a custom __getattr__ returning something truthy (like a
# function) as test classes.
return safe_getattr(obj, '__test__', False) is True
def classnamefilter(self, name):
return self._matches_prefix_or_glob_option('python_classes', name)
def istestfunction(self, obj, name):
return (
(self.funcnamefilter(name) or self.isnosetest(obj))
and safe_getattr(obj, "__call__", False) and getfixturemarker(obj) is None
(self.funcnamefilter(name) or self.isnosetest(obj)) and
safe_getattr(obj, "__call__", False) and getfixturemarker(obj) is None
)
def istestclass(self, obj, name):
@@ -425,7 +452,7 @@ class PyCollector(PyobjMixin, pytest.Collector):
seen = {}
l = []
for dic in dicts:
for name, obj in dic.items():
for name, obj in list(dic.items()):
if name in seen:
continue
seen[name] = True
@@ -584,7 +611,7 @@ class Module(pytest.File, PyCollector):
mod = self.fspath.pyimport(ensuresyspath=importmode)
except SyntaxError:
raise self.CollectError(
py.code.ExceptionInfo().getrepr(style="short"))
_pytest._code.ExceptionInfo().getrepr(style="short"))
except self.fspath.ImportMismatchError:
e = sys.exc_info()[1]
raise self.CollectError(
@@ -690,7 +717,7 @@ class FunctionMixin(PyobjMixin):
def _prunetraceback(self, excinfo):
if hasattr(self, '_obj') and not self.config.option.fulltrace:
code = py.code.Code(get_real_func(self.obj))
code = _pytest._code.Code(get_real_func(self.obj))
path, firstlineno = code.path, code.firstlineno
traceback = excinfo.traceback
ntraceback = traceback.cut(path=path, firstlineno=firstlineno)
@@ -713,7 +740,7 @@ class FunctionMixin(PyobjMixin):
def _repr_failure_py(self, excinfo, style="long"):
if excinfo.errisinstance(pytest.fail.Exception):
if not excinfo.value.pytrace:
return str(excinfo.value)
return py._builtin._totext(excinfo.value)
return super(FunctionMixin, self)._repr_failure_py(excinfo,
style=style)
@@ -844,8 +871,6 @@ class CallSpec2(object):
getattr(self, valtype_for_arg)[arg] = val
self.indices[arg] = param_index
self._arg2scopenum[arg] = scopenum
if val is _notexists:
self._emptyparamspecified = True
self._idlist.append(id)
self.keywords.update(keywords)
@@ -963,6 +988,15 @@ class Metafunc(FuncargnamesCompatAttr):
argvalues = [(val,) for val in argvalues]
if not argvalues:
argvalues = [(_notexists,) * len(argnames)]
# we passed a empty list to parameterize, skip that test
#
fs, lineno = getfslineno(self.function)
newmark = pytest.mark.skip(
reason="got empty parameter set %r, function %s at %s:%d" % (
argnames, self.function.__name__, fs, lineno))
newmarks = newkeywords.setdefault(0, {})
newmarks[newmark.markname] = newmark
if scope is None:
scope = "function"
@@ -1041,6 +1075,8 @@ class Metafunc(FuncargnamesCompatAttr):
if _PY3:
import codecs
def _escape_bytes(val):
"""
If val is pure ascii, returns it as a str(), otherwise escapes
@@ -1053,18 +1089,21 @@ if _PY3:
want to return escaped bytes for any byte, even if they match
a utf-8 string.
"""
# source: http://goo.gl/bGsnwC
import codecs
encoded_bytes, _ = codecs.escape_encode(val)
return encoded_bytes.decode('ascii')
if val:
# source: http://goo.gl/bGsnwC
encoded_bytes, _ = codecs.escape_encode(val)
return encoded_bytes.decode('ascii')
else:
# empty bytes crashes codecs.escape_encode (#1087)
return ''
else:
def _escape_bytes(val):
"""
In py2 bytes and str are the same, so return it unchanged if it
In py2 bytes and str are the same type, so return it unchanged if it
is a full ascii string, otherwise escape it into its binary form.
"""
try:
return val.encode('ascii')
return val.decode('ascii')
except UnicodeDecodeError:
return val.encode('string-escape')
@@ -1083,7 +1122,7 @@ def _idval(val, argname, idx, idfn):
elif isinstance(val, (float, int, str, bool, NoneType)):
return str(val)
elif isinstance(val, REGEX_TYPE):
return val.pattern
return _escape_bytes(val.pattern) if isinstance(val.pattern, bytes) else val.pattern
elif enum is not None and isinstance(val, enum.Enum):
return str(val)
elif isclass(val) and hasattr(val, '__name__'):
@@ -1093,7 +1132,7 @@ def _idval(val, argname, idx, idfn):
# convertible to ascii, return it as an str() object instead
try:
return str(val)
except UnicodeDecodeError:
except UnicodeError:
# fallthrough
pass
return str(argname)+str(idx)
@@ -1129,12 +1168,12 @@ def _showfixtures_main(config, session):
assert fixturedefs is not None
if not fixturedefs:
continue
fixturedef = fixturedefs[-1]
loc = getlocation(fixturedef.func, curdir)
available.append((len(fixturedef.baseid),
fixturedef.func.__module__,
curdir.bestrelpath(loc),
fixturedef.argname, fixturedef))
for fixturedef in fixturedefs:
loc = getlocation(fixturedef.func, curdir)
available.append((len(fixturedef.baseid),
fixturedef.func.__module__,
curdir.bestrelpath(loc),
fixturedef.argname, fixturedef))
available.sort()
currentmodule = None
@@ -1171,10 +1210,10 @@ def getlocation(function, curdir):
# builtin pytest.raises helper
def raises(expected_exception, *args, **kwargs):
""" assert that a code block/function call raises @expected_exception
""" assert that a code block/function call raises ``expected_exception``
and raise a failure exception otherwise.
This helper produces a ``py.code.ExceptionInfo()`` object.
This helper produces a ``ExceptionInfo()`` object (see below).
If using Python 2.5 or above, you may use this function as a
context manager::
@@ -1182,6 +1221,28 @@ def raises(expected_exception, *args, **kwargs):
>>> with raises(ZeroDivisionError):
... 1/0
.. note::
When using ``pytest.raises`` as a context manager, it's worthwhile to
note that normal context manager rules apply and that the exception
raised *must* be the final line in the scope of the context manager.
Lines of code after that, within the scope of the context manager will
not be executed. For example::
>>> with raises(OSError) as exc_info:
assert 1 == 1 # this will execute as expected
raise OSError(errno.EEXISTS, 'directory exists')
assert exc_info.value.errno == errno.EEXISTS # this will not execute
Instead, the following approach must be taken (note the difference in
scope)::
>>> with raises(OSError) as exc_info:
assert 1 == 1 # this will execute as expected
raise OSError(errno.EEXISTS, 'directory exists')
assert exc_info.value.errno == errno.EEXISTS # this will now execute
Or you can specify a callable by passing a to-be-called lambda::
>>> raises(ZeroDivisionError, lambda: 1/0)
@@ -1201,21 +1262,22 @@ def raises(expected_exception, *args, **kwargs):
>>> raises(ZeroDivisionError, "f(0)")
<ExceptionInfo ...>
Performance note:
-----------------
.. autoclass:: _pytest._code.ExceptionInfo
:members:
Similar to caught exception objects in Python, explicitly clearing
local references to returned ``py.code.ExceptionInfo`` objects can
help the Python interpreter speed up its garbage collection.
.. note::
Similar to caught exception objects in Python, explicitly clearing
local references to returned ``ExceptionInfo`` objects can
help the Python interpreter speed up its garbage collection.
Clearing those references breaks a reference cycle
(``ExceptionInfo`` --> caught exception --> frame stack raising
the exception --> current frame stack --> local variables -->
``ExceptionInfo``) which makes Python keep all objects referenced
from that cycle (including all local variables in the current
frame) alive until the next cyclic garbage collection run. See the
official Python ``try`` statement documentation for more detailed
information.
Clearing those references breaks a reference cycle
(``ExceptionInfo`` --> caught exception --> frame stack raising
the exception --> current frame stack --> local variables -->
``ExceptionInfo``) which makes Python keep all objects referenced
from that cycle (including all local variables in the current
frame) alive until the next cyclic garbage collection run. See the
official Python ``try`` statement documentation for more detailed
information.
"""
__tracebackhide__ = True
@@ -1244,19 +1306,19 @@ def raises(expected_exception, *args, **kwargs):
loc.update(kwargs)
#print "raises frame scope: %r" % frame.f_locals
try:
code = py.code.Source(code).compile()
code = _pytest._code.Source(code).compile()
py.builtin.exec_(code, frame.f_globals, loc)
# XXX didn'T mean f_globals == f_locals something special?
# this is destroyed here ...
except expected_exception:
return py.code.ExceptionInfo()
return _pytest._code.ExceptionInfo()
else:
func = args[0]
try:
func(*args[1:], **kwargs)
except expected_exception:
return py.code.ExceptionInfo()
pytest.fail("DID NOT RAISE")
return _pytest._code.ExceptionInfo()
pytest.fail("DID NOT RAISE {0}".format(expected_exception))
class RaisesContext(object):
def __init__(self, expected_exception):
@@ -1264,7 +1326,7 @@ class RaisesContext(object):
self.excinfo = None
def __enter__(self):
self.excinfo = object.__new__(py.code.ExceptionInfo)
self.excinfo = object.__new__(_pytest._code.ExceptionInfo)
return self.excinfo
def __exit__(self, *tp):
@@ -1353,15 +1415,6 @@ class Function(FunctionMixin, pytest.Item, FuncargnamesCompatAttr):
self.ihook.pytest_pyfunc_call(pyfuncitem=self)
def setup(self):
# check if parametrization happend with an empty list
try:
self.callspec._emptyparamspecified
except AttributeError:
pass
else:
fs, lineno = self._getfslineno()
pytest.skip("got empty parameter set, function %s at %s:%d" %(
self.function.__name__, fs, lineno))
super(Function, self).setup()
fillfixtures(self)
@@ -1396,7 +1449,7 @@ class FixtureRequest(FuncargnamesCompatAttr):
self._pyfuncitem = pyfuncitem
#: fixture for which this request is being performed
self.fixturename = None
#: Scope string, one of "function", "cls", "module", "session"
#: Scope string, one of "function", "class", "module", "session"
self.scope = "function"
self._funcargs = {}
self._fixturedefs = {}
@@ -1712,13 +1765,14 @@ class FixtureLookupError(LookupError):
stack.extend(map(lambda x: x.func, self.fixturestack))
msg = self.msg
if msg is not None:
stack = stack[:-1] # the last fixture raise an error, let's present
# it at the requesting side
# the last fixture raise an error, let's present
# it at the requesting side
stack = stack[:-1]
for function in stack:
fspath, lineno = getfslineno(function)
try:
lines, _ = inspect.getsourcelines(get_real_func(function))
except IOError:
except (IOError, IndexError):
error_msg = "file %s, line %s: source code not available"
addline(error_msg % (fspath, lineno+1))
else:
@@ -1970,7 +2024,7 @@ class FixtureManager:
def fail_fixturefunc(fixturefunc, msg):
fs, lineno = getfslineno(fixturefunc)
location = "%s:%s" % (fs, lineno+1)
source = py.code.Source(fixturefunc)
source = _pytest._code.Source(fixturefunc)
pytest.fail(msg + ":\n\n" + str(source.indent()) + "\n" + location,
pytrace=False)
@@ -2103,7 +2157,7 @@ def num_mock_patch_args(function):
def getfuncargnames(function, startindex=None):
# XXX merge with main.py's varnames
#assert not inspect.isclass(function)
#assert not isclass(function)
realfunction = function
while hasattr(realfunction, "__wrapped__"):
realfunction = realfunction.__wrapped__
@@ -2113,14 +2167,14 @@ def getfuncargnames(function, startindex=None):
startindex += num_mock_patch_args(function)
function = realfunction
if isinstance(function, functools.partial):
argnames = inspect.getargs(py.code.getrawcode(function.func))[0]
argnames = inspect.getargs(_pytest._code.getrawcode(function.func))[0]
partial = function
argnames = argnames[len(partial.args):]
if partial.keywords:
for kw in partial.keywords:
argnames.remove(kw)
else:
argnames = inspect.getargs(py.code.getrawcode(function))[0]
argnames = inspect.getargs(_pytest._code.getrawcode(function))[0]
defaults = getattr(function, 'func_defaults',
getattr(function, '__defaults__', None)) or ()
numdefaults = len(defaults)

View File

@@ -1,6 +1,8 @@
""" recording warnings during test function execution. """
import inspect
import _pytest._code
import py
import sys
import warnings
@@ -28,19 +30,48 @@ def pytest_namespace():
'warns': warns}
def deprecated_call(func, *args, **kwargs):
"""Assert that ``func(*args, **kwargs)`` triggers a DeprecationWarning.
"""
wrec = WarningsRecorder()
with wrec:
warnings.simplefilter('always') # ensure all warnings are triggered
ret = func(*args, **kwargs)
def deprecated_call(func=None, *args, **kwargs):
""" assert that calling ``func(*args, **kwargs)`` triggers a
``DeprecationWarning`` or ``PendingDeprecationWarning``.
depwarnings = (DeprecationWarning, PendingDeprecationWarning)
if not any(r.category in depwarnings for r in wrec):
This function can be used as a context manager::
>>> with deprecated_call():
... myobject.deprecated_method()
Note: we cannot use WarningsRecorder here because it is still subject
to the mechanism that prevents warnings of the same type from being
triggered twice for the same module. See #1190.
"""
if not func:
return WarningsChecker(expected_warning=DeprecationWarning)
categories = []
def warn_explicit(message, category, *args, **kwargs):
categories.append(category)
old_warn_explicit(message, category, *args, **kwargs)
def warn(message, category=None, *args, **kwargs):
if isinstance(message, Warning):
categories.append(message.__class__)
else:
categories.append(category)
old_warn(message, category, *args, **kwargs)
old_warn = warnings.warn
old_warn_explicit = warnings.warn_explicit
warnings.warn_explicit = warn_explicit
warnings.warn = warn
try:
ret = func(*args, **kwargs)
finally:
warnings.warn_explicit = old_warn_explicit
warnings.warn = old_warn
deprecation_categories = (DeprecationWarning, PendingDeprecationWarning)
if not any(issubclass(c, deprecation_categories) for c in categories):
__tracebackhide__ = True
raise AssertionError("%r did not produce DeprecationWarning" % (func,))
return ret
@@ -71,7 +102,7 @@ def warns(expected_warning, *args, **kwargs):
loc.update(kwargs)
with wcheck:
code = py.code.Source(code).compile()
code = _pytest._code.Source(code).compile()
py.builtin.exec_(code, frame.f_globals, loc)
else:
func = args[0]
@@ -150,8 +181,8 @@ class WarningsRecorder(object):
self._module.showwarning = showwarning
# allow the same warning to be raised more than once
self._module.simplefilter('always', append=True)
self._module.simplefilter('always')
return self
def __exit__(self, *exc_info):

View File

@@ -5,7 +5,8 @@ from time import time
import py
import pytest
from py._code.code import TerminalRepr
from _pytest._code.code import TerminalRepr, ExceptionInfo
def pytest_namespace():
return {
@@ -151,7 +152,7 @@ class CallInfo:
self.stop = time()
raise
except:
self.excinfo = py.code.ExceptionInfo()
self.excinfo = ExceptionInfo()
self.stop = time()
def __repr__(self):
@@ -177,9 +178,13 @@ class BaseReport(object):
self.__dict__.update(kw)
def toterminal(self, out):
longrepr = self.longrepr
if hasattr(self, 'node'):
out.line(getslaveinfoline(self.node))
longrepr = self.longrepr
if longrepr is None:
return
if hasattr(longrepr, 'toterminal'):
longrepr.toterminal(out)
else:
@@ -211,7 +216,7 @@ def pytest_runtest_makereport(item, call):
outcome = "passed"
longrepr = None
else:
if not isinstance(excinfo, py.code.ExceptionInfo):
if not isinstance(excinfo, ExceptionInfo):
outcome = "failed"
longrepr = excinfo
elif excinfo.errisinstance(pytest.skip.Exception):
@@ -430,7 +435,10 @@ class OutcomeException(Exception):
def __repr__(self):
if self.msg:
return str(self.msg)
val = self.msg
if isinstance(val, bytes):
val = py._builtin._totext(val, errors='replace')
return val
return "<%s instance>" %(self.__class__.__name__,)
__str__ = __repr__
@@ -469,7 +477,7 @@ def skip(msg=""):
skip.Exception = Skipped
def fail(msg="", pytrace=True):
""" explicitely fail an currently-executing test with the given Message.
""" explicitly fail an 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

@@ -5,6 +5,8 @@ import traceback
import py
import pytest
from _pytest.mark import MarkInfo, MarkDecorator
def pytest_addoption(parser):
group = parser.getgroup("general")
@@ -12,6 +14,13 @@ 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")
def pytest_configure(config):
if config.option.runxfail:
old = pytest.xfail
@@ -21,6 +30,11 @@ def pytest_configure(config):
nop.Exception = XFailed
setattr(pytest, "xfail", nop)
config.addinivalue_line("markers",
"skip(reason=None): skip the given test function with an optional reason. "
"Example: skip(reason=\"no way of currently testing this\") skips the "
"test."
)
config.addinivalue_line("markers",
"skipif(condition): skip the given test function if eval(condition) "
"results in a True value. Evaluation happens within the "
@@ -29,27 +43,31 @@ def pytest_configure(config):
"http://pytest.org/latest/skipping.html"
)
config.addinivalue_line("markers",
"xfail(condition, reason=None, run=True, raises=None): mark the the test function "
"as an expected failure if eval(condition) has a True value. "
"Optionally specify a reason for better reporting and run=False if "
"you don't even want to execute the test function. If only specific "
"exception(s) are expected, you can list them in raises, and if the test fails "
"in other ways, it will be reported as a true failure. "
"See http://pytest.org/latest/skipping.html"
"xfail(condition, reason=None, run=True, raises=None, strict=False): "
"mark the the test function as an expected failure if eval(condition) "
"has a True value. Optionally specify a reason for better reporting "
"and run=False if you don't even want to execute the test function. "
"If only specific exception(s) are expected, you can list them in "
"raises, and if the test fails in other ways, it will be reported as "
"a true failure. See http://pytest.org/latest/skipping.html"
)
def pytest_namespace():
return dict(xfail=xfail)
class XFailed(pytest.fail.Exception):
""" raised from an explicit call to pytest.xfail() """
def xfail(reason=""):
""" xfail an executing test or setup functions with the given reason."""
__tracebackhide__ = True
raise XFailed(reason)
xfail.Exception = XFailed
class MarkEvaluator:
def __init__(self, item, name):
self.item = item
@@ -102,7 +120,7 @@ class MarkEvaluator:
return self.result
if self.holder:
d = self._getglobals()
if self.holder.args:
if self.holder.args or 'condition' in self.holder.kwargs:
self.result = False
# "holder" might be a MarkInfo or a MarkDecorator; only
# MarkInfo keeps track of all parameters it received in an
@@ -112,6 +130,8 @@ class MarkEvaluator:
else:
arglist = [(self.holder.args, self.holder.kwargs)]
for args, kwargs in arglist:
if 'condition' in kwargs:
args = (kwargs['condition'],)
for expr in args:
self.expr = expr
if isinstance(expr, py.builtin._basestring):
@@ -147,23 +167,59 @@ class MarkEvaluator:
@pytest.hookimpl(tryfirst=True)
def pytest_runtest_setup(item):
evalskip = MarkEvaluator(item, 'skipif')
if evalskip.istrue():
item._evalskip = evalskip
pytest.skip(evalskip.getexplanation())
# Check if skip or skipif are specified as pytest marks
skipif_info = item.keywords.get('skipif')
if isinstance(skipif_info, (MarkInfo, MarkDecorator)):
eval_skipif = MarkEvaluator(item, 'skipif')
if eval_skipif.istrue():
item._evalskip = eval_skipif
pytest.skip(eval_skipif.getexplanation())
skip_info = item.keywords.get('skip')
if isinstance(skip_info, (MarkInfo, MarkDecorator)):
item._evalskip = True
if 'reason' in skip_info.kwargs:
pytest.skip(skip_info.kwargs['reason'])
elif skip_info.args:
pytest.skip(skip_info.args[0])
else:
pytest.skip("unconditional skip")
item._evalxfail = MarkEvaluator(item, 'xfail')
check_xfail_no_run(item)
@pytest.mark.hookwrapper
def pytest_pyfunc_call(pyfuncitem):
check_xfail_no_run(pyfuncitem)
outcome = yield
passed = outcome.excinfo is None
if passed:
check_strict_xfail(pyfuncitem)
def check_xfail_no_run(item):
"""check xfail(run=False)"""
if not item.config.option.runxfail:
evalxfail = item._evalxfail
if evalxfail.istrue():
if not evalxfail.get('run', True):
pytest.xfail("[NOTRUN] " + evalxfail.getexplanation())
def check_strict_xfail(pyfuncitem):
"""check xfail(strict=True) for the given PASSING test"""
evalxfail = pyfuncitem._evalxfail
if evalxfail.istrue():
strict_default = pyfuncitem.config.getini('xfail_strict')
is_strict_xfail = evalxfail.get('strict', strict_default)
if is_strict_xfail:
del pyfuncitem._evalxfail
explanation = evalxfail.getexplanation()
pytest.fail('[XPASS(strict)] ' + explanation, pytrace=False)
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
outcome = yield
@@ -230,6 +286,9 @@ def pytest_terminal_summary(terminalreporter):
show_skipped(terminalreporter, lines)
elif char == "E":
show_simple(terminalreporter, lines, 'error', "ERROR %s")
elif char == 'p':
show_simple(terminalreporter, lines, 'passed', "PASSED %s")
if lines:
tr._tw.sep("=", "short test summary info")
for line in lines:
@@ -266,9 +325,8 @@ def cached_eval(config, expr, d):
try:
return config._evalcache[expr]
except KeyError:
#import sys
#print >>sys.stderr, ("cache-miss: %r" % expr)
exprcode = py.code.compile(expr, mode="eval")
import _pytest._code
exprcode = _pytest._code.compile(expr, mode="eval")
config._evalcache[expr] = x = eval(exprcode, d)
return x

View File

@@ -22,7 +22,8 @@ def pytest_addoption(parser):
group._addoption('-r',
action="store", dest="reportchars", default=None, metavar="chars",
help="show extra test summary info as specified by chars (f)ailed, "
"(E)error, (s)skipped, (x)failed, (X)passed (w)pytest-warnings (a)all.")
"(E)error, (s)skipped, (x)failed, (X)passed (w)pytest-warnings "
"(p)passed, (P)passed with output, (a)all except pP.")
group._addoption('-l', '--showlocals',
action="store_true", dest="showlocals", default=False,
help="show locals in tracebacks (disabled by default).")
@@ -111,6 +112,7 @@ class TerminalReporter:
self.currentfspath = None
self.reportchars = getreportopt(config)
self.hasmarkup = self._tw.hasmarkup
self.isatty = file.isatty()
def hasopt(self, char):
char = {'xfailed': 'x', 'skipped': 's'}.get(char, char)
@@ -233,7 +235,7 @@ class TerminalReporter:
self.currentfspath = -2
def pytest_collection(self):
if not self.hasmarkup and self.config.option.verbose >= 1:
if not self.isatty and self.config.option.verbose >= 1:
self.write("collecting ... ", bold=True)
def pytest_collectreport(self, report):
@@ -243,7 +245,7 @@ class TerminalReporter:
self.stats.setdefault("skipped", []).append(report)
items = [x for x in report.result if isinstance(x, pytest.Item)]
self._numcollected += len(items)
if self.hasmarkup:
if self.isatty:
#self.write_fspath_result(report.nodeid, 'E')
self.report_collect()
@@ -262,7 +264,7 @@ class TerminalReporter:
line += " / %d errors" % errors
if skipped:
line += " / %d skipped" % skipped
if self.hasmarkup:
if self.isatty:
if final:
line += " \n"
self.rewrite(line, bold=True)
@@ -364,10 +366,11 @@ class TerminalReporter:
EXIT_OK, EXIT_TESTSFAILED, EXIT_INTERRUPTED, EXIT_USAGEERROR,
EXIT_NOTESTSCOLLECTED)
if exitstatus in summary_exit_codes:
self.config.hook.pytest_terminal_summary(terminalreporter=self)
self.summary_errors()
self.summary_failures()
self.summary_warnings()
self.config.hook.pytest_terminal_summary(terminalreporter=self)
self.summary_passes()
if exitstatus == EXIT_INTERRUPTED:
self._report_keyboardinterrupt()
del self._keyboardinterrupt_memo
@@ -389,6 +392,7 @@ class TerminalReporter:
if self.config.option.fulltrace:
excrepr.toterminal(self._tw)
else:
self._tw.line("to show a full traceback on KeyboardInterrupt use --fulltrace", yellow=True)
excrepr.reprcrash.toterminal(self._tw)
def _locationline(self, nodeid, fspath, lineno, domain):
@@ -446,6 +450,18 @@ class TerminalReporter:
self._tw.line("W%s %s %s" % (w.code,
w.fslocation, w.message))
def summary_passes(self):
if self.config.option.tbstyle != "no":
if self.hasopt("P"):
reports = self.getreports('passed')
if not reports:
return
self.write_sep("=", "PASSES")
for rep in reports:
msg = self._getfailureheadline(rep)
self.write_sep("_", msg)
self._outrep_summary(rep)
def summary_failures(self):
if self.config.option.tbstyle != "no":
reports = self.getreports('failed')
@@ -458,7 +474,8 @@ class TerminalReporter:
self.write_line(line)
else:
msg = self._getfailureheadline(rep)
self.write_sep("_", msg)
markup = {'red': True, 'bold': True}
self.write_sep("_", msg, **markup)
self._outrep_summary(rep)
def summary_errors(self):
@@ -543,7 +560,11 @@ def build_summary_stats_line(stats):
if val:
key_name = key_translation.get(key, key)
parts.append("%d %s" % (len(val), key_name))
line = ", ".join(parts)
if parts:
line = ", ".join(parts)
else:
line = "no tests ran"
if 'failed' in stats or 'error' in stats:
color = 'red'

View File

@@ -1,13 +1,12 @@
""" discovery and running of std-library "unittest" style tests. """
from __future__ import absolute_import
import traceback
import sys
import traceback
import pytest
import py
# for transfering markers
import _pytest._code
from _pytest.python import transfer_markers
from _pytest.skipping import MarkEvaluator
@@ -24,9 +23,10 @@ def pytest_pycollect_makeitem(collector, name, obj):
class UnitTestCase(pytest.Class):
nofuncargs = True # marker for fixturemanger.getfixtureinfo()
# to declare that our children do not support funcargs
#
# marker for fixturemanger.getfixtureinfo()
# to declare that our children do not support funcargs
nofuncargs = True
def setup(self):
cls = self.obj
if getattr(cls, '__unittest_skip__', False):
@@ -69,12 +69,26 @@ class TestCaseFunction(pytest.Function):
def setup(self):
self._testcase = self.parent.obj(self.name)
self._fix_unittest_skip_decorator()
self._obj = getattr(self._testcase, self.name)
if hasattr(self._testcase, 'setup_method'):
self._testcase.setup_method(self._obj)
if hasattr(self, "_request"):
self._request._fillfixtures()
def _fix_unittest_skip_decorator(self):
"""
The @unittest.skip decorator calls functools.wraps(self._testcase)
The call to functools.wraps() fails unless self._testcase
has a __name__ attribute. This is usually automatically supplied
if the test is a function or method, but we need to add manually
here.
See issue #1169
"""
if sys.version_info[0] == 2:
setattr(self._testcase, "__name__", self.name)
def teardown(self):
if hasattr(self._testcase, 'teardown_method'):
self._testcase.teardown_method(self._obj)
@@ -86,7 +100,7 @@ class TestCaseFunction(pytest.Function):
# unwrap potential exception info (see twisted trial support below)
rawexcinfo = getattr(rawexcinfo, '_rawexcinfo', rawexcinfo)
try:
excinfo = py.code.ExceptionInfo(rawexcinfo)
excinfo = _pytest._code.ExceptionInfo(rawexcinfo)
except TypeError:
try:
try:
@@ -102,7 +116,7 @@ class TestCaseFunction(pytest.Function):
except KeyboardInterrupt:
raise
except pytest.fail.Exception:
excinfo = py.code.ExceptionInfo()
excinfo = _pytest._code.ExceptionInfo()
self.__dict__.setdefault('_excinfo', []).append(excinfo)
def addError(self, testcase, rawexcinfo):

View File

@@ -573,7 +573,7 @@ class _MultiCall:
# XXX note that the __multicall__ argument is supported only
# for pytest compatibility reasons. It was never officially
# supported there and is explicitely deprecated since 2.8
# supported there and is explicitly deprecated since 2.8
# so we can remove it soon, allowing to avoid the below recursion
# in execute() and simplify/speed up the execute loop.

View File

@@ -1,98 +1,28 @@
environment:
matrix:
# Pre-installed Python versions, which Appveyor may upgrade to
# a later point release.
- PYTHON: "C:\\Python27"
PYTHON_VERSION: "2.7.x" # currently 2.7.9
PYTHON_ARCH: "32"
TESTENV: "py27"
- PYTHON: "C:\\Python27-x64"
PYTHON_VERSION: "2.7.x" # currently 2.7.9
PYTHON_ARCH: "64"
TESTENV: "py27"
- PYTHON: "C:\\Python33"
PYTHON_VERSION: "3.3.x" # currently 3.3.5
PYTHON_ARCH: "32"
TESTENV: "py33"
- PYTHON: "C:\\Python33-x64"
PYTHON_VERSION: "3.3.x" # currently 3.3.5
PYTHON_ARCH: "64"
TESTENV: "py33"
- PYTHON: "C:\\Python34"
PYTHON_VERSION: "3.4.x" # currently 3.4.3
PYTHON_ARCH: "32"
TESTENV: "py34"
- PYTHON: "C:\\Python34-x64"
PYTHON_VERSION: "3.4.x" # currently 3.4.3
PYTHON_ARCH: "64"
TESTENV: "py34"
- PYTHON: "C:\\Python35"
PYTHON_VERSION: "3.5.x" # currently 3.5.0
PYTHON_ARCH: "32"
TESTENV: "py35"
- PYTHON: "C:\\Python35-x64"
PYTHON_VERSION: "3.5.x" # currently 3.5.0
PYTHON_ARCH: "64"
TESTENV: "py35"
# Also test a Python version not pre-installed
# See: https://github.com/ogrisel/python-appveyor-demo/issues/10
- PYTHON: "C:\\Python266"
PYTHON_VERSION: "2.6.6"
PYTHON_ARCH: "32"
TESTENV: "py26"
# xdist testing
- PYTHON: "C:\\Python27"
PYTHON_VERSION: "2.7.x" # currently 2.7.9
PYTHON_ARCH: "32"
TESTENV: "py27-xdist"
- PYTHON: "C:\\Python35"
PYTHON_VERSION: "3.5.x" # currently 3.5.0
PYTHON_ARCH: "32"
TESTENV: "py35-xdist"
COVERALLS_REPO_TOKEN:
secure: 2NJ5Ct55cHJ9WEg3xbSqCuv0rdgzzb6pnzOIG5OkMbTndw3wOBrXntWFoQrXiMFi
# this is pytest's token in coveralls.io, encrypted
# using pytestbot account as detailed here:
# https://www.appveyor.com/docs/build-configuration#secure-variables
install:
- ECHO "Filesystem root:"
- ps: "ls \"C:/\""
- echo Installed Pythons
- dir c:\Python*
- ECHO "Installed SDKs:"
- ps: "ls \"C:/Program Files/Microsoft SDKs/Windows\""
# install pypy using choco (redirect to a file and write to console in case
# choco install returns non-zero, because choco install python.pypy is too
# noisy)
- choco install python.pypy > pypy-inst.log 2>&1 || (type pypy-inst.log & exit /b 1)
- set PATH=C:\tools\pypy\pypy;%PATH% # so tox can find pypy
- echo PyPy installed
- pypy --version
# Install Python (from the official .msi of http://python.org) and pip when
# not already installed.
- ps: if (-not(Test-Path($env:PYTHON))) { & appveyor\install.ps1 }
# Prepend newly installed Python to the PATH of this build (this cannot be
# done from inside the powershell script as it would require to restart
# the parent CMD process).
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
# Check that we have the expected version and architecture for Python
- "python --version"
- "python -c \"import struct; print(struct.calcsize('P') * 8)\""
# Install the build dependencies of the project. If some dependencies contain
# compiled extensions and are not provided as pre-built wheel packages,
# pip will build them from source using the MSVC compiler matching the
# target Python version and architecture
- C:\Python27\python -m pip install tox
- C:\Python35\python -m pip install tox
build: false # Not a C# project, build stuff at the test step instead.
test_script:
# Build the compiled extension and run the project tests
- C:\Python27\python -m tox -e %TESTENV%
- C:\Python35\python -m tox
# coveralls is not in tox's envlist, plus for PRs the secure variable
# is not defined so we have to check for it
- if defined COVERALLS_REPO_TOKEN C:\Python35\python -m tox -e coveralls

View File

@@ -1,180 +0,0 @@
# Sample script to install Python and pip under Windows
# Authors: Olivier Grisel, Jonathan Helmus and Kyle Kastner
# License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/
$MINICONDA_URL = "http://repo.continuum.io/miniconda/"
$BASE_URL = "https://www.python.org/ftp/python/"
$GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py"
$GET_PIP_PATH = "C:\get-pip.py"
function DownloadPython ($python_version, $platform_suffix) {
$webclient = New-Object System.Net.WebClient
$filename = "python-" + $python_version + $platform_suffix + ".msi"
$url = $BASE_URL + $python_version + "/" + $filename
$basedir = $pwd.Path + "\"
$filepath = $basedir + $filename
if (Test-Path $filename) {
Write-Host "Reusing" $filepath
return $filepath
}
# Download and retry up to 3 times in case of network transient errors.
Write-Host "Downloading" $filename "from" $url
$retry_attempts = 2
for($i=0; $i -lt $retry_attempts; $i++){
try {
$webclient.DownloadFile($url, $filepath)
break
}
Catch [Exception]{
Start-Sleep 1
}
}
if (Test-Path $filepath) {
Write-Host "File saved at" $filepath
} else {
# Retry once to get the error message if any at the last try
$webclient.DownloadFile($url, $filepath)
}
return $filepath
}
function InstallPython ($python_version, $architecture, $python_home) {
Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home
if (Test-Path $python_home) {
Write-Host $python_home "already exists, skipping."
return $false
}
if ($architecture -eq "32") {
$platform_suffix = ""
} else {
$platform_suffix = ".amd64"
}
$msipath = DownloadPython $python_version $platform_suffix
Write-Host "Installing" $msipath "to" $python_home
$install_log = $python_home + ".log"
$install_args = "/qn /log $install_log /i $msipath TARGETDIR=$python_home"
$uninstall_args = "/qn /x $msipath"
RunCommand "msiexec.exe" $install_args
if (-not(Test-Path $python_home)) {
Write-Host "Python seems to be installed else-where, reinstalling."
RunCommand "msiexec.exe" $uninstall_args
RunCommand "msiexec.exe" $install_args
}
if (Test-Path $python_home) {
Write-Host "Python $python_version ($architecture) installation complete"
} else {
Write-Host "Failed to install Python in $python_home"
Get-Content -Path $install_log
Exit 1
}
}
function RunCommand ($command, $command_args) {
Write-Host $command $command_args
Start-Process -FilePath $command -ArgumentList $command_args -Wait -Passthru
}
function InstallPip ($python_home) {
$pip_path = $python_home + "\Scripts\pip.exe"
$python_path = $python_home + "\python.exe"
if (-not(Test-Path $pip_path)) {
Write-Host "Installing pip..."
$webclient = New-Object System.Net.WebClient
$webclient.DownloadFile($GET_PIP_URL, $GET_PIP_PATH)
Write-Host "Executing:" $python_path $GET_PIP_PATH
Start-Process -FilePath "$python_path" -ArgumentList "$GET_PIP_PATH" -Wait -Passthru
} else {
Write-Host "pip already installed."
}
}
function DownloadMiniconda ($python_version, $platform_suffix) {
$webclient = New-Object System.Net.WebClient
if ($python_version -eq "3.4") {
$filename = "Miniconda3-3.5.5-Windows-" + $platform_suffix + ".exe"
} else {
$filename = "Miniconda-3.5.5-Windows-" + $platform_suffix + ".exe"
}
$url = $MINICONDA_URL + $filename
$basedir = $pwd.Path + "\"
$filepath = $basedir + $filename
if (Test-Path $filename) {
Write-Host "Reusing" $filepath
return $filepath
}
# Download and retry up to 3 times in case of network transient errors.
Write-Host "Downloading" $filename "from" $url
$retry_attempts = 2
for($i=0; $i -lt $retry_attempts; $i++){
try {
$webclient.DownloadFile($url, $filepath)
break
}
Catch [Exception]{
Start-Sleep 1
}
}
if (Test-Path $filepath) {
Write-Host "File saved at" $filepath
} else {
# Retry once to get the error message if any at the last try
$webclient.DownloadFile($url, $filepath)
}
return $filepath
}
function InstallMiniconda ($python_version, $architecture, $python_home) {
Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home
if (Test-Path $python_home) {
Write-Host $python_home "already exists, skipping."
return $false
}
if ($architecture -eq "32") {
$platform_suffix = "x86"
} else {
$platform_suffix = "x86_64"
}
$filepath = DownloadMiniconda $python_version $platform_suffix
Write-Host "Installing" $filepath "to" $python_home
$install_log = $python_home + ".log"
$args = "/S /D=$python_home"
Write-Host $filepath $args
Start-Process -FilePath $filepath -ArgumentList $args -Wait -Passthru
if (Test-Path $python_home) {
Write-Host "Python $python_version ($architecture) installation complete"
} else {
Write-Host "Failed to install Python in $python_home"
Get-Content -Path $install_log
Exit 1
}
}
function InstallMinicondaPip ($python_home) {
$pip_path = $python_home + "\Scripts\pip.exe"
$conda_path = $python_home + "\Scripts\conda.exe"
if (-not(Test-Path $pip_path)) {
Write-Host "Installing pip..."
$args = "install --yes pip"
Write-Host $conda_path $args
Start-Process -FilePath "$conda_path" -ArgumentList $args -Wait -Passthru
} else {
Write-Host "pip already installed."
}
}
function main () {
InstallPython $env:PYTHON_VERSION $env:PYTHON_ARCH $env:PYTHON
InstallPip $env:PYTHON
}
main

View File

@@ -9,6 +9,7 @@
<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('license') }}">License</a></li>
</ul>
{%- if display_toc %}

View File

@@ -1,5 +1,21 @@
{% extends "!layout.html" %}
{% block header %}
<div align="center" xmlns="http://www.w3.org/1999/html" style="background-color: lightgreen; padding: .5em">
<h4>
Want to help improve pytest? Please
<a href="https://www.indiegogo.com/projects/python-testing-sprint-mid-2016#/">
contribute to
</a>
or
<a href="announce/sprint2016.html">
join
</a>
our upcoming sprint in June 2016!
</h4>
</div>
{{super()}}
{% endblock %}
{% block footer %}
{{ super() }}
<script type="text/javascript">

View File

@@ -1,10 +1,15 @@
<h3>Useful Links</h3>
<ul>
<li>
<a href="https://www.indiegogo.com/projects/python-testing-sprint-mid-2016#/">
<b>Sprint funding campaign</b>
</a>
</li>
<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://github.com/pytest-dev/pytest/">pytest @ GitHub</a></li>
<li><a href="http://pytest.org/latest/plugins_index/index.html">3rd party plugins</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>
<li><a href="http://pytest.org/latest/pytest.pdf">PDF Documentation</a>
</ul>

View File

@@ -5,6 +5,17 @@ Release announcements
.. toctree::
:maxdepth: 2
sprint2016
release-2.9.1
release-2.9.1
release-2.9.0
release-2.8.7
release-2.8.6
release-2.8.5
release-2.8.4
release-2.8.3
release-2.8.2
release-2.7.2
release-2.7.1
release-2.7.0
@@ -36,4 +47,3 @@ Release announcements
release-2.0.2
release-2.0.1
release-2.0.0

View File

@@ -0,0 +1,44 @@
pytest-2.8.2: bug fixes
=======================
pytest is a mature Python testing tool with more than a 1100 tests
against itself, passing on many different interpreters and platforms.
This release is supposed to be drop-in compatible to 2.8.1.
See below for the changes and see docs at:
http://pytest.org
As usual, you can upgrade from pypi via::
pip install -U pytest
Thanks to all who contributed to this release, among them:
Bruno Oliveira
Demian Brecht
Florian Bruhin
Ionel Cristian Mărieș
Raphael Pierzina
Ronny Pfannschmidt
holger krekel
Happy testing,
The py.test Development Team
2.8.2 (compared to 2.7.2)
-----------------------------
- fix #1085: proper handling of encoding errors when passing encoded byte
strings to pytest.parametrize in Python 2.
Thanks Themanwithoutaplan for the report and Bruno Oliveira for the PR.
- fix #1087: handling SystemError when passing empty byte strings to
pytest.parametrize in Python 3.
Thanks Paul Kehrer for the report and Bruno Oliveira for the PR.
- fix #995: fixed internal error when filtering tracebacks where one entry
was generated by an exec() statement.
Thanks Daniel Hahler, Ashley C Straw, Philippe Gauthier and Pavel Savchenko
for contributing and Bruno Oliveira for the PR.

View File

@@ -0,0 +1,59 @@
pytest-2.8.3: bug fixes
=======================
pytest is a mature Python testing tool with more than a 1100 tests
against itself, passing on many different interpreters and platforms.
This release is supposed to be drop-in compatible to 2.8.2.
See below for the changes and see docs at:
http://pytest.org
As usual, you can upgrade from pypi via::
pip install -U pytest
Thanks to all who contributed to this release, among them:
Bruno Oliveira
Florian Bruhin
Gabe Hollombe
Gabriel Reis
Hartmut Goebel
John Vandenberg
Lee Kamentsky
Michael Birtwell
Raphael Pierzina
Ronny Pfannschmidt
William Martin Stewart
Happy testing,
The py.test Development Team
2.8.3 (compared to 2.8.2)
-----------------------------
- fix #1169: add __name__ attribute to testcases in TestCaseFunction to
support the @unittest.skip decorator on functions and methods.
Thanks Lee Kamentsky for the PR.
- fix #1035: collecting tests if test module level obj has __getattr__().
Thanks Suor for the report and Bruno Oliveira / Tom Viner for the PR.
- fix #331: don't collect tests if their failure cannot be reported correctly
e.g. they are a callable instance of a class.
- fix #1133: fixed internal error when filtering tracebacks where one entry
belongs to a file which is no longer available.
Thanks Bruno Oliveira for the PR.
- enhancement made to highlight in red the name of the failing tests so
they stand out in the output.
Thanks Gabriel Reis for the PR.
- add more talks to the documentation
- extend documentation on the --ignore cli option
- use pytest-runner for setuptools integration
- minor fixes for interaction with OS X El Capitan system integrity protection (thanks Florian)

View File

@@ -0,0 +1,52 @@
pytest-2.8.4
============
pytest is a mature Python testing tool with more than a 1100 tests
against itself, passing on many different interpreters and platforms.
This release is supposed to be drop-in compatible to 2.8.2.
See below for the changes and see docs at:
http://pytest.org
As usual, you can upgrade from pypi via::
pip install -U pytest
Thanks to all who contributed to this release, among them:
Bruno Oliveira
Florian Bruhin
Jeff Widman
Mehdy Khoshnoody
Nicholas Chammas
Ronny Pfannschmidt
Tim Chan
Happy testing,
The py.test Development Team
2.8.4 (compared to 2.8.3)
-----------------------------
- fix #1190: ``deprecated_call()`` now works when the deprecated
function has been already called by another test in the same
module. Thanks Mikhail Chernykh for the report and Bruno Oliveira for the
PR.
- fix #1198: ``--pastebin`` option now works on Python 3. Thanks
Mehdy Khoshnoody for the PR.
- fix #1219: ``--pastebin`` now works correctly when captured output contains
non-ascii characters. Thanks Bruno Oliveira for the PR.
- fix #1204: another error when collecting with a nasty __getattr__().
Thanks Florian Bruhin for the PR.
- fix the summary printed when no tests did run.
Thanks Florian Bruhin for the PR.
- a number of documentation modernizations wrt good practices.
Thanks Bruno Oliveira for the PR.

View File

@@ -0,0 +1,39 @@
pytest-2.8.5
============
pytest is a mature Python testing tool with more than a 1100 tests
against itself, passing on many different interpreters and platforms.
This release is supposed to be drop-in compatible to 2.8.4.
See below for the changes and see docs at:
http://pytest.org
As usual, you can upgrade from pypi via::
pip install -U pytest
Thanks to all who contributed to this release, among them:
Alex Gaynor
aselus-hub
Bruno Oliveira
Ronny Pfannschmidt
Happy testing,
The py.test Development Team
2.8.5 (compared to 2.8.4)
-------------------------
- fix #1243: fixed issue where class attributes injected during collection could break pytest.
PR by Alexei Kozlenok, thanks Ronny Pfannschmidt and Bruno Oliveira for the review and help.
- fix #1074: precompute junitxml chunks instead of storing the whole tree in objects
Thanks Bruno Oliveira for the report and Ronny Pfannschmidt for the PR
- fix #1238: fix ``pytest.deprecated_call()`` receiving multiple arguments
(Regression introduced in 2.8.4). Thanks Alex Gaynor for the report and
Bruno Oliveira for the PR.

View File

@@ -0,0 +1,67 @@
pytest-2.8.6
============
pytest is a mature Python testing tool with more than a 1100 tests
against itself, passing on many different interpreters and platforms.
This release is supposed to be drop-in compatible to 2.8.5.
See below for the changes and see docs at:
http://pytest.org
As usual, you can upgrade from pypi via::
pip install -U pytest
Thanks to all who contributed to this release, among them:
AMiT Kumar
Bruno Oliveira
Erik M. Bray
Florian Bruhin
Georgy Dyuldin
Jeff Widman
Kartik Singhal
Loïc Estève
Manu Phatak
Peter Demin
Rick van Hattem
Ronny Pfannschmidt
Ulrich Petri
foxx
Happy testing,
The py.test Development Team
2.8.6 (compared to 2.8.5)
-------------------------
- fix #1259: allow for double nodeids in junitxml,
this was a regression failing plugins combinations
like pytest-pep8 + pytest-flakes
- Workaround for exception that occurs in pyreadline when using
``--pdb`` with standard I/O capture enabled.
Thanks Erik M. Bray for the PR.
- fix #900: Better error message in case the target of a ``monkeypatch`` call
raises an ``ImportError``.
- fix #1292: monkeypatch calls (setattr, setenv, etc.) are now O(1).
Thanks David R. MacIver for the report and Bruno Oliveira for the PR.
- fix #1223: captured stdout and stderr are now properly displayed before
entering pdb when ``--pdb`` is used instead of being thrown away.
Thanks Cal Leeming for the PR.
- fix #1305: pytest warnings emitted during ``pytest_terminal_summary`` are now
properly displayed.
Thanks Ionel Maries Cristian for the report and Bruno Oliveira for the PR.
- fix #628: fixed internal UnicodeDecodeError when doctests contain unicode.
Thanks Jason R. Coombs for the report and Bruno Oliveira for the PR.
- fix #1334: Add captured stdout to jUnit XML report on setup error.
Thanks Georgy Dyuldin for the PR.

View File

@@ -0,0 +1,31 @@
pytest-2.8.7
============
This is a hotfix release to solve a regression
in the builtin monkeypatch plugin that got introduced in 2.8.6.
pytest is a mature Python testing tool with more than a 1100 tests
against itself, passing on many different interpreters and platforms.
This release is supposed to be drop-in compatible to 2.8.5.
See below for the changes and see docs at:
http://pytest.org
As usual, you can upgrade from pypi via::
pip install -U pytest
Thanks to all who contributed to this release, among them:
Ronny Pfannschmidt
Happy testing,
The py.test Development Team
2.8.7 (compared to 2.8.6)
-------------------------
- fix #1338: use predictable object resolution for monkeypatch

View File

@@ -0,0 +1,159 @@
pytest-2.9.0
============
pytest is a mature Python testing tool with more than a 1100 tests
against itself, passing on many different interpreters and platforms.
See below for the changes and see docs at:
http://pytest.org
As usual, you can upgrade from pypi via::
pip install -U pytest
Thanks to all who contributed to this release, among them:
Anatoly Bubenkov
Bruno Oliveira
Buck Golemon
David Vierra
Florian Bruhin
Galaczi Endre
Georgy Dyuldin
Lukas Bednar
Luke Murphy
Marcin Biernat
Matt Williams
Michael Aquilina
Raphael Pierzina
Ronny Pfannschmidt
Ryan Wooden
Tiemo Kieft
TomV
holger krekel
jab
Happy testing,
The py.test Development Team
2.9.0 (compared to 2.8.7)
-------------------------
**New Features**
* New ``pytest.mark.skip`` mark, which unconditionally skips marked tests.
Thanks `@MichaelAquilina`_ for the complete PR (`#1040`_).
* ``--doctest-glob`` may now be passed multiple times in the command-line.
Thanks `@jab`_ and `@nicoddemus`_ for the PR.
* New ``-rp`` and ``-rP`` reporting options give the summary and full output
of passing tests, respectively. Thanks to `@codewarrior0`_ for the PR.
* ``pytest.mark.xfail`` now has a ``strict`` option which makes ``XPASS``
tests to fail the test suite, defaulting to ``False``. There's also a
``xfail_strict`` ini option that can be used to configure it project-wise.
Thanks `@rabbbit`_ for the request and `@nicoddemus`_ for the PR (`#1355`_).
* ``Parser.addini`` now supports options of type ``bool``. Thanks
`@nicoddemus`_ for the PR.
* New ``ALLOW_BYTES`` doctest option strips ``b`` prefixes from byte strings
in doctest output (similar to ``ALLOW_UNICODE``).
Thanks `@jaraco`_ for the request and `@nicoddemus`_ for the PR (`#1287`_).
* give a hint on KeyboardInterrupt to use the --fulltrace option to show the errors,
this fixes `#1366`_.
Thanks to `@hpk42`_ for the report and `@RonnyPfannschmidt`_ for the PR.
* catch IndexError exceptions when getting exception source location. This fixes
pytest internal error for dynamically generated code (fixtures and tests)
where source lines are fake by intention
**Changes**
* **Important**: `py.code <https://pylib.readthedocs.io/en/latest/code.html>`_ has been
merged into the ``pytest`` repository as ``pytest._code``. This decision
was made because ``py.code`` had very few uses outside ``pytest`` and the
fact that it was in a different repository made it difficult to fix bugs on
its code in a timely manner. The team hopes with this to be able to better
refactor out and improve that code.
This change shouldn't affect users, but it is useful to let users aware
if they encounter any strange behavior.
Keep in mind that the code for ``pytest._code`` is **private** and
**experimental**, so you definitely should not import it explicitly!
Please note that the original ``py.code`` is still available in
`pylib <https://pylib.readthedocs.io>`_.
* ``pytest_enter_pdb`` now optionally receives the pytest config object.
Thanks `@nicoddemus`_ for the PR.
* Removed code and documentation for Python 2.5 or lower versions,
including removal of the obsolete ``_pytest.assertion.oldinterpret`` module.
Thanks `@nicoddemus`_ for the PR (`#1226`_).
* Comparisons now always show up in full when ``CI`` or ``BUILD_NUMBER`` is
found in the environment, even when -vv isn't used.
Thanks `@The-Compiler`_ for the PR.
* ``--lf`` and ``--ff`` now support long names: ``--last-failed`` and
``--failed-first`` respectively.
Thanks `@MichaelAquilina`_ for the PR.
* Added expected exceptions to pytest.raises fail message
* Collection only displays progress ("collecting X items") when in a terminal.
This avoids cluttering the output when using ``--color=yes`` to obtain
colors in CI integrations systems (`#1397`_).
**Bug Fixes**
* The ``-s`` and ``-c`` options should now work under ``xdist``;
``Config.fromdictargs`` now represents its input much more faithfully.
Thanks to `@bukzor`_ for the complete PR (`#680`_).
* Fix (`#1290`_): support Python 3.5's ``@`` operator in assertion rewriting.
Thanks `@Shinkenjoe`_ for report with test case and `@tomviner`_ for the PR.
* Fix formatting utf-8 explanation messages (`#1379`_).
Thanks `@biern`_ for the PR.
* Fix `traceback style docs`_ to describe all of the available options
(auto/long/short/line/native/no), with `auto` being the default since v2.6.
Thanks `@hackebrot`_ for the PR.
* Fix (`#1422`_): junit record_xml_property doesn't allow multiple records
with same name.
.. _`traceback style docs`: https://pytest.org/latest/usage.html#modifying-python-traceback-printing
.. _#1422: https://github.com/pytest-dev/pytest/issues/1422
.. _#1379: https://github.com/pytest-dev/pytest/issues/1379
.. _#1366: https://github.com/pytest-dev/pytest/issues/1366
.. _#1040: https://github.com/pytest-dev/pytest/pull/1040
.. _#680: https://github.com/pytest-dev/pytest/issues/680
.. _#1287: https://github.com/pytest-dev/pytest/pull/1287
.. _#1226: https://github.com/pytest-dev/pytest/pull/1226
.. _#1290: https://github.com/pytest-dev/pytest/pull/1290
.. _#1355: https://github.com/pytest-dev/pytest/pull/1355
.. _#1397: https://github.com/pytest-dev/pytest/issues/1397
.. _@biern: https://github.com/biern
.. _@MichaelAquilina: https://github.com/MichaelAquilina
.. _@bukzor: https://github.com/bukzor
.. _@hpk42: https://github.com/hpk42
.. _@nicoddemus: https://github.com/nicoddemus
.. _@jab: https://github.com/jab
.. _@codewarrior0: https://github.com/codewarrior0
.. _@jaraco: https://github.com/jaraco
.. _@The-Compiler: https://github.com/The-Compiler
.. _@Shinkenjoe: https://github.com/Shinkenjoe
.. _@tomviner: https://github.com/tomviner
.. _@RonnyPfannschmidt: https://github.com/RonnyPfannschmidt
.. _@rabbbit: https://github.com/rabbbit
.. _@hackebrot: https://github.com/hackebrot

View File

@@ -0,0 +1,65 @@
pytest-2.9.1
============
pytest is a mature Python testing tool with more than a 1100 tests
against itself, passing on many different interpreters and platforms.
See below for the changes and see docs at:
http://pytest.org
As usual, you can upgrade from pypi via::
pip install -U pytest
Thanks to all who contributed to this release, among them:
Bruno Oliveira
Daniel Hahler
Dmitry Malinovsky
Florian Bruhin
Floris Bruynooghe
Matt Bachmann
Ronny Pfannschmidt
TomV
Vladimir Bolshakov
Zearin
palaviv
Happy testing,
The py.test Development Team
2.9.1 (compared to 2.9.0)
-------------------------
**Bug Fixes**
* Improve error message when a plugin fails to load.
Thanks `@nicoddemus`_ for the PR.
* Fix (`#1178 <https://github.com/pytest-dev/pytest/issues/1178>`_):
``pytest.fail`` with non-ascii characters raises an internal pytest error.
Thanks `@nicoddemus`_ for the PR.
* Fix (`#469`_): junit parses report.nodeid incorrectly, when params IDs
contain ``::``. Thanks `@tomviner`_ for the PR (`#1431`_).
* Fix (`#578 <https://github.com/pytest-dev/pytest/issues/578>`_): SyntaxErrors
containing non-ascii lines at the point of failure generated an internal
py.test error.
Thanks `@asottile`_ for the report and `@nicoddemus`_ for the PR.
* Fix (`#1437`_): When passing in a bytestring regex pattern to parameterize
attempt to decode it as utf-8 ignoring errors.
* Fix (`#649`_): parametrized test nodes cannot be specified to run on the command line.
.. _#1437: https://github.com/pytest-dev/pytest/issues/1437
.. _#469: https://github.com/pytest-dev/pytest/issues/469
.. _#1431: https://github.com/pytest-dev/pytest/pull/1431
.. _#649: https://github.com/pytest-dev/pytest/issues/649
.. _@asottile: https://github.com/asottile

View File

@@ -0,0 +1,73 @@
pytest-2.9.1
============
pytest is a mature Python testing tool with more than a 1100 tests
against itself, passing on many different interpreters and platforms.
See below for the changes and see docs at:
http://pytest.org
As usual, you can upgrade from pypi via::
pip install -U pytest
Thanks to all who contributed to this release, among them:
Adam Chainz
Benjamin Dopplinger
Bruno Oliveira
Florian Bruhin
John Towler
Martin Prusse
Meng Jue
MengJueM
Omar Kohl
Quentin Pradet
Ronny Pfannschmidt
Thomas Güttler
TomV
Tyler Goodlet
Happy testing,
The py.test Development Team
2.9.2 (compared to 2.9.1)
---------------------------
**Bug Fixes**
* fix `#510`_: skip tests where one parameterize dimension was empty
thanks Alex Stapleton for the Report and `@RonnyPfannschmidt`_ for the PR
* Fix Xfail does not work with condition keyword argument.
Thanks `@astraw38`_ for reporting the issue (`#1496`_) and `@tomviner`_
for PR the (`#1524`_).
* Fix win32 path issue when puttinging custom config file with absolute path
in ``pytest.main("-c your_absolute_path")``.
* Fix maximum recursion depth detection when raised error class is not aware
of unicode/encoded bytes.
Thanks `@prusse-martin`_ for the PR (`#1506`_).
* Fix ``pytest.mark.skip`` mark when used in strict mode.
Thanks `@pquentin`_ for the PR and `@RonnyPfannschmidt`_ for
showing how to fix the bug.
* Minor improvements and fixes to the documentation.
Thanks `@omarkohl`_ for the PR.
* Fix ``--fixtures`` to show all fixture definitions as opposed to just
one per fixture name.
Thanks to `@hackebrot`_ for the PR.
.. _#510: https://github.com/pytest-dev/pytest/issues/510
.. _#1506: https://github.com/pytest-dev/pytest/pull/1506
.. _#1496: https://github.com/pytest-dev/pytest/issue/1496
.. _#1524: https://github.com/pytest-dev/pytest/issue/1524
.. _@prusse-martin: https://github.com/prusse-martin
.. _@astraw38: https://github.com/astraw38

View File

@@ -0,0 +1,105 @@
python testing sprint June 20th-26th 2016
======================================================
.. image:: ../img/freiburg2.jpg
:width: 400
The pytest core group is heading towards the biggest sprint
in its history, to take place in the black forest town Freiburg
in Germany. As of February 2016 we have started a `funding
campaign on Indiegogo to cover expenses
<http://igg.me/at/pytest-sprint/x/4034848>`_ The page also mentions
some preliminary topics:
- improving pytest-xdist test scheduling to take into account
fixture setups and explicit user hints.
- provide info on fixture dependencies during --collect-only
- tying pytest-xdist to tox so that you can do "py.test -e py34"
to run tests in a particular tox-managed virtualenv. Also
look into making pytest-xdist use tox environments on
remote ssh-sides so that remote dependency management becomes
easier.
- refactoring the fixture system so more people understand it :)
- integrating PyUnit setup methods as autouse fixtures.
possibly adding ways to influence ordering of same-scoped
fixtures (so you can make a choice of which fixtures come
before others)
- fixing bugs and issues from the tracker, really an endless source :)
Participants
--------------
Here are preliminary participants who said they are likely to come,
given some expenses funding::
Anatoly Bubenkoff, Netherlands
Andreas Pelme, Personalkollen, Sweden
Anthony Wang, Splunk, US
Brianna Laugher, Australia
Bruno Oliveira, Brazil
Danielle Jenkins, Splunk, US
Dave Hunt, UK
Florian Bruhin, Switzerland
Floris Bruynooghe, Cobe.io, UK
Holger Krekel, merlinux, Germany
Oliver Bestwalter, Avira, Germany
Omar Kohl, Germany
Raphael Pierzina, FanDuel, UK
Tom Viner, UK
<your name here?>
Other contributors and experienced newcomers are invited to join as well
but please send a mail to the pytest-dev mailing list if you intend to
do so somewhat soon, also how much funding you need if so. And if you
are working for a company and using pytest heavily you are welcome to
join and we encourage your company to provide some funding for the
sprint. They may see it, and rightfully so, as a very cheap and deep
training which brings you together with the experts in the field :)
Sprint organisation, schedule
-------------------------------
tentative schedule:
- 19/20th arrival in Freiburg
- 20th social get together, initial hacking
- 21/22th full sprint days
- 23rd break day, hiking
- 24/25th full sprint days
- 26th departure
We might adjust according to weather to make sure that if
we do some hiking or excursion we'll have good weather.
Freiburg is one of the sunniest places in Germany so
it shouldn't be too much of a constraint.
Accomodation
----------------
We'll see to arrange for renting a flat with multiple
beds/rooms. Hotels are usually below 100 per night.
The earlier we book the better.
Money / funding
---------------
The Indiegogo campaign asks for 11000 USD which should cover
the costs for flights and accomodation, renting a sprint place
and maybe a bit of food as well.
If your organisation wants to support the sprint but prefers
to give money according to an invoice, get in contact with
holger at http://merlinux.eu who can invoice your organisation
properly.
If we have excess money we'll use for further sprint/travel
funding for pytest/tox contributors.

View File

@@ -26,7 +26,7 @@ you will see the return value of the function call::
$ py.test test_assert1.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items
@@ -81,13 +81,10 @@ and if you need to have access to the actual exception info you may use::
f()
assert 'maximum recursion' in str(excinfo.value)
``excinfo`` is a `py.code.ExceptionInfo`_ instance, which is a wrapper around
``excinfo`` is a ``ExceptionInfo`` instance, which is a wrapper around
the actual exception raised. The main attributes of interest are
``.type``, ``.value`` and ``.traceback``.
.. _py.code.ExceptionInfo:
http://pylib.readthedocs.org/en/latest/code.html#py-code-exceptioninfo
If you want to write test code that works on Python 2.4 as well,
you may also use two other ways to test for an expected exception::
@@ -146,7 +143,7 @@ if you run this module::
$ py.test test_assert2.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items
@@ -159,7 +156,7 @@ if you run this module::
set1 = set("1308")
set2 = set("8035")
> assert set1 == set2
E assert set(['0', '1', '3', '8']) == set(['0', '3', '5', '8'])
E assert {'0', '1', '3', '8'} == {'0', '3', '5', '8'}
E Extra items in the left set:
E '1'
E Extra items in the right set:
@@ -192,8 +189,8 @@ provides an alternative explanation for ``Foo`` objects::
from test_foocompare import Foo
def pytest_assertrepr_compare(op, left, right):
if isinstance(left, Foo) and isinstance(right, Foo) and op == "==":
return ['Comparing Foo instances:',
' vals: %s != %s' % (left.val, right.val)]
return ['Comparing Foo instances:',
' vals: %s != %s' % (left.val, right.val)]
now, given this test module::
@@ -243,10 +240,9 @@ recording the intermediate values. Which technique is used depends on the
location of the assert, ``pytest`` configuration, and Python version being used
to run ``pytest``.
By default, if the Python version is greater than or equal to 2.6, ``pytest``
rewrites assert statements in test modules. Rewritten assert statements put
introspection information into the assertion failure message. ``pytest`` only
rewrites test modules directly discovered by its test collection process, so
By default, ``pytest`` rewrites assert statements in test modules.
Rewritten assert statements put introspection information into the assertion failure message.
``pytest`` only rewrites test modules directly discovered by its test collection process, so
asserts in supporting modules which are not themselves test modules will not be
rewritten.

View File

@@ -5,7 +5,7 @@ Setting up bash completion
==========================
When using bash as your shell, ``pytest`` can use argcomplete
(https://argcomplete.readthedocs.org/) for auto-completion.
(https://argcomplete.readthedocs.io/) for auto-completion.
For this ``argcomplete`` needs to be installed **and** enabled.
Install argcomplete using::

View File

@@ -79,8 +79,8 @@ You can ask for available builtin or project-custom
cache.get(key, default)
cache.set(key, value)
Keys must be strings not containing a "/" separator. Add a unique identifier
(such as plugin/app name) to avoid clashes with other cache users.
Keys must be a ``/`` separated value, where the first part is usually the
name of your plugin or application to avoid clashes with other cache users.
Values can be any object handled by the json stdlib module.
capsys
@@ -131,4 +131,4 @@ You can ask for available builtin or project-custom
directory. The returned object is a `py.path.local`_
path object.
in 0.12 seconds
no tests ran in 0.12 seconds

View File

@@ -5,19 +5,20 @@ Cache: working with cross-testrun state
.. warning::
The functionality of this core plugin was previosuly distributed
The functionality of this core plugin was previously distributed
as a third party plugin named ``pytest-cache``. The core plugin
is compatible regarding command line options and API usage except that you
can only store/receive data between test runs that is json-serializable.
Usage
---------
The plugin provides two command line options to rerun failures from the
last ``py.test`` invocation:
* ``--lf`` (last failures) - to only re-run the failures.
* ``--ff`` (failures first) - to run the failures first and then the rest of
* ``--lf``, ``--last-failed`` - to only re-run the failures.
* ``--ff``, ``--failed-first`` - to run the failures first and then the rest of
the tests.
For cleanup (usually not needed), a ``--cache-clear`` option allows to remove
@@ -26,6 +27,12 @@ all cross-session cache contents ahead of a test run.
Other plugins may access the `config.cache`_ object to set/get
**json encodable** values between ``py.test`` invocations.
.. note::
This plugin is enabled by default, but can be disabled if needed: see
:ref:`cmdunregister` (the internal name for this plugin is
``cacheprovider``).
Rerunning only failures or failures first
-----------------------------------------------
@@ -73,7 +80,7 @@ If you then run it with ``--lf``::
$ py.test --lf
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
run-last-failure: rerun last 2 failures
rootdir: $REGENDOC_TMPDIR, inifile:
collected 50 items
@@ -114,7 +121,7 @@ of ``FF`` and dots)::
$ py.test --ff
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
run-last-failure: rerun last 2 failures first
rootdir: $REGENDOC_TMPDIR, inifile:
collected 50 items
@@ -219,7 +226,7 @@ You can always peek at the content of the cache using the
$ py.test --cache-clear
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items
@@ -255,7 +262,7 @@ than speed.
config.cache API
------------------
The `config.cache`` object allows other plugins,
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

View File

@@ -64,7 +64,7 @@ of the failing function and hide the other one::
$ py.test
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 2 items

View File

@@ -4,4 +4,4 @@
Changelog history
=================================
.. include:: ../../CHANGELOG
.. include:: ../../CHANGELOG.rst

View File

@@ -13,19 +13,25 @@ Full pytest documentation
overview
apiref
example/index
plugins
monkeypatch
tmpdir
capture
recwarn
cache
plugins
contributing
plugins_index/index
talks
.. only:: html
.. toctree::
funcarg_compare
announce/index
.. only:: html
.. toctree::
:hidden:

View File

@@ -162,7 +162,7 @@ Builtin configuration file options
.. versionadded:: 2.8
Sets list of directories that should be searched for tests when
no specific directories or files are given in the command line 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.

View File

@@ -8,7 +8,10 @@ can change the pattern by issuing::
py.test --doctest-glob='*.rst'
on the command line. You can also trigger running of doctests
on the command line. Since version ``2.9``, ``--doctest-glob``
can be given multiple times in the command-line.
You can also trigger running of doctests
from docstrings in all python modules (including regular
python test modules)::
@@ -46,7 +49,7 @@ then you can just invoke ``py.test`` without command line options::
$ py.test
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
collected 1 items
@@ -67,19 +70,32 @@ when executing text doctest files.
The standard ``doctest`` module provides some setting flags to configure the
strictness of doctest tests. In py.test You can enable those flags those flags
using the configuration file. To make pytest ignore trailing whitespaces and
ignore lengthy exception stack traces you can just write::
ignore lengthy exception stack traces you can just write:
.. code-block:: ini
# content of pytest.ini
[pytest]
doctest_optionflags= NORMALIZE_WHITESPACE IGNORE_EXCEPTION_DETAIL
py.test also introduces new options to allow doctests to run in Python 2 and
Python 3 unchanged:
py.test also introduces a new ``ALLOW_UNICODE`` option flag: when enabled, the
``u`` prefix is stripped from unicode strings in expected doctest output. This
allows doctests which use unicode to run in Python 2 and 3 unchanged.
* ``ALLOW_UNICODE``: when enabled, the ``u`` prefix is stripped from unicode
strings in expected doctest output.
As with any other option flag, this flag can be enabled in ``pytest.ini`` using
the ``doctest_optionflags`` ini option or by an inline comment in the doc test
* ``ALLOW_BYTES``: when enabled, the ``b`` prefix is stripped from byte strings
in expected doctest output.
As with any other option flag, these flags can be enabled in ``pytest.ini`` using
the ``doctest_optionflags`` ini option:
.. code-block:: ini
[pytest]
doctest_optionflags = ALLOW_UNICODE ALLOW_BYTES
Alternatively, it can be enabled by an inline comment in the doc test
itself::
# content of example.rst

View File

@@ -1,4 +1,5 @@
from pytest import raises
import _pytest._code
import py
def otherfunc(a,b):
@@ -159,7 +160,7 @@ def test_dynamic_compile_shows_nicely():
src = 'def foo():\n assert 1 == 0\n'
name = 'abc-123'
module = py.std.imp.new_module(name)
code = py.code.compile(src, name, 'exec')
code = _pytest._code.compile(src, name, 'exec')
py.builtin.exec_(code, module.__dict__)
py.std.sys.modules[name] = module
module.foo()

View File

@@ -31,7 +31,7 @@ You can then restrict a test run to only run tests marked with ``webtest``::
$ py.test -v -m webtest
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 4 items
@@ -45,7 +45,7 @@ Or the inverse, running all tests except the webtest ones::
$ py.test -v -m "not webtest"
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 4 items
@@ -66,7 +66,7 @@ tests based on their module, class, method, or function name::
$ py.test -v test_server.py::TestClass::test_method
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 5 items
@@ -79,7 +79,7 @@ You can also select on the class::
$ py.test -v test_server.py::TestClass
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 4 items
@@ -92,7 +92,7 @@ Or select multiple nodes::
$ py.test -v test_server.py::TestClass test_server.py::test_send_http
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 8 items
@@ -130,7 +130,7 @@ select tests based on their names::
$ py.test -v -k http # running with the above defined example module
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 4 items
@@ -144,7 +144,7 @@ And you can also run all tests except the ones that match the keyword::
$ py.test -k "not send_http" -v
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 4 items
@@ -160,7 +160,7 @@ Or to select "http" and "quick" tests::
$ py.test -k "http or quick" -v
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 4 items
@@ -201,9 +201,11 @@ You can ask which markers exist for your test suite - the list includes our just
$ py.test --markers
@pytest.mark.webtest: mark a test as a webtest.
@pytest.mark.skip(reason=None): skip the given test function with an optional reason. Example: skip(reason="no way of currently testing this") skips the test.
@pytest.mark.skipif(condition): skip the given test function if eval(condition) results in a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform. see http://pytest.org/latest/skipping.html
@pytest.mark.xfail(condition, reason=None, run=True, raises=None): mark the the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See http://pytest.org/latest/skipping.html
@pytest.mark.xfail(condition, reason=None, run=True, raises=None, strict=False): mark the the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See http://pytest.org/latest/skipping.html
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see http://pytest.org/latest/parametrize.html for more info and examples.
@@ -219,7 +221,7 @@ For an example on how to add and work with markers from a plugin, see
.. note::
It is recommended to explicitely register markers so that:
It is recommended to explicitly register markers so that:
* there is one place in your test suite defining your markers
@@ -234,8 +236,8 @@ For an example on how to add and work with markers from a plugin, see
Marking whole classes or modules
----------------------------------------------------
If you are programming with Python 2.6 or later you may use ``pytest.mark``
decorators with classes to apply markers to all of its test methods::
You may use ``pytest.mark`` decorators with classes to apply markers to all of
its test methods::
# content of test_mark_classlevel.py
import pytest
@@ -350,7 +352,7 @@ the test needs::
$ py.test -E stage2
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items
@@ -362,7 +364,7 @@ and here is one that specifies exactly the environment needed::
$ py.test -E stage1
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items
@@ -375,9 +377,11 @@ The ``--markers`` option always gives you a list of available markers::
$ py.test --markers
@pytest.mark.env(name): mark test to run only on named environment
@pytest.mark.skip(reason=None): skip the given test function with an optional reason. Example: skip(reason="no way of currently testing this") skips the test.
@pytest.mark.skipif(condition): skip the given test function if eval(condition) results in a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform. see http://pytest.org/latest/skipping.html
@pytest.mark.xfail(condition, reason=None, run=True, raises=None): mark the the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See http://pytest.org/latest/skipping.html
@pytest.mark.xfail(condition, reason=None, run=True, raises=None, strict=False): mark the the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See http://pytest.org/latest/skipping.html
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see http://pytest.org/latest/parametrize.html for more info and examples.
@@ -436,7 +440,7 @@ marking platform specific tests with pytest
.. regendoc:wipe
Consider you have a test suite which marks tests for particular platforms,
namely ``pytest.mark.osx``, ``pytest.mark.win32`` etc. and you
namely ``pytest.mark.darwin``, ``pytest.mark.win32`` etc. and you
also have tests that run on all platforms and have no specific
marker. If you now want to have a way to only run the tests
for your particular platform, you could use the following plugin::
@@ -446,7 +450,7 @@ for your particular platform, you could use the following plugin::
import sys
import pytest
ALL = set("osx linux2 win32".split())
ALL = set("darwin linux2 win32".split())
def pytest_runtest_setup(item):
if isinstance(item, item.Function):
@@ -462,7 +466,7 @@ Let's do a little test file to show how this looks like::
import pytest
@pytest.mark.osx
@pytest.mark.darwin
def test_if_apple_is_evil():
pass
@@ -481,7 +485,7 @@ then you will see two test skipped and two executed tests as expected::
$ py.test -rs # this option reports skip reasons
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 4 items
@@ -495,7 +499,7 @@ Note that if you specify a platform via the marker-command line option like this
$ py.test -m linux2
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 4 items
@@ -547,7 +551,7 @@ We can now use the ``-m option`` to select one set::
$ py.test -m interface --tb=short
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 4 items
@@ -569,7 +573,7 @@ or to select both "event" and "interface" tests::
$ py.test -m "interface or event" --tb=short
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 4 items

View File

@@ -4,6 +4,7 @@ serialization via the pickle module.
"""
import py
import pytest
import _pytest._code
pythonlist = ['python2.6', 'python2.7', 'python3.3']
@pytest.fixture(params=pythonlist)
@@ -23,7 +24,7 @@ class Python:
self.picklefile = picklefile
def dumps(self, obj):
dumpfile = self.picklefile.dirpath("dump.py")
dumpfile.write(py.code.Source("""
dumpfile.write(_pytest._code.Source("""
import pickle
f = open(%r, 'wb')
s = pickle.dump(%r, f, protocol=2)
@@ -33,7 +34,7 @@ class Python:
def load_and_is_true(self, expression):
loadfile = self.picklefile.dirpath("load.py")
loadfile.write(py.code.Source("""
loadfile.write(_pytest._code.Source("""
import pickle
f = open(%r, 'rb')
obj = pickle.load(f)

View File

@@ -27,11 +27,11 @@ now execute the test specification::
nonpython $ py.test test_simple.yml
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
collected 2 items
test_simple.yml F.
test_simple.yml .F
======= FAILURES ========
_______ usecase: hello ________
@@ -59,13 +59,13 @@ consulted when reporting in ``verbose`` mode::
nonpython $ py.test -v
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
cachedir: .cache
rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
collecting ... collected 2 items
test_simple.yml::hello FAILED
test_simple.yml::ok PASSED
test_simple.yml::hello FAILED
======= FAILURES ========
_______ usecase: hello ________
@@ -81,11 +81,11 @@ interesting to just look at the collection tree::
nonpython $ py.test --collect-only
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
collected 2 items
<YamlFile 'test_simple.yml'>
<YamlItem 'hello'>
<YamlItem 'ok'>
<YamlItem 'hello'>
======= in 0.12 seconds ========
======= no tests ran in 0.12 seconds ========

View File

@@ -130,7 +130,7 @@ objects, they are still using the default pytest representation::
$ py.test test_time.py --collect-only
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 6 items
<Module 'test_time.py'>
@@ -141,7 +141,7 @@ objects, they are still using the default pytest representation::
<Function 'test_timedistance_v2[20011212-20011211-expected0]'>
<Function 'test_timedistance_v2[20011211-20011212-expected1]'>
======= in 0.12 seconds ========
======= no tests ran in 0.12 seconds ========
A quick port of "testscenarios"
------------------------------------
@@ -181,7 +181,7 @@ this is a fully self-contained example which you can run with::
$ py.test test_scenarios.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 4 items
@@ -194,7 +194,7 @@ If you just collect tests you'll also nicely see 'advanced' and 'basic' as varia
$ py.test --collect-only test_scenarios.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 4 items
<Module 'test_scenarios.py'>
@@ -205,7 +205,7 @@ If you just collect tests you'll also nicely see 'advanced' and 'basic' as varia
<Function 'test_demo1[advanced]'>
<Function 'test_demo2[advanced]'>
======= in 0.12 seconds ========
======= no tests ran in 0.12 seconds ========
Note that we told ``metafunc.parametrize()`` that your scenario values
should be considered class-scoped. With pytest-2.3 this leads to a
@@ -259,14 +259,14 @@ Let's first see how it looks like at collection time::
$ py.test test_backends.py --collect-only
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 2 items
<Module 'test_backends.py'>
<Function 'test_db_initialized[d1]'>
<Function 'test_db_initialized[d2]'>
======= in 0.12 seconds ========
======= no tests ran in 0.12 seconds ========
And then when we run the test::
@@ -320,25 +320,25 @@ The result of this test will be successful::
$ py.test test_indirect_list.py --collect-only
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items
<Module 'test_indirect_list.py'>
<Function 'test_indirect[a-b]'>
======= in 0.12 seconds ========
======= no tests ran in 0.12 seconds ========
.. regendoc:wipe
Parametrizing test methods through per-class configuration
--------------------------------------------------------------
.. _`unittest parameterizer`: http://code.google.com/p/unittest-ext/source/browse/trunk/params.py
.. _`unittest parametrizer`: https://github.com/testing-cabal/unittest-ext/blob/master/params.py
Here is an example ``pytest_generate_function`` function implementing a
parametrization scheme similar to Michael Foord's `unittest
parameterizer`_ but in a lot less code::
parametrizer`_ but in a lot less code::
# content of ./test_parametrize.py
import pytest
@@ -369,7 +369,7 @@ argument sets to use for each test function. Let's run it::
$ py.test -q
F..
======= FAILURES ========
_______ TestClass.test_equals[2-1] ________
_______ TestClass.test_equals[1-2] ________
self = <test_parametrize.TestClass object at 0xdeadbeef>, a = 1, b = 2
@@ -445,7 +445,7 @@ If you run this with reporting for skips enabled::
$ py.test -rs test_module.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 2 items

View File

@@ -1,6 +1,45 @@
Changing standard (Python) test discovery
===============================================
Ignore paths during test collection
-----------------------------------
You can easily ignore certain test directories and modules during collection
by passing the ``--ignore=path`` option on the cli. ``pytest`` allows multiple
``--ignore`` options. Example::
tests/
|-- example
| |-- test_example_01.py
| |-- test_example_02.py
| '-- test_example_03.py
|-- foobar
| |-- test_foobar_01.py
| |-- test_foobar_02.py
| '-- test_foobar_03.py
'-- hello
'-- world
|-- test_world_01.py
|-- test_world_02.py
'-- test_world_03.py
Now if you invoke ``pytest`` with ``--ignore=tests/foobar/test_foobar_03.py --ignore=tests/hello/``,
you will see that ``pytest`` only collects test-modules, which do not match the patterns specified::
========= test session starts ==========
platform darwin -- Python 2.7.10, pytest-2.8.2, py-1.4.30, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 5 items
tests/example/test_example_01.py .
tests/example/test_example_02.py .
tests/example/test_example_03.py .
tests/foobar/test_foobar_01.py .
tests/foobar/test_foobar_02.py .
======= 5 passed in 0.02 seconds =======
Changing directory recursion
-----------------------------------------------------
@@ -43,7 +82,7 @@ then the test collection looks like this::
$ py.test --collect-only
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile: setup.cfg
collected 2 items
<Module 'check_myapp.py'>
@@ -52,7 +91,7 @@ then the test collection looks like this::
<Function 'simple_check'>
<Function 'complex_check'>
======= in 0.12 seconds ========
======= no tests ran in 0.12 seconds ========
.. note::
@@ -89,7 +128,7 @@ You can always peek at the collection tree without running tests like this::
. $ py.test --collect-only pythoncollection.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
collected 3 items
<Module 'CWD/pythoncollection.py'>
@@ -99,7 +138,7 @@ You can always peek at the collection tree without running tests like this::
<Function 'test_method'>
<Function 'test_anothermethod'>
======= in 0.12 seconds ========
======= no tests ran in 0.12 seconds ========
customizing test collection to find all .py files
---------------------------------------------------------
@@ -136,18 +175,28 @@ And then if you have a module file like this::
and a setup.py dummy file like this::
# content of setup.py
0/0 # will raise exeption if imported
0/0 # will raise exception if imported
then a pytest run on python2 will find the one test when run with a python2
interpreters and will leave out the setup.py file::
then a pytest run on Python2 will find the one test and will leave out the
setup.py file::
#$ py.test --collect-only
====== test session starts ======
platform linux2 -- Python 2.7.10, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
collected 1 items
<Module 'pkg/module_py2.py'>
<Function 'test_only_on_python2'>
====== no tests ran in 0.04 seconds ======
If you run with a Python3 interpreter both the one test and the setup.py file
will be left out::
$ py.test --collect-only
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
collected 0 items
======= in 0.12 seconds ========
If you run with a Python3 interpreter the moduled added through the conftest.py file will not be considered for test collection.
======= no tests ran in 0.12 seconds ========

View File

@@ -13,7 +13,7 @@ get on the terminal - we are working on that):
assertion $ py.test failure_demo.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR/assertion, inifile:
collected 42 items
@@ -28,7 +28,7 @@ get on the terminal - we are working on that):
> assert param1 * 2 < param2
E assert (3 * 2) < 6
failure_demo.py:15: AssertionError
failure_demo.py:16: AssertionError
_______ TestFailing.test_simple ________
self = <failure_demo.TestFailing object at 0xdeadbeef>
@@ -44,7 +44,7 @@ get on the terminal - we are working on that):
E + where 42 = <function TestFailing.test_simple.<locals>.f at 0xdeadbeef>()
E + and 43 = <function TestFailing.test_simple.<locals>.g at 0xdeadbeef>()
failure_demo.py:28: AssertionError
failure_demo.py:29: AssertionError
_______ TestFailing.test_simple_multiline ________
self = <failure_demo.TestFailing object at 0xdeadbeef>
@@ -54,7 +54,7 @@ get on the terminal - we are working on that):
42,
> 6*9)
failure_demo.py:33:
failure_demo.py:34:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
a = 42, b = 54
@@ -64,7 +64,7 @@ get on the terminal - we are working on that):
b)
E assert 42 == 54
failure_demo.py:11: AssertionError
failure_demo.py:12: AssertionError
_______ TestFailing.test_not ________
self = <failure_demo.TestFailing object at 0xdeadbeef>
@@ -76,7 +76,7 @@ get on the terminal - we are working on that):
E assert not 42
E + where 42 = <function TestFailing.test_not.<locals>.f at 0xdeadbeef>()
failure_demo.py:38: AssertionError
failure_demo.py:39: AssertionError
_______ TestSpecialisedExplanations.test_eq_text ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -87,7 +87,7 @@ get on the terminal - we are working on that):
E - spam
E + eggs
failure_demo.py:42: AssertionError
failure_demo.py:43: AssertionError
_______ TestSpecialisedExplanations.test_eq_similar_text ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -100,7 +100,7 @@ get on the terminal - we are working on that):
E + foo 2 bar
E ? ^
failure_demo.py:45: AssertionError
failure_demo.py:46: AssertionError
_______ TestSpecialisedExplanations.test_eq_multiline_text ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -113,7 +113,7 @@ get on the terminal - we are working on that):
E + eggs
E bar
failure_demo.py:48: AssertionError
failure_demo.py:49: AssertionError
_______ TestSpecialisedExplanations.test_eq_long_text ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -130,7 +130,7 @@ get on the terminal - we are working on that):
E + 1111111111b222222222
E ? ^
failure_demo.py:53: AssertionError
failure_demo.py:54: AssertionError
_______ TestSpecialisedExplanations.test_eq_long_text_multiline ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -154,7 +154,7 @@ get on the terminal - we are working on that):
E 2
E 2
failure_demo.py:58: AssertionError
failure_demo.py:59: AssertionError
_______ TestSpecialisedExplanations.test_eq_list ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -165,7 +165,7 @@ get on the terminal - we are working on that):
E At index 2 diff: 2 != 3
E Use -v to get the full diff
failure_demo.py:61: AssertionError
failure_demo.py:62: AssertionError
_______ TestSpecialisedExplanations.test_eq_list_long ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -178,7 +178,7 @@ get on the terminal - we are working on that):
E At index 100 diff: 1 != 2
E Use -v to get the full diff
failure_demo.py:66: AssertionError
failure_demo.py:67: AssertionError
_______ TestSpecialisedExplanations.test_eq_dict ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -195,14 +195,14 @@ get on the terminal - we are working on that):
E {'d': 0}
E Use -v to get the full diff
failure_demo.py:69: AssertionError
failure_demo.py:70: AssertionError
_______ TestSpecialisedExplanations.test_eq_set ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
def test_eq_set(self):
> assert set([0, 10, 11, 12]) == set([0, 20, 21])
E assert set([0, 10, 11, 12]) == set([0, 20, 21])
E assert {0, 10, 11, 12} == {0, 20, 21}
E Extra items in the left set:
E 10
E 11
@@ -212,7 +212,7 @@ get on the terminal - we are working on that):
E 21
E Use -v to get the full diff
failure_demo.py:72: AssertionError
failure_demo.py:73: AssertionError
_______ TestSpecialisedExplanations.test_eq_longer_list ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -223,7 +223,7 @@ get on the terminal - we are working on that):
E Right contains more items, first extra item: 3
E Use -v to get the full diff
failure_demo.py:75: AssertionError
failure_demo.py:76: AssertionError
_______ TestSpecialisedExplanations.test_in_list ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -232,7 +232,7 @@ get on the terminal - we are working on that):
> assert 1 in [0, 2, 3, 4, 5]
E assert 1 in [0, 2, 3, 4, 5]
failure_demo.py:78: AssertionError
failure_demo.py:79: AssertionError
_______ TestSpecialisedExplanations.test_not_in_text_multiline ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -250,7 +250,7 @@ get on the terminal - we are working on that):
E and a
E tail
failure_demo.py:82: AssertionError
failure_demo.py:83: AssertionError
_______ TestSpecialisedExplanations.test_not_in_text_single ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -263,7 +263,7 @@ get on the terminal - we are working on that):
E single foo line
E ? +++
failure_demo.py:86: AssertionError
failure_demo.py:87: AssertionError
_______ TestSpecialisedExplanations.test_not_in_text_single_long ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -276,7 +276,7 @@ get on the terminal - we are working on that):
E head head foo tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail
E ? +++
failure_demo.py:90: AssertionError
failure_demo.py:91: AssertionError
______ TestSpecialisedExplanations.test_not_in_text_single_long_term _______
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -289,7 +289,7 @@ get on the terminal - we are working on that):
E head head fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffftail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail
E ? ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
failure_demo.py:94: AssertionError
failure_demo.py:95: AssertionError
_______ test_attribute ________
def test_attribute():
@@ -300,7 +300,7 @@ get on the terminal - we are working on that):
E assert 1 == 2
E + where 1 = <failure_demo.test_attribute.<locals>.Foo object at 0xdeadbeef>.b
failure_demo.py:101: AssertionError
failure_demo.py:102: AssertionError
_______ test_attribute_instance ________
def test_attribute_instance():
@@ -311,7 +311,7 @@ get on the terminal - we are working on that):
E + where 1 = <failure_demo.test_attribute_instance.<locals>.Foo object at 0xdeadbeef>.b
E + where <failure_demo.test_attribute_instance.<locals>.Foo object at 0xdeadbeef> = <class 'failure_demo.test_attribute_instance.<locals>.Foo'>()
failure_demo.py:107: AssertionError
failure_demo.py:108: AssertionError
_______ test_attribute_failure ________
def test_attribute_failure():
@@ -322,7 +322,7 @@ get on the terminal - we are working on that):
i = Foo()
> assert i.b == 2
failure_demo.py:116:
failure_demo.py:117:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <failure_demo.test_attribute_failure.<locals>.Foo object at 0xdeadbeef>
@@ -331,7 +331,7 @@ get on the terminal - we are working on that):
> raise Exception('Failed to get attrib')
E Exception: Failed to get attrib
failure_demo.py:113: Exception
failure_demo.py:114: Exception
_______ test_attribute_multiple ________
def test_attribute_multiple():
@@ -346,7 +346,7 @@ get on the terminal - we are working on that):
E + and 2 = <failure_demo.test_attribute_multiple.<locals>.Bar object at 0xdeadbeef>.b
E + where <failure_demo.test_attribute_multiple.<locals>.Bar object at 0xdeadbeef> = <class 'failure_demo.test_attribute_multiple.<locals>.Bar'>()
failure_demo.py:124: AssertionError
failure_demo.py:125: AssertionError
_______ TestRaises.test_raises ________
self = <failure_demo.TestRaises object at 0xdeadbeef>
@@ -355,22 +355,22 @@ get on the terminal - we are working on that):
s = 'qwe'
> raises(TypeError, "int(s)")
failure_demo.py:133:
failure_demo.py:134:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
> int(s)
E ValueError: invalid literal for int() with base 10: 'qwe'
<0-codegen $PYTHON_PREFIX/lib/python3.4/site-packages/_pytest/python.py:1247>:1: ValueError
<0-codegen $PYTHON_PREFIX/lib/python3.5/site-packages/_pytest/python.py:1309>:1: ValueError
_______ TestRaises.test_raises_doesnt ________
self = <failure_demo.TestRaises object at 0xdeadbeef>
def test_raises_doesnt(self):
> raises(IOError, "int('3')")
E Failed: DID NOT RAISE
E Failed: DID NOT RAISE <class 'OSError'>
failure_demo.py:136: Failed
failure_demo.py:137: Failed
_______ TestRaises.test_raise ________
self = <failure_demo.TestRaises object at 0xdeadbeef>
@@ -379,16 +379,16 @@ get on the terminal - we are working on that):
> raise ValueError("demo error")
E ValueError: demo error
failure_demo.py:139: ValueError
failure_demo.py:140: ValueError
_______ TestRaises.test_tupleerror ________
self = <failure_demo.TestRaises object at 0xdeadbeef>
def test_tupleerror(self):
> a,b = [1]
E ValueError: need more than 1 value to unpack
E ValueError: not enough values to unpack (expected 2, got 1)
failure_demo.py:142: ValueError
failure_demo.py:143: ValueError
______ TestRaises.test_reinterpret_fails_with_print_for_the_fun_of_it ______
self = <failure_demo.TestRaises object at 0xdeadbeef>
@@ -399,7 +399,7 @@ get on the terminal - we are working on that):
> a,b = l.pop()
E TypeError: 'int' object is not iterable
failure_demo.py:147: TypeError
failure_demo.py:148: TypeError
--------------------------- Captured stdout call ---------------------------
l is [1, 2, 3]
_______ TestRaises.test_some_error ________
@@ -410,26 +410,26 @@ get on the terminal - we are working on that):
> if namenotexi:
E NameError: name 'namenotexi' is not defined
failure_demo.py:150: NameError
failure_demo.py:151: NameError
_______ test_dynamic_compile_shows_nicely ________
def test_dynamic_compile_shows_nicely():
src = 'def foo():\n assert 1 == 0\n'
name = 'abc-123'
module = py.std.imp.new_module(name)
code = py.code.compile(src, name, 'exec')
code = _pytest._code.compile(src, name, 'exec')
py.builtin.exec_(code, module.__dict__)
py.std.sys.modules[name] = module
> module.foo()
failure_demo.py:165:
failure_demo.py:166:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
def foo():
> assert 1 == 0
E assert 1 == 0
<2-codegen 'abc-123' $REGENDOC_TMPDIR/assertion/failure_demo.py:162>:2: AssertionError
<2-codegen 'abc-123' $REGENDOC_TMPDIR/assertion/failure_demo.py:163>:2: AssertionError
_______ TestMoreErrors.test_complex_error ________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
@@ -441,9 +441,9 @@ get on the terminal - we are working on that):
return 43
> somefunc(f(), g())
failure_demo.py:175:
failure_demo.py:176:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
failure_demo.py:8: in somefunc
failure_demo.py:9: in somefunc
otherfunc(x,y)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
@@ -453,7 +453,7 @@ get on the terminal - we are working on that):
> assert a==b
E assert 44 == 43
failure_demo.py:5: AssertionError
failure_demo.py:6: AssertionError
_______ TestMoreErrors.test_z1_unpack_error ________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
@@ -461,9 +461,9 @@ get on the terminal - we are working on that):
def test_z1_unpack_error(self):
l = []
> a,b = l
E ValueError: need more than 0 values to unpack
E ValueError: not enough values to unpack (expected 2, got 0)
failure_demo.py:179: ValueError
failure_demo.py:180: ValueError
_______ TestMoreErrors.test_z2_type_error ________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
@@ -473,7 +473,7 @@ get on the terminal - we are working on that):
> a,b = l
E TypeError: 'int' object is not iterable
failure_demo.py:183: TypeError
failure_demo.py:184: TypeError
_______ TestMoreErrors.test_startswith ________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
@@ -485,7 +485,7 @@ get on the terminal - we are working on that):
E assert <built-in method startswith of str object at 0xdeadbeef>('456')
E + where <built-in method startswith of str object at 0xdeadbeef> = '123'.startswith
failure_demo.py:188: AssertionError
failure_demo.py:189: AssertionError
_______ TestMoreErrors.test_startswith_nested ________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
@@ -501,7 +501,7 @@ get on the terminal - we are working on that):
E + where '123' = <function TestMoreErrors.test_startswith_nested.<locals>.f at 0xdeadbeef>()
E + and '456' = <function TestMoreErrors.test_startswith_nested.<locals>.g at 0xdeadbeef>()
failure_demo.py:195: AssertionError
failure_demo.py:196: AssertionError
_______ TestMoreErrors.test_global_func ________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
@@ -511,7 +511,7 @@ get on the terminal - we are working on that):
E assert isinstance(43, float)
E + where 43 = globf(42)
failure_demo.py:198: AssertionError
failure_demo.py:199: AssertionError
_______ TestMoreErrors.test_instance ________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
@@ -522,7 +522,7 @@ get on the terminal - we are working on that):
E assert 42 != 42
E + where 42 = <failure_demo.TestMoreErrors object at 0xdeadbeef>.x
failure_demo.py:202: AssertionError
failure_demo.py:203: AssertionError
_______ TestMoreErrors.test_compare ________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
@@ -532,7 +532,7 @@ get on the terminal - we are working on that):
E assert 11 < 5
E + where 11 = globf(10)
failure_demo.py:205: AssertionError
failure_demo.py:206: AssertionError
_______ TestMoreErrors.test_try_finally ________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
@@ -543,7 +543,7 @@ get on the terminal - we are working on that):
> assert x == 0
E assert 1 == 0
failure_demo.py:210: AssertionError
failure_demo.py:211: AssertionError
_______ TestCustomAssertMsg.test_single_line ________
self = <failure_demo.TestCustomAssertMsg object at 0xdeadbeef>
@@ -557,7 +557,7 @@ get on the terminal - we are working on that):
E assert 1 == 2
E + where 1 = <class 'failure_demo.TestCustomAssertMsg.test_single_line.<locals>.A'>.a
failure_demo.py:221: AssertionError
failure_demo.py:222: AssertionError
_______ TestCustomAssertMsg.test_multiline ________
self = <failure_demo.TestCustomAssertMsg object at 0xdeadbeef>
@@ -574,7 +574,7 @@ get on the terminal - we are working on that):
E assert 1 == 2
E + where 1 = <class 'failure_demo.TestCustomAssertMsg.test_multiline.<locals>.A'>.a
failure_demo.py:227: AssertionError
failure_demo.py:228: AssertionError
_______ TestCustomAssertMsg.test_custom_repr ________
self = <failure_demo.TestCustomAssertMsg object at 0xdeadbeef>
@@ -594,5 +594,5 @@ get on the terminal - we are working on that):
E assert 1 == 2
E + where 1 = This is JSON\n{\n 'foo': 'bar'\n}.a
failure_demo.py:237: AssertionError
failure_demo.py:238: AssertionError
======= 42 failed in 0.12 seconds ========

View File

@@ -108,11 +108,11 @@ directory with the above conftest.py::
$ py.test
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 0 items
======= in 0.12 seconds ========
======= no tests ran in 0.12 seconds ========
.. _`excontrolskip`:
@@ -131,20 +131,23 @@ line option to control skipping of ``slow`` marked tests::
parser.addoption("--runslow", action="store_true",
help="run slow tests")
def pytest_runtest_setup(item):
if 'slow' in item.keywords and not item.config.getoption("--runslow"):
pytest.skip("need --runslow option to run")
We can now write a test module like this::
# content of test_module.py
import pytest
slow = pytest.mark.slow
slow = pytest.mark.skipif(
not pytest.config.getoption("--runslow"),
reason="need --runslow option to run"
)
def test_func_fast():
pass
@slow
def test_func_slow():
pass
@@ -153,13 +156,13 @@ and when running it will see a skipped "slow" test::
$ py.test -rs # "-rs" means report details on the little 's'
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 2 items
test_module.py .s
======= short test summary info ========
SKIP [1] $REGENDOC_TMPDIR/conftest.py:9: need --runslow option to run
SKIP [1] test_module.py:14: need --runslow option to run
======= 1 passed, 1 skipped in 0.12 seconds ========
@@ -167,7 +170,7 @@ Or run it including the ``slow`` marked test::
$ py.test --runslow
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 2 items
@@ -198,7 +201,7 @@ Example::
The ``__tracebackhide__`` setting influences ``pytest`` showing
of tracebacks: the ``checkconfig`` function will not be shown
unless the ``--fulltrace`` command line option is specified.
unless the ``--full-trace`` command line option is specified.
Let's run our little function::
$ py.test -q test_checkconfig.py
@@ -259,12 +262,12 @@ which will add the string to the test header accordingly::
$ py.test
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
project deps: mylib-1.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 0 items
======= in 0.12 seconds ========
======= no tests ran in 0.12 seconds ========
.. regendoc:wipe
@@ -283,24 +286,24 @@ which will add info only when run with "--v"::
$ py.test -v
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
cachedir: .cache
info1: did you know that ...
did you?
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 0 items
======= in 0.12 seconds ========
======= no tests ran in 0.12 seconds ========
and nothing when run plainly::
$ py.test
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 0 items
======= in 0.12 seconds ========
======= no tests ran in 0.12 seconds ========
profiling test duration
--------------------------
@@ -310,7 +313,7 @@ profiling test duration
.. versionadded: 2.2
If you have a slow running large test suite you might want to find
out which tests are the slowest. Let's make an artifical test suite::
out which tests are the slowest. Let's make an artificial test suite::
# content of test_some_are_slow.py
@@ -329,7 +332,7 @@ Now we can profile which test functions execute the slowest::
$ py.test --durations=3
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 3 items
@@ -338,7 +341,7 @@ Now we can profile which test functions execute the slowest::
======= slowest 3 test durations ========
0.20s call test_some_are_slow.py::test_funcslow2
0.10s call test_some_are_slow.py::test_funcslow1
0.00s setup test_some_are_slow.py::test_funcslow2
0.00s teardown test_some_are_slow.py::test_funcslow2
======= 3 passed in 0.12 seconds ========
incremental testing - test steps
@@ -391,11 +394,14 @@ If we run this::
$ py.test -rx
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 4 items
test_step.py .Fx.
======= short test summary info ========
XFAIL test_step.py::TestUserHandling::()::test_deletion
reason: previous test failed (test_modification)
======= FAILURES ========
_______ TestUserHandling.test_modification ________
@@ -407,9 +413,6 @@ 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``
@@ -424,7 +427,7 @@ by placing fixture functions in a ``conftest.py`` file in that directory
You can use all types of fixtures including :ref:`autouse fixtures
<autouse fixtures>` which are the equivalent of xUnit's setup/teardown
concept. It's however recommended to have explicit fixture references in your
tests or test classes rather than relying on implicitely executing
tests or test classes rather than relying on implicitly executing
setup/teardown functions, especially if they are far away from the actual tests.
Here is a an example for making a ``db`` fixture available in a directory::
@@ -462,7 +465,7 @@ We can run this::
$ py.test
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 7 items
@@ -476,7 +479,7 @@ We can run this::
file $REGENDOC_TMPDIR/b/test_error.py, line 1
def test_root(db): # no db here, will error out
fixture 'db' not found
available fixtures: tmpdir, pytestconfig, record_xml_property, monkeypatch, recwarn, tmpdir_factory, capsys, capfd, cache
available fixtures: tmpdir_factory, cache, tmpdir, pytestconfig, recwarn, monkeypatch, capfd, record_xml_property, capsys
use 'py.test --fixtures [testpath]' for help on them.
$REGENDOC_TMPDIR/b/test_error.py:1
@@ -566,7 +569,7 @@ and run them::
$ py.test test_module.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 2 items
@@ -657,7 +660,7 @@ and run it::
$ py.test -s test_module.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 3 items
@@ -700,7 +703,7 @@ Integrating pytest runner and cx_freeze
-----------------------------------------------------------
If you freeze your application using a tool like
`cx_freeze <http://cx-freeze.readthedocs.org>`_ in order to distribute it
`cx_freeze <https://cx-freeze.readthedocs.io>`_ in order to distribute it
to your end-users, it is a good idea to also package your test runner and run
your tests using the frozen application.
@@ -745,4 +748,4 @@ over to ``pytest`` instead. For example::
This makes it convenient to execute your tests from within your frozen
application, using standard ``py.test`` command-line options::
./app_main --pytest --verbose --tb=long --junit-xml=results.xml test-suite/
./app_main --pytest --verbose --tb=long --junitxml=results.xml test-suite/

View File

@@ -60,15 +60,14 @@ and customizable testing framework for Python. Note, however, that
thus likely not something for Python beginners.
A second "magic" issue was the assert statement debugging feature.
Nowadays, ``pytest`` explicitely rewrites assert statements in test modules
Nowadays, ``pytest`` explicitly rewrites assert statements in test modules
in order to provide more useful :ref:`assert feedback <assertfeedback>`.
This completely avoids previous issues of confusing assertion-reporting.
It also means, that you can use Python's ``-O`` optimization without losing
assertions in test modules.
``pytest`` contains a second, mostly obsolete, assert debugging technique,
invoked via ``--assert=reinterpret``, activated by default on
Python-2.5: When an ``assert`` statement fails, ``pytest`` re-interprets
``pytest`` contains a second, mostly obsolete, assert debugging technique
invoked via ``--assert=reinterpret``: When an ``assert`` statement fails, ``pytest`` re-interprets
the expression part to show intermediate values. This technique suffers
from a caveat that the rewriting does not: If your expression has side
effects (better to avoid them anyway!) the intermediate values may not
@@ -76,7 +75,7 @@ be the same, confusing the reinterpreter and obfuscating the initial
error (this is also explained at the command line if it happens).
You can also turn off all assertion interaction using the
``--assertmode=off`` option.
``--assert=plain`` option.
.. _`py namespaces`: index.html
.. _`py/__init__.py`: http://bitbucket.org/hpk42/py-trunk/src/trunk/py/__init__.py
@@ -121,7 +120,7 @@ in a managed class/module/function scope.
.. _`Convention over Configuration`: http://en.wikipedia.org/wiki/Convention_over_Configuration
Can I yield multiple values from a fixture function function?
Can I yield multiple values from a fixture function?
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
There are two conceptual reasons why yielding from a factory function
@@ -141,10 +140,10 @@ However, with pytest-2.3 you can use the :ref:`@pytest.fixture` decorator
and specify ``params`` so that all tests depending on the factory-created
resource will run multiple times with different parameters.
You can also use the `pytest_generate_tests`_ hook to
implement the `parametrization scheme of your choice`_.
You can also use the ``pytest_generate_tests`` hook to
implement the `parametrization scheme of your choice`_. See also
:ref:`paramexamples` for more examples.
.. _`pytest_generate_tests`: test/funcargs.html#parametrizing-tests
.. _`parametrization scheme of your choice`: http://tetamap.wordpress.com/2009/05/13/parametrizing-python-tests-generalized/
pytest interaction with other packages

View File

@@ -36,9 +36,9 @@ style <unittest.TestCase>` or :ref:`nose based <nosestyle>` projects.
.. note::
pytest-2.4 introduced an additional experimental
:ref:`yield fixture mechanism <yieldfixture>` for easier context manager
integration and more linear writing of teardown code.
pytest-2.4 introduced an additional :ref:`yield fixture mechanism
<yieldfixture>` for easier context manager integration and more linear
writing of teardown code.
.. _`funcargs`:
.. _`funcarg mechanism`:
@@ -75,7 +75,7 @@ marked ``smtp`` fixture function. Running the test looks like this::
$ py.test test_smtpsimple.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items
@@ -193,7 +193,7 @@ inspect what is going on and can now run the tests::
$ py.test test_module.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 2 items
@@ -283,6 +283,14 @@ module itself does not need to change or know about these details
of fixture setup.
Finalization/teardown with yield fixtures
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Another alternative to the *request.addfinalizer()* method is to use *yield
fixtures*. All the code after the *yield* statement serves as the teardown
code. See the :ref:`yield fixture documentation <yieldfixture>`.
.. _`request-context`:
Fixtures can introspect the requesting test context
@@ -480,7 +488,7 @@ Running the above tests results in the following test IDs being used::
$ py.test --collect-only
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 10 items
<Module 'test_anothersmtp.py'>
@@ -497,7 +505,7 @@ Running the above tests results in the following test IDs being used::
<Function 'test_ehlo[mail.python.org]'>
<Function 'test_noop[mail.python.org]'>
======= in 0.12 seconds ========
======= no tests ran in 0.12 seconds ========
.. _`interdependent fixtures`:
@@ -531,7 +539,7 @@ Here we declare an ``app`` fixture which receives the previously defined
$ py.test -v test_appsetup.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 2 items
@@ -577,55 +585,84 @@ to show the setup/teardown flow::
@pytest.fixture(scope="module", params=["mod1", "mod2"])
def modarg(request):
param = request.param
print ("create", param)
print (" SETUP modarg %s" % param)
def fin():
print ("fin %s" % param)
print (" TEARDOWN modarg %s" % param)
request.addfinalizer(fin)
return param
@pytest.fixture(scope="function", params=[1,2])
def otherarg(request):
return request.param
param = request.param
print (" SETUP otherarg %s" % param)
def fin():
print (" TEARDOWN otherarg %s" % param)
request.addfinalizer(fin)
return param
def test_0(otherarg):
print (" test0", otherarg)
print (" RUN test0 with otherarg %s" % otherarg)
def test_1(modarg):
print (" test1", modarg)
print (" RUN test1 with modarg %s" % modarg)
def test_2(otherarg, modarg):
print (" test2", otherarg, modarg)
print (" RUN test2 with otherarg %s and modarg %s" % (otherarg, modarg))
Let's run the tests in verbose mode and with looking at the print-output::
$ py.test -v -s test_module.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 8 items
test_module.py::test_0[1] test0 1
test_module.py::test_0[1] SETUP otherarg 1
RUN test0 with otherarg 1
PASSED TEARDOWN otherarg 1
test_module.py::test_0[2] SETUP otherarg 2
RUN test0 with otherarg 2
PASSED TEARDOWN otherarg 2
test_module.py::test_1[mod1] SETUP modarg mod1
RUN test1 with modarg mod1
PASSED
test_module.py::test_0[2] test0 2
PASSED
test_module.py::test_1[mod1] create mod1
test1 mod1
PASSED
test_module.py::test_2[1-mod1] test2 1 mod1
PASSED
test_module.py::test_2[2-mod1] test2 2 mod1
PASSED
test_module.py::test_1[mod2] create mod2
test1 mod2
PASSED
test_module.py::test_2[1-mod2] test2 1 mod2
PASSED
test_module.py::test_2[2-mod2] test2 2 mod2
test_module.py::test_2[1-mod1] 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
RUN test2 with otherarg 2 and modarg mod1
PASSED TEARDOWN otherarg 2
test_module.py::test_1[mod2] TEARDOWN modarg mod1
SETUP modarg mod2
RUN test1 with modarg mod2
PASSED
test_module.py::test_2[1-mod2] 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
RUN test2 with otherarg 2 and modarg mod2
PASSED TEARDOWN otherarg 2
TEARDOWN modarg mod2
======= 8 passed in 0.12 seconds ========
You can see that the parametrized module-scoped ``modarg`` resource caused
an ordering of test execution that lead to the fewest possible "active" resources. The finalizer for the ``mod1`` parametrized resource was executed
before the ``mod2`` resource was setup.
You can see that the parametrized module-scoped ``modarg`` resource caused an
ordering of test execution that lead to the fewest possible "active" resources.
The finalizer for the ``mod1`` parametrized resource was executed before the
``mod2`` resource was setup.
In particular notice that test_0 is completely independent and finishes first.
Then test_1 is executed with ``mod1``, then test_2 with ``mod1``, then test_1
with ``mod2`` and finally test_2 with ``mod2``.
The ``otherarg`` parametrized resource (having function scope) was set up before
and teared down after every test that used it.
.. _`usefixtures`:

View File

@@ -209,7 +209,7 @@ fixtures:
and let pytest figure things out for you.
* if you used parametrization and funcarg factories which made use of
``request.cached_setup()`` it is recommeneded to invest a few minutes
``request.cached_setup()`` it is recommended to invest a few minutes
and simplify your fixture function code to use the :ref:`@pytest.fixture`
decorator instead. This will also allow to take advantage of
the automatic per-resource grouping of tests.

View File

@@ -27,7 +27,7 @@ Installation options::
To check your installation has installed the correct version::
$ py.test --version
This is pytest version 2.8.1, imported from $PYTHON_PREFIX/lib/python3.4/site-packages/pytest.py
This is pytest version 2.9.2, imported from $PYTHON_PREFIX/lib/python3.5/site-packages/pytest.py
If you get an error checkout :ref:`installation issues`.
@@ -49,7 +49,7 @@ That's it. You can execute the test function now::
$ py.test
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items
@@ -106,8 +106,6 @@ Running it with, this time in "quiet" reporting mode::
.
1 passed in 0.12 seconds
.. todo:: For further ways to assert exceptions see the `raises`
Grouping multiple tests in a class
--------------------------------------------------------------
@@ -195,7 +193,7 @@ Where to go next
Here are a few suggestions where to go next:
* :ref:`cmdline` for command line invocation examples
* :ref:`good practises <goodpractises>` for virtualenv, test layout, genscript support
* :ref:`good practices <goodpractices>` for virtualenv, test layout, genscript support
* :ref:`fixtures` for providing a functional baseline to your tests
* :ref:`apiref` for documentation and examples on using ``pytest``
* :ref:`plugins` managing and writing plugins

View File

@@ -1,32 +1,31 @@
.. highlightlang:: python
.. _`goodpractises`:
.. _`goodpractices`:
Good Integration Practices
=================================================
Work with virtual environments
-----------------------------------------------------------
We recommend to use virtualenv_ environments and use pip_
(or easy_install_) for installing your application and any dependencies
as well as the ``pytest`` package itself. This way you will get an isolated
and reproducible environment. Given you have installed virtualenv_
and execute it from the command line, here is an example session for unix
or windows::
.. _`test discovery`:
.. _`Python test discovery`:
virtualenv . # create a virtualenv directory in the current directory
Conventions for Python test discovery
-------------------------------------------------
source bin/activate # on unix
``pytest`` implements the following standard test discovery:
scripts/activate # on Windows
* If no arguments are specified then collection starts from :confval:`testpaths`
(if configured) or the current directory. Alternatively, command line arguments
can be used in any combination of directories, file names or node ids.
* recurse into directories, unless they match :confval:`norecursedirs`
* ``test_*.py`` or ``*_test.py`` files, imported by their `test package name`_.
* ``Test`` prefixed test classes (without an ``__init__`` method)
* ``test_`` prefixed test functions or methods are test items
We can now install pytest::
For examples of how to customize your test discovery :doc:`example/pythoncollection`.
pip install pytest
Within Python modules, ``pytest`` also discovers tests using the standard
:ref:`unittest.TestCase <unittest.TestCase>` subclassing technique.
Due to the ``activate`` step above the ``pip`` will come from
the virtualenv directory and install any package into the isolated
virtual environment.
Choosing a test layout / import rules
------------------------------------------
@@ -135,8 +134,13 @@ required configurations.
.. _`use tox`:
Use tox and Continuous Integration servers
-------------------------------------------------
Tox
------
For development, we recommend to use virtualenv_ environments and pip_
for installing your application and any dependencies
as well as the ``pytest`` package itself. This ensures your code and
dependencies are isolated from the system Python installation.
If you frequently release code and want to make sure that your actual
package passes all tests you may want to look into `tox`_, the
@@ -148,89 +152,58 @@ options. It will run tests against the installed package and not
against your source code checkout, helping to detect packaging
glitches.
If you want to use Jenkins_ you can use the ``--junitxml=PATH`` option
to create a JUnitXML file that Jenkins_ can pick up and generate reports.
.. _standalone:
.. _`genscript method`:
(deprecated) Create a pytest standalone script
-----------------------------------------------
If you are a maintainer or application developer and want people
who don't deal with python much to easily run tests you may generate
a standalone ``pytest`` script::
py.test --genscript=runtests.py
This generates a ``runtests.py`` script which is a fully functional basic
``pytest`` script, running unchanged under Python2 and Python3.
You can tell people to download the script and then e.g. run it like this::
python runtests.py
.. note::
You must have pytest and its dependencies installed as an sdist, not
as wheels because genscript need the source code for generating a
standalone script.
Continuous integration services such as Jenkins_ can make use of the
``--junitxml=PATH`` option to create a JUnitXML file and generate reports (e.g.
by publishing the results in a nice format with the `Jenkins xUnit Plugin
<https://wiki.jenkins-ci.org/display/JENKINS/xUnit+Plugin>`_).
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.
Integrating with setuptools / ``python setup.py test``
------------------------------------------------------
Add this to ``setup.py`` file:
You can integrate test runs into your
setuptools based project. Use the `genscript method`_
to generate a standalone ``pytest`` script::
py.test --genscript=runtests.py
and make this script part of your distribution and then add
this to your ``setup.py`` file::
from distutils.core import setup, Command
# you can also import from setuptools
class PyTest(Command):
user_options = []
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
import subprocess
import sys
errno = subprocess.call([sys.executable, 'runtests.py'])
raise SystemExit(errno)
.. code-block:: python
from setuptools import setup
setup(
#...,
cmdclass = {'test': PyTest},
setup_requires=['pytest-runner', ...],
tests_require=['pytest', ...],
#...,
)
And create an alias into ``setup.cfg`` file:
.. code-block:: ini
[aliases]
test=pytest
If you now type::
python setup.py test
this will execute your tests using ``runtests.py``. As this is a
this will execute your tests using ``pytest-runner``. As this is a
standalone version of ``pytest`` no prior installation whatsoever is
required for calling the test command. You can also pass additional
arguments to the subprocess-calls such as your test directory or other
options.
arguments to py.test such as your test directory or other
options using ``--addopts``.
Integration with setuptools test commands
----------------------------------------------------
Manual Integration
^^^^^^^^^^^^^^^^^^
Setuptools supports writing our own Test command for invoking pytest.
Most often it is better to use tox_ instead, but here is how you can
get started with setuptools integration::
If for some reason you don't want/can't use ``pytest-runner``, you can write
your own setuptools Test command for invoking pytest.
.. code-block:: python
import sys
@@ -244,11 +217,6 @@ get started with setuptools integration::
TestCommand.initialize_options(self)
self.pytest_args = []
def finalize_options(self):
TestCommand.finalize_options(self)
self.test_args = []
self.test_suite = True
def run_tests(self):
#import here, cause outside the eggs aren't loaded
import pytest
@@ -274,32 +242,39 @@ using the ``--pytest-args`` or ``-a`` command-line option. For example::
is equivalent to running ``py.test --durations=5``.
.. seealso::
For a more powerful solution, take a look at the
`pytest-runner <https://pypi.python.org/pypi/pytest-runner>`_ plugin.
.. _standalone:
.. _`genscript method`:
.. _`test discovery`:
.. _`Python test discovery`:
(deprecated) Create a pytest standalone script
-----------------------------------------------
Conventions for Python test discovery
-------------------------------------------------
.. deprecated:: 2.8
``pytest`` implements the following standard test discovery:
.. note::
* collection starts from paths specified in :confval:`testpaths` if configured,
otherwise from initial command line arguments which may be directories,
filenames or test ids. If :confval:`testpaths` is not configured and no
directories or files were given in the command line, start collection from
the current directory.
* recurse into directories, unless they match :confval:`norecursedirs`
* ``test_*.py`` or ``*_test.py`` files, imported by their `test package name`_.
* ``Test`` prefixed test classes (without an ``__init__`` method)
* ``test_`` prefixed test functions or methods are test items
``genscript`` has been deprecated because:
For examples of how to customize your test discovery :doc:`example/pythoncollection`.
* It cannot support plugins, rendering its usefulness extremely limited;
* Tooling has become much better since ``genscript`` was introduced;
* It is possible to build a zipped ``pytest`` application without the
shortcomings above.
There's no planned version in which this command will be removed
at the moment of this writing, but its use is discouraged for new
applications.
If you are a maintainer or application developer and want people
who don't deal with python much to easily run tests you may generate
a standalone ``pytest`` script::
py.test --genscript=runtests.py
This generates a ``runtests.py`` script which is a fully functional basic
``pytest`` script, running unchanged under Python2 and Python3.
You can tell people to download the script and then e.g. run it like this::
python runtests.py
Within Python modules, ``pytest`` also discovers tests using the standard
:ref:`unittest.TestCase <unittest.TestCase>` subclassing technique.
.. include:: links.inc

BIN
doc/en/img/freiburg2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

View File

@@ -7,6 +7,7 @@ pytest: helps you write better programs
**a mature full-featured Python testing tool**
- runs on Posix/Windows, Python 2.6-3.5, PyPy and (possibly still) Jython-2.5.1
- free and open source software, distributed under the terms of the :ref:`MIT license <license>`
- **well tested** with more than a thousand tests against itself
- **strict backward compatibility policy** for safe pytest upgrades
- :ref:`comprehensive online <toc>` and `PDF documentation <pytest.pdf>`_
@@ -40,7 +41,7 @@ pytest: helps you write better programs
- multi-paradigm: pytest can run ``nose``, ``unittest`` and
``doctest`` style test suites, including running testcases made for
Django and trial
- supports :ref:`good integration practises <goodpractises>`
- supports :ref:`good integration practices <goodpractices>`
- supports extended :ref:`xUnit style setup <xunitsetup>`
- supports domain-specific :ref:`non-python tests`
- supports generating `test coverage reports

32
doc/en/license.rst Normal file
View File

@@ -0,0 +1,32 @@
.. _license:
License
-------
Distributed under the terms of the `MIT`_ license, pytest is free and open source software.
::
The MIT License (MIT)
Copyright (c) 2004-2016 Holger Krekel and others
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
.. _`MIT`: https://github.com/pytest-dev/pytest/blob/master/LICENSE

View File

@@ -6,7 +6,7 @@
.. _`pytest_nose`: plugin/nose.html
.. _`reStructured Text`: http://docutils.sourceforge.net
.. _`Python debugger`: http://docs.python.org/lib/module-pdb.html
.. _nose: https://nose.readthedocs.org/en/latest/
.. _nose: https://nose.readthedocs.io/en/latest/
.. _pytest: http://pypi.python.org/pypi/pytest
.. _mercurial: http://mercurial.selenic.com/wiki/
.. _`setuptools`: http://pypi.python.org/pypi/setuptools
@@ -18,4 +18,4 @@
.. _hudson: http://hudson-ci.org/
.. _jenkins: http://jenkins-ci.org/
.. _tox: http://testrun.org/tox
.. _pylib: http://py.readthedocs.org/en/latest/
.. _pylib: https://py.readthedocs.io/en/latest/

View File

@@ -46,7 +46,7 @@ Unsupported idioms / known issues
(e.g. ``tests/test_mode.py`` and ``other/tests/test_mode.py``)
by extending sys.path/import semantics. pytest does not do that
but there is discussion in `issue268 <https://github.com/pytest-dev/pytest/issues/268>`_ for adding some support. Note that
`nose2 choose to avoid this sys.path/import hackery <https://nose2.readthedocs.org/en/latest/differences.html#test-discovery-and-loading>`_.
`nose2 choose to avoid this sys.path/import hackery <https://nose2.readthedocs.io/en/latest/differences.html#test-discovery-and-loading>`_.
- nose-style doctests are not collected and executed correctly,
also doctest fixtures don't work.

View File

@@ -5,10 +5,9 @@ Getting started basics
.. toctree::
:maxdepth: 2
index
getting-started
usage
goodpractises
goodpractices
projects
faq

View File

@@ -41,21 +41,21 @@ to an expected output::
# content of test_expectation.py
import pytest
@pytest.mark.parametrize("input,expected", [
@pytest.mark.parametrize("test_input,expected", [
("3+5", 8),
("2+4", 6),
("6*9", 42),
])
def test_eval(input, expected):
assert eval(input) == expected
def test_eval(test_input, expected):
assert eval(test_input) == expected
Here, the ``@parametrize`` decorator defines three different ``(input,expected)``
Here, the ``@parametrize`` decorator defines three different ``(test_input,expected)``
tuples so that the ``test_eval`` function will run three times using
them in turn::
$ py.test
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 3 items
@@ -64,15 +64,15 @@ them in turn::
======= FAILURES ========
_______ test_eval[6*9-42] ________
input = '6*9', expected = 42
test_input = '6*9', expected = 42
@pytest.mark.parametrize("input,expected", [
@pytest.mark.parametrize("test_input,expected", [
("3+5", 8),
("2+4", 6),
("6*9", 42),
])
def test_eval(input, expected):
> assert eval(input) == expected
def test_eval(test_input, expected):
> assert eval(test_input) == expected
E assert 54 == 42
E + where 54 = eval('6*9')
@@ -91,19 +91,19 @@ for example with the builtin ``mark.xfail``::
# content of test_expectation.py
import pytest
@pytest.mark.parametrize("input,expected", [
@pytest.mark.parametrize("test_input,expected", [
("3+5", 8),
("2+4", 6),
pytest.mark.xfail(("6*9", 42)),
])
def test_eval(input, expected):
assert eval(input) == expected
def test_eval(test_input, expected):
assert eval(test_input) == expected
Let's run this::
$ py.test
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 3 items
@@ -142,7 +142,7 @@ Sometimes you may want to implement your own parametrization scheme
or implement some dynamism for determining the parameters or scope
of a fixture. For this, you can use the ``pytest_generate_tests`` hook
which is called when collecting a test function. Through the passed in
`metafunc` object you can inspect the requesting test context and, most
``metafunc`` object you can inspect the requesting test context and, most
importantly, you can call ``metafunc.parametrize()`` to cause
parametrization.
@@ -196,12 +196,12 @@ As expected our test function fails.
If you don't specify a stringinput it will be skipped because
``metafunc.parametrize()`` will be called with an empty parameter
listlist::
list::
$ py.test -q -rs test_strings.py
s
======= short test summary info ========
SKIP [1] $PYTHON_PREFIX/lib/python3.4/site-packages/_pytest/python.py:1364: got empty parameter set, function test_valid_string at $REGENDOC_TMPDIR/test_strings.py:1
SKIP [1] test_strings.py:1: got empty parameter set ['stringinput'], function test_valid_string at $REGENDOC_TMPDIR/test_strings.py:1
1 skipped in 0.12 seconds
For further examples, you might want to look at :ref:`more

View File

@@ -14,10 +14,9 @@ Installing a third party plugin can be easily done with ``pip``::
pip uninstall pytest-NAME
If a plugin is installed, ``pytest`` automatically finds and integrates it,
there is no need to activate it. We have a :doc:`page listing
all 3rd party plugins and their status against the latest py.test version
<plugins_index/index>` and here is a little annotated list
for some popular plugins:
there is no need to activate it.
Here is a little annotated list for some popular plugins:
.. _`django`: https://www.djangoproject.com/
@@ -28,7 +27,7 @@ for some popular plugins:
for `twisted <http://twistedmatrix.com>`_ apps, starting a reactor and
processing deferreds from test functions.
* `pytest-capturelog <http://pypi.python.org/pypi/pytest-capturelog>`_:
* `pytest-catchlog <http://pypi.python.org/pypi/pytest-catchlog>`_:
to capture and assert about messages from the logging module
* `pytest-cov <http://pypi.python.org/pypi/pytest-cov>`_:
@@ -50,15 +49,14 @@ for some popular plugins:
* `pytest-timeout <http://pypi.python.org/pypi/pytest-timeout>`_:
to timeout tests based on function marks or global definitions.
* `pytest-cache <http://pypi.python.org/pypi/pytest-cache>`_:
to interactively re-run failing tests and help other plugins to
store test run information across invocations.
* `pytest-pep8 <http://pypi.python.org/pypi/pytest-pep8>`_:
a ``--pep8`` option to enable PEP8 compliance checking.
* `pytest-flakes <https://pypi.python.org/pypi/pytest-flakes>`_:
check source code with pyflakes.
* `oejskit <http://pypi.python.org/pypi/oejskit>`_:
a plugin to run javascript unittests in life browsers
a plugin to run javascript unittests in live browsers.
To see a complete list of all plugins with their latest testing
status against different py.test and Python versions, please visit
@@ -92,7 +90,7 @@ Finding out which plugins are active
If you want to find out which plugins are active in your
environment you can type::
py.test --traceconfig
py.test --trace-config
and will get an extended test header which shows activated plugins
and their names. It will also print local plugins aka
@@ -108,8 +106,21 @@ You can prevent plugins from loading or unregister them::
py.test -p no:NAME
This means that any subsequent try to activate/load the named
plugin will it already existing. See :ref:`findpluginname` for
how to obtain the name of a plugin.
plugin will not work.
If you want to unconditionally disable a plugin for a project, you can add
this option to your ``pytest.ini`` file:
.. code-block:: ini
[pytest]
addopts = -p no:NAME
Alternatively to disable it only in certain environments (for example in a
CI server), you can set ``PYTEST_ADDOPTS`` environment variable to
``-p no:name``.
See :ref:`findpluginname` for how to obtain the name of a plugin.
.. _`builtin plugins`:
@@ -123,6 +134,7 @@ in the `pytest repository <https://github.com/pytest-dev/pytest>`_.
.. autosummary::
_pytest.assertion
_pytest.cacheprovider
_pytest.capture
_pytest.config
_pytest.doctest

Binary file not shown.

Before

Width:  |  Height:  |  Size: 679 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 734 B

View File

@@ -1,290 +0,0 @@
.. _plugins_index:
List of Third-Party Plugins
===========================
The table below contains a listing of plugins found in PyPI and
their status when tested when using latest py.test and python versions.
A complete listing can also be found at
`plugincompat <http://plugincompat.herokuapp.com/>`_, which contains tests
status against other py.test releases.
============================================================================================ ===================================================================================================================== ===================================================================================================================== =========================================================================== =============================================================================================================================================
Name Py27 Py34 Home Summary
============================================================================================ ===================================================================================================================== ===================================================================================================================== =========================================================================== =============================================================================================================================================
`pytest-allure-adaptor <http://pypi.python.org/pypi/pytest-allure-adaptor>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-allure-adaptor-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-allure-adaptor-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Plugin for py.test to generate allure xml reports
:target: http://plugincompat.herokuapp.com/output/pytest-allure-adaptor-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-allure-adaptor-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/allure-framework/allure-python
`pytest-ansible <http://pypi.python.org/pypi/pytest-ansible>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-ansible-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-ansible-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Plugin for py.test to allow running ansible
:target: http://plugincompat.herokuapp.com/output/pytest-ansible-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-ansible-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/jlaska/pytest-ansible
`pytest-asyncio <http://pypi.python.org/pypi/pytest-asyncio>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-asyncio-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-asyncio-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Pytest support for asyncio.
:target: http://plugincompat.herokuapp.com/output/pytest-asyncio-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-asyncio-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/pytest-dev/pytest-asyncio
`pytest-autochecklog <http://pypi.python.org/pypi/pytest-autochecklog>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-autochecklog-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-autochecklog-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png automatically check condition and log all the checks
:target: http://plugincompat.herokuapp.com/output/pytest-autochecklog-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-autochecklog-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/steven004/python-autochecklog
`pytest-bdd <http://pypi.python.org/pypi/pytest-bdd>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-bdd-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-bdd-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png BDD for pytest
:target: http://plugincompat.herokuapp.com/output/pytest-bdd-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-bdd-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/pytest-dev/pytest-bdd
`pytest-beakerlib <http://pypi.python.org/pypi/pytest-beakerlib>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-beakerlib-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-beakerlib-latest?py=py34&pytest=2.8.0.dev4 `link <https://fedorahosted.org/python-pytest-beakerlib/>`_ A pytest plugin that reports test results to the BeakerLib framework
:target: http://plugincompat.herokuapp.com/output/pytest-beakerlib-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-beakerlib-latest?py=py34&pytest=2.8.0.dev4
`pytest-beds <http://pypi.python.org/pypi/pytest-beds>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-beds-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-beds-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Fixtures for testing Google Appengine (GAE) apps
:target: http://plugincompat.herokuapp.com/output/pytest-beds-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-beds-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/kaste/pytest-beds
`pytest-bench <http://pypi.python.org/pypi/pytest-bench>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-bench-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-bench-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Benchmark utility that plugs into pytest.
:target: http://plugincompat.herokuapp.com/output/pytest-bench-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-bench-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/concordusapps/pytest-bench
`pytest-benchmark <http://pypi.python.org/pypi/pytest-benchmark>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-benchmark-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-benchmark-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png py.test fixture for benchmarking code
:target: http://plugincompat.herokuapp.com/output/pytest-benchmark-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-benchmark-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/ionelmc/pytest-benchmark
`pytest-blockage <http://pypi.python.org/pypi/pytest-blockage>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-blockage-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-blockage-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Disable network requests during a test run.
:target: http://plugincompat.herokuapp.com/output/pytest-blockage-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-blockage-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/rob-b/pytest-blockage
`pytest-bpdb <http://pypi.python.org/pypi/pytest-bpdb>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-bpdb-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-bpdb-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png A py.test plug-in to enable drop to bpdb debugger on test failure.
:target: http://plugincompat.herokuapp.com/output/pytest-bpdb-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-bpdb-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/slafs/pytest-bpdb
`pytest-browsermob-proxy <http://pypi.python.org/pypi/pytest-browsermob-proxy>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-browsermob-proxy-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-browsermob-proxy-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png BrowserMob proxy plugin for py.test.
:target: http://plugincompat.herokuapp.com/output/pytest-browsermob-proxy-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-browsermob-proxy-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/davehunt/pytest-browsermob-proxy
`pytest-bugzilla <http://pypi.python.org/pypi/pytest-bugzilla>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-bugzilla-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-bugzilla-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png py.test bugzilla integration plugin
:target: http://plugincompat.herokuapp.com/output/pytest-bugzilla-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-bugzilla-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/nibrahim/pytest_bugzilla
`pytest-marker-bugzilla <http://pypi.python.org/pypi/pytest-marker-bugzilla>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-marker-bugzilla-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-marker-bugzilla-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png py.test bugzilla integration plugin, using markers
:target: http://plugincompat.herokuapp.com/output/pytest-marker-bugzilla-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-marker-bugzilla-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/eanxgeek/pytest_marker_bugzilla
`pytest-remove-stale-bytecode <http://pypi.python.org/pypi/pytest-remove-stale-bytecode>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-remove-stale-bytecode-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-remove-stale-bytecode-latest?py=py34&pytest=2.8.0.dev4 .. image:: bitbucket.png py.test plugin to remove stale byte code files.
:target: http://plugincompat.herokuapp.com/output/pytest-remove-stale-bytecode-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-remove-stale-bytecode-latest?py=py34&pytest=2.8.0.dev4 :target: https://bitbucket.org/gocept/pytest-remove-stale-bytecode/
`pytest-cache <http://pypi.python.org/pypi/pytest-cache>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-cache-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-cache-latest?py=py34&pytest=2.8.0.dev4 .. image:: bitbucket.png pytest plugin with mechanisms for caching across test runs
:target: http://plugincompat.herokuapp.com/output/pytest-cache-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-cache-latest?py=py34&pytest=2.8.0.dev4 :target: http://bitbucket.org/hpk42/pytest-cache/
`pytest-cagoule <http://pypi.python.org/pypi/pytest-cagoule>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-cagoule-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-cagoule-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Pytest plugin to only run tests affected by changes
:target: http://plugincompat.herokuapp.com/output/pytest-cagoule-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-cagoule-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/davidszotten/pytest-cagoule
`pytest-capturelog <http://pypi.python.org/pypi/pytest-capturelog>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-capturelog-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-capturelog-latest?py=py34&pytest=2.8.0.dev4 .. image:: bitbucket.png py.test plugin to capture log messages
:target: http://plugincompat.herokuapp.com/output/pytest-capturelog-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-capturelog-latest?py=py34&pytest=2.8.0.dev4 :target: http://bitbucket.org/memedough/pytest-capturelog/overview
`pytest-django-casperjs <http://pypi.python.org/pypi/pytest-django-casperjs>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-django-casperjs-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-django-casperjs-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Integrate CasperJS with your django tests as a pytest fixture.
:target: http://plugincompat.herokuapp.com/output/pytest-django-casperjs-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-django-casperjs-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/EnTeQuAk/pytest-django-casperjs/
`pytest-catchlog <http://pypi.python.org/pypi/pytest-catchlog>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-catchlog-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-catchlog-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png py.test plugin to catch log messages. This is a fork of pytest-capturelog.
:target: http://plugincompat.herokuapp.com/output/pytest-catchlog-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-catchlog-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/eisensheng/pytest-catchlog
`pytest-circleci <http://pypi.python.org/pypi/pytest-circleci>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-circleci-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-circleci-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png py.test plugin for CircleCI
:target: http://plugincompat.herokuapp.com/output/pytest-circleci-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-circleci-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/micktwomey/pytest-circleci
`pytest-cloud <http://pypi.python.org/pypi/pytest-cloud>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-cloud-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-cloud-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Distributed tests planner plugin for pytest testing framework.
:target: http://plugincompat.herokuapp.com/output/pytest-cloud-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-cloud-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/pytest-dev/pytest-cloud
`pytest-codecheckers <http://pypi.python.org/pypi/pytest-codecheckers>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-codecheckers-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-codecheckers-latest?py=py34&pytest=2.8.0.dev4 .. image:: bitbucket.png pytest plugin to add source code sanity checks (pep8 and friends)
:target: http://plugincompat.herokuapp.com/output/pytest-codecheckers-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-codecheckers-latest?py=py34&pytest=2.8.0.dev4 :target: http://bitbucket.org/RonnyPfannschmidt/pytest-codecheckers/
`pytest-colordots <http://pypi.python.org/pypi/pytest-colordots>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-colordots-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-colordots-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Colorizes the progress indicators
:target: http://plugincompat.herokuapp.com/output/pytest-colordots-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-colordots-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/svenstaro/pytest-colordots
`pytest-paste-config <http://pypi.python.org/pypi/pytest-paste-config>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-paste-config-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-paste-config-latest?py=py34&pytest=2.8.0.dev4 ? Allow setting the path to a paste config file
:target: http://plugincompat.herokuapp.com/output/pytest-paste-config-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-paste-config-latest?py=py34&pytest=2.8.0.dev4
`pytest-config <http://pypi.python.org/pypi/pytest-config>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-config-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-config-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Base configurations and utilities for developing your Python project test suite with pytest.
:target: http://plugincompat.herokuapp.com/output/pytest-config-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-config-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/buzzfeed/pytest_config
`pytest-contextfixture <http://pypi.python.org/pypi/pytest-contextfixture>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-contextfixture-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-contextfixture-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Define pytest fixtures as context managers.
:target: http://plugincompat.herokuapp.com/output/pytest-contextfixture-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-contextfixture-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/pelme/pytest-contextfixture/
`pytest-couchdbkit <http://pypi.python.org/pypi/pytest-couchdbkit>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-couchdbkit-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-couchdbkit-latest?py=py34&pytest=2.8.0.dev4 .. image:: bitbucket.png py.test extension for per-test couchdb databases using couchdbkit
:target: http://plugincompat.herokuapp.com/output/pytest-couchdbkit-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-couchdbkit-latest?py=py34&pytest=2.8.0.dev4 :target: http://bitbucket.org/RonnyPfannschmidt/pytest-couchdbkit
`pytest-cov <http://pypi.python.org/pypi/pytest-cov>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-cov-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-cov-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png py.test plugin for coverage reporting with support for both centralised and distributed testing, including subprocesses and multiprocessing
:target: http://plugincompat.herokuapp.com/output/pytest-cov-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-cov-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/schlamar/pytest-cov
`pytest-cover <http://pypi.python.org/pypi/pytest-cover>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-cover-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-cover-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Pytest plugin for measuring coverage. Forked from `pytest-cov`.
:target: http://plugincompat.herokuapp.com/output/pytest-cover-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-cover-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/ionelmc/pytest-cover
`pytest-coverage <http://pypi.python.org/pypi/pytest-coverage>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-coverage-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-coverage-latest?py=py34&pytest=2.8.0.dev4 `link <https://pypi.python.org/pypi/pytest-cover/>`_ Pytest plugin for measuring coverage. Forked from `pytest-cov`.
:target: http://plugincompat.herokuapp.com/output/pytest-coverage-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-coverage-latest?py=py34&pytest=2.8.0.dev4
`pytest-cpp <http://pypi.python.org/pypi/pytest-cpp>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-cpp-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-cpp-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Use pytest's runner to discover and execute C++ tests
:target: http://plugincompat.herokuapp.com/output/pytest-cpp-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-cpp-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/pytest-dev/pytest-cpp
`pytest-curl-report <http://pypi.python.org/pypi/pytest-curl-report>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-curl-report-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-curl-report-latest?py=py34&pytest=2.8.0.dev4 .. image:: bitbucket.png pytest plugin to generate curl command line report
:target: http://plugincompat.herokuapp.com/output/pytest-curl-report-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-curl-report-latest?py=py34&pytest=2.8.0.dev4 :target: https://bitbucket.org/pytest-dev/pytest-curl-report
`pytest-dbfixtures <http://pypi.python.org/pypi/pytest-dbfixtures>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-dbfixtures-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-dbfixtures-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Databases fixtures plugin for py.test.
:target: http://plugincompat.herokuapp.com/output/pytest-dbfixtures-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-dbfixtures-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/ClearcodeHQ/pytest-dbfixtures
`pytest-dbus-notification <http://pypi.python.org/pypi/pytest-dbus-notification>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-dbus-notification-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-dbus-notification-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png D-BUS notifications for pytest results.
:target: http://plugincompat.herokuapp.com/output/pytest-dbus-notification-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-dbus-notification-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/bmathieu33/pytest-dbus-notification
`pytest-describe <http://pypi.python.org/pypi/pytest-describe>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-describe-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-describe-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Describe-style plugin for pytest
:target: http://plugincompat.herokuapp.com/output/pytest-describe-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-describe-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/ropez/pytest-describe
`pytest-diffeo <http://pypi.python.org/pypi/pytest-diffeo>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-diffeo-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-diffeo-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Common py.test support for Diffeo packages
:target: http://plugincompat.herokuapp.com/output/pytest-diffeo-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-diffeo-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/diffeo/pytest-diffeo
`pytest-django-sqlcounts <http://pypi.python.org/pypi/pytest-django-sqlcounts>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-django-sqlcounts-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-django-sqlcounts-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png py.test plugin for reporting the number of SQLs executed per django testcase.
:target: http://plugincompat.herokuapp.com/output/pytest-django-sqlcounts-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-django-sqlcounts-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/stj/pytest-django-sqlcount
`pytest-django-sqlcount <http://pypi.python.org/pypi/pytest-django-sqlcount>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-django-sqlcount-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-django-sqlcount-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png py.test plugin for reporting the number of SQLs executed per django testcase.
:target: http://plugincompat.herokuapp.com/output/pytest-django-sqlcount-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-django-sqlcount-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/stj/pytest-django-sqlcount
`pytest-django-haystack <http://pypi.python.org/pypi/pytest-django-haystack>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-django-haystack-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-django-haystack-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Cleanup your Haystack indexes between tests
:target: http://plugincompat.herokuapp.com/output/pytest-django-haystack-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-django-haystack-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/rouge8/pytest-django-haystack
`pytest-django-lite <http://pypi.python.org/pypi/pytest-django-lite>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-django-lite-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-django-lite-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png The bare minimum to integrate py.test with Django.
:target: http://plugincompat.herokuapp.com/output/pytest-django-lite-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-django-lite-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/dcramer/pytest-django-lite
`pytest-django <http://pypi.python.org/pypi/pytest-django>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-django-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-django-latest?py=py34&pytest=2.8.0.dev4 `link <http://pytest-django.readthedocs.org/>`_ A Django plugin for py.test.
:target: http://plugincompat.herokuapp.com/output/pytest-django-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-django-latest?py=py34&pytest=2.8.0.dev4
`pytest-doc <http://pypi.python.org/pypi/pytest-doc>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-doc-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-doc-latest?py=py34&pytest=2.8.0.dev4 `link <http://pytest-doc.readthedocs.org/>`_ A documentation plugin for py.test.
:target: http://plugincompat.herokuapp.com/output/pytest-doc-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-doc-latest?py=py34&pytest=2.8.0.dev4
`pytest-dump2json <http://pypi.python.org/pypi/pytest-dump2json>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-dump2json-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-dump2json-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png A pytest plugin for dumping test results to json.
:target: http://plugincompat.herokuapp.com/output/pytest-dump2json-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-dump2json-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/d6e/pytest-dump2json
`pytest-echo <http://pypi.python.org/pypi/pytest-echo>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-echo-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-echo-latest?py=py34&pytest=2.8.0.dev4 `link <http://pypi.python.org/pypi/pytest-echo/>`_ pytest plugin with mechanisms for echoing environment variables, package version and generic attributes
:target: http://plugincompat.herokuapp.com/output/pytest-echo-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-echo-latest?py=py34&pytest=2.8.0.dev4
`pytest-env <http://pypi.python.org/pypi/pytest-env>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-env-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-env-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png py.test plugin that allows you to add environment variables.
:target: http://plugincompat.herokuapp.com/output/pytest-env-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-env-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/MobileDynasty/pytest-env
`pytest-eradicate <http://pypi.python.org/pypi/pytest-eradicate>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-eradicate-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-eradicate-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png pytest plugin to check for commented out code
:target: http://plugincompat.herokuapp.com/output/pytest-eradicate-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-eradicate-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/spil-johan/pytest-eradicate
`pytest-factoryboy <http://pypi.python.org/pypi/pytest-factoryboy>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-factoryboy-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-factoryboy-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Factory Boy support for pytest.
:target: http://plugincompat.herokuapp.com/output/pytest-factoryboy-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-factoryboy-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/pytest-dev/pytest-factoryboy
`pytest-poo-fail <http://pypi.python.org/pypi/pytest-poo-fail>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-poo-fail-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-poo-fail-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Visualize your failed tests with poo
:target: http://plugincompat.herokuapp.com/output/pytest-poo-fail-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-poo-fail-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/alyssa.barela/pytest-poo-fail
`pytest-faker <http://pypi.python.org/pypi/pytest-faker>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-faker-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-faker-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Faker integration for pytest framework.
:target: http://plugincompat.herokuapp.com/output/pytest-faker-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-faker-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/pytest-dev/pytest-faker
`pytest-faulthandler <http://pypi.python.org/pypi/pytest-faulthandler>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-faulthandler-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-faulthandler-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png py.test plugin that activates the fault handler module for tests
:target: http://plugincompat.herokuapp.com/output/pytest-faulthandler-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-faulthandler-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/pytest-dev/pytest-faulthandler
`pytest-fauxfactory <http://pypi.python.org/pypi/pytest-fauxfactory>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-fauxfactory-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-fauxfactory-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Integration of fauxfactory into pytest.
:target: http://plugincompat.herokuapp.com/output/pytest-fauxfactory-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-fauxfactory-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/mfalesni/pytest-fauxfactory
`pytest-figleaf <http://pypi.python.org/pypi/pytest-figleaf>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-figleaf-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-figleaf-latest?py=py34&pytest=2.8.0.dev4 .. image:: bitbucket.png py.test figleaf coverage plugin
:target: http://plugincompat.herokuapp.com/output/pytest-figleaf-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-figleaf-latest?py=py34&pytest=2.8.0.dev4 :target: http://bitbucket.org/hpk42/pytest-figleaf
`pytest-fixture-tools <http://pypi.python.org/pypi/pytest-fixture-tools>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-fixture-tools-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-fixture-tools-latest?py=py34&pytest=2.8.0.dev4 ? Plugin for pytest which provides tools for fixtures
:target: http://plugincompat.herokuapp.com/output/pytest-fixture-tools-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-fixture-tools-latest?py=py34&pytest=2.8.0.dev4
`pytest-flake8 <http://pypi.python.org/pypi/pytest-flake8>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-flake8-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-flake8-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png pytest plugin to check FLAKE8 requirements
:target: http://plugincompat.herokuapp.com/output/pytest-flake8-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-flake8-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/tholo/pytest-flake8
`pytest-flakes <http://pypi.python.org/pypi/pytest-flakes>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-flakes-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-flakes-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png pytest plugin to check source code with pyflakes
:target: http://plugincompat.herokuapp.com/output/pytest-flakes-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-flakes-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/fschulze/pytest-flakes
`pytest-flask <http://pypi.python.org/pypi/pytest-flask>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-flask-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-flask-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png A set of py.test fixtures to test Flask applications.
:target: http://plugincompat.herokuapp.com/output/pytest-flask-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-flask-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/vitalk/pytest-flask
`pytest-gitignore <http://pypi.python.org/pypi/pytest-gitignore>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-gitignore-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-gitignore-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png py.test plugin to ignore the same files as git
:target: http://plugincompat.herokuapp.com/output/pytest-gitignore-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-gitignore-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/tgs/pytest-gitignore
`pytest-greendots <http://pypi.python.org/pypi/pytest-greendots>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-greendots-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-greendots-latest?py=py34&pytest=2.8.0.dev4 ? Green progress dots
:target: http://plugincompat.herokuapp.com/output/pytest-greendots-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-greendots-latest?py=py34&pytest=2.8.0.dev4
`pytest-growl <http://pypi.python.org/pypi/pytest-growl>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-growl-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-growl-latest?py=py34&pytest=2.8.0.dev4 ? Growl notifications for pytest results.
:target: http://plugincompat.herokuapp.com/output/pytest-growl-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-growl-latest?py=py34&pytest=2.8.0.dev4
`pytest-html <http://pypi.python.org/pypi/pytest-html>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-html-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-html-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png pytest plugin for generating HTML reports
:target: http://plugincompat.herokuapp.com/output/pytest-html-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-html-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/davehunt/pytest-html
`pytest-httpbin <http://pypi.python.org/pypi/pytest-httpbin>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-httpbin-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-httpbin-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Easily test your HTTP library against a local copy of httpbin
:target: http://plugincompat.herokuapp.com/output/pytest-httpbin-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-httpbin-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/kevin1024/pytest-httpbin
`pytest-httpretty <http://pypi.python.org/pypi/pytest-httpretty>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-httpretty-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-httpretty-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png A thin wrapper of HTTPretty for pytest
:target: http://plugincompat.herokuapp.com/output/pytest-httpretty-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-httpretty-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/papaeye/pytest-httpretty
`pytest-incremental <http://pypi.python.org/pypi/pytest-incremental>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-incremental-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-incremental-latest?py=py34&pytest=2.8.0.dev4 `link <http://pytest-incremental.readthedocs.org>`_ an incremental test runner (pytest plugin)
:target: http://plugincompat.herokuapp.com/output/pytest-incremental-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-incremental-latest?py=py34&pytest=2.8.0.dev4
`pytest-instafail <http://pypi.python.org/pypi/pytest-instafail>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-instafail-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-instafail-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png py.test plugin to show failures instantly
:target: http://plugincompat.herokuapp.com/output/pytest-instafail-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-instafail-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/jpvanhal/pytest-instafail
`pytest-ipdb <http://pypi.python.org/pypi/pytest-ipdb>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-ipdb-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-ipdb-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png A py.test plug-in to enable drop to ipdb debugger on test failure.
:target: http://plugincompat.herokuapp.com/output/pytest-ipdb-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-ipdb-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/mverteuil/pytest-ipdb
`pytest-ipynb <http://pypi.python.org/pypi/pytest-ipynb>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-ipynb-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-ipynb-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Use pytest's runner to discover and execute tests as cells of IPython notebooks
:target: http://plugincompat.herokuapp.com/output/pytest-ipynb-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-ipynb-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/zonca/pytest-ipynb
`pytest-isort <http://pypi.python.org/pypi/pytest-isort>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-isort-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-isort-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png pytest plugin to perform isort checks (import ordering)
:target: http://plugincompat.herokuapp.com/output/pytest-isort-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-isort-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/moccu/pytest-isort/
`pytest-jira <http://pypi.python.org/pypi/pytest-jira>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-jira-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-jira-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png py.test JIRA integration plugin, using markers
:target: http://plugincompat.herokuapp.com/output/pytest-jira-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-jira-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/jlaska/pytest_jira
`pytest-knows <http://pypi.python.org/pypi/pytest-knows>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-knows-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-knows-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png A pytest plugin that can automaticly skip test case based on dependence info calculated by trace
:target: http://plugincompat.herokuapp.com/output/pytest-knows-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-knows-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/mapix/ptknows
`pytest-konira <http://pypi.python.org/pypi/pytest-konira>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-konira-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-konira-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Run Konira DSL tests with py.test
:target: http://plugincompat.herokuapp.com/output/pytest-konira-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-konira-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/alfredodeza/pytest-konira
`pytest-localserver <http://pypi.python.org/pypi/pytest-localserver>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-localserver-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-localserver-latest?py=py34&pytest=2.8.0.dev4 .. image:: bitbucket.png py.test plugin to test server connections locally.
:target: http://plugincompat.herokuapp.com/output/pytest-localserver-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-localserver-latest?py=py34&pytest=2.8.0.dev4 :target: http://bitbucket.org/basti/pytest-localserver/
`pytest-markfiltration <http://pypi.python.org/pypi/pytest-markfiltration>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-markfiltration-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-markfiltration-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png UNKNOWN
:target: http://plugincompat.herokuapp.com/output/pytest-markfiltration-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-markfiltration-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/adamgoucher/pytest-markfiltration
`pytest-marks <http://pypi.python.org/pypi/pytest-marks>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-marks-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-marks-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png UNKNOWN
:target: http://plugincompat.herokuapp.com/output/pytest-marks-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-marks-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/adamgoucher/pytest-marks
`pytest-mccabe <http://pypi.python.org/pypi/pytest-mccabe>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-mccabe-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-mccabe-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png pytest plugin to run the mccabe code complexity checker.
:target: http://plugincompat.herokuapp.com/output/pytest-mccabe-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-mccabe-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/The-Compiler/pytest-mccabe
`pytest-mock <http://pypi.python.org/pypi/pytest-mock>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-mock-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-mock-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Thin-wrapper around the mock package for easier use with py.test
:target: http://plugincompat.herokuapp.com/output/pytest-mock-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-mock-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/pytest-dev/pytest-mock/
`pytest-monkeyplus <http://pypi.python.org/pypi/pytest-monkeyplus>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-monkeyplus-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-monkeyplus-latest?py=py34&pytest=2.8.0.dev4 .. image:: bitbucket.png pytest's monkeypatch subclass with extra functionalities
:target: http://plugincompat.herokuapp.com/output/pytest-monkeyplus-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-monkeyplus-latest?py=py34&pytest=2.8.0.dev4 :target: http://bitbucket.org/hsoft/pytest-monkeyplus/
`pytest-mozwebqa <http://pypi.python.org/pypi/pytest-mozwebqa>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-mozwebqa-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-mozwebqa-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Mozilla WebQA plugin for py.test.
:target: http://plugincompat.herokuapp.com/output/pytest-mozwebqa-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-mozwebqa-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/mozilla/pytest-mozwebqa
`pytest-mpl <http://pypi.python.org/pypi/pytest-mpl>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-mpl-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-mpl-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png pytest plugin to help with testing figures output from Matplotlib
:target: http://plugincompat.herokuapp.com/output/pytest-mpl-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-mpl-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/astrofrog/pytest-mpl
`pytest-multihost <http://pypi.python.org/pypi/pytest-multihost>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-multihost-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-multihost-latest?py=py34&pytest=2.8.0.dev4 `link <https://fedorahosted.org/python-pytest-multihost/>`_ Utility for writing multi-host tests for pytest
:target: http://plugincompat.herokuapp.com/output/pytest-multihost-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-multihost-latest?py=py34&pytest=2.8.0.dev4
`pytest-oerp <http://pypi.python.org/pypi/pytest-oerp>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-oerp-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-oerp-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png pytest plugin to test OpenERP modules
:target: http://plugincompat.herokuapp.com/output/pytest-oerp-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-oerp-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/santagada/pytest-oerp/
`pytest-oot <http://pypi.python.org/pypi/pytest-oot>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-oot-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-oot-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Run object-oriented tests in a simple format
:target: http://plugincompat.herokuapp.com/output/pytest-oot-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-oot-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/steven004/pytest_oot
`pytest-optional <http://pypi.python.org/pypi/pytest-optional>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-optional-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-optional-latest?py=py34&pytest=2.8.0.dev4 .. image:: bitbucket.png include/exclude values of fixtures in pytest
:target: http://plugincompat.herokuapp.com/output/pytest-optional-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-optional-latest?py=py34&pytest=2.8.0.dev4 :target: http://bitbucket.org/maho/pytest-optional
`pytest-ordering <http://pypi.python.org/pypi/pytest-ordering>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-ordering-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-ordering-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png pytest plugin to run your tests in a specific order
:target: http://plugincompat.herokuapp.com/output/pytest-ordering-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-ordering-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/ftobia/pytest-ordering
`pytest-osxnotify <http://pypi.python.org/pypi/pytest-osxnotify>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-osxnotify-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-osxnotify-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png OS X notifications for py.test results.
:target: http://plugincompat.herokuapp.com/output/pytest-osxnotify-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-osxnotify-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/dbader/pytest-osxnotify
`pytest-pep257 <http://pypi.python.org/pypi/pytest-pep257>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-pep257-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-pep257-latest?py=py34&pytest=2.8.0.dev4 ? py.test plugin for pep257
:target: http://plugincompat.herokuapp.com/output/pytest-pep257-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-pep257-latest?py=py34&pytest=2.8.0.dev4
`pytest-pep8 <http://pypi.python.org/pypi/pytest-pep8>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-pep8-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-pep8-latest?py=py34&pytest=2.8.0.dev4 .. image:: bitbucket.png pytest plugin to check PEP8 requirements
:target: http://plugincompat.herokuapp.com/output/pytest-pep8-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-pep8-latest?py=py34&pytest=2.8.0.dev4 :target: http://bitbucket.org/hpk42/pytest-pep8/
`pytest-pipeline <http://pypi.python.org/pypi/pytest-pipeline>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-pipeline-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-pipeline-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Pytest plugin for functional testing of data analysis pipelines
:target: http://plugincompat.herokuapp.com/output/pytest-pipeline-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-pipeline-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/bow/pytest-pipeline
`pytest-poo <http://pypi.python.org/pypi/pytest-poo>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-poo-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-poo-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Visualize your crappy tests
:target: http://plugincompat.herokuapp.com/output/pytest-poo-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-poo-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/pelme/pytest-poo
`pytest-proper-wheel <http://pypi.python.org/pypi/pytest-proper-wheel>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-proper-wheel-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-proper-wheel-latest?py=py34&pytest=2.8.0.dev4 `link <http://pytest.org>`_ pytest: simple powerful testing with Python
:target: http://plugincompat.herokuapp.com/output/pytest-proper-wheel-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-proper-wheel-latest?py=py34&pytest=2.8.0.dev4
`pytest-purkinje <http://pypi.python.org/pypi/pytest-purkinje>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-purkinje-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-purkinje-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png py.test plugin for purkinje test runner
:target: http://plugincompat.herokuapp.com/output/pytest-purkinje-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-purkinje-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/bbiskup
`pytest-pycharm <http://pypi.python.org/pypi/pytest-pycharm>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-pycharm-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-pycharm-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Plugin for py.test to enter PyCharm debugger on uncaught exceptions
:target: http://plugincompat.herokuapp.com/output/pytest-pycharm-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-pycharm-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/jlubcke/pytest-pycharm
`pytest-pydev <http://pypi.python.org/pypi/pytest-pydev>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-pydev-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-pydev-latest?py=py34&pytest=2.8.0.dev4 .. image:: bitbucket.png py.test plugin to connect to a remote debug server with PyDev or PyCharm.
:target: http://plugincompat.herokuapp.com/output/pytest-pydev-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-pydev-latest?py=py34&pytest=2.8.0.dev4 :target: http://bitbucket.org/basti/pytest-pydev/
`pytest-pylint <http://pypi.python.org/pypi/pytest-pylint>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-pylint-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-pylint-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png pytest plugin to check source code with pylint
:target: http://plugincompat.herokuapp.com/output/pytest-pylint-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-pylint-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/carsongee/pytest-pylint
`pytest-pyq <http://pypi.python.org/pypi/pytest-pyq>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-pyq-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-pyq-latest?py=py34&pytest=2.8.0.dev4 `link <http://pyq.enlnt.com>`_ Pytest fixture "q" for pyq
:target: http://plugincompat.herokuapp.com/output/pytest-pyq-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-pyq-latest?py=py34&pytest=2.8.0.dev4
`pytest-sftpserver <http://pypi.python.org/pypi/pytest-sftpserver>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-sftpserver-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-sftpserver-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png py.test plugin to locally test sftp server connections.
:target: http://plugincompat.herokuapp.com/output/pytest-sftpserver-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-sftpserver-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/ulope/pytest-sftpserver/
`pytest-rage <http://pypi.python.org/pypi/pytest-rage>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-rage-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-rage-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png pytest plugin to implement PEP712
:target: http://plugincompat.herokuapp.com/output/pytest-rage-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-rage-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/santagada/pytest-rage/
`pytest-smartcov <http://pypi.python.org/pypi/pytest-smartcov>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-smartcov-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-smartcov-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Smart coverage plugin for pytest.
:target: http://plugincompat.herokuapp.com/output/pytest-smartcov-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-smartcov-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/carljm/pytest-smartcov/
`pytest-variables <http://pypi.python.org/pypi/pytest-variables>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-variables-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-variables-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png pytest plugin for providing variables to tests/fixtures
:target: http://plugincompat.herokuapp.com/output/pytest-variables-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-variables-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/davehunt/pytest-variables
`pytest-selenium <http://pypi.python.org/pypi/pytest-selenium>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-selenium-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-selenium-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png A selenium plugin for pytest
:target: http://plugincompat.herokuapp.com/output/pytest-selenium-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-selenium-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/codingjoe/pytest-selenium
`pytest-readme <http://pypi.python.org/pypi/pytest-readme>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-readme-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-readme-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Test your README.md file
:target: http://plugincompat.herokuapp.com/output/pytest-readme-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-readme-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/boxed/pytest-readme
`pytest-translations <http://pypi.python.org/pypi/pytest-translations>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-translations-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-translations-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Test your translation files
:target: http://plugincompat.herokuapp.com/output/pytest-translations-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-translations-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/thermondo/pytest-translations
`pytest-xprocess <http://pypi.python.org/pypi/pytest-xprocess>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-xprocess-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-xprocess-latest?py=py34&pytest=2.8.0.dev4 .. image:: bitbucket.png pytest plugin to manage external processes across test runs
:target: http://plugincompat.herokuapp.com/output/pytest-xprocess-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-xprocess-latest?py=py34&pytest=2.8.0.dev4 :target: http://bitbucket.org/hpk42/pytest-xprocess/
`pytest-random <http://pypi.python.org/pypi/pytest-random>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-random-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-random-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png py.test plugin to randomize tests
:target: http://plugincompat.herokuapp.com/output/pytest-random-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-random-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/klrmn/pytest-random
`pytest-sourceorder <http://pypi.python.org/pypi/pytest-sourceorder>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-sourceorder-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-sourceorder-latest?py=py34&pytest=2.8.0.dev4 `link <https://fedorahosted.org/python-pytest-sourceorder/>`_ Test-ordering plugin for pytest
:target: http://plugincompat.herokuapp.com/output/pytest-sourceorder-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-sourceorder-latest?py=py34&pytest=2.8.0.dev4
`pytest-zap <http://pypi.python.org/pypi/pytest-zap>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-zap-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-zap-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png OWASP ZAP plugin for py.test.
:target: http://plugincompat.herokuapp.com/output/pytest-zap-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-zap-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/davehunt/pytest-zap
`pytest-raisesregexp <http://pypi.python.org/pypi/pytest-raisesregexp>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-raisesregexp-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-raisesregexp-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Simple pytest plugin to look for regex in Exceptions
:target: http://plugincompat.herokuapp.com/output/pytest-raisesregexp-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-raisesregexp-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/Walkman/pytest_raisesregexp
`pytest-trialtemp <http://pypi.python.org/pypi/pytest-trialtemp>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-trialtemp-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-trialtemp-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png py.test plugin for using the same _trial_temp working directory as trial
:target: http://plugincompat.herokuapp.com/output/pytest-trialtemp-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-trialtemp-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/jerith/pytest-trialtemp
`pytest-sftpserver <http://pypi.python.org/pypi/pytest-sftpserver>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-sftpserver-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-sftpserver-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png py.test plugin to locally test sftp server connections.
:target: http://plugincompat.herokuapp.com/output/pytest-sftpserver-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-sftpserver-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/ulope/pytest-sftpserver/
`pytest-rerunfailures <http://pypi.python.org/pypi/pytest-rerunfailures>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-rerunfailures-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-rerunfailures-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png py.test plugin to re-run tests to eliminate flakey failures
:target: http://plugincompat.herokuapp.com/output/pytest-rerunfailures-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-rerunfailures-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/klrmn/pytest-rerunfailures
`pytest-spec <http://pypi.python.org/pypi/pytest-spec>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-spec-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-spec-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png pytest plugin to display test execution output like a SPECIFICATION
:target: http://plugincompat.herokuapp.com/output/pytest-spec-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-spec-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/pchomik/pytest-spec
`pytest-testmon <http://pypi.python.org/pypi/pytest-testmon>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-testmon-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-testmon-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png take TDD to a new level with py.test and testmon
:target: http://plugincompat.herokuapp.com/output/pytest-testmon-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-testmon-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/tarpas/pytest-testmon/
`pytest-sftpserver <http://pypi.python.org/pypi/pytest-sftpserver>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-sftpserver-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-sftpserver-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png py.test plugin to locally test sftp server connections.
:target: http://plugincompat.herokuapp.com/output/pytest-sftpserver-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-sftpserver-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/ulope/pytest-sftpserver/
`pytest-stepwise <http://pypi.python.org/pypi/pytest-stepwise>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-stepwise-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-stepwise-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Run a test suite one failing test at a time.
:target: http://plugincompat.herokuapp.com/output/pytest-stepwise-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-stepwise-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/nip3o/pytest-stepwise
`pytest-runfailed <http://pypi.python.org/pypi/pytest-runfailed>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-runfailed-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-runfailed-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png implement a --failed option for pytest
:target: http://plugincompat.herokuapp.com/output/pytest-runfailed-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-runfailed-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/dmerejkowsky/pytest-runfailed
`pytest-tornado <http://pypi.python.org/pypi/pytest-tornado>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-tornado-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-tornado-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png A py.test plugin providing fixtures and markers to simplify testing of asynchronous tornado applications.
:target: http://plugincompat.herokuapp.com/output/pytest-tornado-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-tornado-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/eugeniy/pytest-tornado
`pytest-sftpserver <http://pypi.python.org/pypi/pytest-sftpserver>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-sftpserver-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-sftpserver-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png py.test plugin to locally test sftp server connections.
:target: http://plugincompat.herokuapp.com/output/pytest-sftpserver-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-sftpserver-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/ulope/pytest-sftpserver/
`pytest-timeout <http://pypi.python.org/pypi/pytest-timeout>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-timeout-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-timeout-latest?py=py34&pytest=2.8.0.dev4 .. image:: bitbucket.png py.test plugin to abort hanging tests
:target: http://plugincompat.herokuapp.com/output/pytest-timeout-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-timeout-latest?py=py34&pytest=2.8.0.dev4 :target: http://bitbucket.org/flub/pytest-timeout/
`pytest-sftpserver <http://pypi.python.org/pypi/pytest-sftpserver>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-sftpserver-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-sftpserver-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png py.test plugin to locally test sftp server connections.
:target: http://plugincompat.herokuapp.com/output/pytest-sftpserver-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-sftpserver-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/ulope/pytest-sftpserver/
`pytest-ubersmith <http://pypi.python.org/pypi/pytest-ubersmith>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-ubersmith-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-ubersmith-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Easily mock calls to ubersmith at the `requests` level.
:target: http://plugincompat.herokuapp.com/output/pytest-ubersmith-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-ubersmith-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/hivelocity/pytest-ubersmith
`pytest-services <http://pypi.python.org/pypi/pytest-services>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-services-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-services-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Services plugin for pytest testing framework
:target: http://plugincompat.herokuapp.com/output/pytest-services-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-services-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/pytest-dev/pytest-services
`pytest-pythonpath <http://pypi.python.org/pypi/pytest-pythonpath>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-pythonpath-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-pythonpath-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png pytest plugin for adding to the PYTHONPATH from command line or configs.
:target: http://plugincompat.herokuapp.com/output/pytest-pythonpath-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-pythonpath-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/bigsassy/pytest-pythonpath
`pytest-yamlwsgi <http://pypi.python.org/pypi/pytest-yamlwsgi>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-yamlwsgi-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-yamlwsgi-latest?py=py34&pytest=2.8.0.dev4 ? Run tests against wsgi apps defined in yaml
:target: http://plugincompat.herokuapp.com/output/pytest-yamlwsgi-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-yamlwsgi-latest?py=py34&pytest=2.8.0.dev4
`pytest-trello <http://pypi.python.org/pypi/pytest-trello>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-trello-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-trello-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Plugin for py.test that integrates trello using markers
:target: http://plugincompat.herokuapp.com/output/pytest-trello-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-trello-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/jlaska/pytest-trello
`pytest-quickcheck <http://pypi.python.org/pypi/pytest-quickcheck>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-quickcheck-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-quickcheck-latest?py=py34&pytest=2.8.0.dev4 .. image:: bitbucket.png pytest plugin to generate random data inspired by QuickCheck
:target: http://plugincompat.herokuapp.com/output/pytest-quickcheck-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-quickcheck-latest?py=py34&pytest=2.8.0.dev4 :target: https://bitbucket.org/pytest-dev/pytest-quickcheck
`pytest-twisted <http://pypi.python.org/pypi/pytest-twisted>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-twisted-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-twisted-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png A twisted plugin for py.test.
:target: http://plugincompat.herokuapp.com/output/pytest-twisted-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-twisted-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/schmir/pytest-twisted
`pytest-sftpserver <http://pypi.python.org/pypi/pytest-sftpserver>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-sftpserver-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-sftpserver-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png py.test plugin to locally test sftp server connections.
:target: http://plugincompat.herokuapp.com/output/pytest-sftpserver-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-sftpserver-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/ulope/pytest-sftpserver/
`pytest-watch <http://pypi.python.org/pypi/pytest-watch>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-watch-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-watch-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Local continuous test runner with pytest and watchdog.
:target: http://plugincompat.herokuapp.com/output/pytest-watch-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-watch-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/joeyespo/pytest-watch
`pytest-unmarked <http://pypi.python.org/pypi/pytest-unmarked>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-unmarked-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-unmarked-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Run only unmarked tests
:target: http://plugincompat.herokuapp.com/output/pytest-unmarked-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-unmarked-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/alyssa.barela/pytest-unmarked
`pytest-regtest <http://pypi.python.org/pypi/pytest-regtest>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-regtest-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-regtest-latest?py=py34&pytest=2.8.0.dev4 `link <https://sissource.ethz.ch/uweschmitt/pytest-regtest/tree/master>`_ py.test plugin for regression tests
:target: http://plugincompat.herokuapp.com/output/pytest-regtest-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-regtest-latest?py=py34&pytest=2.8.0.dev4
`pytest-xdist <http://pypi.python.org/pypi/pytest-xdist>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-xdist-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-xdist-latest?py=py34&pytest=2.8.0.dev4 .. image:: bitbucket.png py.test xdist plugin for distributed testing and loop-on-failing modes
:target: http://plugincompat.herokuapp.com/output/pytest-xdist-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-xdist-latest?py=py34&pytest=2.8.0.dev4 :target: http://bitbucket.org/hpk42/pytest-xdist
`pytest-sugar <http://pypi.python.org/pypi/pytest-sugar>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-sugar-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-sugar-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png py.test is a plugin for py.test that changes the default look and feel of py.test (e.g. progressbar, show tests that fail instantly).
:target: http://plugincompat.herokuapp.com/output/pytest-sugar-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-sugar-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/Frozenball/pytest-sugar
`pytest-qt <http://pypi.python.org/pypi/pytest-qt>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-qt-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-qt-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png pytest support for PyQt and PySide applications
:target: http://plugincompat.herokuapp.com/output/pytest-qt-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-qt-latest?py=py34&pytest=2.8.0.dev4 :target: http://github.com/pytest-dev/pytest-qt
`pytest-runner <http://pypi.python.org/pypi/pytest-runner>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-runner-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-runner-latest?py=py34&pytest=2.8.0.dev4 .. image:: bitbucket.png Invoke py.test as distutils command with dependency resolution.
:target: http://plugincompat.herokuapp.com/output/pytest-runner-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-runner-latest?py=py34&pytest=2.8.0.dev4 :target: https://bitbucket.org/pytest-dev/pytest-runner
`pytest-splinter <http://pypi.python.org/pypi/pytest-splinter>`_ .. image:: http://plugincompat.herokuapp.com/status/pytest-splinter-latest?py=py27&pytest=2.8.0.dev4 .. image:: http://plugincompat.herokuapp.com/status/pytest-splinter-latest?py=py34&pytest=2.8.0.dev4 .. image:: github.png Splinter plugin for pytest testing framework
:target: http://plugincompat.herokuapp.com/output/pytest-splinter-latest?py=py27&pytest=2.8.0.dev4 :target: http://plugincompat.herokuapp.com/output/pytest-splinter-latest?py=py34&pytest=2.8.0.dev4 :target: https://github.com/pytest-dev/pytest-splinter
============================================================================================ ===================================================================================================================== ===================================================================================================================== =========================================================================== =============================================================================================================================================
*(Updated on 2015-06-30)*

View File

@@ -1,307 +0,0 @@
"""
Script to generate the file `index.txt` with information about
pytest plugins taken directly from PyPI.
Usage:
python plugins_index.py
This command will update `index.txt` in the same directory found as this script.
This should be issued before every major documentation release to obtain latest
versions from PyPI.
Also includes plugin compatibility between different python and pytest versions,
obtained from http://plugincompat.herokuapp.com.
"""
from __future__ import print_function
from collections import namedtuple
import datetime
from distutils.version import LooseVersion
import itertools
from optparse import OptionParser
import os
import sys
import pytest
def get_proxy(url):
"""
wrapper function to obtain a xmlrpc proxy, taking in account import
differences between python 2.X and 3.X
:param url: url to bind the proxy to
:return: a ServerProxy instance
"""
if sys.version_info < (3, 0):
from xmlrpclib import ServerProxy
else:
from xmlrpc.client import ServerProxy
return ServerProxy(url)
def iter_plugins(client):
"""
Returns an iterator of (name, version) from PyPI.
:param client: ServerProxy
:param search: package names to search for
"""
for plug_data in client.search({'name': 'pytest'}):
if plug_data['name'].startswith('pytest-'):
yield plug_data['name'], plug_data['version']
def get_latest_versions(plugins):
"""
Returns an iterator of (name, version) from the given list of (name,
version), but returning only the latest version of the package. Uses
distutils.LooseVersion to ensure compatibility with PEP386.
"""
plugins = [(name, LooseVersion(version)) for (name, version) in plugins]
for name, grouped_plugins in itertools.groupby(plugins, key=lambda x: x[0]):
name, loose_version = list(grouped_plugins)[-1]
yield name, str(loose_version)
def obtain_plugins_table(plugins, client, verbose, pytest_ver):
"""
Returns information to populate a table of plugins, their versions,
authors, etc.
The returned information is a list of columns of `ColumnData`
namedtuples(text, link). Link can be None if the text for that column
should not be linked to anything.
:param plugins: list of (name, version)
:param client: ServerProxy
:param verbose: print plugin name and version as they are fetch
:param pytest_ver: pytest version to use.
"""
if pytest_ver is None:
pytest_ver = pytest.__version__
def get_repo_markup(repo):
"""
obtains appropriate markup for the given repository, as two lines
that should be output in the same table row. We use this to display an icon
for known repository hosts (github, etc), just a "?" char when
repository is not registered in pypi or a simple link otherwise.
"""
target = repo
if 'github.com' in repo:
image = 'github.png'
elif 'bitbucket.org' in repo:
image = 'bitbucket.png'
elif repo.lower() == 'unknown':
return '?', ''
else:
image = None
if image is not None:
image_markup = '.. image:: %s' % image
target_markup = ' :target: %s' % repo
pad_right = ('%-' + str(len(target_markup)) + 's')
return pad_right % image_markup, target_markup
else:
return ('`link <%s>`_' % target), ''
def sanitize_summary(summary):
"""Make sure summaries don't break our table formatting.
"""
return summary.replace('\n', ' ')
rows = []
ColumnData = namedtuple('ColumnData', 'text link')
headers = ['Name', 'Py27', 'Py34', 'Home', 'Summary']
repositories = obtain_override_repositories()
print('Generating plugins_index page (pytest-{0})'.format(pytest_ver))
plugins = list(plugins)
for index, (package_name, version) in enumerate(plugins):
if verbose:
print(package_name, version, '...', end='')
release_data = client.release_data(package_name, version)
common_params = dict(
site='http://plugincompat.herokuapp.com',
name=package_name,
version=version)
repository = repositories.get(package_name, release_data['home_page'])
repo_markup_1, repo_markup_2 = get_repo_markup(repository)
# first row: name, images and simple links
url = '.. image:: {site}/status/{name}-latest'
image_url = url.format(**common_params)
image_url += '?py={py}&pytest={pytest}'
row = (
ColumnData(package_name, release_data['package_url']),
ColumnData(image_url.format(py='py27', pytest=pytest_ver),
None),
ColumnData(image_url.format(py='py34', pytest=pytest_ver),
None),
ColumnData(
repo_markup_1,
None),
ColumnData(sanitize_summary(release_data['summary']), None),
)
assert len(row) == len(headers)
rows.append(row)
# second row: links for images (they should be in their own line)
url = ' :target: {site}/output/{name}-latest'
output_url = url.format(**common_params)
output_url += '?py={py}&pytest={pytest}'
row = (
ColumnData('', None),
ColumnData(output_url.format(py='py27', pytest=pytest_ver),
None),
ColumnData(output_url.format(py='py34', pytest=pytest_ver),
None),
ColumnData(repo_markup_2, None),
ColumnData('', None),
)
assert len(row) == len(headers)
rows.append(row)
if verbose:
print('OK (%d%%)' % ((index + 1) * 100 / len(plugins)))
print('Done: %d plugins' % len(plugins))
return headers, rows
def obtain_override_repositories():
"""
Used to override the "home_page" obtained from pypi to known
package repositories. Used when the author didn't fill the "home_page"
field in setup.py.
:return: dict of {package_name: repository_url}
"""
return {
'pytest-blockage': 'https://github.com/rob-b/pytest-blockage',
'pytest-konira': 'http://github.com/alfredodeza/pytest-konira',
'pytest-sugar': 'https://github.com/Frozenball/pytest-sugar',
}
def generate_plugins_index_from_table(filename, headers, rows, pytest_ver):
"""
Generates a RST file with the table data given.
:param filename: output filename
:param headers: see `obtain_plugins_table`
:param rows: see `obtain_plugins_table`
:param pytest_ver: see `obtain_plugins_table`
"""
# creates a list of rows, each being a str containing appropriate column
# text and link
table_texts = []
for row in rows:
column_texts = []
for i, col_data in enumerate(row):
text = '`%s <%s>`_' % (
col_data.text,
col_data.link) if col_data.link else col_data.text
column_texts.append(text)
table_texts.append(column_texts)
# compute max length of each column so we can build the rst table
column_lengths = [len(x) for x in headers]
for column_texts in table_texts:
for i, row_text in enumerate(column_texts):
column_lengths[i] = max(column_lengths[i], len(row_text) + 2)
def get_row_limiter(char):
return ' '.join(char * length for length in column_lengths)
with open(filename, 'w') as f:
# header
print(HEADER, file=f)
print(file=f)
# table
print(get_row_limiter('='), file=f)
formatted_headers = [
'{0:^{fill}}'.format(header, fill=column_lengths[i])
for i, header in enumerate(headers)]
print(*formatted_headers, file=f)
print(get_row_limiter('='), file=f)
for column_texts in table_texts:
formatted_rows = [
'{0:^{fill}}'.format(row_text, fill=column_lengths[i])
for i, row_text in enumerate(column_texts)
]
print(*formatted_rows, file=f)
print(file=f)
print(get_row_limiter('='), file=f)
print(file=f)
today = datetime.date.today().strftime('%Y-%m-%d')
print('*(Updated on %s)*' % today, file=f)
def generate_plugins_index(client, filename, verbose, pytest_ver):
"""
Generates an RST file with a table of the latest pytest plugins found in
PyPI.
:param client: ServerProxy
:param filename: output filename
:param verbose: print name and version of each plugin as they are fetch
:param pytest_ver: pytest version to use; if not given, use current pytest
version.
"""
plugins = get_latest_versions(iter_plugins(client))
headers, rows = obtain_plugins_table(plugins, client, verbose, pytest_ver)
generate_plugins_index_from_table(filename, headers, rows, pytest_ver)
def main(argv):
"""
Script entry point. Configures an option parser and calls the appropriate
internal function.
"""
filename = os.path.join(os.path.dirname(__file__), 'index.txt')
url = 'http://pypi.python.org/pypi'
parser = OptionParser(
description='Generates a restructured document of pytest plugins from PyPI')
parser.add_option('-f', '--filename', default=filename,
help='output filename [default: %default]')
parser.add_option('-u', '--url', default=url,
help='url of PyPI server to obtain data from [default: %default]')
parser.add_option('-v', '--verbose', default=False, action='store_true',
help='verbose output')
parser.add_option('--pytest-ver', default=None, action='store',
help='generate index for this pytest version (default current version)')
(options, _) = parser.parse_args(argv[1:])
client = get_proxy(options.url)
generate_plugins_index(client, options.filename, options.verbose, options.pytest_ver)
print()
print('%s updated.' % options.filename)
return 0
# header for the plugins_index page
HEADER = '''.. _plugins_index:
List of Third-Party Plugins
===========================
The table below contains a listing of plugins found in PyPI and
their status when tested when using latest py.test and python versions.
A complete listing can also be found at
`plugincompat <http://plugincompat.herokuapp.com/>`_, which contains tests
status against other py.test releases.
'''
if __name__ == '__main__':
sys.exit(main(sys.argv))

View File

@@ -57,7 +57,7 @@ Here are some examples of projects using ``pytest`` (please send notes via :ref:
* `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>`_
* `pyudev <http://pyudev.readthedocs.org/en/latest/tests/plugins.html>`_ a pure Python binding to the Linux library libudev
* `pyudev <https://pyudev.readthedocs.io/en/latest/tests/plugins.html>`_ a pure Python binding to the Linux library libudev
* `pytest-localserver <https://bitbucket.org/basti/pytest-localserver/>`_ a plugin for pytest that provides a httpserver and smtpserver
* `pytest-monkeyplus <http://pypi.python.org/pypi/pytest-monkeyplus/>`_ a plugin that extends monkeypatch

View File

@@ -41,6 +41,10 @@ additional information::
Alternatively, you can examine raised warnings in detail using the
:ref:`recwarn <recwarn>` fixture (see below).
.. note::
``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated
differently; see :ref:`ensuring_function_triggers`.
.. _recwarn:
Recording warnings
@@ -87,6 +91,9 @@ Each recorded warning has the attributes ``message``, ``category``,
class of the warning. The ``message`` is the warning itself; calling
``str(message)`` will return the actual message of the warning.
.. note::
``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated
differently; see :ref:`ensuring_function_triggers`.
.. _ensuring_function_triggers:
@@ -94,16 +101,17 @@ Ensuring a function triggers a deprecation warning
-------------------------------------------------------
You can also call a global helper for checking
that a certain function call triggers a ``DeprecationWarning``::
that a certain function call triggers a ``DeprecationWarning`` or
``PendingDeprecationWarning``::
import pytest
def test_global():
pytest.deprecated_call(myfunction, 17)
By default, deprecation warnings will not be caught when using ``pytest.warns``
or ``recwarn``, since the default Python warnings filters hide
DeprecationWarnings. If you wish to record them in your own code, use the
By default, ``DeprecationWarning`` and ``PendingDeprecationWarning`` will not be
caught when using ``pytest.warns`` or ``recwarn`` because default Python warnings filters hide
them. If you wish to record them in your own code, use the
command ``warnings.simplefilter('always')``::
import warnings
@@ -114,3 +122,9 @@ command ``warnings.simplefilter('always')``::
warnings.warn("deprecated", DeprecationWarning)
assert len(recwarn) == 1
assert recwarn.pop(DeprecationWarning)
You can also use it as a contextmanager::
def test_global():
with pytest.deprecated_call():
myobject.deprecated_method()

View File

@@ -29,8 +29,23 @@ corresponding to the "short" letters shown in the test progress::
Marking a test function to be skipped
-------------------------------------------
.. versionadded:: 2.9
The simplest way to skip a test function is to mark it with the ``skip`` decorator
which may be passed an optional ``reason``:
.. code-block:: python
@pytest.mark.skip(reason="no way of currently testing this")
def test_the_unknown():
...
``skipif``
~~~~~~~~~~
.. versionadded:: 2.0, 2.4
If you wish to skip something conditionally then you can use ``skipif`` instead.
Here is an example of marking a test function to be skipped
when run on a Python3.3 interpreter::
@@ -79,12 +94,10 @@ between test modules so it's no longer advertised as the primary method.
Skip all test functions of a class or module
---------------------------------------------
As with all function :ref:`marking <mark>` you can skip test functions at the
`whole class- or module level`_. If your code targets python2.6 or above you
use the skipif decorator (and any other marker) on classes::
You can use the ``skipif`` decorator (and any other marker) on classes::
@pytest.mark.skipif(sys.platform != 'win32',
reason="requires windows")
@pytest.mark.skipif(sys.platform == 'win32',
reason="does not run on windows")
class TestPosixCalls:
def test_function(self):
@@ -93,19 +106,6 @@ use the skipif decorator (and any other marker) on classes::
If the condition is true, this marker will produce a skip result for
each of the test methods.
If your code targets python2.5 where class-decorators are not available,
you can set the ``pytestmark`` attribute of a class::
class TestPosixCalls:
pytestmark = pytest.mark.skipif(sys.platform != 'win32',
reason="requires Windows")
def test_function(self):
"will not be setup or run under 'win32' platform"
As with the class-decorator, the ``pytestmark`` special name tells
``pytest`` to apply it to each test function in the class.
If you want to skip all test functions of a module, you must use
the ``pytestmark`` name on the global level:
@@ -125,7 +125,7 @@ Mark a test function as expected to fail
-------------------------------------------------------
You can use the ``xfail`` marker to indicate that you
expect the test to fail::
expect a test to fail::
@pytest.mark.xfail
def test_function():
@@ -133,14 +133,36 @@ expect the test to fail::
This test will be run but no traceback will be reported
when it fails. Instead terminal reporting will list it in the
"expected to fail" or "unexpectedly passing" sections.
"expected to fail" (``XFAIL``) or "unexpectedly passing" (``XPASS``) sections.
By specifying on the commandline::
``strict`` parameter
~~~~~~~~~~~~~~~~~~~~
pytest --runxfail
.. versionadded:: 2.9
you can force the running and reporting of an ``xfail`` marked test
as if it weren't marked at all.
Both ``XFAIL`` and ``XPASS`` don't fail the test suite, unless the ``strict`` keyword-only
parameter is passed as ``True``:
.. code-block:: python
@pytest.mark.xfail(strict=True)
def test_function():
...
This will make ``XPASS`` ("unexpectedly passing") results from this test to fail the test suite.
You can change the default value of the ``strict`` parameter using the
``xfail_strict`` ini option:
.. code-block:: ini
[pytest]
xfail_strict=true
``reason`` parameter
~~~~~~~~~~~~~~~~~~~~
As with skipif_ you can also mark your expectation of a failure
on a particular platform::
@@ -150,14 +172,51 @@ on a particular platform::
def test_function():
...
``raises`` parameter
~~~~~~~~~~~~~~~~~~~~
If you want to be more specific as to why the test is failing, you can specify
a single exception, or a list of exceptions, in the ``raises`` argument. Then
the test will be reported as a regular failure if it fails with an
a single exception, or a list of exceptions, in the ``raises`` argument.
.. code-block:: python
@pytest.mark.xfail(raises=RuntimeError)
def test_function():
...
Then the test will be reported as a regular failure if it fails with an
exception not mentioned in ``raises``.
You can furthermore prevent the running of an "xfail" test or
specify a reason such as a bug ID or similar. Here is
a simple test file with the several usages:
``run`` parameter
~~~~~~~~~~~~~~~~~
If a test should be marked as xfail and reported as such but should not be
even executed, use the ``run`` parameter as ``False``:
.. code-block:: python
@pytest.mark.xfail(run=False)
def test_function():
...
This is specially useful for marking crashing tests for later inspection.
Ignoring xfail marks
~~~~~~~~~~~~~~~~~~~~
By specifying on the commandline::
pytest --runxfail
you can force the running and reporting of an ``xfail`` marked test
as if it weren't marked at all.
Examples
~~~~~~~~
Here is a simple test file with the several usages:
.. literalinclude:: example/xfail_demo.py
@@ -165,7 +224,7 @@ Running it with the report-on-xfail option gives this output::
example $ py.test -rx xfail_demo.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR/example, inifile:
collected 7 items
@@ -186,6 +245,18 @@ Running it with the report-on-xfail option gives this output::
======= 7 xfailed in 0.12 seconds ========
xfail signature summary
~~~~~~~~~~~~~~~~~~~~~~~
Here's the signature of the ``xfail`` marker, using Python 3 keyword-only
arguments syntax:
.. code-block:: python
def xfail(condition=None, *, reason=None, raises=None, run=True, strict=False):
.. _`skip/xfail with parametrize`:
Skip/xfail with parametrize
@@ -194,19 +265,19 @@ Skip/xfail with parametrize
It is possible to apply markers like skip and xfail to individual
test instances when using parametrize::
import pytest
import pytest
@pytest.mark.parametrize(("n", "expected"), [
(1, 2),
pytest.mark.xfail((1, 0)),
pytest.mark.xfail(reason="some bug")((1, 3)),
(2, 3),
(3, 4),
(4, 5),
pytest.mark.skipif("sys.version_info >= (3,0)")((10, 11)),
])
def test_increment(n, expected):
assert n + 1 == expected
@pytest.mark.parametrize(("n", "expected"), [
(1, 2),
pytest.mark.xfail((1, 0)),
pytest.mark.xfail(reason="some bug")((1, 3)),
(2, 3),
(3, 4),
(4, 5),
pytest.mark.skipif("sys.version_info >= (3,0)")((10, 11)),
])
def test_increment(n, expected):
assert n + 1 == expected
Imperative xfail from within a test or setup function

View File

@@ -4,7 +4,7 @@ Talks and Tutorials
.. sidebar:: Next Open Trainings
`professional testing with pytest and tox <http://www.python-academy.com/courses/specialtopics/python_course_testing.html>`_, 24-26th November 2014, Freiburg, Germany
`professional testing with pytest and tox <http://www.python-academy.com/courses/specialtopics/python_course_testing.html>`_, 27-29th June 2016, Freiburg, Germany
.. _`funcargs`: funcargs.html
@@ -14,6 +14,12 @@ Talks and blog postings
.. _`tutorial1 repository`: http://bitbucket.org/pytest-dev/pytest-tutorial1/
.. _`pycon 2010 tutorial PDF`: http://bitbucket.org/pytest-dev/pytest-tutorial1/raw/tip/pytest-basic.pdf
- `pytest - Rapid Simple Testing, Florian Bruhin, Swiss Python Summit 2016
<https://www.youtube.com/watch?v=rCBHkQ_LVIs>`_.
- `Improve your testing with Pytest and Mock, Gabe Hollombe, PyCon SG 2015
<https://www.youtube.com/watch?v=RcN26hznmk4>`_.
- `Introduction to pytest, Andreas Pelme, EuroPython 2014
<https://www.youtube.com/watch?v=LdVJj65ikRY>`_.

View File

@@ -21,7 +21,7 @@ but note that project specific settings will be considered
first. There is a flag that helps you debugging your
conftest.py configurations::
py.test --traceconfig
py.test --trace-config
customizing the collecting and running process

View File

@@ -16,7 +16,7 @@ command line options
display py lib version and import information.
``-p name``
early-load given plugin (multi-allowed).
``--traceconfig``
``--trace-config``
trace considerations of conftest.py files.
``--nomagic``
don't reinterpret asserts, no traceback cutting.

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