Compare commits

...

519 Commits
2.3.4 ... 2.5.1

Author SHA1 Message Date
holger krekel
cc9b3ec296 fix links and add github link 2013-12-17 08:59:50 +01:00
holger krekel
66bd71a5d7 use gudea/gudea as header/body fonts 2013-12-17 08:56:59 +01:00
holger krekel
3365907989 last change to release announce 2013-12-17 08:43:01 +01:00
holger krekel
6b2040f98d regen docs for 2.5.1, add links for coverage reports 2013-12-17 08:30:35 +01:00
holger krekel
90551c6ce2 adding a release announcement and some doc fixes 2013-12-17 07:58:49 +01:00
holger krekel
d3b8390df3 Merge branch 'doc' of git+ssh://github.com/Turbo87/pytest into Turbo87-doc
Conflicts:
	CHANGELOG
2013-12-17 06:49:12 +01:00
holger krekel
9554fe0bf8 pypy has >21K tests now 2013-12-16 18:25:00 +01:00
holger krekel
5a13f31bce fix issue407: fix addoption docstring to point to argparse instead of
optparse. Thanks Daniel D. Wright.
2013-12-16 18:07:05 +01:00
holger krekel
41bddb48a1 remove unused var (fixes flakes tests) 2013-12-16 12:38:15 +01:00
Floris Bruynooghe
b820cf2e39 Fix docstring
This was copied from another paramterize call and I forgot to change
the parameters referred too.
2013-12-16 10:51:50 +00:00
Floris Bruynooghe
fd8638652d Still print this, but use py2/py3 compat syntax
I realised being able to print is probably an essential part of this
test which I may have inadvertendly disabled, so correct that.
2013-12-16 10:51:04 +00:00
holger krekel
01ae5dbb2e amend CHANGELOG with credits and issue mentioning 2013-12-16 08:04:46 +01:00
holger krekel
b4797d6295 fix issue403 : allow same-name parametrized functions within a collector 2013-12-16 07:47:59 +01:00
holger krekel
c9195a0f45 fix py32 failures and remove random print from commit accident 2013-12-16 07:19:49 +01:00
holger krekel
86c56c829a fix issue405 -- xfail the plugin generation test as it is not supposed to run as part of the pytest core tests and only runs on specific environments. 2013-12-16 07:03:59 +01:00
holger krekel
ef023ebad3 merge 2013-12-16 07:01:58 +01:00
Floris Bruynooghe
0c737e3de0 Allow parameterised fixtures to give paramemter IDs
This is just like the markers etc already can do.
2013-12-15 22:15:15 +00:00
Floris Bruynooghe
1b7c70eab4 Do not use py2-only print, breaks py3 testruns 2013-12-15 22:12:38 +00:00
Tobias Bieniek
6a5456f873 doc: Replaced header font 2013-12-14 15:49:56 +01:00
Tobias Bieniek
28b1079548 doc: Moved G+ button 2013-12-14 15:45:01 +01:00
Tobias Bieniek
fe01d1b0df doc: Fixed small res sidebar font color 2013-12-14 15:43:40 +01:00
Tobias Bieniek
54174c308f doc: Use native scrollbars on webkit 2013-12-14 15:38:11 +01:00
holger krekel
901f764825 merge 2013-12-14 14:00:47 +01:00
holger krekel
c1759fc384 fix py25 mention 2013-12-13 15:50:06 +01:00
Ronny Pfannschmidt
e843b028e6 fix issue404 by more strict junitxml escape 2013-12-13 10:28:23 +01:00
Bruno Oliveira
305cbecb34 Updated plugins_index to use pytest 2.5.0
- Also made test for plugins_index less brittle;
2013-12-12 19:20:13 -02:00
Tobias Bieniek
a986b3fb4a doc: Removed unused template 2013-12-12 19:48:45 +01:00
Tobias Bieniek
fd42133d89 doc: Don't use italic font for internal references 2013-12-12 19:48:36 +01:00
Tobias Bieniek
d6d7f3821f doc: Use green color for links 2013-12-12 19:48:13 +01:00
Tobias Bieniek
e79b43eeb2 doc: Moved link colors into variables 2013-12-12 19:41:29 +01:00
Tobias Bieniek
afba6ce907 doc: Don't show not existing logo 2013-12-12 19:36:07 +01:00
Tobias Bieniek
d5948325d4 doc: Use different font combination 2013-12-12 19:32:38 +01:00
Tobias Bieniek
b9b44bb87c doc: Moved font family attributes into variables 2013-12-12 19:29:28 +01:00
Tobias Bieniek
00b00ff931 doc: Adjusted TOC sidebar section 2013-12-12 19:22:37 +01:00
Tobias Bieniek
6f54a6be1e CHANGELOG: Simplified section headings 2013-12-12 19:10:24 +01:00
Tobias Bieniek
d1faccb061 CHANGELOG: Removed trailing whitespace 2013-12-12 19:08:26 +01:00
Tobias Bieniek
0f6bb3b3ef doc: Adjusted sidebar link 2013-12-12 19:03:25 +01:00
Tobias Bieniek
542b87fed3 doc: Added "Useful Links" section to all sidebars 2013-12-12 18:58:16 +01:00
Tobias Bieniek
33f1df2369 doc: Removed overwritten searchbox template
The original one works quite well...
2013-12-12 18:56:13 +01:00
Tobias Bieniek
041a12fdf2 doc: Moved PDF documentation link into links.html 2013-12-12 18:55:09 +01:00
Tobias Bieniek
555999397b doc: Moved searchbox into searchbox.html file 2013-12-12 18:49:44 +01:00
Tobias Bieniek
25a45f6bf7 doc: Moved links section to its own file 2013-12-12 18:48:10 +01:00
Tobias Bieniek
82f017edeb doc: Removed unused themes 2013-12-12 18:48:10 +01:00
Tobias Bieniek
b4842b20f6 doc: Added proper sidebar to index page 2013-12-12 18:48:10 +01:00
Tobias Bieniek
e14e966da9 doc: Activate "Flask" theme 2013-12-12 18:48:09 +01:00
Tobias Bieniek
ebe0c34a02 doc: Imported Flask Sphinx Styles
from https://github.com/mitsuhiko/flask-sphinx-themes
2013-12-12 18:48:09 +01:00
Tobias Bieniek
7d711083ec .gitignore: Removed *.html rule
The Sphinx templates are HTML files and should be included.
2013-12-12 18:48:09 +01:00
holger krekel
0a4d5a423e some doc fixes 2013-12-12 13:06:25 +01:00
holger krekel
b9ccb9d31f Added tag 2.5.0 for changeset a064ad64d167 2013-12-12 13:06:21 +01:00
holger krekel
a056b41070 fix typo 2013-12-12 13:01:57 +01:00
holger krekel
6d26c44895 remove py25 from automated testing 2013-12-12 08:16:16 +01:00
holger krekel
1644cd2da5 don't declare py25 as supported anymore. 2013-12-12 07:30:34 +01:00
holger krekel
98135a3d30 remove unusued import 2013-12-12 06:55:05 +01:00
holger krekel
307a41339c fix expicit assert messages for Python2.6: it turns out python2.6
instantiates the AssertionError differently for tuples.  Test
and fix to neutralize it.
2013-12-12 06:41:48 +01:00
Bruno Oliveira
72ebd74715 Updated plugins_index.txt with latest plugins 2013-12-11 17:28:08 -02:00
holger krekel
bfa53811d3 regen docs and bump version to 2.5.0 2013-12-11 12:20:19 +01:00
holger krekel
0fa77d58c4 preparing 2.5.0 release announcement 2013-12-11 12:12:01 +01:00
holger krekel
fa80b8ad17 add changelog: fix issue319 - correctly show unicode in assertion errors. Many
thanks to Floris Bruynooghe for the complete PR.  Also means
we depend on py>=1.4.19 now.
2013-12-11 11:28:06 +01:00
holger krekel
9cdb6fc724 Merged in paylogic/pytest/override-fixture-via-parametrization (pull request #92)
Paratrization overrides existing fixtures.
2013-12-10 14:59:10 +01:00
holger krekel
cd8e69e33c close issue240 - rework "good practises" document and discuss
discuss the two common test directory layouts in more detail, including
an explicit note on how it interacts with PEP420-namespace packages.
2013-12-10 14:54:13 +01:00
Anatoly Bubenkov
7b87f7b6b5 Paratrization overrides existing fixtures.
--HG--
branch : override-fixture-via-parametrization
2013-12-10 14:27:29 +01:00
holger krekel
dd0da4643a clarify that pytest.mark.parametrize() takes a list of argvalues and not just
arbitrary iterators.  Addresses issue122.
2013-12-10 10:16:27 +01:00
holger krekel
7766526992 address issue122 -- explode "params" into a list in fixture function decorators 2013-12-09 10:48:15 +01:00
holger krekel
5c3d692008 some minor internal cleanup 2013-12-09 10:40:39 +01:00
holger krekel
bdf9147ad4 fix changelog 2013-12-09 10:38:40 +01:00
holger krekel
ad2ac256de speed up reorder for large higher-than-function-scoped parametrizations 2013-12-09 10:05:44 +01:00
holger krekel
a4466342ae make bench.py accept an optional script name and add a slow "manyparam" test 2013-12-09 08:14:58 +01:00
holger krekel
0d7af592c0 speed up a test 2013-12-09 08:14:39 +01:00
holger krekel
66ffc5e0f8 backout allowing @pytest.fixture in front of pytest_funcarg__NAME functions.
It was introduced because of pylint warnings and it's probably better to
go for a pylint-pytest plugin that avoids also other warnings/issues.
2013-12-09 07:07:47 +01:00
holger krekel
320137a4aa Merged in msabramo/pytest/color_option (pull request #91)
Remove u'' literal in test_color_{yes,no} for Python 3.2 compat
2013-12-08 20:56:21 +01:00
Marc Abramowitz
0278dc9b6f Remove u'' literal in test_color_{yes,no} for Python 3.2 compat
--HG--
branch : color_option
2013-12-08 11:39:55 -08:00
holger krekel
7d9297e929 add changelog entry: PR90: add --color=yes|no|auto option to force terminal coloring
mode ("auto" is default).  Thanks Marc Abramowitz.
2013-12-08 20:25:36 +01:00
holger krekel
9e03ea8215 Merged in msabramo/pytest/color_option (pull request #90)
Add option: --color=(yes/no/auto)
2013-12-08 20:19:37 +01:00
Marc Abramowitz
60f5b15f20 Remove superfluous monkeypatch arg to test_color_yes
--HG--
branch : color_option
2013-12-07 12:04:23 -08:00
holger krekel
e67047d629 remove unused cache argument for re-ordering items. 2013-12-07 21:00:33 +01:00
holger krekel
10edfa65dc fix issue396 -- properly sort tests using class-scoped parametrization
also refix issue323 in a better way to avoid recursion for the fixture-grouping
algorithm alltogether.
2013-12-07 20:55:17 +01:00
holger krekel
daec4c70b8 refactor sorting wrt class-scopes. This fixes issue396 and also simplifies
the internal sorting algorithm a bit.
2013-12-07 19:31:27 +01:00
holger krekel
dbfbc2b222 add a skip benchmark file (from issue400). 2013-12-07 19:11:37 +01:00
holger krekel
426907eafb radically simplify eq/neq with nodes by just using Pythons builtin "is" relationship.
The need for comparing two separately instantiated nodes seems to be historic
(related to an already-gone mode of pytest-xdist which would re-collect nodes)
and not actually needed anymore.
2013-12-07 16:39:53 +01:00
holger krekel
4f0879ff9b refactor internal finalization mechanics such that all fixture arguments
in a test invocation will have a corresponding FixtureDef instance.
also fixes issue246 (again).

simplify parametrized fixture teardown by making it work lazy:
during the setup of a parametrized fixture instance any previously
setup instance which was setup with a different param is torn down
before setting up the new one.
2013-12-07 16:37:46 +01:00
Marc Abramowitz
bec6ee5c29 Assert 'test session starts' in output for test_color_{yes,no}
--HG--
branch : color_option
2013-12-06 11:58:04 -08:00
Marc Abramowitz
23fa4cec61 Add option: --color=(yes/no/auto)
--HG--
branch : color_option
2013-12-06 11:49:48 -08:00
holger krekel
4b9dbd3920 remove unused line 2013-12-05 14:40:50 +01:00
holger krekel
98c6ced46e refactor and document parametrized sorting code. 2013-12-05 06:09:29 +01:00
holger krekel
cb485e5af4 reopen #246 -- it turns out parametrized finalization ordering is not fully fixed -- i modified the test and marked it xfail for now. 2013-12-04 16:09:37 +01:00
holger krekel
817b175870 allow to use pytest.fixture decorator on old-style pytest_funcarg__NAME definitions. 2013-12-04 07:16:34 +01:00
holger krekel
bd320951e6 Merged in paylogic/pytest/parametrize-hashable (pull request #89)
implement index-based mechanizm for collection of parametrized tests
2013-12-03 21:07:15 +01:00
Anatoly Bubenkov
0cfd873abe implement index-based mechanizm for collection of parametrized tests
--HG--
branch : parametrize-hashable
2013-12-03 21:05:19 +01:00
holger krekel
d30ad3f5ce fix reporting for @mock'd test functions 2013-12-03 11:23:22 +01:00
holger krekel
5dbf4fc0c2 fix importorskip test 2013-12-03 09:40:40 +01:00
Floris Bruynooghe
e3a945a0b5 Add test for unicode assertion descriptions
Also clean up a few debugging leftovers.
2013-11-29 00:29:14 +00:00
Floris Bruynooghe
a5c075c4e2 Respect unicode in AssertionError argument
This is related to issue319
2013-11-24 17:45:48 +00:00
holger krekel
c0dd7c5975 fix issue275 - allow usefixtures and autouse fixtures
for running doctest text files.
2013-11-22 15:35:20 +01:00
holger krekel
4031588811 add a note to the documentation that pytest does not mimick nose try to perform "import isolation". Addresses issue268. 2013-11-22 15:12:12 +01:00
holger krekel
1dc2a45cb2 fix issue377 by clarifying in the nose-compat docs that pytest
does not duplicate the unittest-API into the "plain" namespace.
2013-11-22 14:07:56 +01:00
holger krekel
40b172ca5a python2.4 is not really tested or supported anymore 2013-11-22 13:57:15 +01:00
holger krekel
94031f9cef apply doc changes from just backed out changeset 2013-11-22 13:53:43 +01:00
holger krekel
a6783cd6f3 Backed out changeset 73b1eed8ce09 2013-11-22 13:52:53 +01:00
holger krekel
438d85b5ad clarify that python_functions does not apply to unittest.TestCase
classes and their methods.  Addresses issue284.
2013-11-22 13:44:56 +01:00
Floris Bruynooghe
90b6ccd321 Ensure the long descriptions and formatting preserve unicode correctly
This is the first stage towards fixing issue319, at least
py.io.saferepr and py.code.ExceptionInfo need to be addressed as well.
2013-11-22 12:28:59 +00:00
Floris Bruynooghe
db778fd456 Correct comment 2013-11-22 12:27:34 +00:00
holger krekel
08f3a0791d fix issue357 - special case "-k" expressions to allow for
filtering with simple strings that are not valid python expressions.
Examples: "-k 1.3" matches all tests parametrized with 1.3.
"-k None" filters all tests that have "None" in their name
and conversely "-k 'not None'".
Previously these examples would raise syntax errors.

Also add a note to the docs about what is allowed.
2013-11-21 15:25:16 +01:00
holger krekel
663f824fc4 simplify basedir isolation 2013-11-21 14:54:46 +01:00
holger krekel
2700a94d49 remove an old duplicate marker and use recent pytest mechanism for parametrization 2013-11-21 14:40:14 +01:00
holger krekel
e31f40c2d0 fix ordering of finalizers of parametrized interdependent fixtures.
This fixes issue246 as reported.  Thanks Ralph Schmitt for the
precise failure example.
2013-11-21 14:16:44 +01:00
holger krekel
fc073cb81c fixed version comparison in pytest.importskip(modname, minverstring) 2013-11-21 13:53:04 +01:00
holger krekel
2e90aaf7af remove _fixturestack attribute now that we have a proper request->subrequest->subrequest chain. 2013-11-21 13:15:32 +01:00
holger krekel
238b890d9b avoid maintaining a fixturestack 2013-11-21 12:42:22 +01:00
holger krekel
49119e31bf fix py25 compat 2013-11-21 12:31:22 +01:00
holger krekel
bb5f1e8173 refactor internal FixtureRequest handling to avoid monkeypatching.
One of the positive user-facing effects is that the "request" object
can now be used in closures.
2013-11-21 12:21:52 +01:00
holger krekel
05fbd490da addresses issue246: add a test for module/function scope that shows that
finalizer ordering is wrong.
2013-11-21 09:42:24 +01:00
holger krekel
5322f057a0 move two fixture test modules into bigger testing/python/fixture.py 2013-11-21 09:26:45 +01:00
holger krekel
086cc03f05 some amendments/fixes to changelog 2013-11-21 07:46:53 +01:00
Floris Bruynooghe
d67514b657 Fixup changelog
This seems to have gone wrong in the merge.
2013-11-21 01:37:48 +00:00
Floris Bruynooghe
a467fbea0d Merge 2013-11-21 01:16:49 +00:00
Floris Bruynooghe
6686c67a41 Re-raise the first exception instead of the last
This will make more sense if multiple fixtures depend on each other.
It would be better if all exceptions could be shown however.

Also depend on python 2.5+ exception hierarchy and use sys module
directly.
2013-11-21 01:15:24 +00:00
holger krekel
73f36fc8b7 fix issue221 - handle importing of namespace-package with no
__init__.py properly. (This is a commit after the fix -- the
original issue steps for failure cannot be reproduced anymore).
2013-11-20 21:04:19 +01:00
holger krekel
bd8a2cc18c removing pexpect from general dependencies because
it doesn't install on windows anymore.  Instead
to specific configurations for pexpect on py27 and py33
which only call the tests that need it.
2013-11-20 20:00:59 +01:00
holger krekel
6d1b7e94d1 add py33-xdist to tox testing 2013-11-20 16:03:55 +01:00
holger krekel
9eff939b02 remove testing of xdist+genscript -- doesn't really make sense
because for installing pytest-xdist you need pytest installed
which defeats the purpose of genscript.
2013-11-20 15:46:23 +01:00
holger krekel
0a8b27ff49 fix ordering when mock.patch or other standard decorator-wrappings
are used with test methods.  This fixues issue346.  Thanks to
Ronny Pfannschmidt and Donald Stufft for helping to isolate it.
2013-11-19 23:22:27 +01:00
Floris Bruynooghe
72752165df Ensure all finalizations are run when one fails
Fixes issue287.
2013-11-19 17:26:18 +00:00
holger krekel
9b21d3f206 internally make varnames() deal with classes's __init__,
although it's not needed by pytest itself atm.  Also
fix caching.  Fixes issue376.
2013-11-19 15:33:52 +01:00
holger krekel
dde0a81677 don't hide an ImportError when importing a plugin produces one.
fixes issue375.
2013-11-19 14:45:51 +01:00
holger krekel
31576fac61 fix issue380 by making --resultlog only rely on longrepr instead
of the "reprcrash" attribute which only exists sometimes.
2013-11-19 14:19:29 +01:00
Ronny Pfannschmidt
82846777a7 add missing importorskip 2013-11-19 12:21:47 +01:00
holger krekel
7f49e0fddc xfail a test on pypy that checks wrong encoding/ascii (pypy does
not error out). fixes issue385.

also re-enable pypy tests in tox.
2013-11-19 11:18:51 +01:00
Ronny Pfannschmidt
eda8b02a8d fix issue384 by removing the trial support code 2013-11-19 10:58:24 +01:00
holger krekel
1fd1617427 fix pexpect-3.0 compatibility for pytest's own tests.
(fixes issue386)
2013-11-19 10:10:27 +01:00
holger krekel
97252a8b66 fix issues link 2013-11-19 09:20:50 +01:00
holger krekel
4a1cc792c9 a script to read bitbucket pytest issues 2013-11-18 21:31:32 +01:00
holger krekel
581b3a110c Merged in hsoft/pytest (pull request #81)
Fix TypeError crash on failed imports under py3.3.
2013-11-15 21:02:30 +01:00
Virgil Dupras
e118682db1 Added test for previous crash on failed import fix
Also, rewrote the fix a bit.
ref #383.
2013-11-15 14:03:57 -05:00
holger krekel
4eeb1c4f31 Merged in paylogic/pytest/fix-fixturedef-merge (pull request #86)
correctly check for fixturedef when  merging
2013-11-14 05:58:50 +01:00
Anatoly Bubenkov
e2c4730e17 correctly check for fixturedef when merging
--HG--
branch : fix-fixturedef-merge
2013-11-13 18:25:55 +01:00
holger krekel
c3e844e561 Merged in nicoddemus/pytest/plugins-index-status-images (pull request #85)
Adding status images in plugins_index
2013-11-13 07:45:40 +01:00
holger krekel
fde947e1a8 Merged in paylogic/pytest/ignore-autocomplete-on-darwin (pull request #84)
ignore argcomplete on darwin
2013-11-13 07:44:24 +01:00
holger krekel
ce0af892aa Merged in paylogic/pytest/multi-level-fixture-deps-override (pull request #83)
When overridden, fixture's dependencies are called from all levels of folder structure
2013-11-13 07:43:59 +01:00
holger krekel
3f389238f8 Merged in paylogic/pytest/python32-test-fix (pull request #82)
support python32 fix
2013-11-13 07:43:35 +01:00
Bruno Oliveira
1faf95273c Adding status images in plugins_index
--HG--
branch : plugins-index-status-images
2013-11-12 23:54:13 -02:00
Anatoly Bubenkov
ba5d4ae42f ignore argcomplete on darwin
--HG--
branch : ignore-autocomplete-on-darwin
2013-11-12 13:52:16 +01:00
Anatoly Bubenkov
d18124f5ed support python32
--HG--
branch : python32-test-fix
2013-11-12 13:48:17 +01:00
Anatoly Bubenkov
846cf781a1 use deepest fixturedef for fixture closure
--HG--
branch : multi-level-fixture-deps-override
2013-11-12 13:45:36 +01:00
Anatoly Bubenkov
85dd51ccc8 initial
--HG--
branch : multi-level-fixture-deps-override
2013-11-12 13:35:32 +01:00
Virgil Dupras
ded88700a3 Fix TypeError crash on failed imports under py3.3.
Starting with Python 3.3, NamespacePath passed to importlib hooks
seem to have lost the ability to be accessed by index.

We wrap the index access in a try..except and wrap the path in a
list if it happens.

Fixes #383.
2013-11-08 16:59:13 -05:00
holger krekel
a9d1f40c29 merge, add changelog entry 2013-11-02 07:04:26 +01:00
holger krekel
e2d19aab39 Merged in jameslan/pytest/multi-marks (pull request #79)
Support multiple marks for individual parametrized argument set
2013-11-02 07:02:28 +01:00
James Lan
7210e443ee Support multiple marks for individual parametrized argument set
--HG--
branch : multi-marks
2013-11-01 21:10:13 -07:00
holger krekel
f674c57d1a add german pytest talk (pyconde2013) 2013-10-25 16:50:05 +02:00
holger krekel
9dec27371d change metadata 2013-10-24 15:08:50 +02:00
holger krekel
75328b66e6 fix tests on py32/py33 2013-10-22 11:26:29 +02:00
holger krekel
cf9d345382 fix unicode handling with junitxml, fixes issue368. 2013-10-21 16:54:25 +02:00
holger krekel
0d8392bc45 fix unicode handling with new monkeypatch.setattr(import_path, value)
API.  Thanks Rob Dennis.  Fixes issue371.
2013-10-21 14:01:02 +02:00
holger krekel
47d2d20d81 fix typo, thank avanderneuth 2013-10-21 13:36:22 +02:00
holger krekel
b0a5740898 fix changelog 2013-10-21 13:34:24 +02:00
holger krekel
bc8c4b3ebd pytestconfig is now session-config as it is the same object during the
whole test run.  Fixes issue370
2013-10-21 13:33:36 +02:00
holger krekel
2eebe6c677 Merged in nicoddemus/pytest/plugins-index-adjustments (pull request #77)
Adjustments to the plugins index page
2013-10-20 07:53:36 +02:00
Floris Bruynooghe
8c326c5e66 Correct hyperlink and target 2013-10-15 12:45:55 +01:00
Bruno Oliveira
8e9034f074 adding column for download counts
--HG--
branch : plugins-index-adjustments
2013-10-14 19:14:05 -03:00
Bruno Oliveira
612fb96d02 using a single column for name and version
--HG--
branch : plugins-index-adjustments
2013-10-14 18:33:41 -03:00
Bruno Oliveira
49d067d72e moved test_plugins_index.py
--HG--
branch : plugins-index-adjustments
2013-10-14 18:22:41 -03:00
Bruno Oliveira
8e1301b6d7 moved plugins_index to its own directory
--HG--
branch : plugins-index-adjustments
2013-10-14 18:14:35 -03:00
holger krekel
8ac5af2896 fix flakes issues and make --flakes run part of tox runs 2013-10-12 15:39:22 +02:00
holger krekel
8550ea0728 Merged in nicoddemus/pytest/plugins-index (pull request #76)
Script to generate docs with a list of pytest plugins from PyPI
2013-10-12 14:27:43 +02:00
Bruno Oliveira
6e619e0a70 fixed 'PyPI' spelling
--HG--
branch : plugins-index
2013-10-11 20:57:35 -03:00
Bruno Oliveira
5b2b71bfd4 included test for plugins_index and added command line options
--HG--
branch : plugins-index
2013-10-11 20:53:03 -03:00
Bruno Oliveira
d92322a574 added plugins_index documentation and generated plugins_index file
--HG--
branch : plugins-index
2013-10-11 19:47:40 -03:00
Bruno Oliveira
7e793b9419 adding first version of plugins_index script
--HG--
branch : plugins-index
2013-10-11 19:14:22 -03:00
holger krekel
d81b703f10 avoid one surprising case of marker malfunction/confusion::
@pytest.mark.some(lambda arg: ...)
    def test_function():

would not work correctly because pytest assumes @pytest.mark.some
gets a function to be decorated already.  We now at least detect if this
arg is an lambda and thus the example will work.  Thanks Alex Gaynor
for bringing it up.
2013-10-11 14:36:54 +02:00
holger krekel
1265cb9952 add changelog entry: refine pytest / pkg_resources interactions: The
AssertionRewritingHook PEP302 compliant loader now registers itself with
setuptools/pkg_resources properly so that the
pkg_resources.resource_stream method works properly.  Fixes issue366.
Thanks for the investigations and full PR to Jason R. Coombs.
2013-10-11 09:29:28 +02:00
holger krekel
124e58e42d merge 2013-10-11 09:30:08 +02:00
Jason R. Coombs
2697b63bcd Fix bytes/string mismatch in test on Python 3 2013-10-10 18:01:56 -04:00
Jason R. Coombs
ee5b836e27 Implement suggestions by HPK 2013-10-10 17:39:37 -04:00
Jason R. Coombs
a4c17dfb19 Register the AssertionRewritingHook loader with pkg_resources; fixes #366. 2013-10-10 11:56:12 -04:00
Jason R. Coombs
00c0d62c9b Adding test capturing #366 where an error occurs when package resources are loaded from the test package. 2013-10-10 11:40:31 -04:00
holger krekel
a5d4c20905 make "--runxfail" turn imperative pytest.xfail calls into no ops
(it already did neutralize pytest.mark.xfail markers)
2013-10-09 22:55:20 +02:00
holger krekel
0335c6d750 bump version to 2.3.3.dev1 2013-10-05 21:39:16 +02:00
Benjamin Peterson
8b6e42317b add test for detecting coding cookie with CRLF newlines 2013-10-05 15:20:32 -04:00
Benjamin Peterson
56e6ae567c fix detection of the coding cookie when it is on the second line of the file (fixes #330) 2013-10-05 15:16:08 -04:00
Benjamin Peterson
33b663e03d fix coding cookie detection logic 2013-10-05 15:03:04 -04:00
holger krekel
4bfbe7ec22 Added tag 2.4.2 for changeset 8d051f89184b 2013-10-04 14:33:12 +02:00
holger krekel
4b395d56cc bump version of docs 2013-10-04 13:59:44 +02:00
holger krekel
018860d626 finalize release announcement, changelog 2013-10-04 12:34:47 +02:00
holger krekel
19a76337a4 add pluginmanager.do_configure(config) as a link to
config.do_configure() for plugin-compatibility
add some more plugins to plugin-test.sh
2013-10-04 11:36:45 +02:00
holger krekel
0780f2573f bump version to 2.4.2, regen docs 2013-10-03 19:09:18 +02:00
holger krekel
cec7d47c1f remove fd-fixing attempt at startup of pytest. It's
not clear it's actually needed and it's not nice
to still do FD-dupping when "-s" is specified.
2013-10-03 18:53:40 +02:00
holger krekel
3d00cd35fc fix python2.5 issues 2013-10-03 18:25:03 +02:00
holger krekel
5aa5b9748d fix argcomplete-test to use sys.argv[0] if it looks like a py.test executable 2013-10-03 18:02:54 +02:00
holger krekel
cb65c56037 fix issue365 and depend on a newer py versions which uses colorama
for coloring instead of its own ctypes hacks.
2013-10-03 17:46:36 +02:00
holger krekel
ae090740c5 always dupfile if os.dup is available 2013-10-03 16:47:55 +02:00
holger krekel
2248a31a44 more fixes regarding marking, in particular plugins should use add_marker/get_marker now. 2013-10-03 15:43:56 +02:00
holger krekel
9fdfa155fb fix issue354: avoid tmpdir fixture to create too long filenames especially
when parametrization is used
2013-10-03 14:22:54 +02:00
holger krekel
e49eca8d59 simplify the implementation of NodeKeywords getting rid of __ descriptors appearing there. 2013-10-03 13:53:22 +02:00
holger krekel
263b0e7d99 add setup.cfg for building sphinx_docs 2013-10-03 12:35:13 +02:00
Andreas Zeidler
42b1033385 allow test items to not be associated with a test function
this is needed for plugins like `pytest-pep8` or `pytest-flakes`
2013-10-02 15:55:28 +02:00
holger krekel
05f6422392 remove unused imports (using "importchecker" project) 2013-10-02 14:32:40 +02:00
holger krekel
f83a65c8ae Added tag 2.4.1 for changeset 8828c924acae 2013-10-02 12:41:03 +02:00
holger krekel
071960250f avoid "IOError: Bad Filedescriptor" on pytest shutdown by not closing
the internal dupped stdout (fix is slightly hand-wavy but work).
2013-10-02 12:39:01 +02:00
holger krekel
16d98541f2 reference CHANGELOG 2013-10-02 12:09:19 +02:00
holger krekel
2b8f4214c3 2.4.1 release preps 2013-10-02 09:16:51 +02:00
holger krekel
d3c9927fee fix regression reported by dstufft: regression when a 1-tuple ("arg",) is used
for specifying parametrization (the values of the parametrization were passed
nested in a tuple).
2013-10-02 08:08:26 +02:00
holger krekel
acc5679bc8 adapt changelog 2013-10-02 07:56:30 +02:00
holger krekel
2ba77cb1f4 merge 2013-10-02 07:49:11 +02:00
holger krekel
8f65418d34 Merged in dirn/pytest/dirn/adjust-syntax-for-parametrize-documentat-1380671670976 (pull request #72)
Adjust syntax for parametrize documentation
2013-10-02 07:48:41 +02:00
Andy Dirnberger
8ae79e09c0 Adjust syntax for parametrize documentation
Without the double colon, reStructuredText won't display treat the block that
follows as pre-formatted text. Also, with this change comes the need to change a
tab to spaces to align it with the adjacent lines.

--HG--
branch : dirn/adjust-syntax-for-parametrize-documentat-1380671670976
2013-10-01 23:54:34 +00:00
holger krekel
c62bfaefd6 add to CHANGELOG 2013-10-01 22:39:23 +02:00
Anthon van der Neut
236fff00ad complete_dotted: fix for #361, filecompleter on dot files had differing behaviour from bash
Now if the prefix to expands ends in the directory seperator, then
'..../.*' is globbed as well.
2013-10-01 16:33:15 +02:00
holger krekel
9b9355b8da fix typos (thanks @faassen) 2013-10-01 14:30:53 +02:00
holger krekel
446bcf44fb strike wrong yield_fixture paragraph 2013-10-01 14:25:59 +02:00
Floris Bruynooghe
1db6fc87c7 Allow unicode strings in parser.add_argument()
This fixes issue360 by also converting unicode strings to the argparse
syntax instead of just native strings.
2013-10-01 13:20:20 +01:00
holger krekel
895d52471b bump version to dev again, new CHANGELOG section for 2.4.1.dev. 2013-10-01 12:51:52 +02:00
holger krekel
d226b2faf4 release announcement, bump version numbers, some test adapatations because of random win32 test failures. 2013-10-01 12:37:11 +02:00
holger krekel
74ea94625a Added tag 2.4.0 for changeset af860de70cc3 2013-10-01 10:43:40 +02:00
holger krekel
f9927e457b bump version to 2.4.0 2013-10-01 10:43:36 +02:00
holger krekel
a71fdd26b3 fix usage docs for "-k" (addresses issue357) 2013-09-30 21:39:32 +02:00
holger krekel
2c7d00579b bump version, some windows test fixes, prevent logging from raising exceptions at the end (finally), add py25 to tox.ini. 2013-09-30 16:09:26 +02:00
holger krekel
de35b077a2 disallow yield in non-yield-fixtures for now. This is an incompataibility but we want to prepare for possibly merging fixture and yield_fixture some day. 2013-09-30 13:56:54 +02:00
holger krekel
086d4e4ced strike keyword argument in favour of new pytest.yield_fixture decorator 2013-09-30 13:42:39 +02:00
holger krekel
431ce79d94 add missing entry to CHANGELOG 2013-09-30 13:23:32 +02:00
holger krekel
db6f347db6 fix issue358 -- introduce new pytest_load_initial_conftests hook and make capturing initialization use it, relying on a new (somewhat internal) parser.parse_known_args() method.
This also addresses issue359 -- plugins like pytest-django could implement a pytest_load_initial_conftests hook like the capture plugin.
2013-09-30 13:14:16 +02:00
holger krekel
4b709037ab some more separation of core pluginmanager from pytest specific functionality.
Idea is to have the PluginManager be re-useable from other projects at some point.
2013-09-30 13:14:14 +02:00
holger krekel
d946299b0a shift pytest_configure/unconfigure/addoption/namespace hook calling to config object.
The _pytest.config module itself is no longer a plugin but the actual
config instance is plugin-registered as ``pytestconfig``.
This allows to put most pytest specific logic to _pytest.config instead
of in the core pluginmanager.
2013-09-30 13:14:14 +02:00
holger krekel
694c6fd0e7 localize some argcomplete-related functionality 2013-09-30 10:19:06 +02:00
holger krekel
8b1e53f6d3 avoid creation of file in os.getcwd() cc avanderneut 2013-09-30 08:36:31 +02:00
holger krekel
a930f44e60 introduce pluginmanager.ensure_teardown() which allows 2013-09-28 22:23:00 +02:00
holger krekel
ac19212b2d remove very likely unused pytest_plugin_unregister hook (pytest itself and all plugins i know don't use it) 2013-09-28 22:22:57 +02:00
holger krekel
03c314e3be refine fromdictargs to avoid an uncessary re-setup of the pluginmanager 2013-09-28 22:22:55 +02:00
holger krekel
fad7bd4393 simplify Config constructor 2013-09-28 22:22:53 +02:00
holger krekel
b80e875525 move FILE_OR_DIR constant out 2013-09-28 09:52:41 +02:00
holger krekel
1fc466e8ac add terminalreporter.section|line methods to print extra information. 2013-09-27 15:48:03 +02:00
holger krekel
209a0cd5b2 is actually a new feature, the syntax: pytest.mark.parametrize("arg1,arg2", ...) 2013-09-27 14:15:53 +02:00
holger krekel
9ddfd62848 re-order CHANGELOG and group it into "features" and "bugs" and "known incompatibilities" 2013-09-27 12:58:26 +02:00
holger krekel
48838727ae bump version 2013-09-27 12:43:49 +02:00
holger krekel
39503932a4 merge monkeypatch.replace into monkeypatch.setattr, also support monkeypatch.delattr. 2013-09-27 12:33:06 +02:00
holger krekel
da7133d201 fix some tests wrt to expecting output now that pytest does no
introduce unwanted "buffering" on "-s" calls.
2013-09-27 12:28:34 +02:00
holger krekel
684d7f8668 remove gittip mention for now. 2013-09-27 10:58:04 +02:00
holger krekel
1327eb7cef rework docs to demonstrate and discuss current yield syntax in more depth. 2013-09-27 10:21:23 +02:00
holger krekel
030c337c68 don't manipulate FDs at all if output capturing is turned off. 2013-09-27 09:49:39 +02:00
holger krekel
3ab9b48782 introduce yieldctx=True in the @pytest.fixture decorator. Refactor tests and docs. 2013-09-26 12:57:21 +02:00
holger krekel
2bdd034242 fix issue355: junitxml generates name="pytest" tag. 2013-09-26 08:45:50 +02:00
Anthon van der Neut
b5a83a6af1 argcomplete_win: skip testing of argcomplete on windows 2013-09-09 12:41:29 +02:00
holger krekel
d565df90ad fix issue333: fix a case of bad unittest/pytest hook interaction. 2013-09-09 09:56:53 +02:00
holger krekel
88dc5f8204 Merge pull request #6 from bubenkoff/fix-failing-travis
only force tox to upgrade
2013-09-08 23:12:07 -07:00
Anatoly Bubenkov
fb6282caaa only force tox to upgrade 2013-09-07 20:23:44 +02:00
holger krekel
f7f569f730 Merged in paylogic/pytest/parametrize-fails-when-values-are (pull request #68)
parametrize fails when values are unhashable - add test
2013-09-07 09:16:35 +02:00
Anatoly Bubenkov
63a924b922 parametrize fails when values are unhashable - tests
--HG--
branch : parametrize-fails-when-values-are
2013-09-07 02:30:09 +02:00
Anatoly Bubenkov
f5897498f3 initial
--HG--
branch : parametrize-fails-when-values-are
2013-09-06 22:42:54 +02:00
holger krekel
c478027805 make "import pdb ; pdb.set_trace()" work natively wrt capturing (no "-s" needed
anymore), turning ``pytest.set_trace()`` into a mere shortcut.
2013-09-06 15:29:00 +02:00
holger krekel
109e2f215f add nose.SkipTest for python2.6 -- which apparently is a subclass from python2.7 on.
addresses issue236
2013-09-06 12:48:54 +02:00
holger krekel
41df742faf fix <py27 tests with nose 2013-09-06 12:32:55 +02:00
holger krekel
86f70f5201 fix issue352: fix reference to py.path.local description in tmpdir documentation
(generally to be found at http://pytest.org/latest/tmpdir.html )
2013-09-06 12:16:05 +02:00
holger krekel
94ee37cdb3 - fix issue181: --pdb now also works on collect errors. This was
implemented by a slight internal refactoring and the introduction
  of a new hook ``pytest_exception_interact`` hook.

- fix issue341: introduce new experimental hook for IDEs/terminals to
  intercept debugging: ``pytest_exception_interact(node, call, report)``.
2013-09-06 11:56:04 +02:00
holger krekel
8360c1e687 xfail some tests for doctest support if pdbpp is installed 2013-09-06 10:07:06 +02:00
holger krekel
ca5d02df06 another way how bash reacts when no argcomplete is there? 2013-09-05 22:32:35 +02:00
holger krekel
c3fcf4d928 show more info if the test fails 2013-09-05 22:22:14 +02:00
holger krekel
517d498285 fix issue169: respect --tb=style with setup/teardown errors as well. 2013-09-05 15:43:19 +02:00
holger krekel
9686a4129c Merged in mcmtroffaes/pytest/fix-nose-docs (pull request #67)
Fix instructions for nose users.
2013-09-05 12:10:25 +02:00
Matthias C. M. Troffaes
b7d31952eb Fix instructions for nose users.
--HG--
branch : fix-nose-docs
2013-08-23 11:59:57 +01:00
holger krekel
b879074a64 refs issue290 -- move and refactor the test the string/int-id parametrization test (Which xfails) 2013-08-16 11:41:31 +02:00
holger krekel
4800aeaef7 Merged in markon/pytest (pull request #66)
Fix @parametrize.
2013-08-16 11:38:00 +02:00
holger krekel
ca7c1f5d8e merge pull request #27: correctly handle nose.SkipTest during collection. Thanks
Antonio Cuni, Ronny Pfannschmidt.  I did a few tweaks to the test and the
activation (depending on if unittest is imported at all).
2013-08-16 11:33:58 +02:00
Marco Buccini
f5a6a84314 test marked as 2013-08-16 09:55:25 +02:00
holger krekel
2888988eb6 fix manifest 2013-08-15 13:05:01 +02:00
holger krekel
e27dbdd071 Merged in bubenkoff/pytest/fix-broken-tests (pull request #65)
Fix broken python3 and python2.5 tests
2013-08-15 13:03:20 +02:00
Marco Buccini
6c54ee03de Fix @parametrize when using an integer and strings as parameters in a test accepting a parameter and a fixture as arguments. 2013-08-15 12:52:34 +02:00
Anatoly Bubenkov
0cd7effe35 fix broken python3 and python2.5 tests
--HG--
branch : fix-broken-tests
2013-08-15 11:52:55 +02:00
Anatoly Bubenkov
5851a99b82 initial
--HG--
branch : fix-broken-tests
2013-08-15 09:25:00 +02:00
holger krekel
863a206727 Merged in bubenkoff/pytest/overriden-fixture-finalizer (pull request #64)
overriden fixture finalizer tests
2013-08-15 07:06:25 +02:00
Anatoly Bubenkov
722b35b0a6 tests for fixture finalizers
--HG--
branch : overriden-fixture-finalizer
2013-08-14 13:58:59 +02:00
Anatoly Bubenkov
51dd63d3e1 ignores
--HG--
branch : overriden-fixture-finalizer
2013-08-14 10:09:02 +02:00
holger krekel
57f997b0b4 strike distribute dep 2013-08-11 18:19:58 +02:00
holger krekel
654212d93b add a note about how a lightweight but more powerful function-mocker could be done
(compared to standard mock).
2013-08-09 10:22:49 +02:00
holger krekel
88aa8f5435 mention pytest_collection_modifyitems in plugin page 2013-08-08 23:32:14 +02:00
holger krekel
0755d0605f remove automatic tox-testing of py25 2013-08-08 13:18:46 +02:00
holger krekel
4b88d6d2d7 monkeypatch.replace() now only accepts a string. Improved error handling and
docs thanks to suggestions from flub, pelme, schmir, ronny.
2013-08-07 16:49:29 +02:00
holger krekel
407283ef81 a new monkeypatch.replace(target, value) call which derives the
monkeypatch location from target (can be class/module/function or
string which is taken as importable python path)
examples:

    monkeypatch.replace(os.path.abspath, lambda x: "")
    monkeypatch.replace("requests.get", ...)
2013-08-07 15:35:27 +02:00
holger krekel
3fddf99661 Merged in anthon_van_der_neut/pytest_argcomplete (pull request #63)
argcomplete: FastFileCompleter that doesn't call bash in subprocess, strip prefix dir
2013-08-06 15:41:54 +02:00
holger krekel
21f72b7163 Merged in pelme/pytest/quiet-color-summary (pull request #61)
Added color to the quite mode summary
2013-08-06 15:38:43 +02:00
Anthon van der Neut
719e89fc1a argcomplete: FastFileCompleter that doesn't call bash in subprocess, strip prefix dir
```
timeit result for 10000 iterations of expanding '/d' (lowered the count in the code afterwards)
#                      2.7.5     3.3.2
# FilesCompleter       75.1109   69.2116
# FastFilesCompleter    0.7383    1.0760
```
- does not display prefix dir (like bash, not like compgen), py.test /usr/<TAB> does not show /usr/bin/ but bin/
2013-08-06 15:33:27 +02:00
Andreas Pelme
afa88a479b Added color to the quite mode summary. Also changed the output format
slightly to match the output of the standard summary.

--HG--
branch : quiet-color-summary
2013-08-05 09:45:10 +02:00
holger krekel
7d86827b5e ref #322 cleanup all teardown calling to only happen when setup succeeded.
don't use autouse fixtures for now because it would cause a proliferation
and overhead for the execution of every test.  Rather introduce a
node.addfinalizer(fin) to attach a finalizer to the respective node
and call it from node.setup() functions if the setup phase succeeded
(i.e. there is no setup function or it finished successfully)
2013-08-02 09:52:40 +02:00
holger krekel
b2ebb80878 fix issue322: tearDownClass is not run if setUpClass failed. Thanks
Mathieu Agopian for fixing.  The patch moves handling setUpClass
into a new autofixture. (XXX impl-decide if rather adding addfinalizer()
API to node's would have a similar effect)
2013-08-02 00:02:28 +02:00
Mathieu Agopian
7fc0d45a4c refs #322: setUpClass and tearDownClass as autouse fixture and finalizer 2013-08-01 23:48:40 +02:00
Ronny Pfannschmidt
3b85a56db2 merge 2013-08-01 22:55:16 +02:00
Ronny Pfannschmidt
743711cd1f fix issue317: assertion rewriter support for the is_package method 2013-08-01 22:11:18 +02:00
Floris Bruynooghe
cfe1d4f7c9 mention fix for issue 336 in changelog 2013-08-01 19:03:53 +01:00
Floris Bruynooghe
2cdb54225c Fix issue 336: autouse fixtures in plugins work again
When an autouse fixture in a plugin was encountered None was stored as nodeid
where it used to be ''.  This broke the lookup of autouse fixtures later on.

This also adds another test for the normal fixture ordering which was slightly
wrong: a fixture without location was always added at the front of the fixture
list rather then at the end of the fixtures without location but before the
fixtures with location.
2013-08-01 18:58:28 +01:00
holger krekel
8f24e10571 add changelog entry for anthon's hynek-fication of options,
and change the docs and tests to use the new style.
2013-08-01 17:32:19 +02:00
holger krekel
3ac36f6572 Merged in anthon_van_der_neut/pytest/opt-drop-non-hyphened-long-options (pull request #57)
changes to addoption() for hyphenated long-options
2013-08-01 17:04:18 +02:00
Anthon van der Neut
e96da76c3b changes to addoption() for hyphenated long-options
--HG--
branch : opt-drop-non-hyphened-long-options
2013-08-01 16:49:26 +02:00
holger krekel
ca6f3d24c3 Merged in anthon_van_der_neut/pytest/opt-drop-non-hyphened-long-options (pull request #56)
drop help for long options if longer versions with hyphens are available
2013-08-01 16:33:50 +02:00
Anthon van der Neut
e24b56af6c removed two superfluous parser arguments
--HG--
branch : opt-drop-non-hyphened-long-options
2013-08-01 16:27:06 +02:00
Anthon van der Neut
007a77c2ba drop help for long options if longer versions with hyphens are available
--HG--
branch : opt-drop-non-hyphened-long-options
2013-08-01 16:21:33 +02:00
Floris Bruynooghe
18fa7d866d Close issue 279: improved assertrepr_compare 2013-08-01 15:02:58 +01:00
holger krekel
9ccd52d538 fix issue305 - ignore any problems in writing a pyc file, but print out a trace. 2013-08-01 15:43:42 +02:00
holger krekel
cbbbfcd101 fix collection imports for python2.5 2013-08-01 15:38:03 +02:00
Mathieu Agopian
72a48d69cd refs #279: sequence assertions can also deal with (Mutable)Sequence instances 2013-08-01 14:48:34 +02:00
holger krekel
7e4b21e9a7 merge 2013-08-01 14:45:24 +02:00
holger krekel
b90d82c17f fix some py33 issues introduced with rev 2985
--HG--
branch : argcomplete
2013-08-01 14:24:25 +02:00
holger krekel
b60a8c12d5 fix Mathieu's name. 2013-08-01 12:12:09 +02:00
holger krekel
a24c7b42f1 add changelog entry for fixed issue 335. 2013-08-01 11:36:05 +02:00
Mathieu Agopian
0350a1929d merge 2013-08-01 11:20:30 +02:00
Mathieu Agopian
99783b6fba refs #335: clarify that the exception info returned by pytest.raises is a py.code.ExceptionInfo() 2013-08-01 11:19:47 +02:00
Mathieu Agopian
0e3abdc1af Merged hpk42/pytest into default 2013-08-01 11:12:59 +02:00
Mathieu Agopian
905c648b99 fixes #335: document ExceptionInfo returned by pytest.raises 2013-08-01 11:12:02 +02:00
holger krekel
b81c257360 small mod to test BND 2013-08-01 10:59:45 +02:00
holger krekel
64e9956770 plugin versions are displayed now. 2013-08-01 10:57:36 +02:00
holger krekel
2385553790 no funcargs for setup functions, rather use autouse fixtures. 2013-08-01 10:43:02 +02:00
holger krekel
4c6a11b8e1 remove an old issue. 2013-08-01 10:40:55 +02:00
holger krekel
1111d5b26b remove an entry 2013-08-01 10:37:45 +02:00
holger krekel
44d5524be2 Merged in anthon_van_der_neut/pytest/argcomplete (pull request #51)
fix for tests running subprocesses of py.test after test_argcomplete
2013-08-01 10:30:24 +02:00
holger krekel
1023390f53 fix issue334: don't recommend distribute but setuptools everywhere, also remove implicit distribute_setup support from setup.py. 2013-08-01 09:42:44 +02:00
holger krekel
02199c218d add license note to README 2013-08-01 09:31:34 +02:00
holger krekel
46901fb813 fix issue338: honor --tb style for setup/teardown errors as well. Thanks Maho.
(this is just changing the CHANGELOG entry because the bug was already fixed earlier)
2013-08-01 07:43:00 +02:00
Anthon van der Neut
ef2ddb6f16 monkeypatch for os.environment changes
--HG--
branch : argcomplete
2013-07-31 21:33:13 +02:00
Anthon van der Neut
e3a2e1bbf8 fix for tests running subprocesses of py.test after test_argcomplete
(which all still ran with argcompletion enabled) -> fail

--HG--
branch : argcomplete
2013-07-31 16:03:53 +02:00
holger krekel
8a0a880294 Merged in anthon_van_der_neut/pytest/argcomplete (pull request #50)
Fixes for argcomplete
2013-07-31 07:51:07 +02:00
Anthon van der Neut
6817a56270 minor adjustment, added test for positional argument completion
--HG--
branch : argcomplete
2013-07-30 12:33:38 +02:00
Anthon van der Neut
87860600fb Fixes for argcomplete
- separate out most argcomplete related stuff in new file _argcomplete.py
  (could probably be in the py library)
- allow positional arguments to be interspaced with optional arguments
  ( + test in test_parseopt.py )
- removed double argument in tox.ini
- add documentation on installing argcomplete (>=0.5.7 as needed for
  Python 3), might need improving/incorporation in index.

This does not work on 2.5 yet. I have patches for argcomplete
(with/print()/"".format) but I am not sure they will be accepted.
Agreed with hpk not to push for that.

Removing argcomplete and leaving completion code active now works by early
exit, so <TAB> no longer re-runs the programs without parameters
(which took long for py.test)

test calls bash with a script that redirects filedescriptor 8 (as used by
argcomplete), so the result can be tested.

--HG--
branch : argcomplete
2013-07-30 11:26:15 +02:00
holger krekel
377f63085a be more liberal with respect to lsof checks because jenkins keeps some files open 2013-07-29 15:39:24 +02:00
holger krekel
2a7c79dbf5 make genscript provide information as to compatibility
(now that argparse is a dependency on python2.6)
2013-07-26 08:59:31 +02:00
holger krekel
7a8134660f merge doc changes 2013-07-26 08:59:29 +02:00
holger krekel
863a255c9d Merged in anthon_van_der_neut/pytest/tox_reference (pull request #47)
update for links to tox
2013-07-26 08:59:12 +02:00
Anthon van der Neut
9b51105afa also update testrun.org in 日本語
--HG--
branch : tox_reference
2013-07-26 08:20:26 +02:00
Anthon van der Neut
4b56b075c1 updated tox to live on testrun.org (the old links are still
working on codespeak.net, but those docs are outdated)

--HG--
branch : tox_reference
2013-07-26 08:14:49 +02:00
holger krekel
788303523e add changelog: integrate option tab-completion when argcomplete is used. Thanks
Anthon van der Neut for the PR.  This also lets pytest use argparse
instead of optparse.
2013-07-26 07:51:33 +02:00
holger krekel
4b87810fc2 Merged in anthon_van_der_neut/pytest/argparse (pull request #46)
argparse / argcomplete
2013-07-26 07:41:43 +02:00
Anthon van der Neut
ad72e7f29d auto change %default -> %(default)s in help parameter string (on retrieval)
added code for warnings on optparse arguments (type, help),
which can be easily switched on with TYPE_WARN = True in config.py


installed and tested ( py.test --help )
pytest-quickcheck-0.7
pytest-gae-0.2.2
pytest-growl-0.1
pytest-bdd-0.4.7
pytest-bdd-splinter-0.4.4
pytest-cache-1.0
pytest-capturelog-0.7
pytest-codecheckers-0.2
pytest-contextfixture-0.1.1
pytest-cov-1.6
pytest-flakes-0.1
pytest-incremental-0.3.0
pytest-xdist-1.8
pytest-localserver-0.1.5
pytest-monkeyplus-1.1.0
pytest-oerp-0.2.0
pytest-pep8-1.0.4
pytest-pydev-0.1
pytest-rage-0.1
pytest-runfailed-0.3
pytest-timeout-0.3
pytest-xprocess-0.7
pytest-browsermob-proxy-0.1
pytest-mozwebqa-1.1.1
pytest-random-0.02
pytest-rerunfailures-0.03
pytest-zap-0.1
pytest-blockage-0.1
pytest-django-2.3.0
pytest-figleaf-1.0
pytest-greendots-0.1
pytest-instafail-0.1.0
pytest-konira-0.2
pytest-marker-bugzilla-0.06
pytest-marks-0.4
pytest-poo-0.2
pytest-twisted-1.4
pytest-yamlwsgi-0.6

--HG--
branch : argparse
2013-07-25 17:26:48 +02:00
Anthon van der Neut
15ec5a898c moving from optparse to argparse. Major difficulty is
that argparse does not have Option objects -> added class Argument
Needed explicit call of MyOptionParser.format_epilog as argparse
does not have that. The parse_arg epilog argument wraps the text,
which is not the same (could be handled with a special formatter).

- parser.parse() now returns single argument (with positional args in
  .file_or_dir)
- "file_or_dir" made a class variable Config._file_or_dir and used in help and tests
- added code for argcomplete (because of which this all started!)

addoption:
- if option type is a string ('int' or 'string', this converted to
  int resp. str
- if option type is 'count' this is changed to the type of choices[0]

testing:
- added tests for Argument
- test_mark.test_keyword_extra split as ['-k', '-mykeyword'] generates argparse
  error test split in two and one marked as fail
- testing hints, multiline and more strickt (for if someone moves format_epilog
  to epilog argument of parse_args without Formatter)
- test for destination derived from long option with internal dash
- renamed second test_parseopt.test_parse() to test_parse2 as it was
  not tested at all (the first was tested.)

--HG--
branch : argparse
2013-07-25 15:33:43 +02:00
holger krekel
9d9dd381bc mention github and bitbucket 2013-07-24 12:14:53 +02:00
holger krekel
997df928b7 stick to virtualenv<1.10 for now because it breaks python2.5 2013-07-24 12:08:20 +02:00
holger krekel
6d145ac93a bump version 2013-07-24 11:16:19 +02:00
holger krekel
1280add047 SO-17664702: call fixture finalizers even if the fixture function
partially failed (finalizers would not always be called before)
2013-07-17 10:29:11 +02:00
holger krekel
c53556b88d paint last line red if "failures" or "errors" occured, attribute theuni 2013-07-17 09:31:55 +02:00
holger krekel
60a53c75a6 some python2.5/3.3 fixes of Brianna's parametrize improvements 2013-07-16 15:43:20 +02:00
holger krekel
ca8281f229 merge better parametrize error messages, thanks Brianna Laugher 2013-07-16 15:32:05 +02:00
holger krekel
af7db5195b Merged in pfctdayelise/pytest (pull request #38)
A couple of improvements to parametrize
2013-07-16 15:30:48 +02:00
holger krekel
c2138e9733 add python testing training 2013-07-16 11:30:21 +02:00
holger krekel
31c1c69901 fix link to kotti 2013-07-11 16:07:58 +02:00
holger krekel
b7054495a7 send IRC notifications to pytest-dev 2013-07-11 12:20:38 +02:00
holger krekel
fc03c35bbd Merge pull request #3 from bubenkoff/travis-status-icon
correct travis build status image url
2013-07-11 03:08:27 -07:00
Anatoly Bubenkov
8320232f38 correct travis build status image url 2013-07-11 12:06:13 +02:00
holger krekel
52838a7834 Merge pull request #2 from bubenkoff/py25-test_skipping_faild
fixes for py25 in test_skipping
2013-07-11 02:30:01 -07:00
Anatoly Bubenkov
87eae6213e fixes for py25 in test_skipping 2013-07-11 11:15:31 +02:00
holger krekel
5d8b950b7f change copyright years 2013-07-11 10:50:59 +02:00
Anatoly Bubenkov
7b54476d71 Update README.rst
change the travis link and image to be correct
2013-07-10 13:51:43 +02:00
holger krekel
13ddce2381 fix issue320 - fix class scope for fixtures when mixed with
module-level functions.  Thanks Anatloy Bubenkoff.
2013-07-08 15:54:38 +02:00
holger krekel
be30eb22a6 fix docs wrt norecursedirs, thanks @mgax 2013-07-08 15:39:14 +02:00
Anatoly Bubenkov
589138ea71 re #320 fallback to test scope if the class-scoped fixture is used in non-class-based test function
--HG--
branch : 320-class-scoped-fixture-caching-is-broken-if
2013-07-06 21:30:24 +02:00
holger krekel
f7a9beaefb Merged in bubenkoff/pytest/329-skipif-requires-expression-as-a-string (pull request #43)
re #329 add test for skipif failure when you pass boolean without the reason. add emphasize to the docs.
2013-07-06 20:13:27 +02:00
Anatoly Bubenkov
214793f697 re #329 add test for skipif failure when you pass boolean without the reason. add emphasize to the docs.
--HG--
branch : 329-skipif-requires-expression-as-a-string
2013-07-06 18:54:24 +02:00
Floris Bruynooghe
c36186ce65 Always check for both ENOENT and ENOTDIR
This fixes issue 326.
2013-07-06 18:53:26 +02:00
Floris Bruynooghe
e6a063ee47 Solve fixture ordering when loading plugins from conftest
Conftests are plugins with a location attached to them while other
plugins do not have a location.  When ordering fixturedefs those from
plugins without a location need to be listed first.
2013-07-06 17:56:54 +02:00
holger krekel
723e414814 add KAsia to authors 2013-07-06 17:17:03 +02:00
holger krekel
a00b516f9a add kasia to changelog 2013-07-06 17:06:51 +02:00
holger krekel
31421cb6d7 merge 2013-07-06 16:03:48 +02:00
holger krekel
a6f2af23fb Merged in katarzynaanna/pytest (pull request #42)
Improved reporting
2013-07-06 16:03:27 +02:00
Katarzyna Jachim
ffa1bf726d merge 2013-07-06 15:54:33 +02:00
holger krekel
199f0314b9 Merged in bubenkoff/pytest/travis-integration (pull request #41)
add travis integration, fixes for py25 and py27 no pyc tox env
2013-07-06 15:51:14 +02:00
Katarzyna Jachim
87df85f12d improved reporting
added intermediate level of quiet reporting:
 * -q now shows short summary (# passed/failed tests + time)
 * the former -q is now -qq
2013-07-06 15:43:59 +02:00
holger krekel
ae327ef435 move to development doc target 2013-07-06 15:38:40 +02:00
Anatoly Bubenkov
1736242bd7 add travis integration, fixes for py25 and py27 no pyc tox env
--HG--
branch : travis-integration
2013-07-06 14:23:02 +02:00
Floris Bruynooghe
1a319056fc Mention issue 300 in changelog 2013-07-06 11:00:29 +02:00
Anatoly Bubenkov
ea7a997afc remove unnecessary print 2013-07-06 10:26:14 +02:00
Anatoly Bubenkov
2c7613c15c merge with upstream 2013-07-06 10:06:12 +02:00
Christian Theune
ab637e028b Merged in ctheune/pytest-greenbar-1/ctheune/typo-1372873724648 (pull request #1)
Typo
2013-07-03 19:49:28 +02:00
Christian Theune
1934f515ef Typo
--HG--
branch : ctheune/typo-1372873724648
2013-07-03 17:48:57 +00:00
Christian Theune
d9f0a28da2 Compatibility with my spinal cord reflexes: colorize last summary line.
Provide a red bar if there are any 'failures'. Otherwise make it green.
2013-07-03 19:43:18 +02:00
Christian Theune
ca88c02507 Support working in a local virtualenv. 2013-07-03 19:41:05 +02:00
holger krekel
ea4a3adfd6 - add my ep2013 talk to talks page
- add "talks/blogs" to the navigation side bar
2013-07-03 11:47:18 +02:00
holger krekel
c4c966683c fix issue323 - parametrize() of many module-scoped params 2013-06-28 12:57:10 +02:00
holger krekel
469830fffa some internal renaming to make more sense of the sorting algo,
no semantical changes.
2013-06-28 12:54:10 +02:00
holger krekel
f9720a38fe mention added support for setUpModule/tearDownModule detection, thanks Brian Okken. 2013-06-23 09:24:48 +02:00
Brian Okken
28b2859718 change how the test is called 2013-06-22 09:42:31 -07:00
Brian Okken
5e77eb23eb add test_unittest_style_setup_teardown()
to test setUpModule() and tearDownModule()
2013-06-22 09:35:10 -07:00
variedthoughts
3bcd3317ad support unittest setUpModule/tearDownModule 2013-06-20 14:43:42 +00:00
holger krekel
4af052b098 added some endorsements, not quite properly layouted 2013-06-20 14:05:16 +02:00
holger krekel
ac3d8800fd make sessionfinish hooks execute with the same cwd-context as at
session start (helps fix plugin behaviour which write output files
with relative path such as pytest-cov)
2013-06-10 10:09:28 +02:00
holger krekel
2951c3ace9 Merged in embray/pytest (pull request #37)
Adds a test for and fixes #112
2013-06-09 15:07:44 +02:00
Erik Bray
17e1106584 reindent a few of the blockquotes in these tests 2013-06-07 17:30:10 -04:00
holger krekel
7e2d4daa68 Merge pull request #1 from alfredodeza/297
add ref targets on recwarn
2013-06-03 07:13:04 -07:00
Alfredo Deza
0cfc4a49ea adding ref targets on recwarn 2013-06-03 10:07:14 -04:00
holger krekel
da1996b5f5 fix issue316 - properly reference collection hooks in docs 2013-06-03 10:04:50 +02:00
Brianna Laugher
345b8391c4 A couple of improvements to parametrize
- When not specifying ids, let None and bools use their native string form (like str, int, float) rather than obfuscated form used for objects
- When specifying ids, explicitly raise a ValueError if a different number of ids are specified compared to the test cases
- Add tests for both these items.
2013-05-29 12:59:47 +10:00
Erik Bray
b1595d3f61 Adds a test for and fixes #112. If attempting to write to the __pycache__ directory raises a permission error _write_pyc() should just return False to prevent any further write attempts. 2013-05-28 18:11:12 -04:00
holger krekel
c294a417bd allow to specify parametrize inputs as a comma-separated string
add Wouter to changelog and to authors
2013-05-28 10:32:54 +02:00
Benjamin Peterson
bc5a5a63f2 use __dict__ not func_dict for Python 3 compatibility 2013-05-27 14:04:53 -07:00
holger krekel
655afba17d Merged in w00t0r/pytest-fixes (pull request #35)
Fixed issue #306: Keywords and markers are now matched in a defined way. Also applied some pep8 formatting while fixing.
2013-05-27 21:40:41 +02:00
Wouter van Ackooy
212f4b4d64 Issue 306: Used a set for the extra_keywords, and used listchain for parent iteration. 2013-05-27 18:14:35 +02:00
Wouter van Ackooy
60906f7a46 Issue 306: Use the names of all the parents in the chain for matching, except the Instance objects. 2013-05-27 17:58:39 +02:00
Wouter van Ackooy
72afbbbd71 Added new test to check on matching markers to full test names, which was possible before. Also adjusted check on number of deselected tests. 2013-05-23 12:21:40 +02:00
Wouter van Ackooy
583c736f0c Added a test to check there is no matching on magic values. 2013-05-23 09:12:50 +02:00
holger krekel
8a0a18e9b3 - add Brianna (@pfctdayelise ) to changelog and contributors
- fix some broken tests on py32/py33 (related to issue308 merge)
- re-format docstrings
-
2013-05-22 15:24:58 +02:00
holger krekel
bbc61c85ac Merged in pfctdayelise/pytest (pull request #36)
issue 308
2013-05-22 13:36:39 +02:00
Wouter van Ackooy
02511d1564 Added lost space. 2013-05-22 07:41:46 +02:00
holger krekel
f78408df77 add holger's gittip account, would also like to add ronny's 2013-05-21 16:05:32 +02:00
Brianna Laugher
f2175146a9 Merged hpk42/pytest into default 2013-05-21 11:18:37 +10:00
Brianna Laugher
d8bc40271a issue #308
+ docs
2013-05-21 11:12:45 +10:00
Wouter van Ackooy
fe27f3cc7d Fixed issue #306: Keywords and markers are now matched in a defined way. Also applied some pep8 formatting while fixing. 2013-05-20 14:37:58 +02:00
Brianna Laugher
3aa0e4a526 ? pull/merge 2013-05-20 12:56:30 +10:00
Brianna Laugher
ee65ca10f4 issue #308
address some comments by @hpk42 on 0b9d82e :

- move tests into their own class, rename
- add test showing metafunc.parametrize called in pytest_generate_tests rather than as decorator
- add test and fix single-argname case
- convert two loops into one in parametrize()

also
- renamed 'input' to 'n', since 'input' is a built-in
2013-05-20 12:52:20 +10:00
holger krekel
afbeb056f0 added changelog for improved doctest counting 2013-05-17 20:48:51 +02:00
Danilo de Jesus da Silva Bellini
242b67de17 zero to many doctests from module instead of one 2013-05-17 12:18:22 -03:00
holger krekel
ed1095565b Merged hpk42/pytest into default 2013-05-17 11:32:52 +02:00
Brianna Laugher
5373a63008 issue #308
first attempt, mark individual parametrize test instances with other marks (like xfail)
2013-05-17 18:46:36 +10:00
holger krekel
e6e86fa462 fix issue307 - use yaml.safe_load instead of yaml.load, thanks Mark Eichin. 2013-05-16 09:59:48 +02:00
holger krekel
5a1ce3c45c add Jaap Broekhuizen for junitxml gen 2013-05-10 08:14:39 +02:00
holger krekel
67279418ff fix junitxml generation when test output contains control characters,
addressing issue267
2013-05-10 08:13:35 +02:00
holger krekel
1f1c24fe15 Merged in jaapz/pytest-xml-escape-control-chars (pull request #32)
Fix junitxml generation when using special characters in parametrized tests.
2013-05-10 08:06:31 +02:00
Jaap Broekhuizen
7803bca335 Implemented a test for xml control character fail. 2013-05-09 21:16:57 +02:00
holger krekel
c610c903f6 mention --tb style change in changelog 2013-05-09 15:50:28 +02:00
holger krekel
36e7cc1b9c honor --tb style for setup/teardown errors as well. 2013-05-09 15:50:09 +02:00
holger krekel
d69c9da656 add maho as contributor 2013-05-09 15:37:51 +02:00
holger krekel
a113c4c6d3 Merged in maho/pytest (pull request #31)
#299
2013-05-09 15:32:29 +02:00
maho
9e3cd03721 #299 - polishing 2013-05-08 17:01:20 +02:00
Jaap Broekhuizen
0e5f2847f1 Fix pytest.py permissions. 2013-05-08 16:11:55 +02:00
Jaap Broekhuizen
963b944e79 Fix junitxml generation when using special characters in parametrized tests. 2013-05-08 15:15:43 +02:00
holger krekel
55cd3d8bf3 bump version 2013-05-07 21:39:30 +02:00
holger krekel
150ad0172f document context fixtures, also improve plugin docs 2013-05-07 21:37:08 +02:00
holger krekel
9d8645b45d enhance index page, fix announcement index 2013-05-07 21:34:59 +02:00
holger krekel
bbd265184d support boolean condition expressions in skipif/xfail
change documentation to prefer it over string expressions
2013-05-07 18:40:26 +02:00
holger krekel
3279cfa28b don't use indexservers anymore 2013-05-07 16:26:56 +02:00
holger krekel
5fb4a100c9 Removed tag 1.4.14 2013-05-07 10:55:41 +02:00
holger krekel
71b4908233 Removed tag 1.4.14 2013-05-07 10:54:46 +02:00
holger krekel
77d2f6adde fix issue245 by depending on py-1.4.14 which fixes py.io.dupfile
to not assume file.mode is present.
2013-05-07 10:54:05 +02:00
holger krekel
51688270ac implemented as context managers. Thanks Andreas Pelme,
ladimir Keleshev.
fix issue245 by depending on the released py-1.4.14
which fixes py.io.dupfile to work with files with no
mode. Thanks Jason R. Coombs.
2013-05-07 10:53:31 +02:00
holger krekel
19f3e06ab0 Added tag 1.4.14 for changeset b93ac0cdae02 2013-05-07 10:48:13 +02:00
hg
d2dc797779 #299 2013-05-05 22:15:06 +02:00
holger krekel
56aa9962fc allow fixture functions to be implemented as context managers:
@pytest.fixture
def myfix():
    # setup
    yield 1
    # teardown
2013-05-05 14:48:37 +02:00
holger krekel
8e41ef5776 bump version 2013-05-05 14:48:17 +02:00
holger krekel
331bd84ef4 change version 2013-05-05 14:23:47 +02:00
holger krekel
4ac3445056 Added tag 2.3.5 for changeset fc3a793e87ec 2013-04-30 16:41:01 +02:00
holger krekel
8c7ae7f7a5 release 2.3.5 packaging 2013-04-30 12:26:30 +02:00
holger krekel
05c4ecf892 fix recursion within import hook and source.decode in particular 2013-04-30 12:05:58 +02:00
holger krekel
c5f9958783 never consider a fixture function for test function collection 2013-04-29 10:31:51 +02:00
Floris Bruynooghe
7a90515d49 Treat frozenset as a set
Thanks to Brianna Laugher.
2013-04-28 20:59:10 +01:00
Floris Bruynooghe
3ab94544b9 Ingore rope auto-generated files 2013-04-28 20:57:52 +01:00
Floris Bruynooghe
3c317dc35e Minor style cleanup 2013-04-28 20:56:56 +01:00
holger krekel
b2cb93e06d allow re-running of a test item (as exercised by the
pytest-rerunfailures plugins) by re-initializing and removing
request/funcargs information in runtestprotocol() - which is a slightly
odd place to add funcarg-related functionality but it allows all
pytest_runtest_setup/teardown hooks to properly see a valid
request/funcarg content on test items.
2013-04-22 10:35:48 +02:00
Floris Bruynooghe
c93fbb0e57 Load conftest files in the correct order initially
When the conftest.py files are looked for intially they got loaded
starting from the subdir ending at the parent dir(s).  Later on during
collection any conftest.py files are loaded starting from the parent
dir ending at the subdir.  Due to how extending fixtures works the
latter is correct as otherwise the wrong fixture will be available.
So this changes the initial conftest loading to start at the root and
go towards the subdir.

This does also affect the order of other hooks, hence the order of the
reporting being different in testing/test_terminal.py.
2013-04-18 12:24:53 +01:00
Ronny Pfannschmidt
cf7cae0780 pdb plugin: move entering pdb into a toplevel function
this prepares pdb at collect time
2013-04-18 11:18:24 +02:00
Ronny Pfannschmidt
55c349a9eb charify pdb visible stack end finding by turning it into a function 2013-04-16 10:19:20 +02:00
Ronny Pfannschmidt
73446e98be turn the postmortem traceback selection to a function 2013-04-16 10:18:08 +02:00
holger krekel
0bc98eb9d2 add to changelog: put captured stdout/stderr into junitxml output even
for passing tests (thanks Adam Goucher)
2013-04-16 09:14:47 +02:00
holger krekel
bfe9779b37 merge 2013-04-16 09:13:58 +02:00
holger krekel
bb6f3ebd31 slightly improve -k help string
cosmetic change to test_nose.py
2013-04-16 09:04:05 +02:00
holger krekel
ee69b43c7a Merged in adamgoucher/pytest (pull request #29)
stdout/stderr now captured by junitxml
2013-04-16 09:02:08 +02:00
Ronny Pfannschmidt
63a6936d82 move pdb plugin post morten traceback selection to a own function
this is preparation for making it resillent against broken envs
that can't import doctest
2013-04-16 08:46:55 +02:00
Adam Goucher
1cbd2db621 stdout/stderr now captured by junitxml 2013-04-16 00:45:14 -04:00
holger krekel
94aa76fec0 fix reference 2013-04-04 14:36:44 +02:00
Sasha Hart
265a4de06e doc fix: 'x' should be '-x' so it is not interpreted as a filename 2013-04-03 14:51:06 -05:00
holger krekel
712898cfe1 - add release announce 2013-03-28 10:21:03 +01:00
Floris Bruynooghe
f31dc7a8b7 Attempt to improve detailed failure reporting
* If --verbose is used do not truncate.

* Add a special dict comparison instead of diffing
  pprint output.
2013-03-28 01:39:01 +00:00
Ronny Pfannschmidt
9c9679945e fix Issue 265 - integrate nose setup/teardown with setupstate
as sideeffect teardown is only called if setup doesnt fail
2013-03-25 10:52:02 +01:00
Ronny Pfannschmidt
ba79c1926c add a test for issue 14 that will xfail on python < 2.7 2013-03-25 08:53:08 +01:00
Ronny Pfannschmidt
76fb51a4ba fix issue 271 - dont write junitxml on slave nodes 2013-03-24 20:43:25 +01:00
Ronny Pfannschmidt
93da606763 fix Issue 274 - dont fail when doctest does not know the example location
instead only the last test is shown, this could use some further enhancement
2013-03-24 20:05:29 +01:00
Benjamin Peterson
5e479c94ce disable assertion rewriting on CPython 2.6.0 because of bugs (fixes #280) 2013-03-21 12:19:01 -05:00
holger krekel
1884be0121 added changelog entry for getfixture() for doctests 2013-03-21 12:41:39 +01:00
holger krekel
8f8466ee40 Merged in witsch/pytest/doctest-fixtures (pull request #25)
fixture support in doctests
2013-03-21 12:33:43 +01:00
Andreas Zeidler
dfcb0e322c rename get_fixture to getfixture to better match the current API style
--HG--
branch : doctest-fixtures
2013-03-21 12:04:14 +01:00
Andreas Zeidler
da3b42ce46 remove debugging left-overs
--HG--
branch : doctest-fixtures
2013-03-21 01:03:59 +01:00
Andreas Zeidler
fa9bd8443f update the documentation regarding the get_fixture helper
please note that the japanese translation was done using "google translate" and should probably be checked again... :)

--HG--
branch : doctest-fixtures
2013-03-20 17:54:38 +01:00
Andreas Zeidler
5a3547dd7e also provide get_fixture helper for module level doctests
--HG--
branch : doctest-fixtures
2013-03-20 17:32:48 +01:00
Andreas Zeidler
c4b3a09886 test get_fixture helper for doctests
--HG--
branch : doctest-fixtures
2013-03-20 17:14:28 +01:00
Andreas Zeidler
f747d363b0 don't expose the FixtureRequest object itself in doctests. in most cases get_fixture is sufficient, and you can always call get_fixture('request') anyway
--HG--
branch : doctest-fixtures
2013-03-20 16:36:48 +01:00
Benjamin Peterson
65c69a34ac python 2.4 compatibility 2013-03-16 20:08:01 -07:00
Antonio Cuni
ba0100e057 (antocuni, ronny around): import directly from _pytest.runner to avoid the usage of @property 2013-03-14 16:53:57 +01:00
Antonio Cuni
37c47155e0 correctly handle nose.SkipTest during collection 2013-03-14 16:10:33 +01:00
Takafumi Arakaki
5ba2a7f628 Add texinfo build target to doc/*/Makefile 2013-03-10 07:25:14 +01:00
Benjamin Peterson
0cf79b29cd in the default Python 2 case, manually check the source is ASCII (fixes #269) 2013-03-08 10:44:41 -05:00
Floris Bruynooghe
6d1662e4b7 Use py.builtin._basestring 2013-02-15 13:38:40 +00:00
Floris Bruynooghe
850fd2b7f7 Mention fix of issue 266 in changelog 2013-02-15 13:28:26 +00:00
Floris Bruynooghe
48e6aa9dc7 Allow MarkEvaluator expressions to be unicode
This fixes issue #266.
2013-02-15 11:47:48 +00:00
Ronny Pfannschmidt
0dd05023b8 fix issue 251 - report a skip instead of ignoring classes with init 2013-02-15 10:18:00 +01:00
Ronny Pfannschmidt
aeba66ac6a fix typo in link 2013-02-14 14:15:13 +01:00
Ronny Pfannschmidt
d23f9fab46 update changelog 2013-02-14 13:17:05 +01:00
Ronny Pfannschmidt
69ef750091 fix issue134 - print the collect errors that prevent running specified test items 2013-02-14 12:21:42 +01:00
Ronny Pfannschmidt
ca8b3c2307 unify logic for error exit on test failures 2013-02-14 12:13:04 +01:00
holger krekel
857c99d354 fix py32 incompatible syntax 2013-02-14 12:17:23 +01:00
holger krekel
3785f1aae3 make dev pytest depend on installing from pypi.testrun.org 2013-02-14 11:57:32 +01:00
holger krekel
d0e18ac63f issue250 unicode/str mixes in parametrization names and values now works 2013-02-12 23:30:34 +01:00
holger krekel
296f752cca fix --genscript option to generate standalone scripts that also
work with python3.3 (importer ordering)
2013-02-12 22:59:29 +01:00
holger krekel
456731ed0f fix issue257 assertion-triggered compilation of source ending in a
comment line doesn't blow up in python2.5 (fixed through py>=1.4.13.dev6)
2013-02-12 22:43:33 +01:00
holger krekel
c8653b4c02 merge 2013-02-12 20:45:01 +01:00
holger krekel
e7a86caac2 strike python3.1 tox testing, 3.2 and 3.3 is enough 2013-02-12 20:44:04 +01:00
Ronny Pfannschmidt
162c3689c6 fix issue 260 - don't use nose specials on plain unittest cases 2013-02-07 17:53:13 +01:00
Ronny Pfannschmidt
b94c3084a6 small line length fix in nose plugin call optional 2013-02-07 10:41:07 +01:00
holger krekel
570ad36eaf fix parametrized testid to provide for uniqueness 2013-02-05 17:41:45 +01:00
holger krekel
9d107523a1 py3 fixes 2013-02-04 16:07:51 +01:00
holger krekel
06ab38a2fc strip old comment and hack 2013-02-03 20:47:39 +01:00
holger krekel
e007f2dc54 add note on leipzig course in june 2013 2013-02-02 20:15:01 +01:00
Andreas Zeidler
25547e3afb pass fixture request object (and convenience shortcut to get fixtures) into doctest files
--HG--
branch : doctest-fixtures
2013-01-30 17:32:37 +01:00
Ronny Pfannschmidt
64e6c71bf6 merge 2013-01-27 02:10:52 +01:00
Ronny Pfannschmidt
80f590288b add some bits to ISSUES 2013-01-27 02:10:29 +01:00
Ronny Pfannschmidt
570688f701 ensure OutcomeExceptions like skip/fail have initialized exception attributes 2013-01-27 02:06:19 +01:00
holger krekel
c5f587d6db don't test on py24 for now because tox/virtualenv-1.8 does not support
python2.4
2013-01-26 14:49:33 +01:00
holger krekel
ee713ad036 add Brian Okken's blog post as a tutorial 2013-01-21 09:04:01 +01:00
Floris Bruynooghe
51b40dd22c Add isolation plugin as a feature 2013-01-16 17:09:17 +00:00
Benjamin Peterson
65edf87ea6 display the repr of some global names (fixes #171) 2013-01-10 11:59:08 -06:00
holger krekel
4d4b551079 adapt locations of ML to new @python.org location 2012-12-27 16:48:17 +01:00
holger krekel
e13fedc256 fix pylib links 2012-12-27 16:48:14 +01:00
holger krekel
97f9bc2e46 fix/enhance example 2012-12-20 15:57:07 +01:00
holger krekel
d0bf65e6c8 adding an example on how to do interact with the list of collected tests once before any tests are run 2012-12-16 11:28:17 +01:00
holger krekel
8d25e52e1e add sentry 2012-12-15 08:09:23 +01:00
Dusty Phillips
6fefab0e3a pocoo no longer has a pastebin service, so this section title is incorrect. 2012-12-11 12:04:12 -07:00
holger krekel
1e94d900f2 fixed versioning, thanks Arfrever 2012-12-09 09:19:33 +01:00
holger krekel
5f99511ab7 fix test after ronny's pytest-debug improvements 2012-12-04 20:31:37 +01:00
holger krekel
22dd5e29e2 when informations gets truncated, mention use of "-vv" to see it. 2012-11-30 12:18:12 +01:00
Ronny Pfannschmidt
725e63db66 improve PYTEST_DEBUG tracing output
by putingextra data on a new lines
with additional indent
2012-11-29 10:04:39 +01:00
holger krekel
3d79e7060e allow to specify prefixes starting with "_" when
customizing python_functions test discovery. (thanks Graham Horler)
2012-11-28 09:23:36 +01:00
Graham Horler
1d7c71884e Remove check for "_" prefix on python functions (use python_functions)
(See IRC hpk 2012-11-27 14:56: after the python_functions customization
 was introduced, it makes sense to disregard the preliminary "_" check)
2012-11-27 16:58:08 +00:00
Wieland Hoffmann
ffb5b8efa1 Fix a broken link to pytest-twisted 2012-11-22 19:59:15 +01:00
holger krekel
68786a6434 fix bug where using capsys with pytest.set_trace() in a test
function would break when looking at capsys.readouterr()
2012-11-21 20:43:31 +01:00
holger krekel
b97de57ebe improve docstring for metafunc.parametrize() 2012-11-21 10:13:44 +01:00
holger krekel
03445913e0 reanme README.txt to README.rst 2012-11-20 14:37:39 +01:00
holger krekel
8580058ffb move long description into README 2012-11-20 14:24:26 +01:00
holger krekel
1c9ef2443f bump version, fix -k option help 2012-11-20 14:20:39 +01:00
holger krekel
cac1a48fc7 Added tag 2.3.4 for changeset ef299e57f242 2012-11-20 14:09:40 +01:00
171 changed files with 9135 additions and 3123 deletions

32
.gitignore vendored Normal file
View File

@@ -0,0 +1,32 @@
# Automatically generated by `hgimportsvn`
.svn
.hgsvn
# Ignore local virtualenvs
lib/
bin/
include/
.Python/
# These lines are suggested according to the svn:ignore property
# Feel free to enable them by uncommenting them
*.pyc
*.pyo
*.swp
*.class
*.orig
*~
doc/*/_build
build/
dist/
*.egg-info
issue/
env/
3rdparty/
.tox
.cache
.coverage
.ropeproject
.idea

View File

@@ -1,9 +1,15 @@
# Automatically generated by `hgimportsvn`
syntax:glob
.svn
.hgsvn
# Ignore local virtualenvs
syntax:glob
lib/
bin/
include/
.Python/
# These lines are suggested according to the svn:ignore property
# Feel free to enable them by uncommenting them
syntax:glob
@@ -25,3 +31,5 @@ env/
.tox
.cache
.coverage
.ropeproject
*.sublime-*

11
.hgtags
View File

@@ -53,3 +53,14 @@ c27a60097767c16a54ae56d9669a77925b213b9b 2.3.0
acf0e1477fb19a1d35a4e40242b77fa6af32eb17 2.3.1
8738b828dec53937765db71951ef955cca4c51f6 2.3.2
7fe44182c434f8ac89149a3c340479872a5d5ccb 2.3.3
ef299e57f24218dbdd949498d7e660723636bcc3 2.3.4
fc3a793e87ec907000a47ea0d3a372a2fe218c0a 2.3.5
b93ac0cdae02effaa3c136a681cc45bba757fe46 1.4.14
b93ac0cdae02effaa3c136a681cc45bba757fe46 1.4.14
0000000000000000000000000000000000000000 1.4.14
0000000000000000000000000000000000000000 1.4.14
0000000000000000000000000000000000000000 1.4.14
af860de70cc3f157ac34ca1d4bf557a057bff775 2.4.0
8828c924acae0b4cad2e2cb92943d51da7cb744a 2.4.1
8d051f89184bfa3033f5e59819dff9f32a612941 2.4.2
a064ad64d167508a8e9e73766b1a4e6bd10c85db 2.5.0

10
.travis.yml Normal file
View File

@@ -0,0 +1,10 @@
language: python
# command to install dependencies
install: "pip install -U detox"
# # command to run tests
script: detox --recreate
notifications:
irc:
- "chat.freenode.net#pytest-dev"
email:
- pytest-commit@python.org

13
AUTHORS
View File

@@ -6,9 +6,15 @@ Contributors include::
Ronny Pfannschmidt
Benjamin Peterson
Floris Bruynooghe
Jason R. Coombs
Wouter van Ackooy
Samuele Pedroni
Anatoly Bubenkoff
Brianna Laugher
Carl Friedrich Bolz
Armin Rigo
Maho
Jaap Broekhuizen
Maciek Fijalkowski
Guido Wesdorp
Brian Dorsey
@@ -23,3 +29,10 @@ Grig Gheorghiu
Bob Ippolito
Christian Tismer
Daniel Nuri
Graham Horler
Andreas Zeidler
Brian Okken
Katarzyna Jachim
Christian Theunert
Anthon van der Neut
Mark Abramowitz

564
CHANGELOG
View File

@@ -1,7 +1,445 @@
Changes between 2.3.3 and 2.3.4
2.5.1
-----------------------------------
- yielded test functions will now have autouse-fixtures active but
- merge new documentation styling PR from Tobias Bieniek.
- fix issue403: allow parametrize of multiple same-name functions within
a collection node. Thanks Andreas Kloeckner and Alex Gaynor for reporting
and analysis.
- Allow parameterized fixtures to specify the ID of the parameters by
adding an ids argument to pytest.fixture() and pytest.yield_fixture().
Thanks Floris Bruynooghe.
- fix issue404 by always using the binary xml escape in the junitxml
plugin. Thanks Ronny Pfannschmidt.
- fix issue407: fix addoption docstring to point to argparse instead of
optparse. Thanks Daniel D. Wright.
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
with this release we believe).
- simplified and fixed implementation for calling finalizers when
parametrized fixtures or function arguments are involved. finalization
is now performed lazily at setup time instead of in the "teardown phase".
While this might sound odd at first, it helps to ensure that we are
correctly handling setup/teardown even in complex code. User-level code
should not be affected unless it's implementing the pytest_runtest_teardown
hook and expecting certain fixture instances are torn down within (very
unlikely and would have been unreliable anyway).
- PR90: add --color=yes|no|auto option to force terminal coloring
mode ("auto" is default). Thanks Marc Abramowitz.
- fix issue319 - correctly show unicode in assertion errors. Many
thanks to Floris Bruynooghe for the complete PR. Also means
we depend on py>=1.4.19 now.
- fix issue396 - correctly sort and finalize class-scoped parametrized
tests independently from number of methods on the class.
- refix issue323 in a better way -- parametrization should now never
cause Runtime Recursion errors because the underlying algorithm
for re-ordering tests per-scope/per-fixture is not recursive
anymore (it was tail-call recursive before which could lead
to problems for more than >966 non-function scoped parameters).
- fix issue290 - there is preliminary support now for parametrizing
with repeated same values (sometimes useful to to test if calling
a second time works as with the first time).
- close issue240 - document precisely how pytest module importing
works, discuss the two common test directory layouts, and how it
interacts with PEP420-namespace packages.
- fix issue246 fix finalizer order to be LIFO on independent fixtures
depending on a parametrized higher-than-function scoped fixture.
(was quite some effort so please bear with the complexity of this sentence :)
Thanks Ralph Schmitt for the precise failure example.
- fix issue244 by implementing special index for parameters to only use
indices for paramentrized test ids
- fix issue287 by running all finalizers but saving the exception
from the first failing finalizer and re-raising it so teardown will
still have failed. We reraise the first failing exception because
it might be the cause for other finalizers to fail.
- fix ordering when mock.patch or other standard decorator-wrappings
are used with test methods. This fixues issue346 and should
help with random "xdist" collection failures. Thanks to
Ronny Pfannschmidt and Donald Stufft for helping to isolate it.
- fix issue357 - special case "-k" expressions to allow for
filtering with simple strings that are not valid python expressions.
Examples: "-k 1.3" matches all tests parametrized with 1.3.
"-k None" filters all tests that have "None" in their name
and conversely "-k 'not None'".
Previously these examples would raise syntax errors.
- fix issue384 by removing the trial support code
since the unittest compat enhancements allow
trial to handle it on its own
- don't hide an ImportError when importing a plugin produces one.
fixes issue375.
- fix issue275 - allow usefixtures and autouse fixtures
for running doctest text files.
- fix issue380 by making --resultlog only rely on longrepr instead
of the "reprcrash" attribute which only exists sometimes.
- address issue122: allow @pytest.fixture(params=iterator) by exploding
into a list early on.
- fix pexpect-3.0 compatibility for pytest's own tests.
(fixes issue386)
- allow nested parametrize-value markers, thanks James Lan for the PR.
- fix unicode handling with new monkeypatch.setattr(import_path, value)
API. Thanks Rob Dennis. Fixes issue371.
- fix unicode handling with junitxml, fixes issue368.
- In assertion rewriting mode on Python 2, fix the detection of coding
cookies. See issue #330.
- make "--runxfail" turn imperative pytest.xfail calls into no ops
(it already did neutralize pytest.mark.xfail markers)
- refine pytest / pkg_resources interactions: The AssertionRewritingHook
PEP302 compliant loader now registers itself with setuptools/pkg_resources
properly so that the pkg_resources.resource_stream method works properly.
Fixes issue366. Thanks for the investigations and full PR to Jason R. Coombs.
- pytestconfig fixture is now session-scoped as it is the same object during the
whole test run. Fixes issue370.
- avoid one surprising case of marker malfunction/confusion::
@pytest.mark.some(lambda arg: ...)
def test_function():
would not work correctly because pytest assumes @pytest.mark.some
gets a function to be decorated already. We now at least detect if this
arg is an lambda and thus the example will work. Thanks Alex Gaynor
for bringing it up.
- xfail a test on pypy that checks wrong encoding/ascii (pypy does
not error out). fixes issue385.
- internally make varnames() deal with classes's __init__,
although it's not needed by pytest itself atm. Also
fix caching. Fixes issue376.
- fix issue221 - handle importing of namespace-package with no
__init__.py properly.
- refactor internal FixtureRequest handling to avoid monkeypatching.
One of the positive user-facing effects is that the "request" object
can now be used in closures.
- fixed version comparison in pytest.importskip(modname, minverstring)
- fix issue377 by clarifying in the nose-compat docs that pytest
does not duplicate the unittest-API into the "plain" namespace.
- fix verbose reporting for @mock'd test functions
v2.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)
thanks Paul Moore for bringing it up.
- fix "-k" matching of tests where "repr" and "attr" and other names would
cause wrong matches because of an internal implementation quirk
(don't ask) which is now properly implemented. fixes issue345.
- avoid tmpdir fixture to create too long filenames especially
when parametrization is used (issue354)
- fix pytest-pep8 and pytest-flakes / pytest interactions
(collection names in mark plugin was assuming an item always
has a function which is not true for those plugins etc.)
Thanks Andi Zeidler.
- introduce node.get_marker/node.add_marker API for plugins
like pytest-pep8 and pytest-flakes to avoid the messy
details of the node.keywords pseudo-dicts. Adapated
docs.
- remove attempt to "dup" stdout at startup as it's icky.
the normal capturing should catch enough possibilities
of tests messing up standard FDs.
- add pluginmanager.do_configure(config) as a link to
config.do_configure() for plugin-compatibility
v2.4.1
-----------------------------------
- When using parser.addoption() unicode arguments to the
"type" keyword should also be converted to the respective types.
thanks Floris Bruynooghe, @dnozay. (fixes issue360 and issue362)
- fix dotted filename completion when using argcomplete
thanks Anthon van der Neuth. (fixes issue361)
- fix regression when a 1-tuple ("arg",) is used for specifying
parametrization (the values of the parametrization were passed
nested in a tuple). Thanks Donald Stufft.
- merge doc typo fixes, thanks Andy Dirnberger
v2.4
-----------------------------------
known incompatibilities:
- if calling --genscript from python2.7 or above, you only get a
standalone script which works on python2.7 or above. Use Python2.6
to also get a python2.5 compatible version.
- all xunit-style teardown methods (nose-style, pytest-style,
unittest-style) will not be called if the corresponding setup method failed,
see issue322 below.
- the pytest_plugin_unregister hook wasn't ever properly called
and there is no known implementation of the hook - so it got removed.
- pytest.fixture-decorated functions cannot be generators (i.e. use
yield) anymore. This change might be reversed in 2.4.1 if it causes
unforeseen real-life issues. However, you can always write and return
an inner function/generator and change the fixture consumer to iterate
over the returned generator. This change was done in lieu of the new
``pytest.yield_fixture`` decorator, see below.
new features:
- experimentally introduce a new ``pytest.yield_fixture`` decorator
which accepts exactly the same parameters as pytest.fixture but
mandates a ``yield`` statement instead of a ``return statement`` from
fixture functions. This allows direct integration with "with-style"
context managers in fixture functions and generally avoids registering
of finalization callbacks in favour of treating the "after-yield" as
teardown code. Thanks Andreas Pelme, Vladimir Keleshev, Floris
Bruynooghe, Ronny Pfannschmidt and many others for discussions.
- allow boolean expression directly with skipif/xfail
if a "reason" is also specified. Rework skipping documentation
to recommend "condition as booleans" because it prevents surprises
when importing markers between modules. Specifying conditions
as strings will remain fully supported.
- reporting: color the last line red or green depending if
failures/errors occured or everything passed. thanks Christian
Theunert.
- make "import pdb ; pdb.set_trace()" work natively wrt capturing (no
"-s" needed anymore), making ``pytest.set_trace()`` a mere shortcut.
- fix issue181: --pdb now also works on collect errors (and
on internal errors) . This was implemented by a slight internal
refactoring and the introduction of a new hook
``pytest_exception_interact`` hook (see next item).
- fix issue341: introduce new experimental hook for IDEs/terminals to
intercept debugging: ``pytest_exception_interact(node, call, report)``.
- new monkeypatch.setattr() variant to provide a shorter
invocation for patching out classes/functions from modules:
monkeypatch.setattr("requests.get", myfunc)
will replace the "get" function of the "requests" module with ``myfunc``.
- fix issue322: tearDownClass is not run if setUpClass failed. Thanks
Mathieu Agopian for the initial fix. Also make all of pytest/nose
finalizer mimick the same generic behaviour: if a setupX exists and
fails, don't run teardownX. This internally introduces a new method
"node.addfinalizer()" helper which can only be called during the setup
phase of a node.
- simplify pytest.mark.parametrize() signature: allow to pass a
CSV-separated string to specify argnames. For example:
``pytest.mark.parametrize("input,expected", [(1,2), (2,3)])``
works as well as the previous:
``pytest.mark.parametrize(("input", "expected"), ...)``.
- add support for setUpModule/tearDownModule detection, thanks Brian Okken.
- integrate tab-completion on options through use of "argcomplete".
Thanks Anthon van der Neut for the PR.
- change option names to be hyphen-separated long options but keep the
old spelling backward compatible. py.test -h will only show the
hyphenated version, for example "--collect-only" but "--collectonly"
will remain valid as well (for backward-compat reasons). Many thanks to
Anthon van der Neut for the implementation and to Hynek Schlawack for
pushing us.
- fix issue 308 - allow to mark/xfail/skip individual parameter sets
when parametrizing. Thanks Brianna Laugher.
- call new experimental pytest_load_initial_conftests hook to allow
3rd party plugins to do something before a conftest is loaded.
Bug fixes:
- fix issue358 - capturing options are now parsed more properly
by using a new parser.parse_known_args method.
- pytest now uses argparse instead of optparse (thanks Anthon) which
means that "argparse" is added as a dependency if installing into python2.6
environments or below.
- fix issue333: fix a case of bad unittest/pytest hook interaction.
- PR27: correctly handle nose.SkipTest during collection. Thanks
Antonio Cuni, Ronny Pfannschmidt.
- fix issue355: junitxml puts name="pytest" attribute to testsuite tag.
- fix issue336: autouse fixture in plugins should work again.
- fix issue279: improve object comparisons on assertion failure
for standard datatypes and recognise collections.abc. Thanks to
Brianna Laugher and Mathieu Agopian.
- fix issue317: assertion rewriter support for the is_package method
- fix issue335: document py.code.ExceptionInfo() object returned
from pytest.raises(), thanks Mathieu Agopian.
- remove implicit distribute_setup support from setup.py.
- fix issue305: ignore any problems when writing pyc files.
- SO-17664702: call fixture finalizers even if the fixture function
partially failed (finalizers would not always be called before)
- fix issue320 - fix class scope for fixtures when mixed with
module-level functions. Thanks Anatloy Bubenkoff.
- you can specify "-q" or "-qq" to get different levels of "quieter"
reporting (thanks Katarzyna Jachim)
- fix issue300 - Fix order of conftest loading when starting py.test
in a subdirectory.
- fix issue323 - sorting of many module-scoped arg parametrizations
- make sessionfinish hooks execute with the same cwd-context as at
session start (helps fix plugin behaviour which write output files
with relative path such as pytest-cov)
- fix issue316 - properly reference collection hooks in docs
- fix issue 306 - cleanup of -k/-m options to only match markers/test
names/keywords respectively. Thanks Wouter van Ackooy.
- improved doctest counting for doctests in python modules --
files without any doctest items will not show up anymore
and doctest examples are counted as separate test items.
thanks Danilo Bellini.
- fix issue245 by depending on the released py-1.4.14
which fixes py.io.dupfile to work with files with no
mode. Thanks Jason R. Coombs.
- fix junitxml generation when test output contains control characters,
addressing issue267, thanks Jaap Broekhuizen
- fix issue338: honor --tb style for setup/teardown errors as well. Thanks Maho.
- fix issue307 - use yaml.safe_load in example, thanks Mark Eichin.
- better parametrize error messages, thanks Brianna Laugher
- pytest_terminal_summary(terminalreporter) hooks can now use
".section(title)" and ".line(msg)" methods to print extra
information at the end of a test run.
v2.3.5
-----------------------------------
- fix issue169: respect --tb=style with setup/teardown errors as well.
- never consider a fixture function for test function collection
- allow re-running of test items / helps to fix pytest-reruntests plugin
and also help to keep less fixture/resource references alive
- put captured stdout/stderr into junitxml output even for passing tests
(thanks Adam Goucher)
- Issue 265 - integrate nose setup/teardown with setupstate
so it doesnt try to teardown if it did not setup
- issue 271 - dont write junitxml on slave nodes
- Issue 274 - dont try to show full doctest example
when doctest does not know the example location
- issue 280 - disable assertion rewriting on buggy CPython 2.6.0
- inject "getfixture()" helper to retrieve fixtures from doctests,
thanks Andreas Zeidler
- issue 259 - when assertion rewriting, be consistent with the default
source encoding of ASCII on Python 2
- issue 251 - report a skip instead of ignoring classes with init
- issue250 unicode/str mixes in parametrization names and values now works
- issue257, assertion-triggered compilation of source ending in a
comment line doesn't blow up in python2.5 (fixed through py>=1.4.13.dev6)
- fix --genscript option to generate standalone scripts that also
work with python3.3 (importer ordering)
- issue171 - in assertion rewriting, show the repr of some
global variables
- fix option help for "-k"
- move long description of distribution into README.rst
- improve docstring for metafunc.parametrize()
- fix bug where using capsys with pytest.set_trace() in a test
function would break when looking at capsys.readouterr()
- allow to specify prefixes starting with "_" when
customizing python_functions test discovery. (thanks Graham Horler)
- improve PYTEST_DEBUG tracing output by puting
extra data on a new lines with additional indent
- ensure OutcomeExceptions like skip/fail have initialized exception attributes
- issue 260 - don't use nose special setup on plain unittest cases
- fix issue134 - print the collect errors that prevent running specified test items
- fix issue266 - accept unicode in MarkEvaluator expressions
v2.3.4
-----------------------------------
- yielded test functions will now have autouse-fixtures active but
cannot accept fixtures as funcargs - it's anyway recommended to
rather use the post-2.0 parametrize features instead of yield, see:
http://pytest.org/latest/example/parametrize.html
@@ -16,9 +454,9 @@ Changes between 2.3.3 and 2.3.4
can write: -k "name1 or name2" etc. This is a slight incompatibility
if you used special syntax like "TestClass.test_method" which you now
need to write as -k "TestClass and test_method" to match a certain
method in a certain test class.
method in a certain test class.
Changes between 2.3.2 and 2.3.3
v2.3.3
-----------------------------------
- fix issue214 - parse modules that contain special objects like e. g.
@@ -50,10 +488,10 @@ Changes between 2.3.2 and 2.3.3
- fix issue127 - improve documentation for pytest_addoption() and
add a ``config.getoption(name)`` helper function for consistency.
Changes between 2.3.1 and 2.3.2
v2.3.2
-----------------------------------
- fix issue208 and fix issue29 use new py version to avoid long pauses
- fix issue208 and fix issue29 use new py version to avoid long pauses
when printing tracebacks in long modules
- fix issue205 - conftests in subdirs customizing
@@ -83,7 +521,7 @@ Changes between 2.3.1 and 2.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
Changes between 2.3.0 and 2.3.1
v2.3.1
-----------------------------------
- fix issue202 - fix regression: using "self" from fixture functions now
@@ -96,12 +534,12 @@ Changes between 2.3.0 and 2.3.1
- link to web pages from --markers output which provides help for
pytest.mark.* usage.
Changes between 2.2.4 and 2.3.0
v2.3.0
-----------------------------------
- fix issue202 - better automatic names for parametrized test functions
- fix issue139 - introduce @pytest.fixture which allows direct scoping
and parametrization of funcarg factories.
and parametrization of funcarg factories.
- fix issue198 - conftest fixtures were not found on windows32 in some
circumstances with nested directory structures due to path manipulation issues
- fix issue193 skip test functions with were parametrized with empty
@@ -137,7 +575,7 @@ Changes between 2.2.4 and 2.3.0
- pluginmanager.register(...) now raises ValueError if the
plugin has been already registered or the name is taken
- fix issue159: improve http://pytest.org/latest/faq.html
- fix issue159: improve http://pytest.org/latest/faq.html
especially with respect to the "magic" history, also mention
pytest-django, trial and unittest integration.
@@ -168,14 +606,14 @@ Changes between 2.2.4 and 2.3.0
you can use startdir.bestrelpath(yourpath) to show
nice relative path
- allow plugins to implement both pytest_report_header and
- allow plugins to implement both pytest_report_header and
pytest_sessionstart (sessionstart is invoked first).
- don't show deselected reason line if there is none
- py.test -vv will show all of assert comparisations instead of truncating
Changes between 2.2.3 and 2.2.4
v2.2.4
-----------------------------------
- fix error message for rewritten assertions involving the % operator
@@ -192,17 +630,17 @@ Changes between 2.2.3 and 2.2.4
- fix issue #144: better mangle test ids to junitxml classnames
- upgrade distribute_setup.py to 0.6.27
Changes between 2.2.2 and 2.2.3
v2.2.3
----------------------------------------
- fix uploaded package to only include neccesary files
Changes between 2.2.1 and 2.2.2
v2.2.2
----------------------------------------
- fix issue101: wrong args to unittest.TestCase test function now
produce better output
- fix issue102: report more useful errors and hints for when a
- fix issue102: report more useful errors and hints for when a
test directory was renamed and some pyc/__pycache__ remain
- fix issue106: allow parametrize to be applied multiple times
e.g. from module, class and at function level.
@@ -217,11 +655,11 @@ Changes between 2.2.1 and 2.2.2
- allow adding of attributes to test reports such that it also works
with distributed testing (no upgrade of pytest-xdist needed)
Changes between 2.2.0 and 2.2.1
v2.2.1
----------------------------------------
- fix issue99 (in pytest and py) internallerrors with resultlog now
produce better output - fixed by normalizing pytest_internalerror
produce better output - fixed by normalizing pytest_internalerror
input arguments.
- fix issue97 / traceback issues (in pytest and py) improve traceback output
in conjunction with jinja2 and cython which hack tracebacks
@@ -229,25 +667,25 @@ Changes between 2.2.0 and 2.2.1
the final test in a test node will now run its teardown directly
instead of waiting for the end of the session. Thanks Dave Hunt for
the good reporting and feedback. The pytest_runtest_protocol as well
as the pytest_runtest_teardown hooks now have "nextitem" available
as the pytest_runtest_teardown hooks now have "nextitem" available
which will be None indicating the end of the test run.
- fix collection crash due to unknown-source collected items, thanks
to Ralf Schmitt (fixed by depending on a more recent pylib)
Changes between 2.1.3 and 2.2.0
v2.2.0
----------------------------------------
- fix issue90: introduce eager tearing down of test items so that
teardown function are called earlier.
- add an all-powerful metafunc.parametrize function which allows to
- add an all-powerful metafunc.parametrize function which allows to
parametrize test function arguments in multiple steps and therefore
from indepdenent plugins and palces.
from indepdenent plugins and palces.
- add a @pytest.mark.parametrize helper which allows to easily
call a test function with different argument values
- Add examples to the "parametrize" example page, including a quick port
- Add examples to the "parametrize" example page, including a quick port
of Test scenarios and the new parametrize function and decorator.
- introduce registration for "pytest.mark.*" helpers via ini-files
or through plugin hooks. Also introduce a "--strict" option which
or through plugin hooks. Also introduce a "--strict" option which
will treat unregistered markers as errors
allowing to avoid typos and maintain a well described set of markers
for your test suite. See exaples at http://pytest.org/latest/mark.html
@@ -256,12 +694,12 @@ Changes between 2.1.3 and 2.2.0
(this is a stricter and more predictable version of '-k' in that "-m"
only matches complete markers and has more obvious rules for and/or
semantics.
- new feature to help optimizing the speed of your tests:
--durations=N option for displaying N slowest test calls
- new feature to help optimizing the speed of your tests:
--durations=N option for displaying N slowest test calls
and setup/teardown methods.
- fix issue87: --pastebin now works with python3
- fix issue89: --pdb with unexpected exceptions in doctest work more sensibly
- fix and cleanup pytest's own test suite to not leak FDs
- fix and cleanup pytest's own test suite to not leak FDs
- fix issue83: link to generated funcarg list
- fix issue74: pyarg module names are now checked against imp.find_module false positives
- fix compatibility with twisted/trial-11.1.0 use cases
@@ -269,7 +707,7 @@ Changes between 2.1.3 and 2.2.0
- simplify junitxml output code by relying on py.xml
- add support for skip properties on unittest classes and functions
Changes between 2.1.2 and 2.1.3
v2.1.3
----------------------------------------
- fix issue79: assertion rewriting failed on some comparisons in boolops
@@ -278,7 +716,7 @@ Changes between 2.1.2 and 2.1.3
- fix issue75 / skipping test failure on jython
- fix issue77 / Allow assertrepr_compare hook to apply to a subset of tests
Changes between 2.1.1 and 2.1.2
v2.1.2
----------------------------------------
- fix assertion rewriting on files with windows newlines on some Python versions
@@ -288,7 +726,7 @@ Changes between 2.1.1 and 2.1.2
- fix issue66: use different assertion rewriting caches when the -O option is passed
- don't try assertion rewriting on Jython, use reinterp
Changes between 2.1.0 and 2.1.1
v2.1.1
----------------------------------------------
- fix issue64 / pytest.set_trace now works within pytest_generate_tests hooks
@@ -301,7 +739,7 @@ Changes between 2.1.0 and 2.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"
Changes between 2.0.3 and 2.1.0.DEV
v2.1.0
----------------------------------------------
- fix issue53 call nosestyle setup functions with correct ordering
@@ -321,7 +759,7 @@ Changes between 2.0.3 and 2.1.0.DEV
- report KeyboardInterrupt even if interrupted during session startup
- fix issue 35 - provide PDF doc version and download link from index page
Changes between 2.0.2 and 2.0.3
v2.0.3
----------------------------------------------
- fix issue38: nicer tracebacks on calls to hooks, particularly early
@@ -341,7 +779,7 @@ Changes between 2.0.2 and 2.0.3
- fix issue37: avoid invalid characters in junitxml's output
Changes between 2.0.1 and 2.0.2
v2.0.2
----------------------------------------------
- tackle issue32 - speed up test runs of very quick test functions
@@ -353,17 +791,17 @@ Changes between 2.0.1 and 2.0.2
Also you can now access module globals from xfail/skipif
expressions so that this for example works now::
import pytest
import mymodule
@pytest.mark.skipif("mymodule.__version__[0] == "1")
def test_function():
pass
This will not run the test function if the module's version string
This will not run the test function if the module's version string
does not start with a "1". Note that specifying a string instead
of a boolean expressions allows py.test to report meaningful information
when summarizing a test run as to what conditions lead to skipping
of a boolean expressions allows py.test to report meaningful information
when summarizing a test run as to what conditions lead to skipping
(or xfail-ing) tests.
- fix issue28 - setup_method and pytest_generate_tests work together
@@ -386,14 +824,14 @@ Changes between 2.0.1 and 2.0.2
- fixed typos in the docs (thanks Victor Garcia, Brianna Laugher) and particular
thanks to Laura Creighton who also revieved parts of the documentation.
- fix slighly wrong output of verbose progress reporting for classes
- fix slighly wrong output of verbose progress reporting for classes
(thanks Amaury)
- more precise (avoiding of) deprecation warnings for node.Class|Function accesses
- avoid std unittest assertion helper code in tracebacks (thanks Ronny)
Changes between 2.0.0 and 2.0.1
v2.0.1
----------------------------------------------
- refine and unify initial capturing so that it works nicely
@@ -402,7 +840,7 @@ Changes between 2.0.0 and 2.0.1
- allow to omit "()" in test ids to allow for uniform test ids
as produced by Alfredo's nice pytest.vim plugin.
- fix issue12 - show plugin versions with "--version" and
"--traceconfig" and also document how to add extra information
"--traceconfig" and also document how to add extra information
to reporting test header
- fix issue17 (import-* reporting issue on python3) by
requiring py>1.4.0 (1.4.1 is going to include it)
@@ -432,17 +870,17 @@ Changes between 2.0.0 and 2.0.1
- fix issue14: no logging errors at process exit
- refinements to "collecting" output on non-ttys
- refine internal plugin registration and --traceconfig output
- introduce a mechanism to prevent/unregister plugins from the
- introduce a mechanism to prevent/unregister plugins from the
command line, see http://pytest.org/plugins.html#cmdunregister
- activate resultlog plugin by default
- fix regression wrt yielded tests which due to the
collection-before-running semantics were not
collection-before-running semantics were not
setup as with pytest 1.3.4. Note, however, that
the recommended and much cleaner way to do test
the recommended and much cleaner way to do test
parametraization remains the "pytest_generate_tests"
mechanism, see the docs.
Changes between 1.3.4 and 2.0.0
v2.0.0
----------------------------------------------
- pytest-2.0 is now its own package and depends on pylib-2.0
@@ -487,7 +925,7 @@ Changes between 1.3.4 and 2.0.0
- add ability to use "class" level for cached_setup helper
- fix strangeness: mark.* objects are now immutable, create new instances
Changes between 1.3.3 and 1.3.4
v1.3.4
----------------------------------------------
- fix issue111: improve install documentation for windows
@@ -496,7 +934,7 @@ Changes between 1.3.3 and 1.3.4
- fix issue115: unify internal exception passthrough/catching/GeneratorExit
- fix issue118: new --tb=native for presenting cpython-standard exceptions
Changes between 1.3.2 and 1.3.3
v1.3.3
----------------------------------------------
- fix issue113: assertion representation problem with triple-quoted strings
@@ -511,7 +949,7 @@ Changes between 1.3.2 and 1.3.3
(thanks Armin Ronacher for reporting)
- remove trailing whitespace in all py/text distribution files
Changes between 1.3.1 and 1.3.2
v1.3.2
----------------------------------------------
New features
@@ -586,7 +1024,7 @@ Bug fixes / Maintenance
- fix homedir detection on Windows
- ship distribute_setup.py version 0.6.13
Changes between 1.3.0 and 1.3.1
v1.3.1
---------------------------------------------
New features
@@ -658,7 +1096,7 @@ Fixes / Maintenance
(and internally be more careful when presenting unexpected byte sequences)
Changes between 1.2.1 and 1.3.0
v1.3.0
---------------------------------------------
- deprecate --report option in favour of a new shorter and easier to
@@ -723,7 +1161,7 @@ Changes between 1.2.1 and 1.3.0
- added links to the new capturelog and coverage plugins
Changes between 1.2.1 and 1.2.0
v1.2.0
---------------------------------------------
- refined usage and options for "py.cleanup"::
@@ -762,7 +1200,7 @@ Changes between 1.2.1 and 1.2.0
- fix plugin links
Changes between 1.2 and 1.1.1
v1.1.1
---------------------------------------------
- moved dist/looponfailing from py.test core into a new
@@ -846,7 +1284,7 @@ Changes between 1.2 and 1.1.1
- fix docs, fix internal bin/ script generation
Changes between 1.1.1 and 1.1.0
v1.1.0
---------------------------------------------
- introduce automatic plugin registration via 'pytest11'
@@ -865,7 +1303,7 @@ Changes between 1.1.1 and 1.1.0
- try harder to have deprecation warnings for py.compat.* accesses
report a correct location
Changes between 1.1.0 and 1.0.2
v1.0.2
---------------------------------------------
* adjust and improve docs
@@ -950,7 +1388,7 @@ Changes between 1.1.0 and 1.0.2
* simplified internal localpath implementation
Changes between 1.0.1 and 1.0.2
v1.0.2
-------------------------------------------
* fixing packaging issues, triggered by fedora redhat packaging,
@@ -958,7 +1396,7 @@ Changes between 1.0.1 and 1.0.2
* added a documentation link to the new django plugin.
Changes between 1.0.0 and 1.0.1
v1.0.1
-------------------------------------------
* added a 'pytest_nose' plugin which handles nose.SkipTest,
@@ -992,13 +1430,13 @@ Changes between 1.0.0 and 1.0.1
* simplified multicall mechanism and plugin architecture,
renamed some internal methods and argnames
Changes between 1.0.0b9 and 1.0.0
v1.0.0
-------------------------------------------
* more terse reporting try to show filesystem path relatively to current dir
* improve xfail output a bit
Changes between 1.0.0b8 and 1.0.0b9
v1.0.0b9
-------------------------------------------
* cleanly handle and report final teardown of test setup
@@ -1032,7 +1470,7 @@ Changes between 1.0.0b8 and 1.0.0b9
* item.repr_failure(excinfo) instead of item.repr_failure(excinfo, outerr)
Changes between 1.0.0b7 and 1.0.0b8
v1.0.0b8
-------------------------------------------
* pytest_unittest-plugin is now enabled by default
@@ -1061,7 +1499,7 @@ Changes between 1.0.0b7 and 1.0.0b8
* tweaked doctest output for docstrings in py modules,
thanks Radomir.
Changes between 1.0.0b3 and 1.0.0b7
v1.0.0b7
-------------------------------------------
* renamed py.test.xfail back to py.test.mark.xfail to avoid
@@ -1086,7 +1524,7 @@ Changes between 1.0.0b3 and 1.0.0b7
* make __name__ == "__channelexec__" for remote_exec code
Changes between 1.0.0b1 and 1.0.0b3
v1.0.0b3
-------------------------------------------
* plugin classes are removed: one now defines
@@ -1103,7 +1541,7 @@ Changes between 1.0.0b1 and 1.0.0b3
well with function arguments.
Changes between 0.9.2 and 1.0.0b1
v1.0.0b1
-------------------------------------------
* introduced new "funcarg" setup method,
@@ -1127,7 +1565,7 @@ Changes between 0.9.2 and 1.0.0b1
XXX lots of things missing here XXX
Changes between 0.9.1 and 0.9.2
v0.9.2
-------------------------------------------
* refined installation and metadata, created new setup.py,
@@ -1160,10 +1598,10 @@ Changes between 0.9.1 and 0.9.2
* there now is a py.__version__ attribute
Changes between 0.9.0 and 0.9.1
v0.9.1
-------------------------------------------
This is a fairly complete list of changes between 0.9 and 0.9.1, which can
This is a fairly complete list of v0.9.1, which can
serve as a reference for developers.
* allowing + signs in py.path.svn urls [39106]

View File

@@ -1,10 +1,22 @@
improve / add to dependency/test resource injection
recorder = monkeypatch.function(".......")
-------------------------------------------------------------
tags: wish feature docs
tags: nice feature
write up better examples showing the connection between
the two.
Like monkeypatch.replace but sets a mock-like call recorder:
recorder = monkeypatch.function("os.path.abspath")
recorder.set_return("/hello")
os.path.abspath("hello")
call, = recorder.calls
assert call.args.path == "hello"
assert call.returned == "/hello"
...
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
-------------------------------------------------------------
@@ -94,20 +106,6 @@ appropriately to avoid this issue. Moreover/Alternatively, we could
record which implementations of a hook succeeded and only call their
teardown.
consider and document __init__ file usage in test directories
---------------------------------------------------------------
tags: bug core
Currently, a test module is imported with its fully qualified
package path, determined by checking __init__ files upwards.
This has the side effect that a source package at the root
of the test dir could be imported as well. This is somewhat
convenient but complicates the picture for running tests against
different versions of a package. Also, implicit sys.path
manipulations are problematic per-se. Maybe factorting out
a pytest_addsyspath hook which can be disabled from the command line
makes sense. In any case documentation/recommendations for
certain scenarios makes sense.
relax requirement to have tests/testing contain an __init__
----------------------------------------------------------------
@@ -220,32 +218,10 @@ to further reduce py.test globality. Also consider
having py.test.config and ensuretemp coming from
a plugin rather than being there from the start.
consider allowing funcargs for setup methods
--------------------------------------------------------------
tags: experimental-wish
Users have expressed the wish to have funcargs available to setup
functions. Experiment with allowing funcargs there - it might
also help to make the py.test.ensuretemp and config deprecation.
For filling funcargs for setup methods, we could call funcarg
factories with a request object that not have a cls/function
attributes. However, how to handle parametrized test functions
and funcargs?
maybe introduce a setup method like:
setup_invocation(self, request)
which has full access to the test invocation through "request"
through which you can get funcargvalues, use cached_setup etc.
Therefore, the access to funcargs would be indirect but it
could be consistently implemented. setup_invocation() would
be a "glue" function for bringing together the xUnit and funcargs
world.
consider pytest_addsyspath hook
-----------------------------------------
tags:
tags: wish
py.test could call a new pytest_addsyspath() in order to systematically
allow manipulation of sys.path and to inhibit it via --no-addsyspath
@@ -255,13 +231,6 @@ Alternatively it could also be done via the config object
and pytest_configure.
show plugin information in test header
----------------------------------------------------------------
tags: feature
Now that external plugins are becoming more numerous
it would be useful to have external plugins along with
their versions displayed as a header line.
deprecate global py.test.config usage
----------------------------------------------------------------
@@ -330,3 +299,75 @@ in one content string::
This could be run with at least three different ways to invoke pytest:
through the shell, through "python -m pytest" and inlined. As inlined
would be the fastest it could be run first (or "--fast" mode).
Create isolate plugin
---------------------
tags: feature
The idea is that you can e.g. import modules in a test and afterwards
sys.modules, sys.meta_path etc would be reverted. It can go further
then just importing however, e.g. current working direcroty, file
descriptors, ...
This would probably be done by marking::
@pytest.mark.isolate(importing=True, cwd=True, fds=False)
def test_foo():
...
With the possibility of doing this globally in an ini-file.
fnmatch for test names
----------------------
tags: feature-wish
various testsuites use suffixes instead of prefixes for test classes
also it lends itself to bdd style test names::
class UserBehaviour:
def anonymous_should_not_have_inbox(user):
...
def registred_should_have_inbox(user):
..
using the following in pytest.ini::
[pytest]
python_classes = Test *Behaviour *Test
python_functions = test *_should_*
mechanism for running named parts of tests with different reporting behaviour
------------------------------------------------------------------------------
tags: feature-wish-incomplete
a few use-cases come to mind:
* fail assertions and record that without stopping a complete test
* this is in particular hepfull if a small bit of a test is known to fail/xfail::
def test_fun():
with pytest.section('fdcheck, marks=pytest.mark.xfail_if(...)):
breaks_on_windows()
* divide functional/acceptance tests into sections
* provide a different mechanism for generators, maybe something like::
def pytest_runtest_call(item)
if not generator:
...
prepare_check = GeneratorCheckprepare()
gen = item.obj(**fixtures)
for check in gen
id, call = prepare_check(check)
# bubble should only prevent exception propagation after a failure
# the whole test should still fail
# there might be need for a loer level api and taking custom markers into account
with pytest.section(id, bubble=False):
call()

View File

@@ -1,7 +1,6 @@
include CHANGELOG
include README.txt
include README.rst
include setup.py
include distribute_setup.py
include tox.ini
include LICENSE
graft doc

52
README.rst Normal file
View File

@@ -0,0 +1,52 @@
Documentation: http://pytest.org/latest/
Changelog: http://pytest.org/latest/changelog.html
Issues: https://bitbucket.org/hpk42/pytest/issues?status=open
The ``py.test`` testing tool makes it easy to write small tests, yet
scales to support complex functional testing. It provides
- `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 ``py.test`` 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 to Python2.4 all the way up to Python3.3,
PyPy-1.9 and Jython-2.5.1.
- many `external plugins <http://pytest.org/latest/plugins.html#installing-external-plugins-searching>`_.
.. image:: https://secure.travis-ci.org/hpk42/pytest.png
:target: http://travis-ci.org/hpk42/pytest
A simple example for a test::
# 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:
http://bitbucket.org/hpk42/pytest/issues/
and checkout repos at:
http://github.com/hpk42/pytest/ (mirror)
http://bitbucket.org/hpk42/pytest/
Copyright Holger Krekel and others, 2004-2013
Licensed under the MIT license.

View File

@@ -1,4 +0,0 @@
py.test is a simple and popular testing tool for Python.
See http://pytest.org for more documentation.

View File

@@ -1,2 +1,2 @@
#
__version__ = '2.3.4'
__version__ = '2.5.1'

104
_pytest/_argcomplete.py Normal file
View File

@@ -0,0 +1,104 @@
"""allow bash-completion for argparse with argcomplete if installed
needs argcomplete>=0.5.6 for python 3.2/3.3 (older versions fail
to find the magic string, so _ARGCOMPLETE env. var is never set, and
this does not need special code.
argcomplete does not support python 2.5 (although the changes for that
are minor).
Function try_argcomplete(parser) should be called directly before
the call to ArgumentParser.parse_args().
The filescompleter is what you normally would use on the positional
arguments specification, in order to get "dirname/" after "dirn<TAB>"
instead of the default "dirname ":
optparser.add_argument(Config._file_or_dir, nargs='*'
).completer=filescompleter
Other, application specific, completers should go in the file
doing the add_argument calls as they need to be specified as .completer
attributes as well. (If argcomplete is not installed, the function the
attribute points to will not be used).
SPEEDUP
=======
The generic argcomplete script for bash-completion
(/etc/bash_completion.d/python-argcomplete.sh )
uses a python program to determine startup script generated by pip.
You can speed up completion somewhat by changing this script to include
# PYTHON_ARGCOMPLETE_OK
so the the python-argcomplete-check-easy-install-script does not
need to be called to find the entry point of the code and see if that is
marked with PYTHON_ARGCOMPLETE_OK
INSTALL/DEBUGGING
=================
To include this support in another application that has setup.py generated
scripts:
- add the line:
# PYTHON_ARGCOMPLETE_OK
near the top of the main python entry point
- include in the file calling parse_args():
from _argcomplete import try_argcomplete, filescompleter
, call try_argcomplete just before parse_args(), and optionally add
filescompleter to the positional arguments' add_argument()
If things do not work right away:
- switch on argcomplete debugging with (also helpful when doing custom
completers):
export _ARC_DEBUG=1
- run:
python-argcomplete-check-easy-install-script $(which appname)
echo $?
will echo 0 if the magic line has been found, 1 if not
- sometimes it helps to find early on errors using:
_ARGCOMPLETE=1 _ARC_DEBUG=1 appname
which should throw a KeyError: 'COMPLINE' (which is properly set by the
global argcomplete script).
"""
import sys
import os
from glob import glob
class FastFilesCompleter:
'Fast file completer class'
def __init__(self, directories=True):
self.directories = directories
def __call__(self, prefix, **kwargs):
"""only called on non option completions"""
if os.path.sep in prefix[1:]: #
prefix_dir = len(os.path.dirname(prefix) + os.path.sep)
else:
prefix_dir = 0
completion = []
globbed = []
if '*' not in prefix and '?' not in prefix:
if prefix[-1] == os.path.sep: # we are on unix, otherwise no bash
globbed.extend(glob(prefix + '.*'))
prefix += '*'
globbed.extend(glob(prefix))
for x in sorted(globbed):
if os.path.isdir(x):
x += '/'
# append stripping the prefix (like bash, not like compgen)
completion.append(x[prefix_dir:])
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:
sys.exit(-1)
filescompleter = FastFilesCompleter()
def try_argcomplete(parser):
argcomplete.autocomplete(parser)
else:
def try_argcomplete(parser): pass
filescompleter = None

View File

@@ -3,7 +3,6 @@ support for presenting detailed information in failing assertions.
"""
import py
import sys
import pytest
from _pytest.monkeypatch import monkeypatch
from _pytest.assertion import util
@@ -19,8 +18,8 @@ def pytest_addoption(parser):
to provide assert expression information. """)
group.addoption('--no-assert', action="store_true", default=False,
dest="noassert", help="DEPRECATED equivalent to --assert=plain")
group.addoption('--nomagic', action="store_true", default=False,
dest="nomagic", help="DEPRECATED equivalent to --assert=plain")
group.addoption('--nomagic', '--no-magic', action="store_true",
default=False, help="DEPRECATED equivalent to --assert=plain")
class AssertionState:
"""State for the assertion plugin."""
@@ -35,21 +34,24 @@ def pytest_configure(config):
mode = "plain"
if mode == "rewrite":
try:
import ast
import ast # noqa
except ImportError:
mode = "reinterp"
else:
if sys.platform.startswith('java'):
# Both Jython and CPython 2.6.0 have AST bugs that make the
# assertion rewriting hook malfunction.
if (sys.platform.startswith('java') or
sys.version_info[:3] == (2, 6, 0)):
mode = "reinterp"
if mode != "plain":
_load_modules(mode)
m = monkeypatch()
config._cleanup.append(m.undo)
m.setattr(py.builtin.builtins, 'AssertionError',
reinterpret.AssertionError)
reinterpret.AssertionError) # noqa
hook = None
if mode == "rewrite":
hook = rewrite.AssertionRewritingHook()
hook = rewrite.AssertionRewritingHook() # noqa
sys.meta_path.insert(0, hook)
warn_about_missing_assertion(mode)
config._assertstate = AssertionState(config, mode)
@@ -76,10 +78,13 @@ def pytest_runtest_setup(item):
for new_expl in hook_result:
if new_expl:
# Don't include pageloads of data unless we are very verbose (-vv)
if len(''.join(new_expl[1:])) > 80*8 and item.config.option.verbose < 2:
new_expl[1:] = ['Detailed information too verbose, truncated']
res = '\n~'.join(new_expl)
# Don't include pageloads of data unless we are very
# verbose (-vv)
if (len(py.builtin._totext('').join(new_expl[1:])) > 80*8
and item.config.option.verbose < 2):
new_expl[1:] = [py.builtin._totext(
'Detailed information truncated, use "-vv" to see')]
res = py.builtin._totext('\n~').join(new_expl)
if item.config.getvalue("assertmode") == "rewrite":
# The result will be fed back a python % formatting
# operation, which will fail if there are extraneous
@@ -99,9 +104,9 @@ def pytest_sessionfinish(session):
def _load_modules(mode):
"""Lazily import assertion related code."""
global rewrite, reinterpret
from _pytest.assertion import reinterpret
from _pytest.assertion import reinterpret # noqa
if mode == "rewrite":
from _pytest.assertion import rewrite
from _pytest.assertion import rewrite # noqa
def warn_about_missing_assertion(mode):
try:

View File

@@ -1,18 +1,26 @@
import sys
import py
from _pytest.assertion.util import BuiltinAssertionError
u = py.builtin._totext
class AssertionError(BuiltinAssertionError):
def __init__(self, *args):
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
# with args[0] being the (x,y) tuple.
if len(args) > 1:
toprint = args
else:
toprint = args[0]
try:
self.msg = str(args[0])
except py.builtin._sysex:
raise
except:
self.msg = "<[broken __repr__] %s at %0xd>" %(
args[0].__class__, id(args[0]))
self.msg = u(toprint)
except Exception:
self.msg = u(
"<[broken __repr__] %s at %0xd>"
% (toprint.__class__, id(toprint)))
else:
f = py.code.Frame(sys._getframe(1))
try:

View File

@@ -6,6 +6,7 @@ import itertools
import imp
import marshal
import os
import re
import struct
import sys
import types
@@ -14,12 +15,6 @@ import py
from _pytest.assertion import util
# Windows gives ENOENT in places *nix gives ENOTDIR.
if sys.platform.startswith("win"):
PATH_COMPONENT_NOT_DIR = errno.ENOENT
else:
PATH_COMPONENT_NOT_DIR = errno.ENOTDIR
# py.test caches rewritten pycs in __pycache__.
if hasattr(imp, "get_tag"):
PYTEST_TAG = imp.get_tag() + "-PYTEST"
@@ -38,6 +33,7 @@ PYC_EXT = ".py" + (__debug__ and "c" or "o")
PYC_TAIL = "." + PYTEST_TAG + PYC_EXT
REWRITE_NEWLINES = sys.version_info[:2] != (2, 7) and sys.version_info < (3, 2)
ASCII_IS_DEFAULT_ENCODING = sys.version_info[0] < 3
class AssertionRewritingHook(object):
"""PEP302 Import hook which rewrites asserts."""
@@ -45,6 +41,7 @@ class AssertionRewritingHook(object):
def __init__(self):
self.session = None
self.modules = {}
self._register_with_pkg_resources()
def set_session(self, session):
self.fnpats = session.config.getini("python_files")
@@ -59,8 +56,12 @@ class AssertionRewritingHook(object):
names = name.rsplit(".", 1)
lastname = names[-1]
pth = None
if path is not None and len(path) == 1:
pth = path[0]
if path is not None:
# Starting with Python 3.3, path is a _NamespacePath(), which
# causes problems if not converted to list.
path = list(path)
if len(path) == 1:
pth = path[0]
if pth is None:
try:
fd, fn, desc = imp.find_module(lastname, path)
@@ -117,7 +118,7 @@ class AssertionRewritingHook(object):
# common case) or it's blocked by a non-dir node. In the
# latter case, we'll ignore it in _write_pyc.
pass
elif e == PATH_COMPONENT_NOT_DIR:
elif e in [errno.ENOENT, errno.ENOTDIR]:
# One of the path components was not a directory, likely
# because we're in a zip file.
write = False
@@ -154,13 +155,44 @@ class AssertionRewritingHook(object):
mod.__file__ = co.co_filename
# Normally, this attribute is 3.2+.
mod.__cached__ = pyc
mod.__loader__ = self
py.builtin.exec_(co, mod.__dict__)
except:
del sys.modules[name]
raise
return sys.modules[name]
def _write_pyc(co, source_path, pyc):
def is_package(self, name):
try:
fd, fn, desc = imp.find_module(name)
except ImportError:
return False
if fd is not None:
fd.close()
tp = desc[2]
return tp == imp.PKG_DIRECTORY
@classmethod
def _register_with_pkg_resources(cls):
"""
Ensure package resources can be loaded from this loader. May be called
multiple times, as the operation is idempotent.
"""
try:
import pkg_resources
# access an attribute in case a deferred importer is present
pkg_resources.__name__
except ImportError:
return
# Since pytest tests are always located in the file system, the
# DefaultProvider is appropriate.
pkg_resources.register_loader_type(cls, pkg_resources.DefaultProvider)
def _write_pyc(state, co, source_path, pyc):
# Technically, we don't have to have the same pyc format as
# (C)Python, since these "pycs" should never be seen by builtin
# import. However, there's little reason deviate, and I hope
@@ -171,11 +203,11 @@ def _write_pyc(co, source_path, pyc):
fp = open(pyc, "wb")
except IOError:
err = sys.exc_info()[1].errno
if err == PATH_COMPONENT_NOT_DIR:
# This happens when we get a EEXIST in find_module creating the
# __pycache__ directory and __pycache__ is by some non-dir node.
return False
raise
state.trace("error writing pyc file at %s: errno=%s" %(pyc, err))
# we ignore any failure to write the cache file
# there are many reasons, permission-denied, __pycache__ being a
# file etc.
return False
try:
fp.write(imp.get_magic())
fp.write(struct.pack("<l", mtime))
@@ -187,12 +219,43 @@ def _write_pyc(co, source_path, pyc):
RN = "\r\n".encode("utf-8")
N = "\n".encode("utf-8")
cookie_re = re.compile(r"^[ \t\f]*#.*coding[:=][ \t]*[-\w.]+")
BOM_UTF8 = '\xef\xbb\xbf'
def _rewrite_test(state, fn):
"""Try to read and rewrite *fn* and return the code object."""
try:
source = fn.read("rb")
except EnvironmentError:
return None
if ASCII_IS_DEFAULT_ENCODING:
# ASCII is the default encoding in Python 2. Without a coding
# declaration, Python 2 will complain about any bytes in the file
# outside the ASCII range. Sadly, this behavior does not extend to
# compile() or ast.parse(), which prefer to interpret the bytes as
# latin-1. (At least they properly handle explicit coding cookies.) To
# preserve this error behavior, we could force ast.parse() to use ASCII
# as the encoding by inserting a coding cookie. Unfortunately, that
# messes up line numbers. Thus, we have to check ourselves if anything
# is outside the ASCII range in the case no encoding is explicitly
# declared. For more context, see issue #269. Yay for Python 3 which
# gets this right.
end1 = source.find("\n")
end2 = source.find("\n", end1 + 1)
if (not source.startswith(BOM_UTF8) and
cookie_re.match(source[0:end1]) is None and
cookie_re.match(source[end1 + 1:end2]) is None):
if hasattr(state, "_indecode"):
return None # encodings imported us again, we don't rewrite
state._indecode = True
try:
try:
source.decode("ascii")
except UnicodeDecodeError:
# Let it fail in real import.
return None
finally:
del state._indecode
# On Python versions which are not 2.7 and less than or equal to 3.1, the
# parser expects *nix newlines.
if REWRITE_NEWLINES:
@@ -218,12 +281,12 @@ def _make_rewritten_pyc(state, fn, pyc, co):
if sys.platform.startswith("win"):
# Windows grants exclusive access to open files and doesn't have atomic
# rename, so just write into the final file.
_write_pyc(co, fn, pyc)
_write_pyc(state, co, fn, pyc)
else:
# When not on windows, assume rename is atomic. Dump the code object
# into a file specific to this process and atomically replace it.
proc_pyc = pyc + "." + str(os.getpid())
if _write_pyc(co, fn, proc_pyc):
if _write_pyc(state, co, fn, proc_pyc):
os.rename(proc_pyc, pyc)
def _read_pyc(source, pyc):
@@ -260,7 +323,10 @@ def rewrite_asserts(mod):
_saferepr = py.io.saferepr
from _pytest.assertion.util import format_explanation as _format_explanation
from _pytest.assertion.util import format_explanation as _format_explanation # noqa
def _should_repr_global_name(obj):
return not hasattr(obj, "__name__") and not py.builtin.callable(obj)
def _format_boolop(explanations, is_or):
return "(" + (is_or and " or " or " and ").join(explanations) + ")"
@@ -473,11 +539,12 @@ class AssertionRewriter(ast.NodeVisitor):
return self.statements
def visit_Name(self, name):
# Check if the name is local or not.
# Display the repr of the name if it's a local variable or
# _should_repr_global_name() thinks it's acceptable.
locs = ast.Call(self.builtin("locals"), [], [], None, None)
globs = ast.Call(self.builtin("globals"), [], [], None, None)
ops = [ast.In(), ast.IsNot()]
test = ast.Compare(ast.Str(name.id), ops, [locs, globs])
inlocs = ast.Compare(ast.Str(name.id), [ast.In()], [locs])
dorepr = self.helper("should_repr_global_name", name)
test = ast.BoolOp(ast.Or(), [inlocs, dorepr])
expr = ast.IfExp(test, self.display(name), ast.Str(name.id))
return name, self.explanation_param(expr)
@@ -494,7 +561,8 @@ class AssertionRewriter(ast.NodeVisitor):
for i, v in enumerate(boolop.values):
if i:
fail_inner = []
self.on_failure.append(ast.If(cond, fail_inner, []))
# cond is set in a prior loop iteration below
self.on_failure.append(ast.If(cond, fail_inner, [])) # noqa
self.on_failure = fail_inner
self.push_format_context()
res, expl = self.visit(v)
@@ -587,7 +655,7 @@ class AssertionRewriter(ast.NodeVisitor):
res_expr = ast.Compare(left_res, [op], [next_res])
self.statements.append(ast.Assign([store_names[i]], res_expr))
left_res, left_expl = next_res, next_expl
# Use py.code._reprcompare if that's available.
# Use pytest.assertion.util._reprcompare if that's available.
expl_call = self.helper("call_reprcompare",
ast.Tuple(syms, ast.Load()),
ast.Tuple(load_names, ast.Load()),

View File

@@ -1,8 +1,17 @@
"""Utilities for assertion debugging"""
import py
try:
from collections.abc import Sequence
except ImportError:
try:
from collections import Sequence
except ImportError:
Sequence = list
BuiltinAssertionError = py.builtin.builtins.AssertionError
u = py.builtin._totext
# The _reprcompare attribute on the util module is used by the new assertion
# interpretation code and assertion rewriter to detect this plugin was
@@ -10,6 +19,7 @@ BuiltinAssertionError = py.builtin.builtins.AssertionError
# DebugInterpreter.
_reprcompare = None
def format_explanation(explanation):
"""This formats an explanation
@@ -20,7 +30,18 @@ def format_explanation(explanation):
for when one explanation needs to span multiple lines, e.g. when
displaying diffs.
"""
# simplify 'assert False where False = ...'
explanation = _collapse_false(explanation)
lines = _split_explanation(explanation)
result = _format_lines(lines)
return u('\n').join(result)
def _collapse_false(explanation):
"""Collapse expansions of False
So this strips out any "assert False\n{where False = ...\n}"
blocks.
"""
where = 0
while True:
start = where = explanation.find("False\n{False = ", where)
@@ -42,28 +63,48 @@ def format_explanation(explanation):
explanation = (explanation[:start] + explanation[start+15:end-1] +
explanation[end+1:])
where -= 17
raw_lines = (explanation or '').split('\n')
# escape newlines not followed by {, } and ~
return explanation
def _split_explanation(explanation):
"""Return a list of individual lines in the explanation
This will return a list of lines split on '\n{', '\n}' and '\n~'.
Any other newlines will be escaped and appear in the line as the
literal '\n' characters.
"""
raw_lines = (explanation or u('')).split('\n')
lines = [raw_lines[0]]
for l in raw_lines[1:]:
if l.startswith('{') or l.startswith('}') or l.startswith('~'):
lines.append(l)
else:
lines[-1] += '\\n' + l
return lines
def _format_lines(lines):
"""Format the individual lines
This will replace the '{', '}' and '~' characters of our mini
formatting language with the proper 'where ...', 'and ...' and ' +
...' text, taking care of indentation along the way.
Return a list of formatted lines.
"""
result = lines[:1]
stack = [0]
stackcnt = [0]
for line in lines[1:]:
if line.startswith('{'):
if stackcnt[-1]:
s = 'and '
s = u('and ')
else:
s = 'where '
s = u('where ')
stack.append(len(result))
stackcnt[-1] += 1
stackcnt.append(0)
result.append(' +' + ' '*(len(stack)-1) + s + line[1:])
result.append(u(' +') + u(' ')*(len(stack)-1) + s + line[1:])
elif line.startswith('}'):
assert line.startswith('}')
stack.pop()
@@ -71,9 +112,9 @@ def format_explanation(explanation):
result[stack[-1]] += line[1:]
else:
assert line.startswith('~')
result.append(' '*len(stack) + line[1:])
result.append(u(' ')*len(stack) + line[1:])
assert len(stack) == 1
return '\n'.join(result)
return result
# Provide basestring in python3
@@ -83,127 +124,157 @@ except NameError:
basestring = str
def assertrepr_compare(op, left, right):
"""return specialised explanations for some operators/operands"""
width = 80 - 15 - len(op) - 2 # 15 chars indentation, 1 space around op
def assertrepr_compare(config, op, left, right):
"""Return specialised explanations for some operators/operands"""
width = 80 - 15 - len(op) - 2 # 15 chars indentation, 1 space around op
left_repr = py.io.saferepr(left, maxsize=int(width/2))
right_repr = py.io.saferepr(right, maxsize=width-len(left_repr))
summary = '%s %s %s' % (left_repr, op, right_repr)
summary = u('%s %s %s') % (left_repr, op, right_repr)
issequence = lambda x: isinstance(x, (list, tuple))
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)
isset = lambda x: isinstance(x, (set, frozenset))
verbose = config.getoption('verbose')
explanation = None
try:
if op == '==':
if istext(left) and istext(right):
explanation = _diff_text(left, right)
explanation = _diff_text(left, right, verbose)
elif issequence(left) and issequence(right):
explanation = _compare_eq_sequence(left, right)
explanation = _compare_eq_sequence(left, right, verbose)
elif isset(left) and isset(right):
explanation = _compare_eq_set(left, right)
explanation = _compare_eq_set(left, right, verbose)
elif isdict(left) and isdict(right):
explanation = _diff_text(py.std.pprint.pformat(left),
py.std.pprint.pformat(right))
explanation = _compare_eq_dict(left, right, verbose)
elif op == 'not in':
if istext(left) and istext(right):
explanation = _notin_text(left, right)
except py.builtin._sysex:
raise
except:
explanation = _notin_text(left, right, verbose)
except Exception:
excinfo = py.code.ExceptionInfo()
explanation = ['(pytest_assertion plugin: representation of '
'details failed. Probably an object has a faulty __repr__.)',
str(excinfo)]
explanation = [
u('(pytest_assertion plugin: representation of details failed. '
'Probably an object has a faulty __repr__.)'),
u(excinfo)]
if not explanation:
return None
return [summary] + explanation
def _diff_text(left, right):
def _diff_text(left, right, verbose=False):
"""Return the explanation for the diff between text
This will skip leading and trailing characters which are
identical to keep the diff minimal.
Unless --verbose is used this will skip leading and trailing
characters which are identical to keep the diff minimal.
"""
explanation = []
i = 0 # just in case left or right has zero length
for i in range(min(len(left), len(right))):
if left[i] != right[i]:
break
if i > 42:
i -= 10 # Provide some context
explanation = ['Skipping %s identical '
'leading characters in diff' % i]
left = left[i:]
right = right[i:]
if len(left) == len(right):
for i in range(len(left)):
if left[-i] != right[-i]:
if not verbose:
i = 0 # just in case left or right has zero length
for i in range(min(len(left), len(right))):
if left[i] != right[i]:
break
if i > 42:
i -= 10 # Provide some context
explanation += ['Skipping %s identical '
'trailing characters in diff' % i]
left = left[:-i]
right = right[:-i]
i -= 10 # Provide some context
explanation = [u('Skipping %s identical leading '
'characters in diff, use -v to show') % i]
left = left[i:]
right = right[i:]
if len(left) == len(right):
for i in range(len(left)):
if left[-i] != right[-i]:
break
if i > 42:
i -= 10 # Provide some context
explanation += [u('Skipping %s identical trailing '
'characters in diff, use -v to show') % i]
left = left[:-i]
right = right[:-i]
explanation += [line.strip('\n')
for line in py.std.difflib.ndiff(left.splitlines(),
right.splitlines())]
return explanation
def _compare_eq_sequence(left, right):
def _compare_eq_sequence(left, right, verbose=False):
explanation = []
for i in range(min(len(left), len(right))):
if left[i] != right[i]:
explanation += ['At index %s diff: %r != %r' %
(i, left[i], right[i])]
explanation += [u('At index %s diff: %r != %r')
% (i, left[i], right[i])]
break
if len(left) > len(right):
explanation += ['Left contains more items, '
'first extra item: %s' % py.io.saferepr(left[len(right)],)]
explanation += [u('Left contains more items, first extra item: %s')
% py.io.saferepr(left[len(right)],)]
elif len(left) < len(right):
explanation += ['Right contains more items, '
'first extra item: %s' % py.io.saferepr(right[len(left)],)]
return explanation # + _diff_text(py.std.pprint.pformat(left),
# py.std.pprint.pformat(right))
explanation += [
u('Right contains more items, first extra item: %s') %
py.io.saferepr(right[len(left)],)]
return explanation # + _diff_text(py.std.pprint.pformat(left),
# py.std.pprint.pformat(right))
def _compare_eq_set(left, right):
def _compare_eq_set(left, right, verbose=False):
explanation = []
diff_left = left - right
diff_right = right - left
if diff_left:
explanation.append('Extra items in the left set:')
explanation.append(u('Extra items in the left set:'))
for item in diff_left:
explanation.append(py.io.saferepr(item))
if diff_right:
explanation.append('Extra items in the right set:')
explanation.append(u('Extra items in the right set:'))
for item in diff_right:
explanation.append(py.io.saferepr(item))
return explanation
def _notin_text(term, text):
def _compare_eq_dict(left, right, verbose=False):
explanation = []
common = set(left).intersection(set(right))
same = dict((k, left[k]) for k in common if left[k] == right[k])
if same and not verbose:
explanation += [u('Omitting %s identical items, use -v to show') %
len(same)]
elif same:
explanation += [u('Common items:')]
explanation += py.std.pprint.pformat(same).splitlines()
diff = set(k for k in common if left[k] != right[k])
if diff:
explanation += [u('Differing items:')]
for k in diff:
explanation += [py.io.saferepr({k: left[k]}) + ' != ' +
py.io.saferepr({k: right[k]})]
extra_left = set(left) - set(right)
if extra_left:
explanation.append(u('Left contains more items:'))
explanation.extend(py.std.pprint.pformat(
dict((k, left[k]) for k in extra_left)).splitlines())
extra_right = set(right) - set(left)
if extra_right:
explanation.append(u('Right contains more items:'))
explanation.extend(py.std.pprint.pformat(
dict((k, right[k]) for k in extra_right)).splitlines())
return explanation
def _notin_text(term, text, verbose=False):
index = text.find(term)
head = text[:index]
tail = text[index+len(term):]
correct_text = head + tail
diff = _diff_text(correct_text, text)
newdiff = ['%s is contained here:' % py.io.saferepr(term, maxsize=42)]
diff = _diff_text(correct_text, text, verbose)
newdiff = [u('%s is contained here:') % py.io.saferepr(term, maxsize=42)]
for line in diff:
if line.startswith('Skipping'):
if line.startswith(u('Skipping')):
continue
if line.startswith('- '):
if line.startswith(u('- ')):
continue
if line.startswith('+ '):
newdiff.append(' ' + line[2:])
if line.startswith(u('+ ')):
newdiff.append(u(' ') + line[2:])
else:
newdiff.append(line)
return newdiff

View File

@@ -1,27 +1,51 @@
""" per-test stdout/stderr capturing mechanisms, ``capsys`` and ``capfd`` function arguments. """
import pytest, py
import sys
import os
def pytest_addoption(parser):
group = parser.getgroup("general")
group._addoption('--capture', action="store", default=None,
metavar="method", type="choice", choices=['fd', 'sys', 'no'],
metavar="method", choices=['fd', 'sys', 'no'],
help="per-test capturing method: one of fd (default)|sys|no.")
group._addoption('-s', action="store_const", const="no", dest="capture",
help="shortcut for --capture=no.")
@pytest.mark.tryfirst
def pytest_cmdline_parse(pluginmanager, args):
# we want to perform capturing already for plugin/conftest loading
if '-s' in args or "--capture=no" in args:
method = "no"
elif hasattr(os, 'dup') and '--capture=sys' not in args:
def pytest_load_initial_conftests(early_config, parser, args, __multicall__):
ns = parser.parse_known_args(args)
method = ns.capture
if not method:
method = "fd"
else:
if method == "fd" and not hasattr(os, "dup"):
method = "sys"
capman = CaptureManager(method)
pluginmanager.register(capman, "capturemanager")
early_config.pluginmanager.register(capman, "capturemanager")
# make sure that capturemanager is properly reset at final shutdown
def teardown():
try:
capman.reset_capturings()
except ValueError:
pass
early_config.pluginmanager.add_shutdown(teardown)
# make sure logging does not raise exceptions at the end
def silence_logging_at_shutdown():
if "logging" in sys.modules:
sys.modules["logging"].raiseExceptions = False
early_config.pluginmanager.add_shutdown(silence_logging_at_shutdown)
# finally trigger conftest loading but while capturing (issue93)
capman.resumecapture()
try:
try:
return __multicall__.execute()
finally:
out, err = capman.suspendcapture()
except:
sys.stdout.write(out)
sys.stderr.write(err)
raise
def addouterr(rep, outerr):
for secname, content in zip(["out", "err"], outerr):
@@ -173,8 +197,7 @@ class CaptureManager:
if funcarg_outerr is not None:
outerr = (outerr[0] + funcarg_outerr[0],
outerr[1] + funcarg_outerr[1])
if not rep.passed:
addouterr(rep, outerr)
addouterr(rep, outerr)
if not rep.passed or rep.when == "teardown":
outerr = ('', '')
item.outerr = outerr
@@ -211,12 +234,15 @@ class CaptureFixture:
def _finalize(self):
if hasattr(self, 'capture'):
outerr = self.capture.reset()
outerr = self._outerr = self.capture.reset()
del self.capture
return outerr
def readouterr(self):
return self.capture.readouterr()
try:
return self.capture.readouterr()
except AttributeError:
return self._outerr
def close(self):
self._finalize()

View File

@@ -2,21 +2,86 @@
import py
import sys, os
from _pytest import hookspec # the extension point definitions
from _pytest.core import PluginManager
import pytest
def pytest_cmdline_parse(pluginmanager, args):
config = Config(pluginmanager)
config.parse(args)
return config
# pytest startup
def main(args=None, plugins=None):
""" return exit code, after performing an in-process test run.
:arg args: list of command line arguments.
:arg plugins: list of plugin objects to be auto-registered during
initialization.
"""
config = _prepareconfig(args, plugins)
return config.hook.pytest_cmdline_main(config=config)
class cmdline: # compatibility namespace
main = staticmethod(main)
class UsageError(Exception):
""" error in py.test usage or invocation"""
_preinit = []
default_plugins = (
"mark main terminal runner python pdb unittest capture skipping "
"tmpdir monkeypatch recwarn pastebin helpconfig nose assertion genscript "
"junitxml resultlog doctest").split()
def _preloadplugins():
assert not _preinit
_preinit.append(get_plugin_manager())
def get_plugin_manager():
if _preinit:
return _preinit.pop(0)
# subsequent calls to main will create a fresh instance
pluginmanager = PytestPluginManager()
pluginmanager.config = Config(pluginmanager) # XXX attr needed?
for spec in default_plugins:
pluginmanager.import_plugin(spec)
return pluginmanager
def _prepareconfig(args=None, plugins=None):
if args is None:
args = sys.argv[1:]
elif isinstance(args, py.path.local):
args = [str(args)]
elif not isinstance(args, (tuple, list)):
if not isinstance(args, str):
raise ValueError("not a string or argument list: %r" % (args,))
args = py.std.shlex.split(args)
pluginmanager = get_plugin_manager()
if plugins:
for plugin in plugins:
pluginmanager.register(plugin)
return pluginmanager.hook.pytest_cmdline_parse(
pluginmanager=pluginmanager, args=args)
class PytestPluginManager(PluginManager):
def __init__(self, hookspecs=[hookspec]):
super(PytestPluginManager, self).__init__(hookspecs=hookspecs)
self.register(self)
if os.environ.get('PYTEST_DEBUG'):
err = sys.stderr
encoding = getattr(err, 'encoding', 'utf8')
try:
err = py.io.dupfile(err, encoding=encoding)
except Exception:
pass
self.trace.root.setwriter(err.write)
def pytest_configure(self, config):
config.addinivalue_line("markers",
"tryfirst: mark a hook implementation function such that the "
"plugin machinery will try to call it first/as early as possible.")
config.addinivalue_line("markers",
"trylast: mark a hook implementation function such that the "
"plugin machinery will try to call it last/as late as possible.")
def pytest_unconfigure(config):
while 1:
try:
fin = config._cleanup.pop()
except IndexError:
break
fin()
class Parser:
""" Parser for command line arguments and ini-file values. """
@@ -63,8 +128,8 @@ class Parser:
:opts: option names, can be short or long options.
:attrs: same attributes which the ``add_option()`` function of the
`optparse library
<http://docs.python.org/library/optparse.html#module-optparse>`_
`argparse library
<http://docs.python.org/2/library/argparse.html>`_
accepts.
After command line parsing options are available on the pytest config
@@ -75,21 +140,38 @@ class Parser:
self._anonymous.addoption(*opts, **attrs)
def parse(self, args):
self.optparser = optparser = MyOptionParser(self)
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])
def _getparser(self):
from _pytest._argcomplete import filescompleter
optparser = MyOptionParser(self)
groups = self._groups + [self._anonymous]
for group in groups:
if group.options:
desc = group.description or group.name
optgroup = py.std.optparse.OptionGroup(optparser, desc)
optgroup.add_options(group.options)
optparser.add_option_group(optgroup)
return self.optparser.parse_args([str(x) for x in args])
arggroup = optparser.add_argument_group(desc)
for option in group.options:
n = option.names()
a = option.attrs()
arggroup.add_argument(*n, **a)
# bash like autocompletion for dirs (appending '/')
optparser.add_argument(FILE_OR_DIR, nargs='*'
).completer=filescompleter
return optparser
def parse_setoption(self, args, option):
parsedoption, args = self.parse(args)
parsedoption = self.parse(args)
for name, value in parsedoption.__dict__.items():
setattr(option, name, value)
return args
return getattr(parsedoption, FILE_OR_DIR)
def parse_known_args(self, args):
optparser = self._getparser()
args = [str(x) for x in args]
return optparser.parse_known_args(args)[0]
def addini(self, name, help, type=None, default=None):
""" register an ini-file option.
@@ -106,6 +188,158 @@ class Parser:
self._ininames.append(name)
class ArgumentError(Exception):
"""
Raised if an Argument instance is created with invalid or
inconsistent arguments.
"""
def __init__(self, msg, option):
self.msg = msg
self.option_id = str(option)
def __str__(self):
if self.option_id:
return "option %s: %s" % (self.option_id, self.msg)
else:
return self.msg
class Argument:
"""class that mimics the necessary behaviour of py.std.optparse.Option """
_typ_map = {
'int': int,
'string': str,
}
# enable after some grace period for plugin writers
TYPE_WARN = False
def __init__(self, *names, **attrs):
"""store parms in private vars for use in add_argument"""
self._attrs = attrs
self._short_opts = []
self._long_opts = []
self.dest = attrs.get('dest')
if self.TYPE_WARN:
try:
help = attrs['help']
if '%default' in help:
py.std.warnings.warn(
'py.test now uses argparse. "%default" should be'
' changed to "%(default)s" ',
FutureWarning,
stacklevel=3)
except KeyError:
pass
try:
typ = attrs['type']
except KeyError:
pass
else:
# this might raise a keyerror as well, don't want to catch that
if isinstance(typ, py.builtin._basestring):
if typ == 'choice':
if self.TYPE_WARN:
py.std.warnings.warn(
'type argument to addoption() is a string %r.'
' For parsearg this is optional and when supplied '
' should be a type.'
' (options: %s)' % (typ, names),
FutureWarning,
stacklevel=3)
# argparse expects a type here take it from
# the type of the first element
attrs['type'] = type(attrs['choices'][0])
else:
if self.TYPE_WARN:
py.std.warnings.warn(
'type argument to addoption() is a string %r.'
' For parsearg this should be a type.'
' (options: %s)' % (typ, names),
FutureWarning,
stacklevel=3)
attrs['type'] = Argument._typ_map[typ]
# used in test_parseopt -> test_parse_defaultgetter
self.type = attrs['type']
else:
self.type = typ
try:
# attribute existence is tested in Config._processopt
self.default = attrs['default']
except KeyError:
pass
self._set_opt_strings(names)
if not self.dest:
if self._long_opts:
self.dest = self._long_opts[0][2:].replace('-', '_')
else:
try:
self.dest = self._short_opts[0][1:]
except IndexError:
raise ArgumentError(
'need a long or short option', self)
def names(self):
return self._short_opts + self._long_opts
def attrs(self):
# update any attributes set by processopt
attrs = 'default dest help'.split()
if self.dest:
attrs.append(self.dest)
for attr in attrs:
try:
self._attrs[attr] = getattr(self, attr)
except AttributeError:
pass
if self._attrs.get('help'):
a = self._attrs['help']
a = a.replace('%default', '%(default)s')
#a = a.replace('%prog', '%(prog)s')
self._attrs['help'] = a
return self._attrs
def _set_opt_strings(self, opts):
"""directly from optparse
might not be necessary as this is passed to argparse later on"""
for opt in opts:
if len(opt) < 2:
raise ArgumentError(
"invalid option string %r: "
"must be at least two characters long" % opt, self)
elif len(opt) == 2:
if not (opt[0] == "-" and opt[1] != "-"):
raise ArgumentError(
"invalid short option string %r: "
"must be of the form -x, (x any non-dash char)" % opt,
self)
self._short_opts.append(opt)
else:
if not (opt[0:2] == "--" and opt[2] != "-"):
raise ArgumentError(
"invalid long option string %r: "
"must start with --, followed by non-dash" % opt,
self)
self._long_opts.append(opt)
def __repr__(self):
retval = 'Argument('
if self._short_opts:
retval += '_short_opts: ' + repr(self._short_opts) + ', '
if self._long_opts:
retval += '_long_opts: ' + repr(self._long_opts) + ', '
retval += 'dest: ' + repr(self.dest) + ', '
if hasattr(self, 'type'):
retval += 'type: ' + repr(self.type) + ', '
if hasattr(self, 'default'):
retval += 'default: ' + repr(self.default) + ', '
if retval[-2:] == ', ': # always long enough to test ("Argument(" )
retval = retval[:-2]
retval += ')'
return retval
class OptionGroup:
def __init__(self, name, description="", parser=None):
self.name = name
@@ -114,12 +348,18 @@ class OptionGroup:
self.parser = parser
def addoption(self, *optnames, **attrs):
""" add an option to this group. """
option = py.std.optparse.Option(*optnames, **attrs)
""" add an option to this group.
if a shortened version of a long option is specified it will
be suppressed in the help. addoption('--twowords', '--two-words')
results in help showing '--two-words' only, but --twowords gets
accepted **and** the automatic destination is in args.twowords
"""
option = Argument(*optnames, **attrs)
self._addoption_instance(option, shortupper=False)
def _addoption(self, *optnames, **attrs):
option = py.std.optparse.Option(*optnames, **attrs)
option = Argument(*optnames, **attrs)
self._addoption_instance(option, shortupper=True)
def _addoption_instance(self, option, shortupper=False):
@@ -132,11 +372,12 @@ class OptionGroup:
self.options.append(option)
class MyOptionParser(py.std.optparse.OptionParser):
class MyOptionParser(py.std.argparse.ArgumentParser):
def __init__(self, parser):
self._parser = parser
py.std.optparse.OptionParser.__init__(self, usage=parser._usage,
add_help_option=False)
py.std.argparse.ArgumentParser.__init__(self, usage=parser._usage,
add_help=False, formatter_class=DropShorterLongHelpFormatter)
def format_epilog(self, formatter):
hints = self._parser.hints
if hints:
@@ -145,6 +386,66 @@ class MyOptionParser(py.std.optparse.OptionParser):
return s
return ""
def parse_args(self, args=None, namespace=None):
"""allow splitting of positional arguments"""
args, argv = self.parse_known_args(args, namespace)
if argv:
for arg in argv:
if arg and arg[0] == '-':
msg = py.std.argparse._('unrecognized arguments: %s')
self.error(msg % ' '.join(argv))
getattr(args, FILE_OR_DIR).extend(argv)
return args
class DropShorterLongHelpFormatter(py.std.argparse.HelpFormatter):
"""shorten help for long options that differ only in extra hyphens
- collapse **long** options that are the same except for extra hyphens
- special action attribute map_long_option allows surpressing additional
long options
- shortcut if there are only two options and one of them is a short one
- cache result on action object as this is called at least 2 times
"""
def _format_action_invocation(self, action):
orgstr = py.std.argparse.HelpFormatter._format_action_invocation(self, action)
if orgstr and orgstr[0] != '-': # only optional arguments
return orgstr
res = getattr(action, '_formatted_action_invocation', None)
if res:
return res
options = orgstr.split(', ')
if len(options) == 2 and (len(options[0]) == 2 or len(options[1]) == 2):
# a shortcut for '-h, --help' or '--abc', '-a'
action._formatted_action_invocation = orgstr
return orgstr
return_list = []
option_map = getattr(action, 'map_long_option', {})
if option_map is None:
option_map = {}
short_long = {}
for option in options:
if len(option) == 2 or option[2] == ' ':
continue
if not option.startswith('--'):
raise ArgumentError('long optional argument without "--": [%s]'
% (option), self)
xxoption = option[2:]
if xxoption.split()[0] not in option_map:
shortened = xxoption.replace('-', '')
if shortened not in short_long or \
len(short_long[shortened]) < len(xxoption):
short_long[shortened] = xxoption
# now short_long has been filled out to the longest with dashes
# **and** we keep the right option ordering from add_argument
for option in options: #
if len(option) == 2 or option[2] == ' ':
return_list.append(option)
if option[2:] == short_long.get(option.replace('-', '')):
return_list.append(option)
action._formatted_action_invocation = ', '.join(return_list)
return action._formatted_action_invocation
class Conftest(object):
""" the single place for accessing values and interacting
towards conftest modules from py.test objects.
@@ -196,27 +497,20 @@ class Conftest(object):
self.getconftestmodules(x)
def getconftestmodules(self, path):
""" return a list of imported conftest modules for the given path. """
try:
clist = self._path2confmods[path]
except KeyError:
if path is None:
raise ValueError("missing default confest.")
dp = path.dirpath()
raise ValueError("missing default conftest.")
clist = []
if dp != path:
cutdir = self._confcutdir
if cutdir and path != cutdir and not path.relto(cutdir):
pass
else:
conftestpath = path.join("conftest.py")
if conftestpath.check(file=1):
clist.append(self.importconftest(conftestpath))
clist[:0] = self.getconftestmodules(dp)
for parent in path.parts():
if self._confcutdir and self._confcutdir.relto(parent):
continue
conftestpath = parent.join("conftest.py")
if conftestpath.check(file=1):
clist.append(self.importconftest(conftestpath))
self._path2confmods[path] = clist
# be defensive: avoid changes from caller side to
# affect us by always returning a copy of the actual list
return clist[:]
return clist
def rget(self, name, path=None):
mod, value = self.rget_with_confmod(name, path)
@@ -268,32 +562,88 @@ class CmdOptions(object):
def __repr__(self):
return "<CmdOptions %r>" %(self.__dict__,)
FILE_OR_DIR = 'file_or_dir'
class Config(object):
""" access to configuration values, pluginmanager and plugin hooks. """
def __init__(self, pluginmanager=None):
def __init__(self, pluginmanager):
#: access to command line option as attributes.
#: (deprecated), use :py:func:`getoption() <_pytest.config.Config.getoption>` instead
self.option = CmdOptions()
_a = FILE_OR_DIR
self._parser = Parser(
usage="usage: %prog [options] [file_or_dir] [file_or_dir] [...]",
usage="%%(prog)s [options] [%s] [%s] [...]" % (_a, _a),
processopt=self._processopt,
)
#: a pluginmanager instance
self.pluginmanager = pluginmanager or PluginManager(load=True)
self.pluginmanager = pluginmanager
self.trace = self.pluginmanager.trace.root.get("config")
self._conftest = Conftest(onimport=self._onimportconftest)
self.hook = self.pluginmanager.hook
self._inicache = {}
self._opt2dest = {}
self._cleanup = []
self.pluginmanager.register(self, "pytestconfig")
self.pluginmanager.set_register_callback(self._register_plugin)
self._configured = False
def _register_plugin(self, plugin, name):
call_plugin = self.pluginmanager.call_plugin
call_plugin(plugin, "pytest_addhooks",
{'pluginmanager': self.pluginmanager})
self.hook.pytest_plugin_registered(plugin=plugin,
manager=self.pluginmanager)
dic = call_plugin(plugin, "pytest_namespace", {}) or {}
if dic:
import pytest
setns(pytest, dic)
call_plugin(plugin, "pytest_addoption", {'parser': self._parser})
if self._configured:
call_plugin(plugin, "pytest_configure", {'config': self})
def do_configure(self):
assert not self._configured
self._configured = True
self.hook.pytest_configure(config=self)
def do_unconfigure(self):
assert self._configured
self._configured = False
self.hook.pytest_unconfigure(config=self)
self.pluginmanager.ensure_shutdown()
def pytest_cmdline_parse(self, pluginmanager, args):
assert self == pluginmanager.config, (self, pluginmanager.config)
self.parse(args)
return self
def pytest_unconfigure(config):
while config._cleanup:
fin = config._cleanup.pop()
fin()
def notify_exception(self, excinfo, option=None):
if option and option.fulltrace:
style = "long"
else:
style = "native"
excrepr = excinfo.getrepr(funcargs=True,
showlocals=getattr(option, 'showlocals', False),
style=style,
)
res = self.hook.pytest_internalerror(excrepr=excrepr,
excinfo=excinfo)
if not py.builtin.any(res):
for line in str(excrepr).split("\n"):
sys.stderr.write("INTERNALERROR> %s\n" %line)
sys.stderr.flush()
@classmethod
def fromdictargs(cls, option_dict, args):
""" constructor useable for subprocesses. """
config = cls()
# XXX slightly crude way to initialize capturing
import _pytest.capture
_pytest.capture.pytest_cmdline_parse(config.pluginmanager, args)
pluginmanager = get_plugin_manager()
config = pluginmanager.config
config._preparse(args, addopts=False)
config.option.__dict__.update(option_dict)
for x in config.option.plugins:
@@ -319,21 +669,9 @@ class Config(object):
plugins += self._conftest.getconftestmodules(fspath)
return plugins
def _setinitialconftest(self, args):
# capture output during conftest init (#issue93)
# XXX introduce load_conftest hook to avoid needing to know
# about capturing plugin here
capman = self.pluginmanager.getplugin("capturemanager")
capman.resumecapture()
try:
try:
self._conftest.setinitial(args)
finally:
out, err = capman.suspendcapture() # logging might have got it
except:
sys.stdout.write(out)
sys.stderr.write(err)
raise
def pytest_load_initial_conftests(self, parser, args):
self._conftest.setinitial(args)
pytest_load_initial_conftests.trylast = True
def _initini(self, args):
self.inicfg = getcfg(args, ["pytest.ini", "tox.ini", "setup.cfg"])
@@ -348,12 +686,11 @@ class Config(object):
self.pluginmanager.consider_preparse(args)
self.pluginmanager.consider_setuptools_entrypoints()
self.pluginmanager.consider_env()
self._setinitialconftest(args)
self.pluginmanager.do_addoption(self._parser)
if addopts:
self.hook.pytest_cmdline_preparse(config=self, args=args)
self.hook.pytest_load_initial_conftests(early_config=self,
args=args, parser=self._parser)
def _checkversion(self):
import pytest
minver = self.inicfg.get('minversion', None)
if minver:
ver = minver.split(".")
@@ -371,6 +708,8 @@ class Config(object):
"can only parse cmdline args at most once per Config object")
self._origargs = args
self._preparse(args)
# XXX deprecated hook:
self.hook.pytest_cmdline_preparse(config=self, args=args)
self._parser.hints.extend(self.pluginmanager._hints)
args = self._parser.parse_setoption(args, self.option)
if not args:
@@ -500,3 +839,23 @@ def getcfg(args, inibasenames):
return iniconfig['pytest']
return {}
def setns(obj, dic):
import pytest
for name, value in dic.items():
if isinstance(value, dict):
mod = getattr(obj, name, None)
if mod is None:
modname = "pytest.%s" % name
mod = py.std.types.ModuleType(modname)
sys.modules[modname] = mod
mod.__all__ = []
setattr(obj, name, mod)
obj.__all__.append(name)
setns(mod, value)
else:
setattr(obj, name, value)
obj.__all__.append(name)
#if obj != pytest:
# pytest.__all__.append(name)
setattr(pytest, name, value)

View File

@@ -1,20 +1,13 @@
"""
pytest PluginManager, basic initialization and tracing.
(c) Holger Krekel 2004-2010
"""
import sys, os
import sys
import inspect
import py
from _pytest import hookspec # the extension point definitions
assert py.__version__.split(".")[:2] >= ['1', '4'], ("installation problem: "
"%s is too old, remove or upgrade 'py'" % (py.__version__))
default_plugins = (
"config mark main terminal runner python pdb unittest capture skipping "
"tmpdir monkeypatch recwarn pastebin helpconfig nose assertion genscript "
"junitxml resultlog doctest").split()
class TagTracer:
def __init__(self):
self._tag2proc = {}
@@ -24,12 +17,28 @@ class TagTracer:
def get(self, name):
return TagTracerSub(self, (name,))
def format_message(self, tags, args):
if isinstance(args[-1], dict):
extra = args[-1]
args = args[:-1]
else:
extra = {}
content = " ".join(map(str, args))
indent = " " * self.indent
lines = [
"%s%s [%s]\n" %(indent, content, ":".join(tags))
]
for name, value in extra.items():
lines.append("%s %s: %s\n" % (indent, name, value))
return lines
def processmessage(self, tags, args):
if self.writer is not None:
if args:
indent = " " * self.indent
content = " ".join(map(str, args))
self.writer("%s%s [%s]\n" %(indent, content, ":".join(tags)))
if self.writer is not None and args:
lines = self.format_message(tags, args)
self.writer(''.join(lines))
try:
self._tag2proc[tags](tags, args)
except KeyError:
@@ -57,37 +66,36 @@ class TagTracerSub:
return self.__class__(self.root, self.tags + (name,))
class PluginManager(object):
def __init__(self, load=False):
def __init__(self, hookspecs=None):
self._name2plugin = {}
self._listattrcache = {}
self._plugins = []
self._hints = []
self.trace = TagTracer().get("pluginmanage")
self._plugin_distinfo = []
if os.environ.get('PYTEST_DEBUG'):
err = sys.stderr
encoding = getattr(err, 'encoding', 'utf8')
try:
err = py.io.dupfile(err, encoding=encoding)
except Exception:
pass
self.trace.root.setwriter(err.write)
self.hook = HookRelay([hookspec], pm=self)
self.register(self)
if load:
for spec in default_plugins:
self.import_plugin(spec)
self._shutdown = []
self.hook = HookRelay(hookspecs or [], pm=self)
def do_configure(self, config):
# backward compatibility
config.do_configure()
def set_register_callback(self, callback):
assert not hasattr(self, "_registercallback")
self._registercallback = callback
def register(self, plugin, name=None, prepend=False):
if self._name2plugin.get(name, None) == -1:
return
name = name or getattr(plugin, '__name__', str(id(plugin)))
if self.isregistered(plugin, name):
raise ValueError("Plugin already registered: %s=%s" %(name, plugin))
raise ValueError("Plugin already registered: %s=%s\n%s" %(
name, plugin, self._name2plugin))
#self.trace("registering", name, plugin)
self._name2plugin[name] = plugin
self.call_plugin(plugin, "pytest_addhooks", {'pluginmanager': self})
self.hook.pytest_plugin_registered(manager=self, plugin=plugin)
reg = getattr(self, "_registercallback", None)
if reg is not None:
reg(plugin, name)
if not prepend:
self._plugins.append(plugin)
else:
@@ -98,11 +106,21 @@ class PluginManager(object):
if plugin is None:
plugin = self.getplugin(name=name)
self._plugins.remove(plugin)
self.hook.pytest_plugin_unregistered(plugin=plugin)
for name, value in list(self._name2plugin.items()):
if value == plugin:
del self._name2plugin[name]
def add_shutdown(self, func):
self._shutdown.append(func)
def ensure_shutdown(self):
while self._shutdown:
func = self._shutdown.pop()
func()
self._plugins = []
self._name2plugin.clear()
self._listattrcache.clear()
def isregistered(self, plugin, name=None):
if self.getplugin(name) is not None:
return True
@@ -110,8 +128,8 @@ class PluginManager(object):
if plugin == val:
return True
def addhooks(self, spec):
self.hook._addhooks(spec, prefix="pytest_")
def addhooks(self, spec, prefix="pytest_"):
self.hook._addhooks(spec, prefix=prefix)
def getplugins(self):
return list(self._plugins)
@@ -193,7 +211,6 @@ class PluginManager(object):
if self.getplugin(modname) is not None:
return
try:
#self.trace("importing", modname)
mod = importplugin(modname)
except KeyboardInterrupt:
raise
@@ -212,82 +229,6 @@ class PluginManager(object):
self.register(mod, modname)
self.consider_module(mod)
def pytest_configure(self, config):
config.addinivalue_line("markers",
"tryfirst: mark a hook implementation function such that the "
"plugin machinery will try to call it first/as early as possible.")
config.addinivalue_line("markers",
"trylast: mark a hook implementation function such that the "
"plugin machinery will try to call it last/as late as possible.")
def pytest_plugin_registered(self, plugin):
import pytest
dic = self.call_plugin(plugin, "pytest_namespace", {}) or {}
if dic:
self._setns(pytest, dic)
if hasattr(self, '_config'):
self.call_plugin(plugin, "pytest_addoption",
{'parser': self._config._parser})
self.call_plugin(plugin, "pytest_configure",
{'config': self._config})
def _setns(self, obj, dic):
import pytest
for name, value in dic.items():
if isinstance(value, dict):
mod = getattr(obj, name, None)
if mod is None:
modname = "pytest.%s" % name
mod = py.std.types.ModuleType(modname)
sys.modules[modname] = mod
mod.__all__ = []
setattr(obj, name, mod)
obj.__all__.append(name)
self._setns(mod, value)
else:
setattr(obj, name, value)
obj.__all__.append(name)
#if obj != pytest:
# pytest.__all__.append(name)
setattr(pytest, name, value)
def pytest_terminal_summary(self, terminalreporter):
tw = terminalreporter._tw
if terminalreporter.config.option.traceconfig:
for hint in self._hints:
tw.line("hint: %s" % hint)
def do_addoption(self, parser):
mname = "pytest_addoption"
methods = reversed(self.listattr(mname))
MultiCall(methods, {'parser': parser}).execute()
def do_configure(self, config):
assert not hasattr(self, '_config')
self._config = config
config.hook.pytest_configure(config=self._config)
def do_unconfigure(self, config):
config = self._config
del self._config
config.hook.pytest_unconfigure(config=config)
config.pluginmanager.unregister(self)
def notify_exception(self, excinfo, option=None):
if option and option.fulltrace:
style = "long"
else:
style = "native"
excrepr = excinfo.getrepr(funcargs=True,
showlocals=getattr(option, 'showlocals', False),
style=style,
)
res = self.hook.pytest_internalerror(excrepr=excrepr)
if not py.builtin.any(res):
for line in str(excrepr).split("\n"):
sys.stderr.write("INTERNALERROR> %s\n" %line)
sys.stderr.flush()
def listattr(self, attrname, plugins=None):
if plugins is None:
plugins = self._plugins
@@ -325,12 +266,8 @@ def importplugin(importspec):
__import__(mod)
return sys.modules[mod]
except ImportError:
#e = py.std.sys.exc_info()[1]
#if str(e).find(name) == -1:
# raise
pass #
__import__(importspec)
return sys.modules[importspec]
__import__(importspec)
return sys.modules[importspec]
class MultiCall:
""" execute a call into multiple python functions/methods. """
@@ -367,19 +304,36 @@ class MultiCall:
return kwargs
def varnames(func):
""" return argument name tuple for a function, method, class or callable.
In case of a class, its "__init__" method is considered.
For methods the "self" parameter is not included unless you are passing
an unbound method with Python3 (which has no supports for unbound methods)
"""
cache = getattr(func, "__dict__", {})
try:
return func._varnames
except AttributeError:
return cache["_varnames"]
except KeyError:
pass
if not inspect.isfunction(func) and not inspect.ismethod(func):
func = getattr(func, '__call__', func)
ismethod = inspect.ismethod(func)
if inspect.isclass(func):
try:
func = func.__init__
except AttributeError:
return ()
ismethod = True
else:
if not inspect.isfunction(func) and not inspect.ismethod(func):
func = getattr(func, '__call__', func)
ismethod = inspect.ismethod(func)
rawcode = py.code.getrawcode(func)
try:
x = rawcode.co_varnames[ismethod:rawcode.co_argcount]
except AttributeError:
x = ()
py.builtin._getfuncdict(func)['_varnames'] = x
try:
cache["_varnames"] = x
except TypeError:
pass
return x
class HookRelay:
@@ -437,43 +391,3 @@ class HookCaller:
self.trace.root.indent -= 1
return res
_preinit = []
def _preloadplugins():
_preinit.append(PluginManager(load=True))
def _prepareconfig(args=None, plugins=None):
if args is None:
args = sys.argv[1:]
elif isinstance(args, py.path.local):
args = [str(args)]
elif not isinstance(args, (tuple, list)):
if not isinstance(args, str):
raise ValueError("not a string or argument list: %r" % (args,))
args = py.std.shlex.split(args)
if _preinit:
_pluginmanager = _preinit.pop(0)
else: # subsequent calls to main will create a fresh instance
_pluginmanager = PluginManager(load=True)
hook = _pluginmanager.hook
if plugins:
for plugin in plugins:
_pluginmanager.register(plugin)
return hook.pytest_cmdline_parse(
pluginmanager=_pluginmanager, args=args)
def main(args=None, plugins=None):
""" return exit code, after performing an in-process test run.
:arg args: list of command line arguments.
:arg plugins: list of plugin objects to be auto-registered during
initialization.
"""
config = _prepareconfig(args, plugins)
exitstatus = config.hook.pytest_cmdline_main(config=config)
return exitstatus
class UsageError(Exception):
""" error in py.test usage or invocation"""

View File

@@ -1,6 +1,7 @@
""" discover and run doctests in modules and test files."""
import pytest, py
from _pytest.python import FixtureRequest, FuncFixtureInfo
from py._code.code import TerminalRepr, ReprFileLocation
def pytest_addoption(parser):
@@ -33,6 +34,14 @@ class ReprFailDoctest(TerminalRepr):
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
def runtest(self):
self.runner.run(self.dtest)
def repr_failure(self, excinfo):
doctest = py.std.doctest
if excinfo.errisinstance((doctest.DocTestFailure,
@@ -41,17 +50,27 @@ class DoctestItem(pytest.Item):
example = doctestfailure.example
test = doctestfailure.test
filename = test.filename
lineno = test.lineno + example.lineno + 1
if test.lineno is None:
lineno = None
else:
lineno = test.lineno + example.lineno + 1
message = excinfo.type.__name__
reprlocation = ReprFileLocation(filename, lineno, message)
checker = py.std.doctest.OutputChecker()
REPORT_UDIFF = py.std.doctest.REPORT_UDIFF
filelines = py.path.local(filename).readlines(cr=0)
i = max(test.lineno, max(0, lineno - 10)) # XXX?
lines = []
for line in filelines[i:lineno]:
lines.append("%03d %s" % (i+1, line))
i += 1
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
else:
lines.append('EXAMPLE LOCATION UNKNOWN, not showing all tests of that example')
indent = '>>>'
for line in example.source.splitlines():
lines.append('??? %s %s' % (indent, line))
indent = '...'
if excinfo.errisinstance(doctest.DocTestFailure):
lines += checker.output_difference(example,
doctestfailure.got, REPORT_UDIFF).split("\n")
@@ -65,23 +84,42 @@ class DoctestItem(pytest.Item):
return super(DoctestItem, self).repr_failure(excinfo)
def reportinfo(self):
return self.fspath, None, "[doctest]"
return self.fspath, None, "[doctest] %s" % self.name
class DoctestTextfile(DoctestItem, pytest.File):
def runtest(self):
doctest = py.std.doctest
# satisfy `FixtureRequest` constructor...
self.funcargs = {}
fm = self.session._fixturemanager
def func():
pass
self._fixtureinfo = fm.getfixtureinfo(node=self, func=func,
cls=None, funcargs=False)
fixture_request = FixtureRequest(self)
fixture_request._fillfixtures()
failed, tot = doctest.testfile(
str(self.fspath), module_relative=False,
optionflags=doctest.ELLIPSIS,
extraglobs=dict(getfixture=fixture_request.getfuncargvalue),
raise_on_error=True, verbose=0)
class DoctestModule(DoctestItem, pytest.File):
def runtest(self):
class DoctestModule(pytest.File):
def collect(self):
doctest = py.std.doctest
if self.fspath.basename == "conftest.py":
module = self.config._conftest.importconftest(self.fspath)
else:
module = self.fspath.pyimport()
failed, tot = doctest.testmod(
module, raise_on_error=True, verbose=0,
optionflags=doctest.ELLIPSIS)
# satisfy `FixtureRequest` constructor...
self.funcargs = {}
self._fixtureinfo = FuncFixtureInfo((), [], {})
fixture_request = FixtureRequest(self)
doctest_globals = dict(getfixture=fixture_request.getfuncargvalue)
# uses internal doctest module parsing mechanism
finder = doctest.DocTestFinder()
runner = doctest.DebugRunner(verbose=0, optionflags=doctest.ELLIPSIS)
for test in finder.find(module, module.__name__,
extraglobs=doctest_globals):
if test.examples: # skip empty doctests
yield DoctestItem(test.name, self, runner, test)

View File

@@ -1,5 +1,6 @@
""" generate a single-file self-contained version of py.test """
import py
import sys
def find_toplevel(name):
for syspath in py.std.sys.path:
@@ -59,11 +60,21 @@ def pytest_addoption(parser):
def pytest_cmdline_main(config):
genscript = config.getvalue("genscript")
if genscript:
tw = py.io.TerminalWriter()
deps = ['py', '_pytest', 'pytest']
if sys.version_info < (2,7):
deps.append("argparse")
tw.line("generated script will run on python2.5-python3.3++")
else:
tw.line("WARNING: generated script will not run on python2.6 "
"or below due to 'argparse' dependency. Use python2.6 "
"to generate a python2.5/6 compatible script", red=True)
script = generate_script(
'import py; raise SystemExit(py.test.cmdline.main())',
['py', '_pytest', 'pytest'],
deps,
)
genscript = py.path.local(genscript)
genscript.write(script)
tw.line("generated pytest standalone script: %s" % genscript,
bold=True)
return 0

View File

@@ -13,8 +13,8 @@ def pytest_addoption(parser):
group._addoption('-p', action="append", dest="plugins", default = [],
metavar="name",
help="early-load given plugin (multi-allowed).")
group.addoption('--traceconfig',
action="store_true", dest="traceconfig", default=False,
group.addoption('--traceconfig', '--trace-config',
action="store_true", default=False,
help="trace considerations of conftest.py files."),
group.addoption('--debug',
action="store_true", dest="debug", default=False,
@@ -54,14 +54,15 @@ def pytest_cmdline_main(config):
sys.stderr.write(line + "\n")
return 0
elif config.option.help:
config.pluginmanager.do_configure(config)
config.do_configure()
showhelp(config)
config.pluginmanager.do_unconfigure(config)
config.do_unconfigure()
return 0
def showhelp(config):
tw = py.io.TerminalWriter()
tw.write(config._parser.optparser.format_help())
tw.write(config._parser.optparser.format_epilog(None))
tw.line()
tw.line()
#tw.sep( "=", "config file settings")
@@ -119,7 +120,6 @@ def pytest_report_header(config):
if config.option.traceconfig:
lines.append("active plugins:")
plugins = []
items = config.pluginmanager._name2plugin.items()
for name, plugin in items:
if hasattr(plugin, '__file__'):

View File

@@ -20,10 +20,10 @@ def pytest_cmdline_parse(pluginmanager, args):
pytest_cmdline_parse.firstresult = True
def pytest_cmdline_preparse(config, args):
"""modify command line arguments before option parsing. """
"""(deprecated) modify command line arguments before option parsing. """
def pytest_addoption(parser):
"""register optparse-style options and ini-style config values.
"""register argparse-style options and ini-style config values.
This function must be implemented in a :ref:`plugin <pluginorder>` and is
called once at the beginning of a test run.
@@ -52,6 +52,10 @@ def pytest_cmdline_main(config):
implementation will invoke the configure hooks and runtest_mainloop. """
pytest_cmdline_main.firstresult = True
def pytest_load_initial_conftests(args, early_config, parser):
""" implements loading initial conftests.
"""
def pytest_configure(config):
""" called after command line options have been parsed
and all plugins and initial conftest files been loaded.
@@ -221,7 +225,7 @@ def pytest_report_teststatus(report):
pytest_report_teststatus.firstresult = True
def pytest_terminal_summary(terminalreporter):
""" add additional section in terminal summary reporting. """
""" add additional section in terminal summary reporting. """
# -------------------------------------------------------------------------
# doctest hooks
@@ -236,13 +240,20 @@ pytest_doctest_prepare_content.firstresult = True
# -------------------------------------------------------------------------
def pytest_plugin_registered(plugin, manager):
""" a new py lib plugin got registered. """
""" a new pytest plugin got registered. """
def pytest_plugin_unregistered(plugin):
""" a py lib plugin got unregistered. """
def pytest_internalerror(excrepr):
def pytest_internalerror(excrepr, excinfo):
""" called for internal errors. """
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
interactively handled.
This hook is only called if an exception was raised
that is not an internal exception like "skip.Exception".
"""

View File

@@ -9,7 +9,6 @@ import re
import sys
import time
# Python 2.X and 3.X compatibility
try:
unichr(65)
@@ -36,7 +35,8 @@ class Junit(py.xml.Namespace):
# | [#x10000-#x10FFFF]
_legal_chars = (0x09, 0x0A, 0x0d)
_legal_ranges = (
(0x20, 0xD7FF),
(0x20, 0x7E),
(0x80, 0xD7FF),
(0xE000, 0xFFFD),
(0x10000, 0x10FFFF),
)
@@ -61,16 +61,17 @@ def bin_xml_escape(arg):
def pytest_addoption(parser):
group = parser.getgroup("terminal reporting")
group.addoption('--junitxml', action="store", dest="xmlpath",
metavar="path", default=None,
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', action="store", dest="junitprefix",
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
if 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)
@@ -102,15 +103,24 @@ class LogXML(object):
classnames.insert(0, self.prefix)
self.tests.append(Junit.testcase(
classname=".".join(classnames),
name=names[-1],
name=bin_xml_escape(names[-1]),
time=getattr(report, 'duration', 0)
))
def _write_captured_output(self, report):
sec = dict(report.sections)
for name in ('out', 'err'):
content = sec.get("Captured std%s" % name)
if content:
tag = getattr(Junit, 'system-'+name)
self.append(tag(bin_xml_escape(content)))
def append(self, obj):
self.tests[-1].append(obj)
def append_pass(self, report):
self.passed += 1
self._write_captured_output(report)
def append_failure(self, report):
#msg = str(report.longrepr.reprtraceback.extraline)
@@ -119,48 +129,44 @@ class LogXML(object):
Junit.skipped(message="xfail-marked test passes unexpectedly"))
self.skipped += 1
else:
sec = dict(report.sections)
fail = Junit.failure(message="test failure")
fail.append(str(report.longrepr))
fail.append(bin_xml_escape(report.longrepr))
self.append(fail)
for name in ('out', 'err'):
content = sec.get("Captured std%s" % name)
if content:
tag = getattr(Junit, 'system-'+name)
self.append(tag(bin_xml_escape(content)))
self.failed += 1
self._write_captured_output(report)
def append_collect_failure(self, report):
#msg = str(report.longrepr.reprtraceback.extraline)
self.append(Junit.failure(str(report.longrepr),
self.append(Junit.failure(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(str(report.longrepr),
self.append(Junit.skipped(bin_xml_escape(report.longrepr),
message="collection skipped"))
self.skipped += 1
def append_error(self, report):
self.append(Junit.error(str(report.longrepr),
self.append(Junit.error(bin_xml_escape(report.longrepr),
message="test setup failure"))
self.errors += 1
def append_skipped(self, report):
if hasattr(report, "wasxfail"):
self.append(Junit.skipped(str(report.wasxfail),
self.append(Junit.skipped(bin_xml_escape(report.wasxfail),
message="expected test failure"))
else:
filename, lineno, skipreason = report.longrepr
if skipreason.startswith("Skipped: "):
skipreason = skipreason[9:]
skipreason = bin_xml_escape(skipreason[9:])
self.append(
Junit.skipped("%s:%s: %s" % report.longrepr,
type="pytest.skip",
message=skipreason
))
self.skipped += 1
self._write_captured_output(report)
def pytest_runtest_logreport(self, report):
if report.passed:
@@ -187,17 +193,17 @@ class LogXML(object):
def pytest_internalerror(self, excrepr):
self.errors += 1
data = py.xml.escape(excrepr)
data = bin_xml_escape(excrepr)
self.tests.append(
Junit.testcase(
Junit.error(data, message="internal error"),
classname="pytest",
name="internal"))
def pytest_sessionstart(self, session):
def pytest_sessionstart(self):
self.suite_start_time = time.time()
def pytest_sessionfinish(self, session, exitstatus, __multicall__):
def pytest_sessionfinish(self):
if py.std.sys.version_info[0] < 3:
logfile = py.std.codecs.open(self.logfile, 'w', encoding='utf-8')
else:
@@ -210,7 +216,7 @@ class LogXML(object):
logfile.write('<?xml version="1.0" encoding="utf-8"?>')
logfile.write(Junit.testsuite(
self.tests,
name="",
name="pytest",
errors=self.errors,
failures=self.failed,
skips=self.skipped,

View File

@@ -2,14 +2,13 @@
import py
import pytest, _pytest
import inspect
import os, sys, imp
try:
from collections import MutableMapping as MappingMixin
except ImportError:
from UserDict import DictMixin as MappingMixin
from _pytest.mark import MarkInfo
from _pytest.runner import collect_one_node, Skipped
tracebackcutdir = py.path.local(_pytest.__file__).dirpath()
@@ -35,19 +34,20 @@ def pytest_addoption(parser):
dest="exitfirst",
help="exit instantly on first error or failed test."),
group._addoption('--maxfail', metavar="num",
action="store", type="int", dest="maxfail", default=0,
action="store", type=int, dest="maxfail", default=0,
help="exit after first num failures or errors.")
group._addoption('--strict', action="store_true",
help="run pytest in strict mode, warnings become errors.")
group = parser.getgroup("collect", "collection")
group.addoption('--collectonly',
action="store_true", dest="collectonly",
group.addoption('--collectonly', '--collect-only', action="store_true",
help="only collect tests, don't execute them."),
group.addoption('--pyargs', action="store_true",
help="try to interpret all arguments as python packages.")
group.addoption("--ignore", action="append", metavar="path",
help="ignore path during collection (multi-allowed).")
# when changing this to --conf-cut-dir, config.py Conftest.setinitial
# needs upgrading as well
group.addoption('--confcutdir', dest="confcutdir", default=None,
metavar="dir",
help="only load conftest.py's relative to specified dir.")
@@ -74,7 +74,7 @@ def wrap_session(config, doit):
initstate = 0
try:
try:
config.pluginmanager.do_configure(config)
config.do_configure()
initstate = 1
config.hook.pytest_sessionstart(session=session)
initstate = 2
@@ -89,18 +89,22 @@ def wrap_session(config, doit):
session.exitstatus = EXIT_INTERRUPTED
except:
excinfo = py.code.ExceptionInfo()
config.pluginmanager.notify_exception(excinfo, config.option)
config.notify_exception(excinfo, config.option)
session.exitstatus = EXIT_INTERNALERROR
if excinfo.errisinstance(SystemExit):
sys.stderr.write("mainloop: caught Spurious SystemExit!\n")
else:
if session._testsfailed:
session.exitstatus = EXIT_TESTSFAILED
finally:
session.startdir.chdir()
if initstate >= 2:
config.hook.pytest_sessionfinish(session=session,
exitstatus=session.exitstatus or (session._testsfailed and 1))
if not session.exitstatus and session._testsfailed:
session.exitstatus = EXIT_TESTSFAILED
config.hook.pytest_sessionfinish(
session=session,
exitstatus=session.exitstatus)
if initstate >= 1:
config.pluginmanager.do_unconfigure(config)
config.do_unconfigure()
config.pluginmanager.ensure_shutdown()
return session.exitstatus
def pytest_cmdline_main(config):
@@ -166,30 +170,39 @@ def compatproperty(name):
class NodeKeywords(MappingMixin):
def __init__(self, node):
parent = node.parent
bases = parent and (parent.keywords._markers,) or ()
self._markers = type("dynmarker", bases, {node.name: True})
self.node = node
self.parent = node.parent
self._markers = {node.name: True}
def __getitem__(self, key):
try:
return getattr(self._markers, key)
except AttributeError:
raise KeyError(key)
return self._markers[key]
except KeyError:
if self.parent is None:
raise
return self.parent.keywords[key]
def __setitem__(self, key, value):
setattr(self._markers, key, value)
self._markers[key] = value
def __delitem__(self, key):
delattr(self._markers, key)
raise ValueError("cannot delete key in keywords dict")
def __iter__(self):
return iter(self.keys())
seen = set(self._markers)
if self.parent is not None:
seen.update(self.parent.keywords)
return iter(seen)
def __len__(self):
return len(self.keys())
return len(self.__iter__())
def keys(self):
return dir(self._markers)
return list(self)
def __repr__(self):
return "<NodeKeywords for node %s>" % (self.node, )
class Node(object):
""" base class for Collector and Item the test collection tree.
@@ -214,6 +227,11 @@ class Node(object):
#: keywords/markers collected from all scopes
self.keywords = NodeKeywords(self)
#: allow adding of extra keywords to use for matching
self.extra_keyword_matches = set()
# used for storing artificial fixturedefs for direct parametrization
self._name2pseudofixturedef = {}
#self.extrainit()
@property
@@ -254,21 +272,11 @@ class Node(object):
self._nodeid = x = self._makeid()
return x
def _makeid(self):
return self.parent.nodeid + "::" + self.name
def __eq__(self, other):
if not isinstance(other, Node):
return False
return (self.__class__ == other.__class__ and
self.name == other.name and self.parent == other.parent)
def __ne__(self, other):
return not self == other
def __hash__(self):
return hash((self.name, self.parent))
return hash(self.nodeid)
def setup(self):
pass
@@ -305,13 +313,52 @@ class Node(object):
chain.reverse()
return chain
def add_marker(self, marker):
""" dynamically add a marker object to the node.
``marker`` can be a string or pytest.mark.* instance.
"""
from _pytest.mark import MarkDecorator
if isinstance(marker, py.builtin._basestring):
marker = MarkDecorator(marker)
elif not isinstance(marker, MarkDecorator):
raise ValueError("is not a string or pytest.mark.* Marker")
self.keywords[marker.name] = marker
def get_marker(self, name):
""" get a marker object from this node or None if
the node doesn't have a marker with that name. """
val = self.keywords.get(name, None)
if val is not None:
from _pytest.mark import MarkInfo, MarkDecorator
if isinstance(val, (MarkDecorator, MarkInfo)):
return val
def listextrakeywords(self):
""" Return a set of all extra keywords in self and any parents."""
extra_keywords = set()
item = self
for item in self.listchain():
extra_keywords.update(item.extra_keyword_matches)
return extra_keywords
def listnames(self):
return [x.name for x in self.listchain()]
def getplugins(self):
return self.config._getmatchingplugins(self.fspath)
def addfinalizer(self, fin):
""" register a function to be called when this node is finalized.
This method can only be called when this node is active
in a setup chain, for example during self.setup().
"""
self.session._setupstate.addfinalizer(fin, self)
def getparent(self, cls):
""" get the next parent node (including ourself)
which is an instance of the given class"""
current = self
while current and not isinstance(current, cls):
current = current.parent
@@ -345,6 +392,11 @@ class Collector(Node):
""" Collector instances create children through collect()
and thus iteratively build a tree.
"""
# the set of exceptions to interpret as "Skip the whole module" during
# collection
skip_exceptions = (Skipped,)
class CollectError(Exception):
""" an error during collection, contains a custom message. """
@@ -367,7 +419,6 @@ class Collector(Node):
def _prunetraceback(self, excinfo):
if hasattr(self, 'fspath'):
path = self.fspath
traceback = excinfo.traceback
ntraceback = traceback.cut(path=self.fspath)
if ntraceback == traceback:
@@ -439,6 +490,7 @@ class Session(FSCollector):
self.shouldstop = False
self.trace = config.trace.root.get("collection")
self._norecursepatterns = config.getini("norecursedirs")
self.startdir = py.path.local()
def pytest_collectstart(self):
if self.shouldstop:
@@ -482,8 +534,7 @@ class Session(FSCollector):
parts = self._parsearg(arg)
self._initialparts.append(parts)
self._initialpaths.add(parts[0])
self.ihook.pytest_collectstart(collector=self)
rep = self.ihook.pytest_make_collect_report(collector=self)
rep = collect_one_node(self)
self.ihook.pytest_collectreport(report=rep)
self.trace.root.indent -= 1
if self._notfound:
@@ -612,8 +663,7 @@ class Session(FSCollector):
resultnodes.append(node)
continue
assert isinstance(node, pytest.Collector)
node.ihook.pytest_collectstart(collector=node)
rep = node.ihook.pytest_make_collect_report(collector=node)
rep = collect_one_node(node)
if rep.passed:
has_matched = False
for x in rep.result:
@@ -634,19 +684,11 @@ class Session(FSCollector):
yield node
else:
assert isinstance(node, pytest.Collector)
node.ihook.pytest_collectstart(collector=node)
rep = node.ihook.pytest_make_collect_report(collector=node)
rep = collect_one_node(node)
if rep.passed:
for subnode in rep.result:
for x in self.genitems(subnode):
yield x
node.ihook.pytest_collectreport(report=rep)
def getfslineno(obj):
# xxx let decorators etc specify a sane ordering
if hasattr(obj, 'place_as'):
obj = obj.place_as
fslineno = py.code.getfslineno(obj)
assert isinstance(fslineno[1], int), obj
return fslineno

View File

@@ -1,43 +1,56 @@
""" generic mechanism for marking and selecting python functions. """
import pytest, py
import py
def pytest_namespace():
return {'mark': MarkGenerator()}
def pytest_addoption(parser):
group = parser.getgroup("general")
group._addoption('-k',
action="store", dest="keyword", default='', metavar="KEYWORDEXPR",
help="only run tests which match given keyword expression. "
"An expression consists of space-separated terms. "
"Each term must match. Precede a term with '-' to negate. "
"Terminate expression with ':' to make the first match match "
"all subsequent tests (usually file-order). ")
group._addoption(
'-k',
action="store", dest="keyword", default='', metavar="EXPRESSION",
help="only run tests which match the given substring expression. "
"An expression is a python evaluatable expression "
"where all names are substring-matched against test names "
"and their parent classes. Example: -k 'test_method or test "
"other' matches all test functions and classes whose name "
"contains 'test_method' or 'test_other'. "
"Additionally keywords are matched to classes and functions "
"containing extra names in their 'extra_keyword_matches' set, "
"as well as functions which have names assigned directly to them."
)
group._addoption("-m",
group._addoption(
"-m",
action="store", dest="markexpr", default="", metavar="MARKEXPR",
help="only run tests matching given mark expression. "
"example: -m 'mark1 and not mark2'."
)
)
group.addoption("--markers", action="store_true", help=
"show markers (builtin, plugin and per-project ones).")
group.addoption(
"--markers", action="store_true",
help="show markers (builtin, plugin and per-project ones)."
)
parser.addini("markers", "markers for test functions", 'linelist')
def pytest_cmdline_main(config):
if config.option.markers:
config.pluginmanager.do_configure(config)
config.do_configure()
tw = py.io.TerminalWriter()
for line in config.getini("markers"):
name, rest = line.split(":", 1)
tw.write("@pytest.mark.%s:" % name, bold=True)
tw.write("@pytest.mark.%s:" % name, bold=True)
tw.line(rest)
tw.line()
config.pluginmanager.do_unconfigure(config)
config.do_unconfigure()
return 0
pytest_cmdline_main.tryfirst = True
def pytest_collection_modifyitems(items, config):
keywordexpr = config.option.keyword
matchexpr = config.option.markexpr
@@ -66,32 +79,82 @@ def pytest_collection_modifyitems(items, config):
config.hook.pytest_deselected(items=deselected)
items[:] = remaining
class BoolDict:
def __init__(self, mydict):
self._mydict = mydict
def __getitem__(self, name):
return name in self._mydict
class SubstringDict:
def __init__(self, mydict):
self._mydict = mydict
class MarkMapping:
"""Provides a local mapping for markers where item access
resolves to True if the marker is present. """
def __init__(self, keywords):
mymarks = set()
for key, value in keywords.items():
if isinstance(value, MarkInfo) or isinstance(value, MarkDecorator):
mymarks.add(key)
self._mymarks = mymarks
def __getitem__(self, name):
for key in self._mydict:
if name in key:
return name in self._mymarks
class KeywordMapping:
"""Provides a local mapping for keywords.
Given a list of names, map any substring of one of these names to True.
"""
def __init__(self, names):
self._names = names
def __getitem__(self, subname):
for name in self._names:
if subname in name:
return True
return False
def matchmark(colitem, matchexpr):
return eval(matchexpr, {}, BoolDict(colitem.keywords))
def matchmark(colitem, markexpr):
"""Tries to match on any marker names, attached to the given colitem."""
return eval(markexpr, {}, MarkMapping(colitem.keywords))
def matchkeyword(colitem, keywordexpr):
"""Tries to match given keyword expression to given collector item.
Will match on the name of colitem, including the names of its parents.
Only matches names of items which are either a :class:`Class` or a
:class:`Function`.
Additionally, matches on names in the 'extra_keyword_matches' set of
any item, as well as names directly assigned to test functions.
"""
keywordexpr = keywordexpr.replace("-", "not ")
return eval(keywordexpr, {}, SubstringDict(colitem.keywords))
mapped_names = set()
# Add the names of the current item and any parent items
import pytest
for item in colitem.listchain():
if not isinstance(item, pytest.Instance):
mapped_names.add(item.name)
# Add the names added as extra keywords to current or parent items
for name in colitem.listextrakeywords():
mapped_names.add(name)
# Add the names attached to the current function through direct assignment
if hasattr(colitem, 'function'):
for name in colitem.function.__dict__:
mapped_names.add(name)
mapping = KeywordMapping(mapped_names)
if " " not in keywordexpr:
# special case to allow for simple "-k pass" and "-k 1.3"
return mapping[keywordexpr]
elif keywordexpr.startswith("not ") and " " not in keywordexpr[4:]:
return not mapping[keywordexpr[4:]]
return eval(keywordexpr, {}, mapping)
def pytest_configure(config):
import pytest
if config.option.strict:
pytest.mark._config = config
class MarkGenerator:
""" Factory for :class:`MarkDecorator` objects - exposed as
a ``py.test.mark`` singleton instance. Example::
@@ -125,6 +188,10 @@ class MarkGenerator:
if name not in self._markers:
raise AttributeError("%r not a registered marker" % (name,))
def istestfunc(func):
return hasattr(func, "__call__") and \
getattr(func, "__name__", "<lambda>") != "<lambda>"
class MarkDecorator:
""" A decorator for test functions and test classes. When applied
it will create :class:`MarkInfo` objects which may be
@@ -141,43 +208,50 @@ class MarkDecorator:
pass
"""
def __init__(self, name, args=None, kwargs=None):
self.markname = name
self.name = name
self.args = args or ()
self.kwargs = kwargs or {}
@property
def markname(self):
return self.name # for backward-compat (2.4.1 had this attr)
def __repr__(self):
d = self.__dict__.copy()
name = d.pop('markname')
return "<MarkDecorator %r %r>" %(name, d)
name = d.pop('name')
return "<MarkDecorator %r %r>" % (name, d)
def __call__(self, *args, **kwargs):
""" if passed a single callable argument: decorate it with mark info.
otherwise add *args/**kwargs in-place to mark information. """
if args:
func = args[0]
if len(args) == 1 and hasattr(func, '__call__') or \
hasattr(func, '__bases__'):
if len(args) == 1 and (istestfunc(func) or
hasattr(func, '__bases__')):
if hasattr(func, '__bases__'):
if hasattr(func, 'pytestmark'):
l = func.pytestmark
if not isinstance(l, list):
func.pytestmark = [l, self]
func.pytestmark = [l, self]
else:
l.append(self)
l.append(self)
else:
func.pytestmark = [self]
func.pytestmark = [self]
else:
holder = getattr(func, self.markname, None)
holder = getattr(func, self.name, None)
if holder is None:
holder = MarkInfo(self.markname, self.args, self.kwargs)
setattr(func, self.markname, holder)
holder = MarkInfo(
self.name, self.args, self.kwargs
)
setattr(func, self.name, holder)
else:
holder.add(self.args, self.kwargs)
return func
kw = self.kwargs.copy()
kw.update(kwargs)
args = self.args + args
return self.__class__(self.markname, args=args, kwargs=kw)
return self.__class__(self.name, args=args, kwargs=kw)
class MarkInfo:
""" Marking object created by :class:`MarkDecorator` instances. """
@@ -192,7 +266,8 @@ class MarkInfo:
def __repr__(self):
return "<MarkInfo %r args=%r kwargs=%r>" % (
self.name, self.args, self.kwargs)
self.name, self.args, self.kwargs
)
def add(self, args, kwargs):
""" add a MarkInfo with the given args and kwargs. """
@@ -204,4 +279,3 @@ class MarkInfo:
""" yield MarkInfo objects each relating to a marking-call. """
for args, kwargs in self._arglist:
yield MarkInfo(self.name, args, kwargs)

View File

@@ -1,6 +1,7 @@
""" monkeypatching and mocking functionality. """
import os, sys, inspect
import os, sys
from py.builtin import _basestring
def pytest_funcarg__monkeypatch(request):
"""The returned ``monkeypatch`` funcarg provides these
@@ -24,6 +25,40 @@ def pytest_funcarg__monkeypatch(request):
request.addfinalizer(mpatch.undo)
return mpatch
def derive_importpath(import_path):
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]
getattr(obj, attr)
except AttributeError:
__tracebackhide__ = True
pytest.fail("object %r has no attribute %r" % (obj, attr))
return attr, obj
notset = object()
class monkeypatch:
@@ -33,28 +68,67 @@ class monkeypatch:
self._setitem = []
self._cwd = None
def setattr(self, obj, name, value, raising=True):
""" set attribute ``name`` on ``obj`` to ``value``, by default
raise AttributeEror if the attribute did not exist. """
oldval = getattr(obj, name, notset)
def setattr(self, target, name, value=notset, raising=True):
""" set attribute value on target, memorizing the old value.
By default raise AttributeError if the attribute did not exist.
For convenience you can specify a string as ``target`` which
will be interpreted as a dotted import path, with the last part
being the attribute name. Example:
``monkeypatch.setattr("os.getcwd", lambda x: "/")``
would set the ``getcwd`` function of the ``os`` module.
The ``raising`` value determines if the setattr should fail
if the attribute is not already present (defaults to True
which means it will raise).
"""
__tracebackhide__ = True
import inspect
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")
value = name
name, target = derive_importpath(target)
oldval = getattr(target, name, notset)
if raising and oldval is notset:
raise AttributeError("%r has no attribute %r" %(obj, name))
raise AttributeError("%r has no attribute %r" %(target, name))
# avoid class descriptors like staticmethod/classmethod
if inspect.isclass(obj):
oldval = obj.__dict__.get(name, notset)
self._setattr.insert(0, (obj, name, oldval))
setattr(obj, name, value)
if inspect.isclass(target):
oldval = target.__dict__.get(name, notset)
self._setattr.insert(0, (target, name, oldval))
setattr(target, name, value)
def delattr(self, obj, name, raising=True):
""" delete attribute ``name`` from ``obj``, by default raise
AttributeError it the attribute did not previously exist. """
if not hasattr(obj, name):
def delattr(self, target, name=notset, raising=True):
""" delete attribute ``name`` from ``target``, by default raise
AttributeError it the attribute did not previously exist.
If no ``name`` is specified and ``target`` is a string
it will be interpreted as a dotted import path with the
last part being the attribute name.
If raising is set to false, the attribute is allowed to not
pre-exist.
"""
__tracebackhide__ = True
if name is notset:
if not isinstance(target, _basestring):
raise TypeError("use delattr(target, name) or "
"delattr(target) with target being a dotted "
"import string")
name, target = derive_importpath(target)
if not hasattr(target, name):
if raising:
raise AttributeError(name)
else:
self._setattr.insert(0, (obj, name, getattr(obj, name, notset)))
delattr(obj, name)
self._setattr.insert(0, (target, name,
getattr(target, name, notset)))
delattr(target, name)
def setitem(self, dic, name, value):
""" set dictionary entry ``name`` to value. """

View File

@@ -1,21 +1,22 @@
""" run test suites written for nose. """
import pytest, py
import inspect
import sys
from _pytest import unittest
def pytest_runtest_makereport(__multicall__, item, call):
SkipTest = getattr(sys.modules.get('nose', None), 'SkipTest', None)
if SkipTest:
if call.excinfo and call.excinfo.errisinstance(SkipTest):
# let's substitute the excinfo with a py.test.skip one
call2 = call.__class__(lambda: py.test.skip(str(call.excinfo.value)), call.when)
call2 = call.__class__(lambda:
pytest.skip(str(call.excinfo.value)), call.when)
call.excinfo = call2.excinfo
@pytest.mark.trylast
def pytest_runtest_setup(item):
if isinstance(item, (pytest.Function)):
if is_potential_nosetest(item):
if isinstance(item.parent, pytest.Generator):
gen = item.parent
if not hasattr(gen, '_nosegensetup'):
@@ -26,9 +27,11 @@ def pytest_runtest_setup(item):
if not call_optional(item.obj, 'setup'):
# call module level setup if there is no object level one
call_optional(item.parent.obj, 'setup')
#XXX this implies we only call teardown when setup worked
item.session._setupstate.addfinalizer((lambda: teardown_nose(item)), item)
def pytest_runtest_teardown(item):
if isinstance(item, pytest.Function):
def teardown_nose(item):
if is_potential_nosetest(item):
if not call_optional(item.obj, 'teardown'):
call_optional(item.parent.obj, 'teardown')
#if hasattr(item.parent, '_nosegensetup'):
@@ -36,12 +39,27 @@ def pytest_runtest_teardown(item):
# del item.parent._nosegensetup
def pytest_make_collect_report(collector):
SkipTest = getattr(sys.modules.get('unittest', None), 'SkipTest', None)
if SkipTest is not None:
collector.skip_exceptions += (SkipTest,)
SkipTest = getattr(sys.modules.get('nose', None), 'SkipTest', None)
if SkipTest is not None:
collector.skip_exceptions += (SkipTest,)
if isinstance(collector, pytest.Generator):
call_optional(collector.obj, 'setup')
def is_potential_nosetest(item):
# extra check needed since we do not do nose style setup/teardown
# on direct unittest style classes
return isinstance(item, pytest.Function) and \
not isinstance(item, unittest.TestCaseFunction)
def call_optional(obj, name):
method = getattr(obj, name, None)
if method is not None and not hasattr(method, "_pytestfixturefunction") and py.builtin.callable(method):
isfixture = hasattr(method, "_pytestfixturefunction")
if method is not None and not isfixture and py.builtin.callable(method):
# If there's any problems allow the exception to raise rather than
# silently ignoring them
method()

View File

@@ -10,7 +10,7 @@ def pytest_addoption(parser):
group = parser.getgroup("terminal reporting")
group._addoption('--pastebin', metavar="mode",
action='store', dest="pastebin", default=None,
type="choice", choices=['failed', 'all'],
choices=['failed', 'all'],
help="send failed|all info to bpaste.net pastebin service.")
def pytest_configure(__multicall__, config):

View File

@@ -16,6 +16,12 @@ def pytest_configure(config):
if config.getvalue("usepdb"):
config.pluginmanager.register(PdbInvoke(), 'pdbinvoke')
old_trace = py.std.pdb.set_trace
def fin():
py.std.pdb.set_trace = old_trace
py.std.pdb.set_trace = pytest.set_trace
config._cleanup.append(fin)
class pytestPDB:
""" Pseudo PDB that defers to the real pdb. """
item = None
@@ -52,33 +58,47 @@ def pytest_runtest_makereport():
pytestPDB.item = None
class PdbInvoke:
@pytest.mark.tryfirst
def pytest_runtest_makereport(self, item, call, __multicall__):
rep = __multicall__.execute()
if not call.excinfo or \
call.excinfo.errisinstance(pytest.skip.Exception) or \
call.excinfo.errisinstance(py.std.bdb.BdbQuit):
return rep
if hasattr(rep, "wasxfail"):
return rep
# we assume that the above execute() suspended capturing
# XXX we re-use the TerminalReporter's terminalwriter
# because this seems to avoid some encoding related troubles
# for not completely clear reasons.
tw = item.config.pluginmanager.getplugin("terminalreporter")._tw
tw.line()
tw.sep(">", "traceback")
rep.toterminal(tw)
tw.sep(">", "entering PDB")
# A doctest.UnexpectedException is not useful for post_mortem.
# Use the underlying exception instead:
if isinstance(call.excinfo.value, py.std.doctest.UnexpectedException):
tb = call.excinfo.value.exc_info[2]
else:
tb = call.excinfo._excinfo[2]
def pytest_exception_interact(self, node, call, report):
return _enter_pdb(node, call.excinfo, report)
def pytest_internalerror(self, excrepr, excinfo):
for line in str(excrepr).split("\n"):
sys.stderr.write("INTERNALERROR> %s\n" %line)
sys.stderr.flush()
tb = _postmortem_traceback(excinfo)
post_mortem(tb)
rep._pdbshown = True
return rep
def _enter_pdb(node, excinfo, rep):
# XXX we re-use the TerminalReporter's terminalwriter
# because this seems to avoid some encoding related troubles
# for not completely clear reasons.
tw = node.config.pluginmanager.getplugin("terminalreporter")._tw
tw.line()
tw.sep(">", "traceback")
rep.toterminal(tw)
tw.sep(">", "entering PDB")
tb = _postmortem_traceback(excinfo)
post_mortem(tb)
rep._pdbshown = True
return rep
def _postmortem_traceback(excinfo):
# A doctest.UnexpectedException is not useful for post_mortem.
# Use the underlying exception instead:
if isinstance(excinfo.value, py.std.doctest.UnexpectedException):
return excinfo.value.exc_info[2]
else:
return excinfo._excinfo[2]
def _find_last_non_hidden_frame(stack):
i = max(0, len(stack) - 1)
while i and stack[i][0].f_locals.get("__tracebackhide__", False):
i -= 1
return i
def post_mortem(t):
pdb = py.std.pdb
@@ -86,9 +106,7 @@ def post_mortem(t):
def get_stack(self, f, t):
stack, i = pdb.Pdb.get_stack(self, f, t)
if f is None:
i = max(0, len(stack) - 1)
while i and stack[i][0].f_locals.get("__tracebackhide__", False):
i-=1
i = _find_last_non_hidden_frame(stack)
return stack, i
p = Pdb()
p.reset()

View File

@@ -4,13 +4,18 @@ import py, pytest
import sys, os
import codecs
import re
import inspect
import time
from fnmatch import fnmatch
from _pytest.main import Session, EXIT_OK
from py.builtin import print_
from _pytest.core import HookRelay
def get_public_names(l):
"""Only return names from iterator l without a leading underscore."""
return [x for x in l if x[0] != "_"]
def pytest_addoption(parser):
group = parser.getgroup("pylib")
group.addoption('--no-tools-on-path',
@@ -21,7 +26,6 @@ def pytest_addoption(parser):
def pytest_configure(config):
# This might be called multiple times. Only take the first.
global _pytest_fullpath
import pytest
try:
_pytest_fullpath
except NameError:
@@ -77,7 +81,8 @@ class HookRecorder:
def finish_recording(self):
for recorder in self._recorders.values():
self._pluginmanager.unregister(recorder)
if self._pluginmanager.isregistered(recorder):
self._pluginmanager.unregister(recorder)
self._recorders.clear()
def _makecallparser(self, method):
@@ -115,7 +120,6 @@ class HookRecorder:
def contains(self, entries):
__tracebackhide__ = True
from py.builtin import print_
i = 0
entries = list(entries)
backlocals = py.std.sys._getframe(1).f_locals
@@ -195,10 +199,7 @@ class TmpTestdir:
except py.error.EEXIST:
continue
break
# we need to create another subdir
# because Directory.collect() currently loads
# conftest.py from sibling directories
self.tmpdir = tmpdir.mkdir(name)
self.tmpdir = tmpdir
self.plugins = []
self._syspathremove = []
self.chdir() # always chdir
@@ -257,9 +258,6 @@ class TmpTestdir:
def makefile(self, ext, *args, **kwargs):
return self._makefile(ext, args, kwargs)
def makeini(self, source):
return self.makefile('cfg', setup=source)
def makeconftest(self, source):
return self.makepyfile(conftest=source)
@@ -358,7 +356,7 @@ class TmpTestdir:
if not plugins:
plugins = []
plugins.append(Collect())
ret = self.pytestmain(list(args), plugins=plugins)
ret = pytest.main(list(args), plugins=plugins)
reprec = rec[0]
reprec.ret = ret
assert len(rec) == 1
@@ -371,23 +369,24 @@ class TmpTestdir:
break
else:
args.append("--basetemp=%s" % self.tmpdir.dirpath('basetemp'))
import _pytest.core
config = _pytest.core._prepareconfig(args, self.plugins)
# the in-process pytest invocation needs to avoid leaking FDs
# so we register a "reset_capturings" callmon the capturing manager
# and make sure it gets called
config._cleanup.append(
config.pluginmanager.getplugin("capturemanager").reset_capturings)
import _pytest.config
self.request.addfinalizer(
lambda: _pytest.config.pytest_unconfigure(config))
config = _pytest.config._prepareconfig(args, self.plugins)
# we don't know what the test will do with this half-setup config
# object and thus we make sure it gets unconfigured properly in any
# case (otherwise capturing could still be active, for example)
def ensure_unconfigure():
if hasattr(config.pluginmanager, "_config"):
config.pluginmanager.do_unconfigure(config)
config.pluginmanager.ensure_shutdown()
self.request.addfinalizer(ensure_unconfigure)
return config
def parseconfigure(self, *args):
config = self.parseconfig(*args)
config.pluginmanager.do_configure(config)
config.do_configure()
self.request.addfinalizer(lambda:
config.pluginmanager.do_unconfigure(config))
config.do_unconfigure())
return config
def getitem(self, source, funcname="test_func"):
@@ -422,18 +421,8 @@ class TmpTestdir:
str(os.getcwd()), env.get('PYTHONPATH', '')]))
kw['env'] = env
#print "env", env
return py.std.subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr, **kw)
def pytestmain(self, *args, **kwargs):
class ResetCapturing:
@pytest.mark.trylast
def pytest_unconfigure(self, config):
capman = config.pluginmanager.getplugin("capturemanager")
capman.reset_capturings()
plugins = kwargs.setdefault("plugins", [])
rc = ResetCapturing()
plugins.append(rc)
return pytest.main(*args, **kwargs)
return py.std.subprocess.Popen(cmdargs,
stdout=stdout, stderr=stderr, **kw)
def run(self, *cmdargs):
return self._run(*cmdargs)
@@ -481,7 +470,7 @@ class TmpTestdir:
# XXX we rely on script refering to the correct environment
# we cannot use "(py.std.sys.executable,script)"
# becaue on windows the script is e.g. a py.test.exe
return (py.std.sys.executable, _pytest_fullpath,)
return (py.std.sys.executable, _pytest_fullpath,) # noqa
else:
py.test.skip("cannot run %r with --no-tools-on-path" % scriptname)
@@ -527,15 +516,16 @@ class TmpTestdir:
return self.spawn(cmd, expect_timeout=expect_timeout)
def spawn(self, cmd, expect_timeout=10.0):
pexpect = py.test.importorskip("pexpect", "2.4")
pexpect = py.test.importorskip("pexpect", "3.0")
if hasattr(sys, 'pypy_version_info') and '64' in py.std.platform.machine():
pytest.skip("pypy-64 bit not supported")
if sys.platform == "darwin":
pytest.xfail("pexpect does not work reliably on darwin?!")
if sys.platform.startswith("freebsd"):
pytest.xfail("pexpect does not work reliably on freebsd")
logfile = self.tmpdir.join("spawn.out")
child = pexpect.spawn(cmd, logfile=logfile.open("w"))
logfile = self.tmpdir.join("spawn.out").open("wb")
child = pexpect.spawn(cmd, logfile=logfile)
self.request.addfinalizer(logfile.close)
child.timeout = expect_timeout
return child
@@ -600,9 +590,10 @@ class ReportRecorder(object):
passed = []
skipped = []
failed = []
for rep in self.getreports("pytest_runtest_logreport"):
for rep in self.getreports(
"pytest_collectreport pytest_runtest_logreport"):
if rep.passed:
if rep.when == "call":
if getattr(rep, "when", None) == "call":
passed.append(rep)
elif rep.skipped:
skipped.append(rep)
@@ -665,6 +656,12 @@ class LineMatcher:
else:
raise ValueError("line %r not found in output" % line)
def get_lines_after(self, fnline):
for i, line in enumerate(self.lines):
if fnline == line or fnmatch(line, fnline):
return self.lines[i+1:]
raise ValueError("line %r not found in output" % fnline)
def fnmatch_lines(self, lines2):
def show(arg1, arg2):
py.builtin.print_(arg1, arg2, file=py.std.sys.stderr)

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,14 @@
""" recording warnings during test function execution. """
import py
import sys, os
import sys
def pytest_funcarg__recwarn(request):
"""Return a WarningsRecorder instance that provides these methods:
* ``pop(category=None)``: return last warning matching the category.
* ``clear()``: clear list of warnings
See http://docs.python.org/library/warnings.html for information
on warning categories.
"""

View File

@@ -6,7 +6,7 @@ import py
def pytest_addoption(parser):
group = parser.getgroup("terminal reporting", "resultlog plugin options")
group.addoption('--resultlog', action="store", dest="resultlog",
group.addoption('--resultlog', '--result-log', action="store",
metavar="path", default=None,
help="path for machine-readable result log.")
@@ -85,7 +85,7 @@ class ResultLog(object):
if not report.passed:
if report.failed:
code = "F"
longrepr = str(report.longrepr.reprcrash)
longrepr = str(report.longrepr)
else:
assert report.skipped
code = "S"

View File

@@ -18,7 +18,7 @@ def pytest_namespace():
def pytest_addoption(parser):
group = parser.getgroup("terminal reporting", "reporting", after="general")
group.addoption('--durations',
action="store", type="int", default=None, metavar="N",
action="store", type=int, default=None, metavar="N",
help="show N slowest setup/test durations (N=0 for all)."),
def pytest_terminal_summary(terminalreporter):
@@ -63,12 +63,20 @@ def pytest_runtest_protocol(item, nextitem):
return True
def runtestprotocol(item, log=True, nextitem=None):
hasrequest = hasattr(item, "_request")
if hasrequest and not item._request:
item._initrequest()
rep = call_and_report(item, "setup", log)
reports = [rep]
if rep.passed:
reports.append(call_and_report(item, "call", log))
reports.append(call_and_report(item, "teardown", log,
nextitem=nextitem))
# after all teardown hooks have been called
# want funcargs and request info to go away
if hasrequest:
item._request = False
item.funcargs = None
return reports
def pytest_runtest_setup(item):
@@ -100,8 +108,16 @@ def call_and_report(item, when, log=True, **kwds):
report = hook.pytest_runtest_makereport(item=item, call=call)
if log:
hook.pytest_runtest_logreport(report=report)
if check_interactive_exception(call, report):
hook.pytest_exception_interact(node=item, call=call, report=report)
return report
def check_interactive_exception(call, report):
return call.excinfo and not (
hasattr(report, "wasxfail") or
call.excinfo.errisinstance(skip.Exception) or
call.excinfo.errisinstance(py.std.bdb.BdbQuit))
def call_runtest_hook(item, when, **kwds):
hookname = "pytest_runtest_" + when
ihook = getattr(item.ihook, hookname)
@@ -177,7 +193,6 @@ def pytest_runtest_makereport(item, call):
outcome = "passed"
longrepr = None
else:
excinfo = call.excinfo
if not isinstance(excinfo, py.code.ExceptionInfo):
outcome = "failed"
longrepr = excinfo
@@ -190,7 +205,8 @@ def pytest_runtest_makereport(item, call):
if call.when == "call":
longrepr = item.repr_failure(excinfo)
else: # exception in setup or teardown
longrepr = item._repr_failure_py(excinfo)
longrepr = item._repr_failure_py(excinfo,
style=item.config.option.tbstyle)
return TestReport(item.nodeid, item.location,
keywords, outcome, longrepr, when,
duration=duration)
@@ -249,7 +265,7 @@ def pytest_make_collect_report(collector):
if not call.excinfo:
outcome = "passed"
else:
if call.excinfo.errisinstance(py.test.skip.Exception):
if call.excinfo.errisinstance(collector.skip_exceptions):
outcome = "skipped"
r = collector._repr_failure_py(call.excinfo, "line").reprcrash
longrepr = (str(r.path), r.lineno, r.message)
@@ -259,8 +275,11 @@ def pytest_make_collect_report(collector):
if not hasattr(errorinfo, "toterminal"):
errorinfo = CollectErrorRepr(errorinfo)
longrepr = errorinfo
return CollectReport(collector.nodeid, outcome, longrepr,
rep = CollectReport(collector.nodeid, outcome, longrepr,
getattr(call, 'result', None))
rep.call = call # see collect_one_node
return rep
class CollectReport(BaseReport):
def __init__(self, nodeid, outcome, longrepr, result, sections=(), **extra):
@@ -308,9 +327,18 @@ class SetupState(object):
def _callfinalizers(self, colitem):
finalizers = self._finalizers.pop(colitem, None)
exc = None
while finalizers:
fin = finalizers.pop()
fin()
try:
fin()
except Exception:
# XXX Only first exception will be seen by user,
# ideally all should be reported.
if exc is None:
exc = sys.exc_info()
if exc:
py.builtin._reraise(*exc)
def _teardown_with_finalization(self, colitem):
self._callfinalizers(colitem)
@@ -355,6 +383,16 @@ class SetupState(object):
col._prepare_exc = sys.exc_info()
raise
def collect_one_node(collector):
ihook = collector.ihook
ihook.pytest_collectstart(collector=collector)
rep = ihook.pytest_make_collect_report(collector=collector)
call = rep.__dict__.pop("call", None)
if call and check_interactive_exception(call, rep):
ihook.pytest_exception_interact(node=collector, call=call, report=rep)
return rep
# =============================================================
# Test OutcomeExceptions and helpers for creating them.
@@ -364,6 +402,7 @@ class OutcomeException(Exception):
contain info about test and collection outcomes.
"""
def __init__(self, msg=None, pytrace=True):
Exception.__init__(self, msg)
self.msg = msg
self.pytrace = pytrace
@@ -419,25 +458,25 @@ fail.Exception = Failed
def importorskip(modname, minversion=None):
""" return imported module if it has a higher __version__ than the
optionally specified 'minversion' - otherwise call py.test.skip()
with a message detailing the mismatch.
""" return imported module if it has at least "minversion" as its
__version__ attribute. If no minversion is specified the a skip
is only triggered if the module can not be imported.
Note that version comparison only works with simple version strings
like "1.2.3" but not "1.2.3.dev1" or others.
"""
__tracebackhide__ = True
compile(modname, '', 'eval') # to catch syntaxerrors
try:
__import__(modname)
except ImportError:
py.test.skip("could not import %r" %(modname,))
skip("could not import %r" %(modname,))
mod = sys.modules[modname]
if minversion is None:
return mod
verattr = getattr(mod, '__version__', None)
if isinstance(minversion, str):
minver = minversion.split(".")
else:
minver = list(minversion)
if verattr is None or verattr.split(".") < minver:
py.test.skip("module %r has __version__ %r, required is: %r" %(
modname, verattr, minversion))
def intver(verstring):
return [int(x) for x in verstring.split(".")]
if verattr is None or intver(verattr) < intver(minversion):
skip("module %r has __version__ %r, required is: %r" %(
modname, verattr, minversion))
return mod

View File

@@ -10,6 +10,14 @@ def pytest_addoption(parser):
help="run tests even if they are marked xfail")
def pytest_configure(config):
if config.option.runxfail:
old = pytest.xfail
config._cleanup.append(lambda: setattr(pytest, "xfail", old))
def nop(*args, **kwargs):
pass
nop.Exception = XFailed
setattr(pytest, "xfail", nop)
config.addinivalue_line("markers",
"skipif(condition): skip the given test function if eval(condition) "
"results in a True value. Evaluation happens within the "
@@ -86,10 +94,14 @@ class MarkEvaluator:
self.result = False
for expr in self.holder.args:
self.expr = expr
if isinstance(expr, str):
if isinstance(expr, py.builtin._basestring):
result = cached_eval(self.item.config, expr, d)
else:
pytest.fail("expression is not a string")
if self.get("reason") is None:
# XXX better be checked at collection time
pytest.fail("you need to specify reason=STRING "
"when using booleans as conditions.")
result = bool(expr)
if result:
self.result = True
self.expr = expr
@@ -205,7 +217,6 @@ def pytest_terminal_summary(terminalreporter):
tr._tw.line(line)
def show_simple(terminalreporter, lines, stat, format):
tw = terminalreporter._tw
failed = terminalreporter.stats.get(stat)
if failed:
for rep in failed:

View File

@@ -6,13 +6,16 @@ sources = """
import sys
import base64
import zlib
import imp
class DictImporter(object):
def __init__(self, sources):
self.sources = sources
def find_module(self, fullname, path=None):
if fullname == "argparse" and sys.version_info >= (2,7):
# we were generated with <python2.7 (which pulls in argparse)
# but we are running now on a stdlib which has it, so use that.
return None
if fullname in self.sources:
return self
if fullname + '.__init__' in self.sources:
@@ -36,7 +39,7 @@ class DictImporter(object):
if is_pkg:
module.__path__ = [fullname]
do_exec(co, module.__dict__)
do_exec(co, module.__dict__) # noqa
return sys.modules[fullname]
def get_source(self, name):
@@ -57,7 +60,7 @@ if __name__ == "__main__":
sources = pickle.loads(zlib.decompress(base64.decodestring(sources)))
importer = DictImporter(sources)
sys.meta_path.append(importer)
sys.meta_path.insert(0, importer)
entry = "@ENTRY@"
do_exec(entry, locals())
do_exec(entry, locals()) # noqa

View File

@@ -5,7 +5,6 @@ This is a good source for looking at the various reporting hooks.
import pytest
import py
import sys
import os
def pytest_addoption(parser):
group = parser.getgroup("terminal reporting", "reporting", after="general")
@@ -25,29 +24,19 @@ def pytest_addoption(parser):
help="(deprecated, use -r)")
group._addoption('--tb', metavar="style",
action="store", dest="tbstyle", default='long',
type="choice", choices=['long', 'short', 'no', 'line', 'native'],
choices=['long', 'short', 'no', 'line', 'native'],
help="traceback print mode (long/short/line/native/no).")
group._addoption('--fulltrace',
action="store_true", dest="fulltrace", default=False,
group._addoption('--fulltrace', '--full-trace',
action="store_true", default=False,
help="don't cut any tracebacks (default is to cut).")
group._addoption('--color', metavar="color",
action="store", dest="color", default='auto',
choices=['yes', 'no', 'auto'],
help="color terminal output (yes/no/auto).")
def pytest_configure(config):
config.option.verbose -= config.option.quiet
# we try hard to make printing resilient against
# later changes on FD level.
stdout = py.std.sys.stdout
if hasattr(os, 'dup') and hasattr(stdout, 'fileno'):
try:
newstdout = py.io.dupfile(stdout, buffering=1,
encoding=stdout.encoding)
except ValueError:
pass
else:
config._cleanup.append(lambda: newstdout.close())
assert stdout.encoding == newstdout.encoding
stdout = newstdout
reporter = TerminalReporter(config, stdout)
reporter = TerminalReporter(config, sys.stdout)
config.pluginmanager.register(reporter, 'terminalreporter')
if config.option.debug or config.option.traceconfig:
def mywriter(tags, args):
@@ -99,13 +88,17 @@ class TerminalReporter:
self.startdir = self.curdir = py.path.local()
if file is None:
file = py.std.sys.stdout
self._tw = py.io.TerminalWriter(file)
self._tw = self.writer = py.io.TerminalWriter(file)
if self.config.option.color == 'yes':
self._tw.hasmarkup = True
if self.config.option.color == 'no':
self._tw.hasmarkup = False
self.currentfspath = None
self.reportchars = getreportopt(config)
self.hasmarkup = self._tw.hasmarkup
def hasopt(self, char):
char = {'xfailed': 'x', 'skipped': 's'}.get(char,char)
char = {'xfailed': 'x', 'skipped': 's'}.get(char, char)
return char in self.reportchars
def write_fspath_result(self, fspath, res):
@@ -147,6 +140,12 @@ class TerminalReporter:
self.ensure_newline()
self._tw.sep(sep, title, **markup)
def section(self, title, sep="=", **kw):
self._tw.sep(sep, title, **kw)
def line(self, msg, **kw):
self._tw.line(msg, **kw)
def pytest_internalerror(self, excrepr):
for line in str(excrepr).split("\n"):
self.write_line("INTERNALERROR> " + line)
@@ -154,7 +153,7 @@ class TerminalReporter:
def pytest_plugin_registered(self, plugin):
if self.config.option.traceconfig:
msg = "PLUGIN registered: %s" %(plugin,)
msg = "PLUGIN registered: %s" % (plugin,)
# XXX this event may happen during setup/teardown time
# which unfortunately captures our output here
# which garbles our output if we use self.write_line
@@ -178,6 +177,7 @@ class TerminalReporter:
res = self.config.hook.pytest_report_teststatus(report=rep)
cat, letter, word = res
self.stats.setdefault(cat, []).append(rep)
self._tests_ran = True
if not letter and not word:
# probably passed setup/teardown
return
@@ -209,7 +209,7 @@ class TerminalReporter:
self.currentfspath = -2
def pytest_collection(self):
if not self.hasmarkup and self.config.option.verbose >=1:
if not self.hasmarkup and self.config.option.verbose >= 1:
self.write("collecting ... ", bold=True)
def pytest_collectreport(self, report):
@@ -325,15 +325,16 @@ class TerminalReporter:
stack.append(col)
#if col.name == "()":
# continue
indent = (len(stack)-1) * " "
self._tw.line("%s%s" %(indent, col))
indent = (len(stack) - 1) * " "
self._tw.line("%s%s" % (indent, col))
def pytest_sessionfinish(self, exitstatus, __multicall__):
__multicall__.execute()
self._tw.line("")
if exitstatus in (0, 1, 2):
if exitstatus in (0, 1, 2, 4):
self.summary_errors()
self.summary_failures()
self.summary_hints()
self.config.hook.pytest_terminal_summary(terminalreporter=self)
if exitstatus == 2:
self._report_keyboardinterrupt()
@@ -399,6 +400,11 @@ class TerminalReporter:
l.append(x)
return l
def summary_hints(self):
if self.config.option.traceconfig:
for hint in self.config.pluginmanager._hints:
self._tw.line("hint: %s" % hint)
def summary_failures(self):
if self.config.option.tbstyle != "no":
reports = self.getreports('failed')
@@ -452,14 +458,20 @@ class TerminalReporter:
if key: # setup/teardown reports have an empty key, ignore them
val = self.stats.get(key, None)
if val:
parts.append("%d %s" %(len(val), key))
parts.append("%d %s" % (len(val), key))
line = ", ".join(parts)
# XXX coloring
msg = "%s in %.2f seconds" %(line, session_duration)
msg = "%s in %.2f seconds" % (line, session_duration)
markup = {'bold': True}
if 'failed' in self.stats or 'error' in self.stats:
markup = {'red': True, 'bold': True}
else:
markup = {'green': True, 'bold': True}
if self.verbosity >= 0:
self.write_sep("=", msg, bold=True)
#else:
# self.write_line(msg, bold=True)
self.write_sep("=", msg, **markup)
if self.verbosity == -1:
self.write_line(msg, **markup)
def summary_deselected(self):
if 'deselected' in self.stats:
@@ -471,7 +483,7 @@ class TerminalReporter:
if m:
l.append("-m %r" % m)
if l:
self.write_sep("=", "%d tests deselected by %r" %(
self.write_sep("=", "%d tests deselected by %r" % (
len(self.stats['deselected']), " ".join(l)), bold=True)
def repr_pythonversion(v=None):

View File

@@ -64,5 +64,8 @@ def tmpdir(request):
"""
name = request.node.name
name = py.std.re.sub("[\W]", "_", name)
MAXVAL = 30
if len(name) > MAXVAL:
name = name[:MAXVAL]
x = request.config._tmpdirhandler.mktemp(name, numbered=True)
return x

View File

@@ -1,27 +1,44 @@
""" discovery and running of std-library "unittest" style tests. """
import pytest, py
import sys, pdb
import sys
# for transfering markers
from _pytest.python import transfer_markers
def pytest_pycollect_makeitem(collector, name, obj):
def is_unittest(obj):
"""Is obj a subclass of unittest.TestCase?"""
unittest = sys.modules.get('unittest')
if unittest is None:
return # nobody can have derived unittest.TestCase
return # nobody can have derived unittest.TestCase
try:
isunit = issubclass(obj, unittest.TestCase)
return issubclass(obj, unittest.TestCase)
except KeyboardInterrupt:
raise
except Exception:
pass
else:
if isunit:
return UnitTestCase(name, parent=collector)
except:
return False
def pytest_pycollect_makeitem(collector, name, obj):
if is_unittest(obj):
return UnitTestCase(name, parent=collector)
class UnitTestCase(pytest.Class):
nofuncargs = True # marker for fixturemanger.getfixtureinfo()
# to declare that our children do not support funcargs
#
def setup(self):
cls = self.obj
if getattr(cls, '__unittest_skip__', False):
return # skipped
setup = getattr(cls, 'setUpClass', None)
if setup is not None:
setup()
teardown = getattr(cls, 'tearDownClass', None)
if teardown is not None:
self.addfinalizer(teardown)
super(UnitTestCase, self).setup()
def collect(self):
self.session._fixturemanager.parsefactories(self, unittest=True)
@@ -33,8 +50,6 @@ class UnitTestCase(pytest.Class):
x = getattr(self.obj, name)
funcobj = getattr(x, 'im_func', x)
transfer_markers(funcobj, cls, module)
if hasattr(funcobj, 'todo'):
pytest.mark.xfail(reason=str(funcobj.todo))(funcobj)
yield TestCaseFunction(name, parent=self)
foundsomething = True
@@ -45,21 +60,7 @@ class UnitTestCase(pytest.Class):
if ut is None or runtest != ut.TestCase.runTest:
yield TestCaseFunction('runTest', parent=self)
def setup(self):
if getattr(self.obj, '__unittest_skip__', False):
return
meth = getattr(self.obj, 'setUpClass', None)
if meth is not None:
meth()
super(UnitTestCase, self).setup()
def teardown(self):
if getattr(self.obj, '__unittest_skip__', False):
return
meth = getattr(self.obj, 'tearDownClass', None)
if meth is not None:
meth()
super(UnitTestCase, self).teardown()
class TestCaseFunction(pytest.Function):
_excinfo = None
@@ -67,10 +68,6 @@ class TestCaseFunction(pytest.Function):
def setup(self):
self._testcase = self.parent.obj(self.name)
self._obj = getattr(self._testcase, self.name)
if hasattr(self._testcase, 'skip'):
pytest.skip(self._testcase.skip)
if hasattr(self._obj, 'skip'):
pytest.skip(self._obj.skip)
if hasattr(self._testcase, 'setup_method'):
self._testcase.setup_method(self._obj)
if hasattr(self, "_request"):
@@ -147,7 +144,10 @@ def pytest_runtest_makereport(item, call):
if isinstance(item, TestCaseFunction):
if item._excinfo:
call.excinfo = item._excinfo.pop(0)
del call.result
try:
del call.result
except AttributeError:
pass
# twisted trial support
def pytest_runtest_protocol(item, __multicall__):

View File

@@ -1,10 +1,12 @@
import sys
if __name__ == '__main__':
import cProfile
import py
import pstats
stats = cProfile.run('py.test.cmdline.main(["empty.py", ])', 'prof')
script = sys.argv[1] if len(sys.argv) > 1 else "empty.py"
stats = cProfile.run('py.test.cmdline.main([%r])' % script, 'prof')
p = pstats.Stats("prof")
p.strip_dirs()
p.sort_stats('cumulative')
print(p.print_stats(30))
print(p.print_stats(250))

View File

@@ -0,0 +1,19 @@
# 10000 iterations, just for relative comparison
# 2.7.5 3.3.2
# FilesCompleter 75.1109 69.2116
# FastFilesCompleter 0.7383 1.0760
if __name__ == '__main__':
import sys
import timeit
from argcomplete.completers import FilesCompleter
from _pytest._argcomplete import FastFilesCompleter
count = 1000 # only a few seconds
setup = 'from __main__ import FastFilesCompleter\nfc = FastFilesCompleter()'
run = 'fc("/d")'
sys.stdout.write('%s\n' % (timeit.timeit(run,
setup=setup.replace('Fast', ''), number=count)))
sys.stdout.write('%s\n' % (timeit.timeit(run, setup=setup, number=count)))

12
bench/manyparam.py Normal file
View File

@@ -0,0 +1,12 @@
import pytest
@pytest.fixture(scope='module', params=range(966))
def foo(request):
return request.param
def test_it(foo):
pass
def test_it2(foo):
pass

10
bench/skip.py Normal file
View File

@@ -0,0 +1,10 @@
import pytest
SKIP = True
@pytest.mark.parametrize("x", xrange(5000))
def test_foo(x):
if SKIP:
pytest.skip("heh")

View File

@@ -1,497 +0,0 @@
#!python
"""Bootstrap distribute installation
If you want to use setuptools in your package's setup.py, just include this
file in the same directory with it, and add this to the top of your setup.py::
from distribute_setup import use_setuptools
use_setuptools()
If you want to require a specific version of setuptools, set a download
mirror, or use an alternate download directory, you can do so by supplying
the appropriate options to ``use_setuptools()``.
This file can also be run as a script to install or upgrade setuptools.
"""
import os
import sys
import time
import fnmatch
import tempfile
import tarfile
from distutils import log
try:
from site import USER_SITE
except ImportError:
USER_SITE = None
try:
import subprocess
def _python_cmd(*args):
args = (sys.executable,) + args
return subprocess.call(args) == 0
except ImportError:
# will be used for python 2.3
def _python_cmd(*args):
args = (sys.executable,) + args
# quoting arguments if windows
if sys.platform == 'win32':
def quote(arg):
if ' ' in arg:
return '"%s"' % arg
return arg
args = [quote(arg) for arg in args]
return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
DEFAULT_VERSION = "0.6.27"
DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
SETUPTOOLS_FAKED_VERSION = "0.6c11"
SETUPTOOLS_PKG_INFO = """\
Metadata-Version: 1.0
Name: setuptools
Version: %s
Summary: xxxx
Home-page: xxx
Author: xxx
Author-email: xxx
License: xxx
Description: xxx
""" % SETUPTOOLS_FAKED_VERSION
def _install(tarball, install_args=()):
# extracting the tarball
tmpdir = tempfile.mkdtemp()
log.warn('Extracting in %s', tmpdir)
old_wd = os.getcwd()
try:
os.chdir(tmpdir)
tar = tarfile.open(tarball)
_extractall(tar)
tar.close()
# going in the directory
subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
os.chdir(subdir)
log.warn('Now working in %s', subdir)
# installing
log.warn('Installing Distribute')
if not _python_cmd('setup.py', 'install', *install_args):
log.warn('Something went wrong during the installation.')
log.warn('See the error message above.')
finally:
os.chdir(old_wd)
def _build_egg(egg, tarball, to_dir):
# extracting the tarball
tmpdir = tempfile.mkdtemp()
log.warn('Extracting in %s', tmpdir)
old_wd = os.getcwd()
try:
os.chdir(tmpdir)
tar = tarfile.open(tarball)
_extractall(tar)
tar.close()
# going in the directory
subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
os.chdir(subdir)
log.warn('Now working in %s', subdir)
# building an egg
log.warn('Building a Distribute egg in %s', to_dir)
_python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
finally:
os.chdir(old_wd)
# returning the result
log.warn(egg)
if not os.path.exists(egg):
raise IOError('Could not build the egg.')
def _do_download(version, download_base, to_dir, download_delay):
egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg'
% (version, sys.version_info[0], sys.version_info[1]))
if not os.path.exists(egg):
tarball = download_setuptools(version, download_base,
to_dir, download_delay)
_build_egg(egg, tarball, to_dir)
sys.path.insert(0, egg)
import setuptools
setuptools.bootstrap_install_from = egg
def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
to_dir=os.curdir, download_delay=15, no_fake=True):
# making sure we use the absolute path
to_dir = os.path.abspath(to_dir)
was_imported = 'pkg_resources' in sys.modules or \
'setuptools' in sys.modules
try:
try:
import pkg_resources
if not hasattr(pkg_resources, '_distribute'):
if not no_fake:
_fake_setuptools()
raise ImportError
except ImportError:
return _do_download(version, download_base, to_dir, download_delay)
try:
pkg_resources.require("distribute>="+version)
return
except pkg_resources.VersionConflict:
e = sys.exc_info()[1]
if was_imported:
sys.stderr.write(
"The required version of distribute (>=%s) is not available,\n"
"and can't be installed while this script is running. Please\n"
"install a more recent version first, using\n"
"'easy_install -U distribute'."
"\n\n(Currently using %r)\n" % (version, e.args[0]))
sys.exit(2)
else:
del pkg_resources, sys.modules['pkg_resources'] # reload ok
return _do_download(version, download_base, to_dir,
download_delay)
except pkg_resources.DistributionNotFound:
return _do_download(version, download_base, to_dir,
download_delay)
finally:
if not no_fake:
_create_fake_setuptools_pkg_info(to_dir)
def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
to_dir=os.curdir, delay=15):
"""Download distribute from a specified location and return its filename
`version` should be a valid distribute version number that is available
as an egg for download under the `download_base` URL (which should end
with a '/'). `to_dir` is the directory where the egg will be downloaded.
`delay` is the number of seconds to pause before an actual download
attempt.
"""
# making sure we use the absolute path
to_dir = os.path.abspath(to_dir)
try:
from urllib.request import urlopen
except ImportError:
from urllib2 import urlopen
tgz_name = "distribute-%s.tar.gz" % version
url = download_base + tgz_name
saveto = os.path.join(to_dir, tgz_name)
src = dst = None
if not os.path.exists(saveto): # Avoid repeated downloads
try:
log.warn("Downloading %s", url)
src = urlopen(url)
# Read/write all in one block, so we don't create a corrupt file
# if the download is interrupted.
data = src.read()
dst = open(saveto, "wb")
dst.write(data)
finally:
if src:
src.close()
if dst:
dst.close()
return os.path.realpath(saveto)
def _no_sandbox(function):
def __no_sandbox(*args, **kw):
try:
from setuptools.sandbox import DirectorySandbox
if not hasattr(DirectorySandbox, '_old'):
def violation(*args):
pass
DirectorySandbox._old = DirectorySandbox._violation
DirectorySandbox._violation = violation
patched = True
else:
patched = False
except ImportError:
patched = False
try:
return function(*args, **kw)
finally:
if patched:
DirectorySandbox._violation = DirectorySandbox._old
del DirectorySandbox._old
return __no_sandbox
def _patch_file(path, content):
"""Will backup the file then patch it"""
existing_content = open(path).read()
if existing_content == content:
# already patched
log.warn('Already patched.')
return False
log.warn('Patching...')
_rename_path(path)
f = open(path, 'w')
try:
f.write(content)
finally:
f.close()
return True
_patch_file = _no_sandbox(_patch_file)
def _same_content(path, content):
return open(path).read() == content
def _rename_path(path):
new_name = path + '.OLD.%s' % time.time()
log.warn('Renaming %s into %s', path, new_name)
os.rename(path, new_name)
return new_name
def _remove_flat_installation(placeholder):
if not os.path.isdir(placeholder):
log.warn('Unkown installation at %s', placeholder)
return False
found = False
for file in os.listdir(placeholder):
if fnmatch.fnmatch(file, 'setuptools*.egg-info'):
found = True
break
if not found:
log.warn('Could not locate setuptools*.egg-info')
return
log.warn('Removing elements out of the way...')
pkg_info = os.path.join(placeholder, file)
if os.path.isdir(pkg_info):
patched = _patch_egg_dir(pkg_info)
else:
patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO)
if not patched:
log.warn('%s already patched.', pkg_info)
return False
# now let's move the files out of the way
for element in ('setuptools', 'pkg_resources.py', 'site.py'):
element = os.path.join(placeholder, element)
if os.path.exists(element):
_rename_path(element)
else:
log.warn('Could not find the %s element of the '
'Setuptools distribution', element)
return True
_remove_flat_installation = _no_sandbox(_remove_flat_installation)
def _after_install(dist):
log.warn('After install bootstrap.')
placeholder = dist.get_command_obj('install').install_purelib
_create_fake_setuptools_pkg_info(placeholder)
def _create_fake_setuptools_pkg_info(placeholder):
if not placeholder or not os.path.exists(placeholder):
log.warn('Could not find the install location')
return
pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
setuptools_file = 'setuptools-%s-py%s.egg-info' % \
(SETUPTOOLS_FAKED_VERSION, pyver)
pkg_info = os.path.join(placeholder, setuptools_file)
if os.path.exists(pkg_info):
log.warn('%s already exists', pkg_info)
return
if not os.access(pkg_info, os.W_OK):
log.warn("Don't have permissions to write %s, skipping", pkg_info)
log.warn('Creating %s', pkg_info)
f = open(pkg_info, 'w')
try:
f.write(SETUPTOOLS_PKG_INFO)
finally:
f.close()
pth_file = os.path.join(placeholder, 'setuptools.pth')
log.warn('Creating %s', pth_file)
f = open(pth_file, 'w')
try:
f.write(os.path.join(os.curdir, setuptools_file))
finally:
f.close()
_create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info)
def _patch_egg_dir(path):
# let's check if it's already patched
pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
if os.path.exists(pkg_info):
if _same_content(pkg_info, SETUPTOOLS_PKG_INFO):
log.warn('%s already patched.', pkg_info)
return False
_rename_path(path)
os.mkdir(path)
os.mkdir(os.path.join(path, 'EGG-INFO'))
pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
f = open(pkg_info, 'w')
try:
f.write(SETUPTOOLS_PKG_INFO)
finally:
f.close()
return True
_patch_egg_dir = _no_sandbox(_patch_egg_dir)
def _before_install():
log.warn('Before install bootstrap.')
_fake_setuptools()
def _under_prefix(location):
if 'install' not in sys.argv:
return True
args = sys.argv[sys.argv.index('install')+1:]
for index, arg in enumerate(args):
for option in ('--root', '--prefix'):
if arg.startswith('%s=' % option):
top_dir = arg.split('root=')[-1]
return location.startswith(top_dir)
elif arg == option:
if len(args) > index:
top_dir = args[index+1]
return location.startswith(top_dir)
if arg == '--user' and USER_SITE is not None:
return location.startswith(USER_SITE)
return True
def _fake_setuptools():
log.warn('Scanning installed packages')
try:
import pkg_resources
except ImportError:
# we're cool
log.warn('Setuptools or Distribute does not seem to be installed.')
return
ws = pkg_resources.working_set
try:
setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools',
replacement=False))
except TypeError:
# old distribute API
setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools'))
if setuptools_dist is None:
log.warn('No setuptools distribution found')
return
# detecting if it was already faked
setuptools_location = setuptools_dist.location
log.warn('Setuptools installation detected at %s', setuptools_location)
# if --root or --preix was provided, and if
# setuptools is not located in them, we don't patch it
if not _under_prefix(setuptools_location):
log.warn('Not patching, --root or --prefix is installing Distribute'
' in another location')
return
# let's see if its an egg
if not setuptools_location.endswith('.egg'):
log.warn('Non-egg installation')
res = _remove_flat_installation(setuptools_location)
if not res:
return
else:
log.warn('Egg installation')
pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO')
if (os.path.exists(pkg_info) and
_same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
log.warn('Already patched.')
return
log.warn('Patching...')
# let's create a fake egg replacing setuptools one
res = _patch_egg_dir(setuptools_location)
if not res:
return
log.warn('Patched done.')
_relaunch()
def _relaunch():
log.warn('Relaunching...')
# we have to relaunch the process
# pip marker to avoid a relaunch bug
if sys.argv[:3] == ['-c', 'install', '--single-version-externally-managed']:
sys.argv[0] = 'setup.py'
args = [sys.executable] + sys.argv
sys.exit(subprocess.call(args))
def _extractall(self, path=".", members=None):
"""Extract all members from the archive to the current working
directory and set owner, modification time and permissions on
directories afterwards. `path' specifies a different directory
to extract to. `members' is optional and must be a subset of the
list returned by getmembers().
"""
import copy
import operator
from tarfile import ExtractError
directories = []
if members is None:
members = self
for tarinfo in members:
if tarinfo.isdir():
# Extract directories with a safe mode.
directories.append(tarinfo)
tarinfo = copy.copy(tarinfo)
tarinfo.mode = 448 # decimal for oct 0700
self.extract(tarinfo, path)
# Reverse sort directories.
if sys.version_info < (2, 4):
def sorter(dir1, dir2):
return cmp(dir1.name, dir2.name)
directories.sort(sorter)
directories.reverse()
else:
directories.sort(key=operator.attrgetter('name'), reverse=True)
# Set correct owner, mtime and filemode on directories.
for tarinfo in directories:
dirpath = os.path.join(path, tarinfo.name)
try:
self.chown(tarinfo, dirpath)
self.utime(tarinfo, dirpath)
self.chmod(tarinfo, dirpath)
except ExtractError:
e = sys.exc_info()[1]
if self.errorlevel > 1:
raise
else:
self._dbg(1, "tarfile: %s" % e)
def _build_install_args(argv):
install_args = []
user_install = '--user' in argv
if user_install and sys.version_info < (2,6):
log.warn("--user requires Python 2.6 or later")
raise SystemExit(1)
if user_install:
install_args.append('--user')
return install_args
def main(argv, version=DEFAULT_VERSION):
"""Install or upgrade setuptools and EasyInstall"""
tarball = download_setuptools()
_install(tarball, _build_install_args(argv))
if __name__ == '__main__':
main(sys.argv[1:])

View File

@@ -31,6 +31,8 @@ help:
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@@ -142,3 +144,18 @@ doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
texinfo:
mkdir -p $(BUILDDIR)/texinfo
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
mkdir -p $(BUILDDIR)/texinfo
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."

View File

@@ -1,339 +0,0 @@
/*
* sphinxdoc.css_t
* ~~~~~~~~~~~~~~~
*
* Sphinx stylesheet -- sphinxdoc theme. Originally created by
* Armin Ronacher for Werkzeug.
*
* :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
body {
font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
'Verdana', sans-serif;
font-size: 1.1em;
letter-spacing: -0.01em;
line-height: 150%;
text-align: center;
background-color: #BFD1D4;
color: black;
padding: 0;
border: 1px solid #aaa;
margin: 0px 80px 0px 80px;
min-width: 740px;
}
div.document {
background-color: white;
text-align: left;
background-image: url(contents.png);
background-repeat: repeat-x;
}
div.bodywrapper {
margin: 0 240px 0 0;
border-right: 1px solid #ccc;
}
div.body {
margin: 0;
padding: 0.5em 20px 20px 20px;
}
div.related {
font-size: 0.8em;
}
div.related ul {
background-image: url(navigation.png);
height: 2em;
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
}
div.related ul li {
margin: 0;
padding: 0;
height: 2em;
float: left;
}
div.related ul li.right {
float: right;
margin-right: 5px;
}
div.related ul li a {
margin: 0;
padding: 0 5px 0 5px;
line-height: 1.75em;
color: #EE9816;
}
div.related ul li a:hover {
color: #3CA8E7;
}
div.sphinxsidebarwrapper {
padding: 0;
}
div.sphinxsidebar {
margin: 0;
padding: 0.5em 15px 15px 0;
width: 210px;
float: right;
font-size: 1em;
text-align: left;
}
div.sphinxsidebar h3, div.sphinxsidebar h4 {
margin: 1em 0 0.5em 0;
font-size: 1em;
padding: 0.1em 0 0.1em 0.5em;
color: white;
border: 1px solid #86989B;
background-color: #AFC1C4;
}
div.sphinxsidebar h3 a {
color: white;
}
div.sphinxsidebar ul {
padding-left: 1.5em;
margin-top: 7px;
padding: 0;
line-height: 130%;
}
div.sphinxsidebar ul ul {
margin-left: 20px;
}
div.footer {
background-color: #E3EFF1;
color: #86989B;
padding: 3px 8px 3px 0;
clear: both;
font-size: 0.8em;
text-align: right;
}
div.footer a {
color: #86989B;
text-decoration: underline;
}
/* -- body styles ----------------------------------------------------------- */
p {
margin: 0.8em 0 0.5em 0;
}
a {
color: #CA7900;
text-decoration: none;
}
a:hover {
color: #2491CF;
}
div.body a {
text-decoration: underline;
}
h1 {
margin: 0;
padding: 0.7em 0 0.3em 0;
font-size: 1.5em;
color: #11557C;
}
h2 {
margin: 1.3em 0 0.2em 0;
font-size: 1.35em;
padding: 0;
}
h3 {
margin: 1em 0 -0.3em 0;
font-size: 1.2em;
}
div.body h1 a, div.body h2 a, div.body h3 a, div.body h4 a, div.body h5 a, div.body h6 a {
color: black!important;
}
h1 a.anchor, h2 a.anchor, h3 a.anchor, h4 a.anchor, h5 a.anchor, h6 a.anchor {
display: none;
margin: 0 0 0 0.3em;
padding: 0 0.2em 0 0.2em;
color: #aaa!important;
}
h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor,
h5:hover a.anchor, h6:hover a.anchor {
display: inline;
}
h1 a.anchor:hover, h2 a.anchor:hover, h3 a.anchor:hover, h4 a.anchor:hover,
h5 a.anchor:hover, h6 a.anchor:hover {
color: #777;
background-color: #eee;
}
a.headerlink {
color: #c60f0f!important;
font-size: 1em;
margin-left: 6px;
padding: 0 4px 0 4px;
text-decoration: none!important;
}
a.headerlink:hover {
background-color: #ccc;
color: white!important;
}
cite, code, tt {
font-family: 'Consolas', 'Deja Vu Sans Mono',
'Bitstream Vera Sans Mono', monospace;
font-size: 0.95em;
letter-spacing: 0.01em;
}
tt {
background-color: #f2f2f2;
border-bottom: 1px solid #ddd;
color: #333;
}
tt.descname, tt.descclassname, tt.xref {
border: 0;
}
hr {
border: 1px solid #abc;
margin: 2em;
}
a tt {
border: 0;
color: #CA7900;
}
a tt:hover {
color: #2491CF;
}
pre {
font-family: 'Consolas', 'Deja Vu Sans Mono',
'Bitstream Vera Sans Mono', monospace;
font-size: 0.95em;
letter-spacing: 0.015em;
line-height: 120%;
padding: 0.5em;
border: 1px solid #ccc;
background-color: #f8f8f8;
}
pre a {
color: inherit;
text-decoration: underline;
}
td.linenos pre {
padding: 0.5em 0;
}
div.quotebar {
background-color: #f8f8f8;
max-width: 250px;
float: right;
padding: 2px 7px;
border: 1px solid #ccc;
}
div.topic {
background-color: #f8f8f8;
}
table {
border-collapse: collapse;
margin: 0 -0.5em 0 -0.5em;
}
table td, table th {
padding: 0.2em 0.5em 0.2em 0.5em;
}
div.admonition, div.warning {
font-size: 0.9em;
margin: 1em 0 1em 0;
border: 1px solid #86989B;
background-color: #f7f7f7;
padding: 0;
}
div.admonition p, div.warning p {
margin: 0.5em 1em 0.5em 1em;
padding: 0;
}
div.admonition pre, div.warning pre {
margin: 0.4em 1em 0.4em 1em;
}
div.admonition p.admonition-title,
div.warning p.admonition-title {
margin: 0;
padding: 0.1em 0 0.1em 0.5em;
color: white;
border-bottom: 1px solid #86989B;
font-weight: bold;
background-color: #AFC1C4;
}
div.warning {
border: 1px solid #940000;
}
div.warning p.admonition-title {
background-color: #CF0000;
border-bottom-color: #940000;
}
div.admonition ul, div.admonition ol,
div.warning ul, div.warning ol {
margin: 0.1em 0.5em 0.5em 3em;
padding: 0;
}
div.versioninfo {
margin: 1em 0 0 0;
border: 1px solid #ccc;
background-color: #DDEAF0;
padding: 8px;
line-height: 1.3em;
font-size: 0.9em;
}
.viewcode-back {
font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
'Verdana', sans-serif;
}
div.viewcode-block:target {
background-color: #f4debf;
border-top: 1px solid #ac9;
border-bottom: 1px solid #ac9;
}

View File

@@ -0,0 +1,17 @@
<h3><a href="{{ pathto(master_doc) }}">{{ _('Table Of Contents') }}</a></h3>
<ul>
<li><a href="{{ pathto('index') }}">Home</a></li>
<li><a href="{{ pathto('contents') }}">Contents</a></li>
<li><a href="{{ pathto('getting-started') }}">Install</a></li>
<li><a href="{{ pathto('example/index') }}">Examples</a></li>
<li><a href="{{ pathto('customize') }}">Customize</a></li>
<li><a href="{{ pathto('contact') }}">Contact</a></li>
<li><a href="{{ pathto('talks') }}">Talks/Posts</a></li>
<li><a href="{{ pathto('changelog') }}">Changelog</a></li>
</ul>
{%- if display_toc %}
<hr>
{{ toc }}
{%- endif %}

View File

@@ -1,22 +0,0 @@
<h3>Download</h3>
{% if version.endswith('(hg)') %}
<p>This documentation is for version <b>{{ version }}</b>, which is
not released yet.</p>
<p>You can use it from the
<a href="http://bitbucket.org/hpk42/pytest">Bitbucket Repo</a> or look for
released versions in the <a href="http://pypi.python.org/pypi/pytest">Python
Package Index</a>.</p>
{% else %}
<p><b><a href="{{ pathto('announce/index')}}">{{ release }} release</a></b>
[<a href="{{ pathto('changelog') }}">Changelog</a>]</p>
<p>
<a href="http://pypi.python.org/pypi/pytest">pytest/PyPI</a>
</p>
<pre>easy_install pytest</pre>
<pre>pip install pytest</pre>
{% endif %}
<h3>Questions? Suggestions?</h3>
<p><a href="{{ pathto('contact') }}">contact channels</a>
</p>

View File

@@ -1,10 +1,5 @@
{% extends "!layout.html" %}
{% block relbaritems %}
{{ super() }}
<g:plusone></g:plusone>
{% endblock %}
{% block footer %}
{{ super() }}
<script type="text/javascript">

View File

@@ -0,0 +1,11 @@
<h3>Useful Links</h3>
<ul>
<li><a href="{{ pathto('index') }}">The pytest Website</a></li>
<li><a href="https://pypi.python.org/pypi/pytest">pytest @ PyPI</a></li>
<li><a href="https://bitbucket.org/hpk42/pytest/">pytest @ Bitbucket</a></li>
<li><a href="https://github.com/hpk42/pytest/">pytest @ github</a></li>
<li><a href="https://bitbucket.org/hpk42/pytest/issues?status=new&status=open">Issue Tracker</a></li>
<li><a href="http://pytest.org/latest/pytest.pdf">PDF Documentation</a>
</ul>
<g:plusone></g:plusone>

View File

@@ -1,37 +0,0 @@
{%- if pagename != "search" %}
<div id="searchbox" style="display: none">
<form class="search" action="{{ pathto('search') }}" method="get">
<input type="text" name="q" size="18" />
<input type="submit" value="{{ _('Search') }}" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
{%- endif %}
<h3>quicklinks</h3>
<div style="text-align: left; font-size: 100%; vertical-align: middle;">
<table>
<tr>
<td>
<a href="{{ pathto('index') }}">home</a>
</td><td>
<a href="{{ pathto('contents') }}">TOC/contents</a>
</td></tr><tr><td>
<a href="{{ pathto('getting-started') }}">install</a>
</td><td>
<a href="{{ pathto('changelog') }}">changelog</a>
</td></tr><tr><td>
<a href="{{ pathto('example/index') }}">examples</a>
</td><td>
<a href="{{ pathto('customize') }}">customize</a>
</td></tr><tr><td>
<a href="https://bitbucket.org/hpk42/pytest/issues?status=new&status=open">issues[bb]</a>
</td><td>
<a href="{{ pathto('contact') }}">contact</a>
</td></tr></table>
</div>
{% extends "basic/localtoc.html" %}

View File

@@ -0,0 +1,5 @@
<h3>About pytest</h3>
<p>
pytest is a mature full-featured Python testing tool that helps
you write better programs.
</p>

3
doc/en/_themes/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
*.pyc
*.pyo
.DS_Store

37
doc/en/_themes/LICENSE Normal file
View File

@@ -0,0 +1,37 @@
Copyright (c) 2010 by Armin Ronacher.
Some rights reserved.
Redistribution and use in source and binary forms of the theme, with or
without modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* The names of the contributors may not be used to endorse or
promote products derived from this software without specific
prior written permission.
We kindly ask you to only use these themes in an unmodified manner just
for Flask and Flask-related products, not for unrelated projects. If you
like the visual style and want to use it for your own projects, please
consider making some larger changes to the themes (such as changing
font faces, sizes, colors or margins).
THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

31
doc/en/_themes/README Normal file
View File

@@ -0,0 +1,31 @@
Flask Sphinx Styles
===================
This repository contains sphinx styles for Flask and Flask related
projects. To use this style in your Sphinx documentation, follow
this guide:
1. put this folder as _themes into your docs folder. Alternatively
you can also use git submodules to check out the contents there.
2. add this to your conf.py:
sys.path.append(os.path.abspath('_themes'))
html_theme_path = ['_themes']
html_theme = 'flask'
The following themes exist:
- 'flask' - the standard flask documentation theme for large
projects
- 'flask_small' - small one-page theme. Intended to be used by
very small addon libraries for flask.
The following options exist for the flask_small theme:
[options]
index_logo = '' filename of a picture in _static
to be used as replacement for the
h1 in the index.rst file.
index_logo_height = 120px height of the index logo
github_fork = '' repository name on github for the
"fork me" badge

View File

@@ -0,0 +1,25 @@
{%- extends "basic/layout.html" %}
{%- block extrahead %}
{{ super() }}
{% if theme_touch_icon %}
<link rel="apple-touch-icon" href="{{ pathto('_static/' ~ theme_touch_icon, 1) }}" />
{% endif %}
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9">
<link href='http://fonts.googleapis.com/css?family=Gudea|Gudea' rel='stylesheet' type='text/css'>
{% endblock %}
{%- block relbar2 %}{% endblock %}
{% block header %}
{{ super() }}
{% if pagename == 'index' %}
<div class=indexwrapper>
{% endif %}
{% endblock %}
{%- block footer %}
<div class="footer">
&copy; Copyright {{ copyright }}.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
</div>
{% if pagename == 'index' %}
</div>
{% endif %}
{%- endblock %}

View File

@@ -0,0 +1,19 @@
<h3>Related Topics</h3>
<ul>
<li><a href="{{ pathto(master_doc) }}">Documentation overview</a><ul>
{%- for parent in parents %}
<li><a href="{{ parent.link|e }}">{{ parent.title }}</a><ul>
{%- endfor %}
{%- if prev %}
<li>Previous: <a href="{{ prev.link|e }}" title="{{ _('previous chapter')
}}">{{ prev.title }}</a></li>
{%- endif %}
{%- if next %}
<li>Next: <a href="{{ next.link|e }}" title="{{ _('next chapter')
}}">{{ next.title }}</a></li>
{%- endif %}
{%- for parent in parents %}
</ul></li>
{%- endfor %}
</ul></li>
</ul>

View File

@@ -0,0 +1,555 @@
/*
* flasky.css_t
* ~~~~~~~~~~~~
*
* :copyright: Copyright 2010 by Armin Ronacher.
* :license: Flask Design License, see LICENSE for details.
*/
{% set page_width = '940px' %}
{% set sidebar_width = '220px' %}
{% set base_font = '"Gudea", sans-serif' %}
{% set header_font = '"Gudea", sans-serif' %}
{% set link_color = '#490' %}
{% set link_hover_color = '#9c0' %}
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
body {
font-family: {{ base_font }};
font-size: 17px;
background-color: white;
color: #000;
margin: 0;
padding: 0;
}
div.document {
width: {{ page_width }};
margin: 30px auto 0 auto;
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 0 0 0 {{ sidebar_width }};
}
div.sphinxsidebar {
width: {{ sidebar_width }};
}
hr {
border: 0;
border-top: 1px solid #B1B4B6;
}
div.body {
background-color: #ffffff;
color: #3E4349;
padding: 0 30px 0 30px;
}
img.floatingflask {
padding: 0 0 10px 10px;
float: right;
}
div.footer {
width: {{ page_width }};
margin: 20px auto 30px auto;
font-size: 14px;
color: #888;
text-align: right;
}
div.footer a {
color: #888;
}
div.related {
display: none;
}
div.sphinxsidebar a {
color: #444;
text-decoration: none;
border-bottom: 1px dotted #999;
}
div.sphinxsidebar a:hover {
border-bottom: 1px solid #999;
}
div.sphinxsidebar {
font-size: 14px;
line-height: 1.5;
}
div.sphinxsidebarwrapper {
padding: 18px 10px;
}
div.sphinxsidebarwrapper p.logo {
padding: 0 0 20px 0;
margin: 0;
text-align: center;
}
div.sphinxsidebar h3,
div.sphinxsidebar h4 {
font-family: {{ header_font }};
color: #444;
font-size: 24px;
font-weight: normal;
margin: 0 0 5px 0;
padding: 0;
}
div.sphinxsidebar h4 {
font-size: 20px;
}
div.sphinxsidebar h3 a {
color: #444;
}
div.sphinxsidebar p.logo a,
div.sphinxsidebar h3 a,
div.sphinxsidebar p.logo a:hover,
div.sphinxsidebar h3 a:hover {
border: none;
}
div.sphinxsidebar p {
color: #555;
margin: 10px 0;
}
div.sphinxsidebar ul {
margin: 10px 0;
padding: 0;
color: #000;
}
div.sphinxsidebar input {
border: 1px solid #ccc;
font-family: {{ base_font }};
font-size: 1em;
}
/* -- body styles ----------------------------------------------------------- */
a {
color: {{ link_color }};
text-decoration: underline;
}
a:hover {
color: {{ link_hover_color }};
text-decoration: underline;
}
a.reference.internal em {
font-style: normal;
}
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 {
font-family: {{ header_font }};
font-weight: normal;
margin: 30px 0px 10px 0px;
padding: 0;
}
{% if theme_index_logo %}
div.indexwrapper h1 {
text-indent: -999999px;
background: url({{ theme_index_logo }}) no-repeat center center;
height: {{ theme_index_logo_height }};
}
{% else %}
div.indexwrapper div.body h1 {
font-size: 200%;
}
{% endif %}
div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
div.body h2 { font-size: 180%; }
div.body h3 { font-size: 150%; }
div.body h4 { font-size: 130%; }
div.body h5 { font-size: 100%; }
div.body h6 { font-size: 100%; }
a.headerlink {
color: #ddd;
padding: 0 4px;
text-decoration: none;
}
a.headerlink:hover {
color: #444;
background: #eaeaea;
}
div.body p, div.body dd, div.body li {
line-height: 1.4em;
}
div.admonition {
background: #fafafa;
margin: 20px -30px;
padding: 10px 30px;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
div.admonition tt.xref, div.admonition a tt {
border-bottom: 1px solid #fafafa;
}
dd div.admonition {
margin-left: -60px;
padding-left: 60px;
}
div.admonition p.admonition-title {
font-family: {{ header_font }};
font-weight: normal;
font-size: 24px;
margin: 0 0 10px 0;
padding: 0;
line-height: 1;
}
div.admonition p.last {
margin-bottom: 0;
}
div.highlight {
background-color: white;
}
dt:target, .highlight {
background: #FAF3E8;
}
div.note {
background-color: #eee;
border: 1px solid #ccc;
}
div.seealso {
background-color: #ffc;
border: 1px solid #ff6;
}
div.topic {
background-color: #eee;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre, tt {
font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
font-size: 0.9em;
}
img.screenshot {
}
tt.descname, tt.descclassname {
font-size: 0.95em;
}
tt.descname {
padding-right: 0.08em;
}
img.screenshot {
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils {
border: 1px solid #888;
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils td, table.docutils th {
border: 1px solid #888;
padding: 0.25em 0.7em;
}
table.field-list, table.footnote {
border: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
table.footnote {
margin: 15px 0;
width: 100%;
border: 1px solid #eee;
background: #fdfdfd;
font-size: 0.9em;
}
table.footnote + table.footnote {
margin-top: -15px;
border-top: none;
}
table.field-list th {
padding: 0 0.8em 0 0;
}
table.field-list td {
padding: 0;
}
table.footnote td.label {
width: 0px;
padding: 0.3em 0 0.3em 0.5em;
}
table.footnote td {
padding: 0.3em 0.5em;
}
dl {
margin: 0;
padding: 0;
}
dl dd {
margin-left: 30px;
}
blockquote {
margin: 0 0 0 30px;
padding: 0;
}
ul, ol {
margin: 10px 0 10px 30px;
padding: 0;
}
pre {
background: #eee;
padding: 7px 30px;
margin: 15px -30px;
line-height: 1.3em;
}
dl pre, blockquote pre, li pre {
margin-left: -60px;
padding-left: 60px;
}
dl dl pre {
margin-left: -90px;
padding-left: 90px;
}
tt {
background-color: #ecf0f3;
color: #222;
/* padding: 1px 2px; */
}
tt.xref, a tt {
background-color: #FBFBFB;
border-bottom: 1px solid white;
}
a.reference {
text-decoration: none;
border-bottom: 1px dotted {{ link_color }};
}
a.reference:hover {
border-bottom: 1px solid {{ link_hover_color }};
}
a.footnote-reference {
text-decoration: none;
font-size: 0.7em;
vertical-align: top;
border-bottom: 1px dotted {{ link_color }};
}
a.footnote-reference:hover {
border-bottom: 1px solid {{ link_hover_color }};
}
a:hover tt {
background: #EEE;
}
@media screen and (max-width: 870px) {
div.sphinxsidebar {
display: none;
}
div.document {
width: 100%;
}
div.documentwrapper {
margin-left: 0;
margin-top: 0;
margin-right: 0;
margin-bottom: 0;
}
div.bodywrapper {
margin-top: 0;
margin-right: 0;
margin-bottom: 0;
margin-left: 0;
}
ul {
margin-left: 0;
}
.document {
width: auto;
}
.footer {
width: auto;
}
.bodywrapper {
margin: 0;
}
.footer {
width: auto;
}
.github {
display: none;
}
}
@media screen and (max-width: 875px) {
body {
margin: 0;
padding: 20px 30px;
}
div.documentwrapper {
float: none;
background: white;
}
div.sphinxsidebar {
display: block;
float: none;
width: 102.5%;
margin: 50px -30px -20px -30px;
padding: 10px 20px;
background: #333;
color: white;
}
div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,
div.sphinxsidebar h3 a, div.sphinxsidebar ul {
color: white;
}
div.sphinxsidebar a {
color: #aaa;
}
div.sphinxsidebar p.logo {
display: none;
}
div.document {
width: 100%;
margin: 0;
}
div.related {
display: block;
margin: 0;
padding: 10px 0 20px 0;
}
div.related ul,
div.related ul li {
margin: 0;
padding: 0;
}
div.footer {
display: none;
}
div.bodywrapper {
margin: 0;
}
div.body {
min-height: 0;
padding: 0;
}
.rtd_doc_footer {
display: none;
}
.document {
width: auto;
}
.footer {
width: auto;
}
.footer {
width: auto;
}
.github {
display: none;
}
}
/* misc. */
.revsys-inline {
display: none!important;
}

View File

@@ -0,0 +1,9 @@
[theme]
inherit = basic
stylesheet = flasky.css
pygments_style = flask_theme_support.FlaskyStyle
[options]
index_logo = ''
index_logo_height = 120px
touch_icon =

View File

@@ -0,0 +1,86 @@
# flasky extensions. flasky pygments style based on tango style
from pygments.style import Style
from pygments.token import Keyword, Name, Comment, String, Error, \
Number, Operator, Generic, Whitespace, Punctuation, Other, Literal
class FlaskyStyle(Style):
background_color = "#f8f8f8"
default_style = ""
styles = {
# No corresponding class for the following:
#Text: "", # class: ''
Whitespace: "underline #f8f8f8", # class: 'w'
Error: "#a40000 border:#ef2929", # class: 'err'
Other: "#000000", # class 'x'
Comment: "italic #8f5902", # class: 'c'
Comment.Preproc: "noitalic", # class: 'cp'
Keyword: "bold #004461", # class: 'k'
Keyword.Constant: "bold #004461", # class: 'kc'
Keyword.Declaration: "bold #004461", # class: 'kd'
Keyword.Namespace: "bold #004461", # class: 'kn'
Keyword.Pseudo: "bold #004461", # class: 'kp'
Keyword.Reserved: "bold #004461", # class: 'kr'
Keyword.Type: "bold #004461", # class: 'kt'
Operator: "#582800", # class: 'o'
Operator.Word: "bold #004461", # class: 'ow' - like keywords
Punctuation: "bold #000000", # class: 'p'
# because special names such as Name.Class, Name.Function, etc.
# are not recognized as such later in the parsing, we choose them
# to look the same as ordinary variables.
Name: "#000000", # class: 'n'
Name.Attribute: "#c4a000", # class: 'na' - to be revised
Name.Builtin: "#004461", # class: 'nb'
Name.Builtin.Pseudo: "#3465a4", # class: 'bp'
Name.Class: "#000000", # class: 'nc' - to be revised
Name.Constant: "#000000", # class: 'no' - to be revised
Name.Decorator: "#888", # class: 'nd' - to be revised
Name.Entity: "#ce5c00", # class: 'ni'
Name.Exception: "bold #cc0000", # class: 'ne'
Name.Function: "#000000", # class: 'nf'
Name.Property: "#000000", # class: 'py'
Name.Label: "#f57900", # class: 'nl'
Name.Namespace: "#000000", # class: 'nn' - to be revised
Name.Other: "#000000", # class: 'nx'
Name.Tag: "bold #004461", # class: 'nt' - like a keyword
Name.Variable: "#000000", # class: 'nv' - to be revised
Name.Variable.Class: "#000000", # class: 'vc' - to be revised
Name.Variable.Global: "#000000", # class: 'vg' - to be revised
Name.Variable.Instance: "#000000", # class: 'vi' - to be revised
Number: "#990000", # class: 'm'
Literal: "#000000", # class: 'l'
Literal.Date: "#000000", # class: 'ld'
String: "#4e9a06", # class: 's'
String.Backtick: "#4e9a06", # class: 'sb'
String.Char: "#4e9a06", # class: 'sc'
String.Doc: "italic #8f5902", # class: 'sd' - like a comment
String.Double: "#4e9a06", # class: 's2'
String.Escape: "#4e9a06", # class: 'se'
String.Heredoc: "#4e9a06", # class: 'sh'
String.Interpol: "#4e9a06", # class: 'si'
String.Other: "#4e9a06", # class: 'sx'
String.Regex: "#4e9a06", # class: 'sr'
String.Single: "#4e9a06", # class: 's1'
String.Symbol: "#4e9a06", # class: 'ss'
Generic: "#000000", # class: 'g'
Generic.Deleted: "#a40000", # class: 'gd'
Generic.Emph: "italic #000000", # class: 'ge'
Generic.Error: "#ef2929", # class: 'gr'
Generic.Heading: "bold #000080", # class: 'gh'
Generic.Inserted: "#00A000", # class: 'gi'
Generic.Output: "#888", # class: 'go'
Generic.Prompt: "#745334", # class: 'gp'
Generic.Strong: "bold #000000", # class: 'gs'
Generic.Subheading: "bold #800080", # class: 'gu'
Generic.Traceback: "bold #a40000", # class: 'gt'
}

View File

@@ -5,6 +5,12 @@ Release announcements
.. toctree::
:maxdepth: 2
release-2.5.1
release-2.5.0
release-2.4.2
release-2.4.1
release-2.4.0
release-2.3.5
release-2.3.4
release-2.3.3
release-2.3.2

View File

@@ -0,0 +1,97 @@
pytest-2.3.5: bug fixes and little improvements
===========================================================================
pytest-2.3.5 is a maintenance release with many bug fixes and little
improvements. See the changelog below for details. No backward
compatibility issues are foreseen and all plugins which worked with the
prior version are expected to work unmodified. Speaking of which, a
few interesting new plugins saw the light last month:
- pytest-instafail: show failure information while tests are running
- pytest-qt: testing of GUI applications written with QT/Pyside
- pytest-xprocess: managing external processes across test runs
- pytest-random: randomize test ordering
And several others like pytest-django saw maintenance releases.
For a more complete list, check out
https://pypi.python.org/pypi?%3Aaction=search&term=pytest&submit=search.
For general information see:
http://pytest.org/
To install or upgrade pytest:
pip install -U pytest # or
easy_install -U pytest
Particular thanks to Floris, Ronny, Benjamin and the many bug reporters
and fix providers.
may the fixtures be with you,
holger krekel
Changes between 2.3.4 and 2.3.5
-----------------------------------
- never consider a fixture function for test function collection
- allow re-running of test items / helps to fix pytest-reruntests plugin
and also help to keep less fixture/resource references alive
- put captured stdout/stderr into junitxml output even for passing tests
(thanks Adam Goucher)
- Issue 265 - integrate nose setup/teardown with setupstate
so it doesnt try to teardown if it did not setup
- issue 271 - dont write junitxml on slave nodes
- Issue 274 - dont try to show full doctest example
when doctest does not know the example location
- issue 280 - disable assertion rewriting on buggy CPython 2.6.0
- inject "getfixture()" helper to retrieve fixtures from doctests,
thanks Andreas Zeidler
- issue 259 - when assertion rewriting, be consistent with the default
source encoding of ASCII on Python 2
- issue 251 - report a skip instead of ignoring classes with init
- issue250 unicode/str mixes in parametrization names and values now works
- issue257, assertion-triggered compilation of source ending in a
comment line doesn't blow up in python2.5 (fixed through py>=1.4.13.dev6)
- fix --genscript option to generate standalone scripts that also
work with python3.3 (importer ordering)
- issue171 - in assertion rewriting, show the repr of some
global variables
- fix option help for "-k"
- move long description of distribution into README.rst
- improve docstring for metafunc.parametrize()
- fix bug where using capsys with pytest.set_trace() in a test
function would break when looking at capsys.readouterr()
- allow to specify prefixes starting with "_" when
customizing python_functions test discovery. (thanks Graham Horler)
- improve PYTEST_DEBUG tracing output by puting
extra data on a new lines with additional indent
- ensure OutcomeExceptions like skip/fail have initialized exception attributes
- issue 260 - don't use nose special setup on plain unittest cases
- fix issue134 - print the collect errors that prevent running specified test items
- fix issue266 - accept unicode in MarkEvaluator expressions

View File

@@ -0,0 +1,225 @@
pytest-2.4.0: new fixture features/hooks and bug fixes
===========================================================================
The just released pytest-2.4.0 brings many improvements and numerous
bug fixes while remaining plugin- and test-suite compatible apart
from a few supposedly very minor incompatibilities. See below for
a full list of details. A few feature highlights:
- new yield-style fixtures `pytest.yield_fixture
<http://pytest.org/latest/yieldfixture.html>`_, allowing to use
existing with-style context managers in fixture functions.
- improved pdb support: ``import pdb ; pdb.set_trace()`` now works
without requiring prior disabling of stdout/stderr capturing.
Also the ``--pdb`` options works now on collection and internal errors
and we introduced a new experimental hook for IDEs/plugins to
intercept debugging: ``pytest_exception_interact(node, call, report)``.
- shorter monkeypatch variant to allow specifying an import path as
a target, for example: ``monkeypatch.setattr("requests.get", myfunc)``
- better unittest/nose compatibility: all teardown methods are now only
called if the corresponding setup method succeeded.
- integrate tab-completion on command line options if you
have `argcomplete <http://pypi.python.org/pypi/argcomplete>`_
configured.
- allow boolean expression directly with skipif/xfail
if a "reason" is also specified.
- a new hook ``pytest_load_initial_conftests`` allows plugins like
`pytest-django <http://pypi.python.org/pypi/pytest-django>`_ to
influence the environment before conftest files import ``django``.
- reporting: color the last line red or green depending if
failures/errors occured or everything passed.
The documentation has been updated to accomodate the changes,
see `http://pytest.org <http://pytest.org>`_
To install or upgrade pytest::
pip install -U pytest # or
easy_install -U pytest
**Many thanks to all who helped, including Floris Bruynooghe,
Brianna Laugher, Andreas Pelme, Anthon van der Neut, Anatoly Bubenkoff,
Vladimir Keleshev, Mathieu Agopian, Ronny Pfannschmidt, Christian
Theunert and many others.**
may passing tests be with you,
holger krekel
Changes between 2.3.5 and 2.4
-----------------------------------
known incompatibilities:
- if calling --genscript from python2.7 or above, you only get a
standalone script which works on python2.7 or above. Use Python2.6
to also get a python2.5 compatible version.
- all xunit-style teardown methods (nose-style, pytest-style,
unittest-style) will not be called if the corresponding setup method failed,
see issue322 below.
- the pytest_plugin_unregister hook wasn't ever properly called
and there is no known implementation of the hook - so it got removed.
- pytest.fixture-decorated functions cannot be generators (i.e. use
yield) anymore. This change might be reversed in 2.4.1 if it causes
unforeseen real-life issues. However, you can always write and return
an inner function/generator and change the fixture consumer to iterate
over the returned generator. This change was done in lieu of the new
``pytest.yield_fixture`` decorator, see below.
new features:
- experimentally introduce a new ``pytest.yield_fixture`` decorator
which accepts exactly the same parameters as pytest.fixture but
mandates a ``yield`` statement instead of a ``return statement`` from
fixture functions. This allows direct integration with "with-style"
context managers in fixture functions and generally avoids registering
of finalization callbacks in favour of treating the "after-yield" as
teardown code. Thanks Andreas Pelme, Vladimir Keleshev, Floris
Bruynooghe, Ronny Pfannschmidt and many others for discussions.
- allow boolean expression directly with skipif/xfail
if a "reason" is also specified. Rework skipping documentation
to recommend "condition as booleans" because it prevents surprises
when importing markers between modules. Specifying conditions
as strings will remain fully supported.
- reporting: color the last line red or green depending if
failures/errors occured or everything passed. thanks Christian
Theunert.
- make "import pdb ; pdb.set_trace()" work natively wrt capturing (no
"-s" needed anymore), making ``pytest.set_trace()`` a mere shortcut.
- fix issue181: --pdb now also works on collect errors (and
on internal errors) . This was implemented by a slight internal
refactoring and the introduction of a new hook
``pytest_exception_interact`` hook (see next item).
- fix issue341: introduce new experimental hook for IDEs/terminals to
intercept debugging: ``pytest_exception_interact(node, call, report)``.
- new monkeypatch.setattr() variant to provide a shorter
invocation for patching out classes/functions from modules:
monkeypatch.setattr("requests.get", myfunc)
will replace the "get" function of the "requests" module with ``myfunc``.
- fix issue322: tearDownClass is not run if setUpClass failed. Thanks
Mathieu Agopian for the initial fix. Also make all of pytest/nose
finalizer mimick the same generic behaviour: if a setupX exists and
fails, don't run teardownX. This internally introduces a new method
"node.addfinalizer()" helper which can only be called during the setup
phase of a node.
- simplify pytest.mark.parametrize() signature: allow to pass a
CSV-separated string to specify argnames. For example:
``pytest.mark.parametrize("input,expected", [(1,2), (2,3)])``
works as well as the previous:
``pytest.mark.parametrize(("input", "expected"), ...)``.
- add support for setUpModule/tearDownModule detection, thanks Brian Okken.
- integrate tab-completion on options through use of "argcomplete".
Thanks Anthon van der Neut for the PR.
- change option names to be hyphen-separated long options but keep the
old spelling backward compatible. py.test -h will only show the
hyphenated version, for example "--collect-only" but "--collectonly"
will remain valid as well (for backward-compat reasons). Many thanks to
Anthon van der Neut for the implementation and to Hynek Schlawack for
pushing us.
- fix issue 308 - allow to mark/xfail/skip individual parameter sets
when parametrizing. Thanks Brianna Laugher.
- call new experimental pytest_load_initial_conftests hook to allow
3rd party plugins to do something before a conftest is loaded.
Bug fixes:
- fix issue358 - capturing options are now parsed more properly
by using a new parser.parse_known_args method.
- pytest now uses argparse instead of optparse (thanks Anthon) which
means that "argparse" is added as a dependency if installing into python2.6
environments or below.
- fix issue333: fix a case of bad unittest/pytest hook interaction.
- PR27: correctly handle nose.SkipTest during collection. Thanks
Antonio Cuni, Ronny Pfannschmidt.
- fix issue355: junitxml puts name="pytest" attribute to testsuite tag.
- fix issue336: autouse fixture in plugins should work again.
- fix issue279: improve object comparisons on assertion failure
for standard datatypes and recognise collections.abc. Thanks to
Brianna Laugher and Mathieu Agopian.
- fix issue317: assertion rewriter support for the is_package method
- fix issue335: document py.code.ExceptionInfo() object returned
from pytest.raises(), thanks Mathieu Agopian.
- remove implicit distribute_setup support from setup.py.
- fix issue305: ignore any problems when writing pyc files.
- SO-17664702: call fixture finalizers even if the fixture function
partially failed (finalizers would not always be called before)
- fix issue320 - fix class scope for fixtures when mixed with
module-level functions. Thanks Anatloy Bubenkoff.
- you can specify "-q" or "-qq" to get different levels of "quieter"
reporting (thanks Katarzyna Jachim)
- fix issue300 - Fix order of conftest loading when starting py.test
in a subdirectory.
- fix issue323 - sorting of many module-scoped arg parametrizations
- make sessionfinish hooks execute with the same cwd-context as at
session start (helps fix plugin behaviour which write output files
with relative path such as pytest-cov)
- fix issue316 - properly reference collection hooks in docs
- fix issue 306 - cleanup of -k/-m options to only match markers/test
names/keywords respectively. Thanks Wouter van Ackooy.
- improved doctest counting for doctests in python modules --
files without any doctest items will not show up anymore
and doctest examples are counted as separate test items.
thanks Danilo Bellini.
- fix issue245 by depending on the released py-1.4.14
which fixes py.io.dupfile to work with files with no
mode. Thanks Jason R. Coombs.
- fix junitxml generation when test output contains control characters,
addressing issue267, thanks Jaap Broekhuizen
- fix issue338: honor --tb style for setup/teardown errors as well. Thanks Maho.
- fix issue307 - use yaml.safe_load in example, thanks Mark Eichin.
- better parametrize error messages, thanks Brianna Laugher
- pytest_terminal_summary(terminalreporter) hooks can now use
".section(title)" and ".line(msg)" methods to print extra
information at the end of a test run.

View File

@@ -0,0 +1,25 @@
pytest-2.4.1: fixing three regressions compared to 2.3.5
===========================================================================
pytest-2.4.1 is a quick follow up release to fix three regressions
compared to 2.3.5 before they hit more people:
- When using parser.addoption() unicode arguments to the
"type" keyword should also be converted to the respective types.
thanks Floris Bruynooghe, @dnozay. (fixes issue360 and issue362)
- fix dotted filename completion when using argcomplete
thanks Anthon van der Neuth. (fixes issue361)
- fix regression when a 1-tuple ("arg",) is used for specifying
parametrization (the values of the parametrization were passed
nested in a tuple). Thanks Donald Stufft.
- also merge doc typo fixes, thanks Andy Dirnberger
as usual, docs at http://pytest.org and upgrades via::
pip install -U pytest
have fun,
holger krekel

View File

@@ -0,0 +1,39 @@
pytest-2.4.2: colorama on windows, plugin/tmpdir fixes
===========================================================================
pytest-2.4.2 is another bug-fixing release:
- 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)
thanks Paul Moore for bringing it up.
- fix "-k" matching of tests where "repr" and "attr" and other names would
cause wrong matches because of an internal implementation quirk
(don't ask) which is now properly implemented. fixes issue345.
- avoid tmpdir fixture to create too long filenames especially
when parametrization is used (issue354)
- fix pytest-pep8 and pytest-flakes / pytest interactions
(collection names in mark plugin was assuming an item always
has a function which is not true for those plugins etc.)
Thanks Andi Zeidler.
- introduce node.get_marker/node.add_marker API for plugins
like pytest-pep8 and pytest-flakes to avoid the messy
details of the node.keywords pseudo-dicts. Adapated
docs.
- remove attempt to "dup" stdout at startup as it's icky.
the normal capturing should catch enough possibilities
of tests messing up standard FDs.
- add pluginmanager.do_configure(config) as a link to
config.do_configure() for plugin-compatibility
as usual, docs at http://pytest.org and upgrades via::
pip install -U pytest
have fun,
holger krekel

View File

@@ -0,0 +1,175 @@
pytest-2.5.0: now down to ZERO reported bugs!
===========================================================================
pytest-2.5.0 is a big fixing release, the result of two community bug
fixing days plus numerous additional works from many people and
reporters. The release should be fully compatible to 2.4.2, existing
plugins and test suites. We aim at maintaining this level of ZERO reported
bugs because it's no fun if your testing tool has bugs, is it? Under a
condition, though: when submitting a bug report please provide
clear information about the circumstances and a simple example which
reproduces the problem.
The issue tracker is of course not empty now. We have many remaining
"enhacement" issues which we'll hopefully can tackle in 2014 with your
help.
For those who use older Python versions, please note that pytest is not
automatically tested on python2.5 due to virtualenv, setuptools and tox
not supporting it anymore. Manual verification shows that it mostly
works fine but it's not going to be part of the automated release
process and thus likely to break in the future.
As usual, current docs are at
http://pytest.org
and you can upgrade from pypi via::
pip install -U pytest
Particular thanks for helping with this release go to Anatoly Bubenkoff,
Floris Bruynooghe, Marc Abramowitz, Ralph Schmitt, Ronny Pfannschmidt,
Donald Stufft, James Lan, Rob Dennis, Jason R. Coombs, Mathieu Agopian,
Virgil Dupras, Bruno Oliveira, Alex Gaynor and others.
have fun,
holger krekel
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
with this release we believe).
- simplified and fixed implementation for calling finalizers when
parametrized fixtures or function arguments are involved. finalization
is now performed lazily at setup time instead of in the "teardown phase".
While this might sound odd at first, it helps to ensure that we are
correctly handling setup/teardown even in complex code. User-level code
should not be affected unless it's implementing the pytest_runtest_teardown
hook and expecting certain fixture instances are torn down within (very
unlikely and would have been unreliable anyway).
- PR90: add --color=yes|no|auto option to force terminal coloring
mode ("auto" is default). Thanks Marc Abramowitz.
- fix issue319 - correctly show unicode in assertion errors. Many
thanks to Floris Bruynooghe for the complete PR. Also means
we depend on py>=1.4.19 now.
- fix issue396 - correctly sort and finalize class-scoped parametrized
tests independently from number of methods on the class.
- refix issue323 in a better way -- parametrization should now never
cause Runtime Recursion errors because the underlying algorithm
for re-ordering tests per-scope/per-fixture is not recursive
anymore (it was tail-call recursive before which could lead
to problems for more than >966 non-function scoped parameters).
- fix issue290 - there is preliminary support now for parametrizing
with repeated same values (sometimes useful to to test if calling
a second time works as with the first time).
- close issue240 - document precisely how pytest module importing
works, discuss the two common test directory layouts, and how it
interacts with PEP420-namespace packages.
- fix issue246 fix finalizer order to be LIFO on independent fixtures
depending on a parametrized higher-than-function scoped fixture.
(was quite some effort so please bear with the complexity of this sentence :)
Thanks Ralph Schmitt for the precise failure example.
- fix issue244 by implementing special index for parameters to only use
indices for paramentrized test ids
- fix issue287 by running all finalizers but saving the exception
from the first failing finalizer and re-raising it so teardown will
still have failed. We reraise the first failing exception because
it might be the cause for other finalizers to fail.
- fix ordering when mock.patch or other standard decorator-wrappings
are used with test methods. This fixues issue346 and should
help with random "xdist" collection failures. Thanks to
Ronny Pfannschmidt and Donald Stufft for helping to isolate it.
- fix issue357 - special case "-k" expressions to allow for
filtering with simple strings that are not valid python expressions.
Examples: "-k 1.3" matches all tests parametrized with 1.3.
"-k None" filters all tests that have "None" in their name
and conversely "-k 'not None'".
Previously these examples would raise syntax errors.
- fix issue384 by removing the trial support code
since the unittest compat enhancements allow
trial to handle it on its own
- don't hide an ImportError when importing a plugin produces one.
fixes issue375.
- fix issue275 - allow usefixtures and autouse fixtures
for running doctest text files.
- fix issue380 by making --resultlog only rely on longrepr instead
of the "reprcrash" attribute which only exists sometimes.
- address issue122: allow @pytest.fixture(params=iterator) by exploding
into a list early on.
- fix pexpect-3.0 compatibility for pytest's own tests.
(fixes issue386)
- allow nested parametrize-value markers, thanks James Lan for the PR.
- fix unicode handling with new monkeypatch.setattr(import_path, value)
API. Thanks Rob Dennis. Fixes issue371.
- fix unicode handling with junitxml, fixes issue368.
- In assertion rewriting mode on Python 2, fix the detection of coding
cookies. See issue #330.
- make "--runxfail" turn imperative pytest.xfail calls into no ops
(it already did neutralize pytest.mark.xfail markers)
- refine pytest / pkg_resources interactions: The AssertionRewritingHook
PEP302 compliant loader now registers itself with setuptools/pkg_resources
properly so that the pkg_resources.resource_stream method works properly.
Fixes issue366. Thanks for the investigations and full PR to Jason R. Coombs.
- pytestconfig fixture is now session-scoped as it is the same object during the
whole test run. Fixes issue370.
- avoid one surprising case of marker malfunction/confusion::
@pytest.mark.some(lambda arg: ...)
def test_function():
would not work correctly because pytest assumes @pytest.mark.some
gets a function to be decorated already. We now at least detect if this
arg is an lambda and thus the example will work. Thanks Alex Gaynor
for bringing it up.
- xfail a test on pypy that checks wrong encoding/ascii (pypy does
not error out). fixes issue385.
- internally make varnames() deal with classes's __init__,
although it's not needed by pytest itself atm. Also
fix caching. Fixes issue376.
- fix issue221 - handle importing of namespace-package with no
__init__.py properly.
- refactor internal FixtureRequest handling to avoid monkeypatching.
One of the positive user-facing effects is that the "request" object
can now be used in closures.
- fixed version comparison in pytest.importskip(modname, minverstring)
- fix issue377 by clarifying in the nose-compat docs that pytest
does not duplicate the unittest-API into the "plain" namespace.
- fix verbose reporting for @mock'd test functions

View File

@@ -0,0 +1,47 @@
pytest-2.5.1: fixes and new home page styling
===========================================================================
pytest is a mature Python testing tool with more than a 1000 tests
against itself, passing on many different interpreters and platforms.
The 2.5.1 release maintains the "zero-reported-bugs" promise by fixing
the three bugs reported since the last release a few days ago. It also
features a new home page styling implemented by Tobias Bieniek, based on
the flask theme from Armin Ronacher:
http://pytest.org
If you have anything more to improve styling and docs,
we'd be very happy to merge further pull requests.
On the coding side, the release also contains a little enhancement to
fixture decorators allowing to directly influence generation of test
ids, thanks to Floris Bruynooghe. Other thanks for helping with
this release go to Anatoly Bubenkoff and Ronny Pfannschmidt.
As usual, you can upgrade from pypi via::
pip install -U pytest
have fun and a nice remaining "bug-free" time of the year :)
holger krekel
2.5.1
-----------------------------------
- merge new documentation styling PR from Tobias Bieniek.
- fix issue403: allow parametrize of multiple same-name functions within
a collection node. Thanks Andreas Kloeckner and Alex Gaynor for reporting
and analysis.
- Allow parameterized fixtures to specify the ID of the parameters by
adding an ids argument to pytest.fixture() and pytest.yield_fixture().
Thanks Floris Bruynooghe.
- fix issue404 by always using the binary xml escape in the junitxml
plugin. Thanks Ronny Pfannschmidt.
- fix issue407: fix addoption docstring to point to argparse instead of
optparse. Thanks Daniel D. Wright.

View File

@@ -11,6 +11,7 @@ py.test reference documentation
customize.txt
assert.txt
fixture.txt
yieldfixture.txt
parametrize.txt
xunit_setup.txt
capture.txt

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 linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 1 items
test_assert1.py F
@@ -78,6 +78,12 @@ and if you need to have access to the actual exception info you may use::
# do checks related to excinfo.type, excinfo.value, excinfo.traceback
``excinfo`` is a `py.code.ExceptionInfo`_ instance, which is a wrapper around
the actual exception raised.
.. _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::
@@ -110,7 +116,7 @@ if you run this module::
$ py.test test_assert2.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 1 items
test_assert2.py F
@@ -185,6 +191,7 @@ the conftest file::
E vals: 1 != 2
test_foocompare.py:8: AssertionError
1 failed in 0.01 seconds
.. _assert-details:
.. _`assert introspection`:

View File

@@ -129,6 +129,7 @@ Let's run this module without output-capturing::
E NameError: global name 'globresource' is not defined
test_glob.py:5: NameError
2 failed in 0.01 seconds
The two tests see the same global ``globresource`` object.
@@ -177,6 +178,7 @@ And then re-run our test module::
E NameError: global name 'globresource' is not defined
test_glob.py:5: NameError
2 failed in 0.01 seconds
We are now running the two tests twice with two different global resource
instances. Note that the tests are ordered such that only

View File

@@ -0,0 +1,28 @@
.. _bash_completion:
Setting up bash completion
==========================
When using bash as your shell, ``py.test`` can use argcomplete
(https://argcomplete.readthedocs.org/) for auto-completion.
For this ``argcomplete`` needs to be installed **and** enabled.
Install argcomplete using::
sudo pip install 'argcomplete>=0.5.7'
For global activation of all argcomplete enabled python applications run::
sudo activate-global-python-argcomplete
For permanent (but not global) ``py.test`` activation, use::
register-python-argcomplete py.test >> ~/.bashrc
For one-time activation of argcomplete for ``py.test`` only, use::
eval "$(register-python-argcomplete py.test)"

View File

@@ -120,3 +120,4 @@ You can ask for available builtin or project-custom
path object.
in 0.00 seconds

View File

@@ -64,7 +64,7 @@ of the failing function and hide the other one::
$ py.test
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 2 items
test_module.py .F
@@ -78,7 +78,7 @@ of the failing function and hide the other one::
test_module.py:9: AssertionError
----------------------------- Captured stdout ------------------------------
setting up <function test_func2 at 0x1e12f50>
setting up <function test_func2 at 0x1eb37d0>
==================== 1 failed, 1 passed in 0.01 seconds ====================
Accessing captured output from a test function

View File

@@ -17,7 +17,8 @@
#
# The full version, including alpha/beta/rc tags.
# The short X.Y version.
version = release = "2.3.4"
version = "2.5.1"
release = "2.5.1"
import sys, os
@@ -104,14 +105,19 @@ pygments_style = 'sphinx'
# -- Options for HTML output ---------------------------------------------------
sys.path.append(os.path.abspath('_themes'))
html_theme_path = ['_themes']
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'sphinxdoc'
html_theme = 'flask'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
html_theme_options = {}
html_theme_options = {
'index_logo': None
}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
@@ -149,6 +155,23 @@ html_static_path = ['_static']
#html_sidebars = {}
#html_sidebars = {'index': 'indexsidebar.html'}
html_sidebars = {
'index': [
'sidebarintro.html',
'globaltoc.html',
'links.html',
'sourcelink.html',
'searchbox.html'
],
'**': [
'globaltoc.html',
'relations.html',
'links.html',
'sourcelink.html',
'searchbox.html'
]
}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
@@ -273,6 +296,19 @@ epub_copyright = u'2012, holger krekel et alii'
#epub_tocdup = True
# -- Options for texinfo output ------------------------------------------------
texinfo_documents = [
(master_doc, 'pytest', 'pytest Documentation',
('Holger Krekel@*Benjamin Peterson@*Ronny Pfannschmidt@*'
'Floris Bruynooghe@*others'),
'pytest',
'simple powerful testing with Pytho',
'Programming',
1),
]
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'python': ('http://docs.python.org/', None),
# 'lib': ("http://docs.python.org/2.7library/", None),

View File

@@ -5,27 +5,28 @@
Contact channels
===================================
- `new issue tracker`_ to report bugs or suggest features (for version
2.0 and above). You may also peek at the `old issue tracker`_ but please
don't submit bugs there anymore.
- `pytest issue tracker`_ to report bugs or suggest features (for version
2.0 and above).
- `pytest on stackoverflow.com <http://stackoverflow.com/search?q=pytest>`_
to post questions with the tag ``pytest``. New Questions will usually
be seen by pytest users or developers.
be seen by pytest users or developers and answered quickly.
- `Testing In Python`_: a mailing list for Python testing tools and discussion.
- `py-dev developers list`_ pytest specific announcements and discussions.
- `pytest-dev at python.org (mailing list)`_ pytest specific announcements and discussions.
- `pytest-commit at python.org (mailing list)`_: for commits and new issues
- #pylib on irc.freenode.net IRC channel for random questions.
- private mail to Holger.Krekel at gmail com if you want to communicate sensitive issues
- `commit mailing list`_
- `merlinux.eu`_ offers on-site teaching and consulting services.
- `merlinux.eu`_ offers pytest and tox-related professional teaching and
consulting.
.. _`new issue tracker`: http://bitbucket.org/hpk42/pytest/issues/
.. _`pytest issue tracker`: http://bitbucket.org/hpk42/pytest/issues/
.. _`old issue tracker`: http://bitbucket.org/hpk42/py-trunk/issues/
.. _`merlinux.eu`: http://merlinux.eu
@@ -41,7 +42,7 @@ Contact channels
.. _FOAF: http://en.wikipedia.org/wiki/FOAF
.. _`py-dev`:
.. _`development mailing list`:
.. _`py-dev developers list`: http://codespeak.net/mailman/listinfo/py-dev
.. _`pytest-dev at python.org (mailing list)`: http://mail.python.org/mailman/listinfo/pytest-dev
.. _`py-svn`:
.. _`commit mailing list`: http://codespeak.net/mailman/listinfo/py-svn
.. _`pytest-commit at python.org (mailing list)`: http://mail.python.org/mailman/listinfo/pytest-commit

View File

@@ -97,7 +97,7 @@ Builtin configuration file options
[seq] matches any character in seq
[!seq] matches any char not in seq
Default patterns are ``.* _* CVS {args}``. Setting a ``norecursedir``
Default patterns are ``.* _darcs CVS {args}``. Setting a ``norecursedir``
replaces the default. Here is an example of how to avoid
certain directories::
@@ -121,6 +121,8 @@ Builtin configuration file options
.. confval:: python_functions
One or more name prefixes determining which test functions
and methods are considered as test modules.
and methods are considered as test modules. Note that this
has no effect on methods that live on a ``unittest.TestCase``
derived class.
See :ref:`change naming conventions` for examples.

View File

@@ -44,9 +44,19 @@ then you can just invoke ``py.test`` without command line options::
$ py.test
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 1 items
mymodule.py .
========================= 1 passed in 0.02 seconds =========================
========================= 1 passed in 0.01 seconds =========================
It is possible to use fixtures using the ``getfixture`` helper::
# content of example.rst
>>> tmp = getfixture('tmpdir')
>>> ...
>>>
Also, :ref:`usefixtures` and :ref:`autouse` fixtures are supported
when executing text doctest files.

View File

@@ -66,7 +66,7 @@ class TestSpecialisedExplanations(object):
assert a == b
def test_eq_dict(self):
assert {'a': 0, 'b': 1} == {'a': 0, 'b': 2}
assert {'a': 0, 'b': 1, 'c': 0} == {'a': 0, 'b': 2, 'd': 0}
def test_eq_set(self):
assert set([0, 10, 11, 12]) == set([0, 20, 21])

View File

@@ -29,5 +29,6 @@ The following examples aim at various use cases you might encounter.
simple.txt
parametrize.txt
markers.txt
special.txt
pythoncollection.txt
nonpython.txt

View File

@@ -28,7 +28,7 @@ You can then restrict a test run to only run tests marked with ``webtest``::
$ py.test -v -m webtest
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4 -- /home/hpk/p/pytest/.tox/regen/bin/python
platform linux2 -- Python 2.7.3 -- pytest-2.5.1 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 3 items
test_server.py:3: test_send_http PASSED
@@ -40,7 +40,7 @@ Or the inverse, running all tests except the webtest ones::
$ py.test -v -m "not webtest"
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4 -- /home/hpk/p/pytest/.tox/regen/bin/python
platform linux2 -- Python 2.7.3 -- pytest-2.5.1 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 3 items
test_server.py:6: test_something_quick PASSED
@@ -61,7 +61,7 @@ select tests based on their names::
$ py.test -v -k http # running with the above defined example module
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4 -- /home/hpk/p/pytest/.tox/regen/bin/python
platform linux2 -- Python 2.7.3 -- pytest-2.5.1 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 3 items
test_server.py:3: test_send_http PASSED
@@ -73,7 +73,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 linux2 -- Python 2.7.3 -- pytest-2.3.4 -- /home/hpk/p/pytest/.tox/regen/bin/python
platform linux2 -- Python 2.7.3 -- pytest-2.5.1 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 3 items
test_server.py:6: test_something_quick PASSED
@@ -86,7 +86,7 @@ Or to select "http" and "quick" tests::
$ py.test -k "http or quick" -v
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4 -- /home/hpk/p/pytest/.tox/regen/bin/python
platform linux2 -- Python 2.7.3 -- pytest-2.5.1 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 3 items
test_server.py:3: test_send_http PASSED
@@ -95,6 +95,17 @@ Or to select "http" and "quick" tests::
================= 1 tests deselected by '-khttp or quick' ==================
================== 2 passed, 1 deselected in 0.01 seconds ==================
.. note::
If you are using expressions such as "X and Y" then both X and Y
need to be simple non-keyword names. For example, "pass" or "from"
will result in SyntaxErrors because "-k" evaluates the expression.
However, if the "-k" argument is a simple string, no such restrictions
apply. Also "-k 'not STRING'" has no restrictions. You can also
specify numbers like "-k 1.3" to match tests which are parametrized
with the float "1.3".
Registering markers
-------------------------------------
@@ -118,7 +129,7 @@ You can ask which markers exist for your test suite - the list includes our just
@pytest.mark.xfail(condition, reason=None, run=True): 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. See http://pytest.org/latest/skipping.html
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in multiple different argument value sets. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2. see http://pytest.org/latest/parametrize.html for more info and examples.
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see http://pytest.org/latest/parametrize.html for more info and examples.
@pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures
@@ -185,6 +196,29 @@ You can also set a module level marker::
in which case it will be applied to all functions and
methods defined in the module.
.. _`marking individual tests when using parametrize`:
Marking individual tests when using parametrize
-----------------------------------------------
When using parametrize, applying a mark will make it apply
to each individual test. However it is also possible to
apply a marker to an individual test instance::
import pytest
@pytest.mark.foo
@pytest.mark.parametrize(("n", "expected"), [
(1, 2),
pytest.mark.bar((1, 3)),
(2, 3),
])
def test_increment(n, expected):
assert n + 1 == expected
In this example the mark "foo" will apply to each of the three
tests, whereas the "bar" mark is only applied to the second test.
Skip and xfail marks can also be applied in this way, see :ref:`skip/xfail with parametrize`.
.. _`adding a custom marker from a plugin`:
@@ -212,7 +246,7 @@ specifies via named environments::
"env(name): mark test to run only on named environment")
def pytest_runtest_setup(item):
envmarker = item.keywords.get("env", None)
envmarker = item.get_marker("env")
if envmarker is not None:
envname = envmarker.args[0]
if envname != item.config.getoption("-E"):
@@ -232,7 +266,7 @@ the test needs::
$ py.test -E stage2
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 1 items
test_someenv.py s
@@ -243,7 +277,7 @@ and here is one that specifies exactly the environment needed::
$ py.test -E stage1
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 1 items
test_someenv.py .
@@ -259,7 +293,7 @@ The ``--markers`` option always gives you a list of available markers::
@pytest.mark.xfail(condition, reason=None, run=True): 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. See http://pytest.org/latest/skipping.html
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in multiple different argument value sets. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2. see http://pytest.org/latest/parametrize.html for more info and examples.
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see http://pytest.org/latest/parametrize.html for more info and examples.
@pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures
@@ -295,7 +329,7 @@ test function. From a conftest file we can read it like this::
import sys
def pytest_runtest_setup(item):
g = item.keywords.get("glob", None)
g = item.get_marker("glob")
if g is not None:
for info in g:
print ("glob args=%s kwargs=%s" %(info.args, info.kwargs))
@@ -308,6 +342,7 @@ Let's run this without capturing output and see what we get::
glob args=('class',) kwargs={'x': 2}
glob args=('module',) kwargs={'x': 1}
.
1 passed in 0.01 seconds
marking platform specific tests with pytest
--------------------------------------------------------------
@@ -330,7 +365,7 @@ for your particular platform, you could use the following plugin::
def pytest_runtest_setup(item):
if isinstance(item, item.Function):
plat = sys.platform
if plat not in item.keywords:
if not item.get_marker(plat):
if ALL.intersection(item.keywords):
pytest.skip("cannot run on platform %s" %(plat))
@@ -360,12 +395,12 @@ 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 linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 4 items
test_plat.py s.s.
========================= short test summary info ==========================
SKIP [2] /tmp/doc-exec-133/conftest.py:12: cannot run on platform linux2
SKIP [2] /tmp/doc-exec-63/conftest.py:12: cannot run on platform linux2
=================== 2 passed, 2 skipped in 0.01 seconds ====================
@@ -373,7 +408,7 @@ Note that if you specify a platform via the marker-command line option like this
$ py.test -m linux2
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 4 items
test_plat.py .
@@ -416,15 +451,15 @@ We want to dynamically define two markers and can do it in a
def pytest_collection_modifyitems(items):
for item in items:
if "interface" in item.nodeid:
item.keywords["interface"] = pytest.mark.interface
item.add_marker(pytest.mark.interface)
elif "event" in item.nodeid:
item.keywords["event"] = pytest.mark.event
item.add_marker(pytest.mark.event)
We can now use the ``-m option`` to select one set::
$ py.test -m interface --tb=short
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 4 items
test_module.py FF
@@ -445,7 +480,7 @@ or to select both "event" and "interface" tests::
$ py.test -m "interface or event" --tb=short
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 4 items
test_module.py FFF
@@ -464,4 +499,4 @@ or to select both "event" and "interface" tests::
> assert 0
E assert 0
============= 1 tests deselected by "-m 'interface or event'" ==============
================== 3 failed, 1 deselected in 0.02 seconds ==================
================== 3 failed, 1 deselected in 0.01 seconds ==================

View File

@@ -27,7 +27,7 @@ now execute the test specification::
nonpython $ py.test test_simple.yml
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 2 items
test_simple.yml .F
@@ -37,7 +37,7 @@ now execute the test specification::
usecase execution failed
spec failed: 'some': 'other'
no further details known at this point.
==================== 1 failed, 1 passed in 0.09 seconds ====================
==================== 1 failed, 1 passed in 0.03 seconds ====================
You get one dot for the passing ``sub1: sub1`` check and one failure.
Obviously in the above ``conftest.py`` you'll want to implement a more
@@ -56,7 +56,7 @@ consulted when reporting in ``verbose`` mode::
nonpython $ py.test -v
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4 -- /home/hpk/p/pytest/.tox/regen/bin/python
platform linux2 -- Python 2.7.3 -- pytest-2.5.1 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 2 items
test_simple.yml:1: usecase: ok PASSED
@@ -67,17 +67,17 @@ consulted when reporting in ``verbose`` mode::
usecase execution failed
spec failed: 'some': 'other'
no further details known at this point.
==================== 1 failed, 1 passed in 0.04 seconds ====================
==================== 1 failed, 1 passed in 0.03 seconds ====================
While developing your custom test collection and execution it's also
interesting to just look at the collection tree::
nonpython $ py.test --collectonly
nonpython $ py.test --collect-only
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 2 items
<YamlFile 'test_simple.yml'>
<YamlItem 'ok'>
<YamlItem 'hello'>
============================= in 0.04 seconds =============================
============================= in 0.02 seconds =============================

View File

@@ -9,7 +9,7 @@ def pytest_collect_file(parent, path):
class YamlFile(pytest.File):
def collect(self):
import yaml # we need a yaml parser, e.g. PyYAML
raw = yaml.load(self.fspath.open())
raw = yaml.safe_load(self.fspath.open())
for name, spec in raw.items():
yield YamlItem(name, self, spec)

View File

@@ -46,6 +46,7 @@ This means that we only run 2 tests if we do not pass ``--all``::
$ py.test -q test_compute.py
..
2 passed in 0.01 seconds
We run only two computations, so we see two dots.
let's run the full monty::
@@ -62,6 +63,7 @@ let's run the full monty::
E assert 4 < 4
test_compute.py:3: AssertionError
1 failed, 4 passed in 0.01 seconds
As expected when running the full range of ``param1`` values
we'll get an error on the last one.
@@ -104,7 +106,7 @@ this is a fully self-contained example which you can run with::
$ py.test test_scenarios.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 4 items
test_scenarios.py ....
@@ -114,9 +116,9 @@ this is a fully self-contained example which you can run with::
If you just collect tests you'll also nicely see 'advanced' and 'basic' as variants for the test function::
$ py.test --collectonly test_scenarios.py
$ py.test --collect-only test_scenarios.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 4 items
<Module 'test_scenarios.py'>
<Class 'TestSampleWithScenarios'>
@@ -178,9 +180,9 @@ creates a database object for the actual test invocations::
Let's first see how it looks like at collection time::
$ py.test test_backends.py --collectonly
$ py.test test_backends.py --collect-only
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 2 items
<Module 'test_backends.py'>
<Function 'test_db_initialized[d1]'>
@@ -195,7 +197,7 @@ And then when we run the test::
================================= FAILURES =================================
_________________________ test_db_initialized[d2] __________________________
db = <conftest.DB2 instance at 0x13cbcb0>
db = <conftest.DB2 instance at 0x12d4128>
def test_db_initialized(db):
# a dummy test
@@ -204,6 +206,7 @@ And then when we run the test::
E Failed: deliberately failing for demo purposes
test_backends.py:6: Failed
1 failed, 1 passed in 0.01 seconds
The first invocation with ``db == "DB1"`` passed while the second with ``db == "DB2"`` failed. Our ``db`` fixture function has instantiated each of the DB values during the setup phase while the ``pytest_generate_tests`` generated two according calls to the ``test_db_initialized`` during the collection phase.
@@ -248,15 +251,16 @@ argument sets to use for each test function. Let's run it::
$ py.test -q
F..
================================= FAILURES =================================
________________________ TestClass.test_equals[1-2] ________________________
________________________ TestClass.test_equals[2-1] ________________________
self = <test_parametrize.TestClass instance at 0x24e6d88>, a = 1, b = 2
self = <test_parametrize.TestClass instance at 0x14493f8>, a = 1, b = 2
def test_equals(self, a, b):
> assert a == b
E assert 1 == 2
test_parametrize.py:18: AssertionError
1 failed, 2 passed in 0.01 seconds
Indirect parametrization with multiple fixtures
--------------------------------------------------------------
@@ -278,3 +282,74 @@ Running it results in some skips if we don't have all the python interpreters in
............sss............sss............sss............ssssssssssssssssss
========================= short test summary info ==========================
SKIP [27] /home/hpk/p/pytest/doc/en/example/multipython.py:21: 'python2.8' not found
48 passed, 27 skipped in 1.34 seconds
Indirect parametrization of optional implementations/imports
--------------------------------------------------------------------
If you want to compare the outcomes of several implementations of a given
API, you can write test functions that receive the already imported implementations
and get skipped in case the implementation is not importable/available. Let's
say we have a "base" implementation and the other (possibly optimized ones)
need to provide similar results::
# content of conftest.py
import pytest
@pytest.fixture(scope="session")
def basemod(request):
return pytest.importorskip("base")
@pytest.fixture(scope="session", params=["opt1", "opt2"])
def optmod(request):
return pytest.importorskip(request.param)
And then a base implementation of a simple function::
# content of base.py
def func1():
return 1
And an optimized version::
# content of opt1.py
def func1():
return 1.0001
And finally a little test module::
# content of test_module.py
def test_func1(basemod, optmod):
assert round(basemod.func1(), 3) == round(optmod.func1(), 3)
If you run this with reporting for skips enabled::
$ py.test -rs test_module.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 2 items
test_module.py .s
========================= short test summary info ==========================
SKIP [1] /tmp/doc-exec-65/conftest.py:10: could not import 'opt2'
=================== 1 passed, 1 skipped in 0.01 seconds ====================
You'll see that we don't have a ``opt2`` module and thus the second test run
of our ``test_func1`` was skipped. A few notes:
- the fixture functions in the ``conftest.py`` file are "session-scoped" because we
don't need to import more than once
- if you have multiple test functions and a skipped import, you will see
the ``[1]`` count increasing in the report
- you can put :ref:`@pytest.mark.parametrize <@pytest.mark.parametrize>` style
parametrization on the test functions to parametrize input/output
values as well.

View File

@@ -1,5 +1,5 @@
# run this with $ py.test --collectonly test_collectonly.py
# run this with $ py.test --collect-only test_collectonly.py
#
def test_function():
pass

View File

@@ -41,9 +41,9 @@ in functions and classes. For example, if we have::
then the test collection looks like this::
$ py.test --collectonly
$ py.test --collect-only
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 2 items
<Module 'check_myapp.py'>
<Class 'CheckMyApp'>
@@ -53,6 +53,12 @@ then the test collection looks like this::
============================= in 0.01 seconds =============================
.. note::
the ``python_functions`` and ``python_classes`` has no effect
for ``unittest.TestCase`` test discovery because pytest delegates
detection of test case methods to unittest code.
Interpreting cmdline arguments as Python packages
-----------------------------------------------------
@@ -80,9 +86,9 @@ Finding out what is collected
You can always peek at the collection tree without running tests like this::
. $ py.test --collectonly pythoncollection.py
. $ py.test --collect-only pythoncollection.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 3 items
<Module 'pythoncollection.py'>
<Function 'test_function'>
@@ -133,9 +139,9 @@ and a setup.py dummy file like this::
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::
$ py.test --collectonly
$ py.test --collect-only
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 1 items
<Module 'pkg/module_py2.py'>
<Function 'test_only_on_python2'>

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 linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 39 items
failure_demo.py FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
@@ -30,7 +30,7 @@ get on the terminal - we are working on that):
failure_demo.py:15: AssertionError
_________________________ TestFailing.test_simple __________________________
self = <failure_demo.TestFailing object at 0x2ad4550>
self = <failure_demo.TestFailing object at 0x12d9250>
def test_simple(self):
def f():
@@ -40,13 +40,13 @@ get on the terminal - we are working on that):
> assert f() == g()
E assert 42 == 43
E + where 42 = <function f at 0x2a7f578>()
E + and 43 = <function g at 0x2a7f5f0>()
E + where 42 = <function f at 0x1278b90>()
E + and 43 = <function g at 0x1278c08>()
failure_demo.py:28: AssertionError
____________________ TestFailing.test_simple_multiline _____________________
self = <failure_demo.TestFailing object at 0x2a81e50>
self = <failure_demo.TestFailing object at 0x1287210>
def test_simple_multiline(self):
otherfunc_multi(
@@ -66,19 +66,19 @@ get on the terminal - we are working on that):
failure_demo.py:11: AssertionError
___________________________ TestFailing.test_not ___________________________
self = <failure_demo.TestFailing object at 0x2a72b50>
self = <failure_demo.TestFailing object at 0x12c6e10>
def test_not(self):
def f():
return 42
> assert not f()
E assert not 42
E + where 42 = <function f at 0x2a7f9b0>()
E + where 42 = <function f at 0x12861b8>()
failure_demo.py:38: AssertionError
_________________ TestSpecialisedExplanations.test_eq_text _________________
self = <failure_demo.TestSpecialisedExplanations object at 0x2a6eb50>
self = <failure_demo.TestSpecialisedExplanations object at 0x1290c50>
def test_eq_text(self):
> assert 'spam' == 'eggs'
@@ -89,7 +89,7 @@ get on the terminal - we are working on that):
failure_demo.py:42: AssertionError
_____________ TestSpecialisedExplanations.test_eq_similar_text _____________
self = <failure_demo.TestSpecialisedExplanations object at 0x2b07cd0>
self = <failure_demo.TestSpecialisedExplanations object at 0x12877d0>
def test_eq_similar_text(self):
> assert 'foo 1 bar' == 'foo 2 bar'
@@ -102,7 +102,7 @@ get on the terminal - we are working on that):
failure_demo.py:45: AssertionError
____________ TestSpecialisedExplanations.test_eq_multiline_text ____________
self = <failure_demo.TestSpecialisedExplanations object at 0x2a68050>
self = <failure_demo.TestSpecialisedExplanations object at 0x12de1d0>
def test_eq_multiline_text(self):
> assert 'foo\nspam\nbar' == 'foo\neggs\nbar'
@@ -115,15 +115,15 @@ get on the terminal - we are working on that):
failure_demo.py:48: AssertionError
______________ TestSpecialisedExplanations.test_eq_long_text _______________
self = <failure_demo.TestSpecialisedExplanations object at 0x2ad2990>
self = <failure_demo.TestSpecialisedExplanations object at 0x143b5d0>
def test_eq_long_text(self):
a = '1'*100 + 'a' + '2'*100
b = '1'*100 + 'b' + '2'*100
> assert a == b
E assert '111111111111...2222222222222' == '1111111111111...2222222222222'
E Skipping 90 identical leading characters in diff
E Skipping 91 identical trailing characters in diff
E Skipping 90 identical leading characters in diff, use -v to show
E Skipping 91 identical trailing characters in diff, use -v to show
E - 1111111111a222222222
E ? ^
E + 1111111111b222222222
@@ -132,15 +132,15 @@ get on the terminal - we are working on that):
failure_demo.py:53: AssertionError
_________ TestSpecialisedExplanations.test_eq_long_text_multiline __________
self = <failure_demo.TestSpecialisedExplanations object at 0x2ad6c10>
self = <failure_demo.TestSpecialisedExplanations object at 0x1287810>
def test_eq_long_text_multiline(self):
a = '1\n'*100 + 'a' + '2\n'*100
b = '1\n'*100 + 'b' + '2\n'*100
> assert a == b
E assert '1\n1\n1\n1\n...n2\n2\n2\n2\n' == '1\n1\n1\n1\n1...n2\n2\n2\n2\n'
E Skipping 190 identical leading characters in diff
E Skipping 191 identical trailing characters in diff
E Skipping 190 identical leading characters in diff, use -v to show
E Skipping 191 identical trailing characters in diff, use -v to show
E 1
E 1
E 1
@@ -156,7 +156,7 @@ get on the terminal - we are working on that):
failure_demo.py:58: AssertionError
_________________ TestSpecialisedExplanations.test_eq_list _________________
self = <failure_demo.TestSpecialisedExplanations object at 0x2a81c50>
self = <failure_demo.TestSpecialisedExplanations object at 0x12900d0>
def test_eq_list(self):
> assert [0, 1, 2] == [0, 1, 3]
@@ -166,7 +166,7 @@ get on the terminal - we are working on that):
failure_demo.py:61: AssertionError
______________ TestSpecialisedExplanations.test_eq_list_long _______________
self = <failure_demo.TestSpecialisedExplanations object at 0x2a69f50>
self = <failure_demo.TestSpecialisedExplanations object at 0x12c62d0>
def test_eq_list_long(self):
a = [0]*100 + [1] + [3]*100
@@ -178,20 +178,23 @@ get on the terminal - we are working on that):
failure_demo.py:66: AssertionError
_________________ TestSpecialisedExplanations.test_eq_dict _________________
self = <failure_demo.TestSpecialisedExplanations object at 0x2ad5f50>
self = <failure_demo.TestSpecialisedExplanations object at 0x12deb50>
def test_eq_dict(self):
> assert {'a': 0, 'b': 1} == {'a': 0, 'b': 2}
E assert {'a': 0, 'b': 1} == {'a': 0, 'b': 2}
E - {'a': 0, 'b': 1}
E ? ^
E + {'a': 0, 'b': 2}
E ? ^
> assert {'a': 0, 'b': 1, 'c': 0} == {'a': 0, 'b': 2, 'd': 0}
E assert {'a': 0, 'b': 1, 'c': 0} == {'a': 0, 'b': 2, 'd': 0}
E Omitting 1 identical items, use -v to show
E Differing items:
E {'b': 1} != {'b': 2}
E Left contains more items:
E {'c': 0}
E Right contains more items:
E {'d': 0}
failure_demo.py:69: AssertionError
_________________ TestSpecialisedExplanations.test_eq_set __________________
self = <failure_demo.TestSpecialisedExplanations object at 0x2ad4410>
self = <failure_demo.TestSpecialisedExplanations object at 0x128b4d0>
def test_eq_set(self):
> assert set([0, 10, 11, 12]) == set([0, 20, 21])
@@ -207,7 +210,7 @@ get on the terminal - we are working on that):
failure_demo.py:72: AssertionError
_____________ TestSpecialisedExplanations.test_eq_longer_list ______________
self = <failure_demo.TestSpecialisedExplanations object at 0x2ad2d50>
self = <failure_demo.TestSpecialisedExplanations object at 0x12c6b10>
def test_eq_longer_list(self):
> assert [1,2] == [1,2,3]
@@ -217,7 +220,7 @@ get on the terminal - we are working on that):
failure_demo.py:75: AssertionError
_________________ TestSpecialisedExplanations.test_in_list _________________
self = <failure_demo.TestSpecialisedExplanations object at 0x2a81310>
self = <failure_demo.TestSpecialisedExplanations object at 0x143b650>
def test_in_list(self):
> assert 1 in [0, 2, 3, 4, 5]
@@ -226,7 +229,7 @@ get on the terminal - we are working on that):
failure_demo.py:78: AssertionError
__________ TestSpecialisedExplanations.test_not_in_text_multiline __________
self = <failure_demo.TestSpecialisedExplanations object at 0x2a697d0>
self = <failure_demo.TestSpecialisedExplanations object at 0x128be10>
def test_not_in_text_multiline(self):
text = 'some multiline\ntext\nwhich\nincludes foo\nand a\ntail'
@@ -244,7 +247,7 @@ get on the terminal - we are working on that):
failure_demo.py:82: AssertionError
___________ TestSpecialisedExplanations.test_not_in_text_single ____________
self = <failure_demo.TestSpecialisedExplanations object at 0x2ad4d10>
self = <failure_demo.TestSpecialisedExplanations object at 0x12d9fd0>
def test_not_in_text_single(self):
text = 'single foo line'
@@ -257,7 +260,7 @@ get on the terminal - we are working on that):
failure_demo.py:86: AssertionError
_________ TestSpecialisedExplanations.test_not_in_text_single_long _________
self = <failure_demo.TestSpecialisedExplanations object at 0x2ad2fd0>
self = <failure_demo.TestSpecialisedExplanations object at 0x143bdd0>
def test_not_in_text_single_long(self):
text = 'head ' * 50 + 'foo ' + 'tail ' * 20
@@ -270,7 +273,7 @@ get on the terminal - we are working on that):
failure_demo.py:90: AssertionError
______ TestSpecialisedExplanations.test_not_in_text_single_long_term _______
self = <failure_demo.TestSpecialisedExplanations object at 0x2a6f410>
self = <failure_demo.TestSpecialisedExplanations object at 0x12c6390>
def test_not_in_text_single_long_term(self):
text = 'head ' * 50 + 'f'*70 + 'tail ' * 20
@@ -289,7 +292,7 @@ get on the terminal - we are working on that):
i = Foo()
> assert i.b == 2
E assert 1 == 2
E + where 1 = <failure_demo.Foo object at 0x2a81850>.b
E + where 1 = <failure_demo.Foo object at 0x1287790>.b
failure_demo.py:101: AssertionError
_________________________ test_attribute_instance __________________________
@@ -299,8 +302,8 @@ get on the terminal - we are working on that):
b = 1
> assert Foo().b == 2
E assert 1 == 2
E + where 1 = <failure_demo.Foo object at 0x2ad4bd0>.b
E + where <failure_demo.Foo object at 0x2ad4bd0> = <class 'failure_demo.Foo'>()
E + where 1 = <failure_demo.Foo object at 0x12c6bd0>.b
E + where <failure_demo.Foo object at 0x12c6bd0> = <class 'failure_demo.Foo'>()
failure_demo.py:107: AssertionError
__________________________ test_attribute_failure __________________________
@@ -316,7 +319,7 @@ get on the terminal - we are working on that):
failure_demo.py:116:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <failure_demo.Foo object at 0x2ad26d0>
self = <failure_demo.Foo object at 0x12daed0>
def _get_b(self):
> raise Exception('Failed to get attrib')
@@ -332,15 +335,15 @@ get on the terminal - we are working on that):
b = 2
> assert Foo().b == Bar().b
E assert 1 == 2
E + where 1 = <failure_demo.Foo object at 0x2a6ff10>.b
E + where <failure_demo.Foo object at 0x2a6ff10> = <class 'failure_demo.Foo'>()
E + and 2 = <failure_demo.Bar object at 0x2a6fd50>.b
E + where <failure_demo.Bar object at 0x2a6fd50> = <class 'failure_demo.Bar'>()
E + where 1 = <failure_demo.Foo object at 0x128bcd0>.b
E + where <failure_demo.Foo object at 0x128bcd0> = <class 'failure_demo.Foo'>()
E + and 2 = <failure_demo.Bar object at 0x128b050>.b
E + where <failure_demo.Bar object at 0x128b050> = <class 'failure_demo.Bar'>()
failure_demo.py:124: AssertionError
__________________________ TestRaises.test_raises __________________________
self = <failure_demo.TestRaises instance at 0x2a75c68>
self = <failure_demo.TestRaises instance at 0x145c7e8>
def test_raises(self):
s = 'qwe'
@@ -352,10 +355,10 @@ get on the terminal - we are working on that):
> int(s)
E ValueError: invalid literal for int() with base 10: 'qwe'
<0-codegen /home/hpk/p/pytest/.tox/regen/lib/python2.7/site-packages/_pytest/python.py:851>:1: ValueError
<0-codegen /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:983>:1: ValueError
______________________ TestRaises.test_raises_doesnt _______________________
self = <failure_demo.TestRaises instance at 0x2adf3f8>
self = <failure_demo.TestRaises instance at 0x1455f38>
def test_raises_doesnt(self):
> raises(IOError, "int('3')")
@@ -364,7 +367,7 @@ get on the terminal - we are working on that):
failure_demo.py:136: Failed
__________________________ TestRaises.test_raise ___________________________
self = <failure_demo.TestRaises instance at 0x2af1830>
self = <failure_demo.TestRaises instance at 0x1453998>
def test_raise(self):
> raise ValueError("demo error")
@@ -373,7 +376,7 @@ get on the terminal - we are working on that):
failure_demo.py:139: ValueError
________________________ TestRaises.test_tupleerror ________________________
self = <failure_demo.TestRaises instance at 0x2ae5290>
self = <failure_demo.TestRaises instance at 0x1465560>
def test_tupleerror(self):
> a,b = [1]
@@ -382,7 +385,7 @@ get on the terminal - we are working on that):
failure_demo.py:142: ValueError
______ TestRaises.test_reinterpret_fails_with_print_for_the_fun_of_it ______
self = <failure_demo.TestRaises instance at 0x2ae2878>
self = <failure_demo.TestRaises instance at 0x1465758>
def test_reinterpret_fails_with_print_for_the_fun_of_it(self):
l = [1,2,3]
@@ -395,7 +398,7 @@ get on the terminal - we are working on that):
l is [1, 2, 3]
________________________ TestRaises.test_some_error ________________________
self = <failure_demo.TestRaises instance at 0x2af0e18>
self = <failure_demo.TestRaises instance at 0x1468ab8>
def test_some_error(self):
> if namenotexi:
@@ -423,7 +426,7 @@ get on the terminal - we are working on that):
<2-codegen 'abc-123' /home/hpk/p/pytest/doc/en/example/assertion/failure_demo.py:162>:2: AssertionError
____________________ TestMoreErrors.test_complex_error _____________________
self = <failure_demo.TestMoreErrors instance at 0x2adbc68>
self = <failure_demo.TestMoreErrors instance at 0x1442908>
def test_complex_error(self):
def f():
@@ -452,7 +455,7 @@ get on the terminal - we are working on that):
failure_demo.py:5: AssertionError
___________________ TestMoreErrors.test_z1_unpack_error ____________________
self = <failure_demo.TestMoreErrors instance at 0x2aee1b8>
self = <failure_demo.TestMoreErrors instance at 0x145bab8>
def test_z1_unpack_error(self):
l = []
@@ -462,7 +465,7 @@ get on the terminal - we are working on that):
failure_demo.py:179: ValueError
____________________ TestMoreErrors.test_z2_type_error _____________________
self = <failure_demo.TestMoreErrors instance at 0x2ae27a0>
self = <failure_demo.TestMoreErrors instance at 0x1444368>
def test_z2_type_error(self):
l = 3
@@ -472,19 +475,19 @@ get on the terminal - we are working on that):
failure_demo.py:183: TypeError
______________________ TestMoreErrors.test_startswith ______________________
self = <failure_demo.TestMoreErrors instance at 0x2ae1128>
self = <failure_demo.TestMoreErrors instance at 0x146e4d0>
def test_startswith(self):
s = "123"
g = "456"
> assert s.startswith(g)
E assert <built-in method startswith of str object at 0x2adc918>('456')
E + where <built-in method startswith of str object at 0x2adc918> = '123'.startswith
E assert <built-in method startswith of str object at 0x12dfa58>('456')
E + where <built-in method startswith of str object at 0x12dfa58> = '123'.startswith
failure_demo.py:188: AssertionError
__________________ TestMoreErrors.test_startswith_nested ___________________
self = <failure_demo.TestMoreErrors instance at 0x2c720e0>
self = <failure_demo.TestMoreErrors instance at 0x143ed40>
def test_startswith_nested(self):
def f():
@@ -492,15 +495,15 @@ get on the terminal - we are working on that):
def g():
return "456"
> assert f().startswith(g())
E assert <built-in method startswith of str object at 0x2adc918>('456')
E + where <built-in method startswith of str object at 0x2adc918> = '123'.startswith
E + where '123' = <function f at 0x2af5b90>()
E + and '456' = <function g at 0x2af5c08>()
E assert <built-in method startswith of str object at 0x12dfa58>('456')
E + where <built-in method startswith of str object at 0x12dfa58> = '123'.startswith
E + where '123' = <function f at 0x1286500>()
E + and '456' = <function g at 0x126db18>()
failure_demo.py:195: AssertionError
_____________________ TestMoreErrors.test_global_func ______________________
self = <failure_demo.TestMoreErrors instance at 0x2c725f0>
self = <failure_demo.TestMoreErrors instance at 0x1453b90>
def test_global_func(self):
> assert isinstance(globf(42), float)
@@ -510,18 +513,18 @@ get on the terminal - we are working on that):
failure_demo.py:198: AssertionError
_______________________ TestMoreErrors.test_instance _______________________
self = <failure_demo.TestMoreErrors instance at 0x2a67ab8>
self = <failure_demo.TestMoreErrors instance at 0x146b128>
def test_instance(self):
self.x = 6*7
> assert self.x != 42
E assert 42 != 42
E + where 42 = <failure_demo.TestMoreErrors instance at 0x2a67ab8>.x
E + where 42 = <failure_demo.TestMoreErrors instance at 0x146b128>.x
failure_demo.py:202: AssertionError
_______________________ TestMoreErrors.test_compare ________________________
self = <failure_demo.TestMoreErrors instance at 0x2af8710>
self = <failure_demo.TestMoreErrors instance at 0x1469368>
def test_compare(self):
> assert globf(10) < 5
@@ -531,7 +534,7 @@ get on the terminal - we are working on that):
failure_demo.py:205: AssertionError
_____________________ TestMoreErrors.test_try_finally ______________________
self = <failure_demo.TestMoreErrors instance at 0x2af03f8>
self = <failure_demo.TestMoreErrors instance at 0x12c4098>
def test_try_finally(self):
x = 1
@@ -540,4 +543,4 @@ get on the terminal - we are working on that):
E assert 1 == 0
failure_demo.py:210: AssertionError
======================== 39 failed in 0.25 seconds =========================
======================== 39 failed in 0.20 seconds =========================

View File

@@ -55,6 +55,7 @@ Let's run this without supplying our new option::
test_sample.py:6: AssertionError
----------------------------- Captured stdout ------------------------------
first
1 failed in 0.01 seconds
And now with supplying a command line option::
@@ -76,6 +77,7 @@ And now with supplying a command line option::
test_sample.py:6: AssertionError
----------------------------- Captured stdout ------------------------------
second
1 failed in 0.01 seconds
You can see that the command line option arrived in our test. This
completes the basic pattern. However, one often rather wants to process
@@ -106,7 +108,7 @@ directory with the above conftest.py::
$ py.test
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 0 items
============================= in 0.00 seconds =============================
@@ -150,12 +152,12 @@ 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 linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 2 items
test_module.py .s
========================= short test summary info ==========================
SKIP [1] /tmp/doc-exec-138/conftest.py:9: need --runslow option to run
SKIP [1] /tmp/doc-exec-68/conftest.py:9: need --runslow option to run
=================== 1 passed, 1 skipped in 0.01 seconds ====================
@@ -163,7 +165,7 @@ Or run it including the ``slow`` marked test::
$ py.test --runslow
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 2 items
test_module.py ..
@@ -206,6 +208,7 @@ Let's run our little function::
E Failed: not configured: 42
test_checkconfig.py:8: Failed
1 failed in 0.01 seconds
Detect if running from within a py.test run
--------------------------------------------------------------
@@ -253,7 +256,7 @@ which will add the string to the test header accordingly::
$ py.test
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
project deps: mylib-1.1
collected 0 items
@@ -276,7 +279,7 @@ which will add info only when run with "--v"::
$ py.test -v
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4 -- /home/hpk/p/pytest/.tox/regen/bin/python
platform linux2 -- Python 2.7.3 -- pytest-2.5.1 -- /home/hpk/p/pytest/.tox/regen/bin/python
info1: did you know that ...
did you?
collecting ... collected 0 items
@@ -287,7 +290,7 @@ and nothing when run plainly::
$ py.test
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 0 items
============================= in 0.00 seconds =============================
@@ -319,7 +322,7 @@ Now we can profile which test functions execute the slowest::
$ py.test --durations=3
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 3 items
test_some_are_slow.py ...
@@ -327,7 +330,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 call test_some_are_slow.py::test_funcfast
0.00s setup test_some_are_slow.py::test_funcfast
========================= 3 passed in 0.31 seconds =========================
incremental testing - test steps
@@ -380,7 +383,7 @@ If we run this::
$ py.test -rx
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 4 items
test_step.py .Fx.
@@ -388,7 +391,7 @@ If we run this::
================================= FAILURES =================================
____________________ TestUserHandling.test_modification ____________________
self = <test_step.TestUserHandling instance at 0x193bc68>
self = <test_step.TestUserHandling instance at 0x2758c20>
def test_modification(self):
> assert 0
@@ -398,7 +401,7 @@ If we run this::
========================= 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.02 seconds ===============
============== 1 failed, 2 passed, 1 xfailed in 0.01 seconds ===============
We'll see that ``test_deletion`` was not executed because ``test_modification``
failed. It is reported as an "expected failure".
@@ -450,7 +453,7 @@ We can run this::
$ py.test
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 7 items
test_step.py .Fx.
@@ -460,17 +463,17 @@ We can run this::
================================== ERRORS ==================================
_______________________ ERROR at setup of test_root ________________________
file /tmp/doc-exec-138/b/test_error.py, line 1
file /tmp/doc-exec-68/b/test_error.py, line 1
def test_root(db): # no db here, will error out
fixture 'db' not found
available fixtures: pytestconfig, recwarn, monkeypatch, capfd, capsys, tmpdir
available fixtures: recwarn, capfd, pytestconfig, capsys, tmpdir, monkeypatch
use 'py.test --fixtures [testpath]' for help on them.
/tmp/doc-exec-138/b/test_error.py:1
/tmp/doc-exec-68/b/test_error.py:1
================================= FAILURES =================================
____________________ TestUserHandling.test_modification ____________________
self = <test_step.TestUserHandling instance at 0x1492d88>
self = <test_step.TestUserHandling instance at 0x131fc20>
def test_modification(self):
> assert 0
@@ -479,23 +482,23 @@ We can run this::
test_step.py:9: AssertionError
_________________________________ test_a1 __________________________________
db = <conftest.DB instance at 0x1498e60>
db = <conftest.DB instance at 0x1328878>
def test_a1(db):
> assert 0, db # to show value
E AssertionError: <conftest.DB instance at 0x1498e60>
E AssertionError: <conftest.DB instance at 0x1328878>
a/test_db.py:2: AssertionError
_________________________________ test_a2 __________________________________
db = <conftest.DB instance at 0x1498e60>
db = <conftest.DB instance at 0x1328878>
def test_a2(db):
> assert 0, db # to show value
E AssertionError: <conftest.DB instance at 0x1498e60>
E AssertionError: <conftest.DB instance at 0x1328878>
a/test_db2.py:2: AssertionError
========== 3 failed, 2 passed, 1 xfailed, 1 error in 0.04 seconds ==========
========== 3 failed, 2 passed, 1 xfailed, 1 error in 0.03 seconds ==========
The two test modules in the ``a`` directory see the same ``db`` fixture instance
while the one test in the sister-directory ``b`` doesn't see it. We could of course
@@ -550,7 +553,7 @@ and run them::
$ py.test test_module.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 2 items
test_module.py FF
@@ -558,7 +561,7 @@ and run them::
================================= FAILURES =================================
________________________________ test_fail1 ________________________________
tmpdir = local('/tmp/pytest-543/test_fail10')
tmpdir = local('/tmp/pytest-42/test_fail10')
def test_fail1(tmpdir):
> assert 0
@@ -572,12 +575,12 @@ and run them::
E assert 0
test_module.py:4: AssertionError
========================= 2 failed in 0.02 seconds =========================
========================= 2 failed in 0.01 seconds =========================
you will have a "failures" file which contains the failing test ids::
$ cat failures
test_module.py::test_fail1 (/tmp/pytest-543/test_fail10)
test_module.py::test_fail1 (/tmp/pytest-42/test_fail10)
test_module.py::test_fail2
Making test result information available in fixtures
@@ -640,10 +643,12 @@ and run it::
$ py.test -s test_module.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 3 items
test_module.py EFF
test_module.py Esetting up a test failed! test_module.py::test_setup_fails
Fexecuting test failed test_module.py::test_call_fails
F
================================== ERRORS ==================================
____________________ ERROR at setup of test_setup_fails ____________________
@@ -672,8 +677,6 @@ and run it::
test_module.py:15: AssertionError
==================== 2 failed, 1 error in 0.01 seconds =====================
setting up a test failed! test_module.py::test_setup_fails
executing test failed test_module.py::test_call_fails
You'll see that the fixture finalizers could use the precise reporting
information.

View File

@@ -0,0 +1,73 @@
A sesssion-fixture which can look at all collected tests
----------------------------------------------------------------
A session-scoped fixture effectively has access to all
collected test items. Here is an example of a fixture
function which walks all collected tests and looks
if their test class defines a ``callme`` method and
calls it::
# content of conftest.py
import pytest
@pytest.fixture(scope="session", autouse=True)
def callattr_ahead_of_alltests(request):
print "callattr_ahead_of_alltests called"
seen = set([None])
session = request.node
for item in session.items:
cls = item.getparent(pytest.Class)
if cls not in seen:
if hasattr(cls.obj, "callme"):
cls.obj.callme()
seen.add(cls)
test classes may now define a ``callme`` method which
will be called ahead of running any tests::
# content of test_module.py
class TestHello:
@classmethod
def callme(cls):
print "callme called!"
def test_method1(self):
print "test_method1 called"
def test_method2(self):
print "test_method1 called"
class TestOther:
@classmethod
def callme(cls):
print "callme other called"
def test_other(self):
print "test other"
# works with unittest as well ...
import unittest
class SomeTest(unittest.TestCase):
@classmethod
def callme(self):
print "SomeTest callme called"
def test_unit1(self):
print "test_unit1 method called"
If you run this without output capturing::
$ py.test -q -s test_module.py
callattr_ahead_of_alltests called
callme called!
callme other called
SomeTest callme called
test_method1 called
.test_method1 called
.test other
.test_unit1 method called
.
4 passed in 0.01 seconds

View File

@@ -3,9 +3,10 @@ Some Issues and Questions
.. note::
If you don't find an answer here, you may checkout
This FAQ is here only mostly for historic reasons. Checkout
`pytest Q&A at Stackoverflow <http://stackoverflow.com/search?q=pytest>`_
or other :ref:`contact channels` to get help.
for many questions and answers related to pytest and/or use
:ref:`contact channels` to get help.
On naming, nosetests, licensing and magic
------------------------------------------------
@@ -29,7 +30,7 @@ and does not handle Deferreds returned from a test in pytest style.
If you are using trial's unittest.TestCase chances are that you can
just run your tests even if you return Deferreds. In addition,
there also is a dedicated `pytest-twisted
<http://pypi.python.org/pypi/pytest-twisted`` plugin which allows to
<http://pypi.python.org/pypi/pytest-twisted>`_ plugin which allows to
return deferreds from pytest-style tests, allowing to use
:ref:`fixtures` and other features.
@@ -94,12 +95,12 @@ but since many people have gotten used to the old name and there
is another tool named "pytest" we just decided to stick with
``py.test`` for now.
Function arguments, parametrized tests and setup
pytest fixtures, parametrized tests
-------------------------------------------------------
.. _funcargs: funcargs.html
Is using funcarg- versus xUnit setup a style question?
Is using pytest fixtures versus xUnit setup a style question?
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
For simple applications and for people experienced with nose_ or
@@ -117,20 +118,6 @@ in a managed class/module/function scope.
.. _`why pytest_pyfuncarg__ methods?`:
Why the ``pytest_funcarg__*`` name for funcarg factories?
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
We like `Convention over Configuration`_ and didn't see much point
in allowing a more flexible or abstract mechanism. Moreover,
it is nice to be able to search for ``pytest_funcarg__MYARG`` in
source code and safely find all factory functions for
the ``MYARG`` function argument.
.. note::
With pytest-2.3 you can use the :ref:`@pytest.fixture` decorator
to mark a function as a fixture function.
.. _`Convention over Configuration`: http://en.wikipedia.org/wiki/Convention_over_Configuration
Can I yield multiple values from a fixture function function?
@@ -139,16 +126,16 @@ Can I yield multiple values from a fixture function function?
There are two conceptual reasons why yielding from a factory function
is not possible:
* Calling factories for obtaining test function arguments
is part of setting up and running a test. At that
point it is not possible to add new test calls to
the test collection anymore.
* If multiple factories yielded values there would
be no natural place to determine the combination
policy - in real-world examples some combinations
often should not run.
* Calling factories for obtaining test function arguments
is part of setting up and running a test. At that
point it is not possible to add new test calls to
the test collection anymore.
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.
@@ -171,16 +158,8 @@ Unfortunately, setuptools-0.6.11 does not ``if __name__=='__main__'``
protect its generated command line script. This leads to infinite
recursion when running a test that instantiates Processes.
A good solution is to `install Distribute`_ as a drop-in replacement
for setuptools and then re-install ``pytest``. Otherwise you could
fix the script that is created by setuptools by inserting an
``if __name__ == '__main__'``. Or you can create a "pytest.py"
script with this content and invoke that with the python version::
import pytest
if __name__ == '__main__':
pytest.main()
.. _`install distribute`: http://pypi.python.org/pypi/distribute#installation-instructions
As of middle 2013, there shouldn't be a problem anymore when you
use the standard setuptools (note that distribute has been merged
back into setuptools which is now shipped directly with virtualenv).
.. include:: links.inc

View File

@@ -7,14 +7,14 @@ pytest fixtures: explicit, modular, scalable
.. currentmodule:: _pytest.python
.. versionadded:: 2.0/2.3
.. versionadded:: 2.0/2.3/2.4
.. _`xUnit`: http://en.wikipedia.org/wiki/XUnit
.. _`general purpose of test fixtures`: http://en.wikipedia.org/wiki/Test_fixture#Software
.. _`purpose of test fixtures`: http://en.wikipedia.org/wiki/Test_fixture#Software
.. _`Dependency injection`: http://en.wikipedia.org/wiki/Dependency_injection#Definition
The `general purpose of test fixtures`_ is to provide a fixed baseline
upon which tests can reliably and repeatedly execute. pytest-2.3 fixtures
The `purpose of test fixtures`_ is to provide a fixed baseline
upon which tests can reliably and repeatedly execute. pytest fixtures
offer dramatic improvements over the classic xUnit style of setup/teardown
functions:
@@ -22,8 +22,7 @@ functions:
from test functions, modules, classes or whole projects.
* fixtures are implemented in a modular manner, as each fixture name
triggers a *fixture function* which can itself easily use other
fixtures.
triggers a *fixture function* which can itself use other fixtures.
* fixture management scales from simple unit to complex
functional testing, allowing to parametrize fixtures and tests according
@@ -35,13 +34,19 @@ both styles, moving incrementally from classic to new style, as you
prefer. You can also start out from existing :ref:`unittest.TestCase
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.
.. _`funcargs`:
.. _`funcarg mechanism`:
.. _`fixture function`:
.. _`@pytest.fixture`:
.. _`pytest.fixture`:
Fixtures as Function arguments (funcargs)
Fixtures as Function arguments
-----------------------------------------
Test functions can receive fixture objects by naming them as an input
@@ -71,7 +76,7 @@ marked ``smtp`` fixture function. Running the test looks like this::
$ py.test test_smtpsimple.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 1 items
test_smtpsimple.py F
@@ -79,7 +84,7 @@ marked ``smtp`` fixture function. Running the test looks like this::
================================= FAILURES =================================
________________________________ test_ehlo _________________________________
smtp = <smtplib.SMTP instance at 0x236cab8>
smtp = <smtplib.SMTP instance at 0x2ae3469203f8>
def test_ehlo(smtp):
response, msg = smtp.ehlo()
@@ -89,7 +94,7 @@ marked ``smtp`` fixture function. Running the test looks like this::
E assert 0
test_smtpsimple.py:12: AssertionError
========================= 1 failed in 0.17 seconds =========================
========================= 1 failed in 0.21 seconds =========================
In the failure traceback we see that the test function was called with a
``smtp`` argument, the ``smtplib.SMTP()`` instance created by the fixture
@@ -124,15 +129,15 @@ with a list of available function arguments.
but is not anymore advertised as the primary means of declaring fixture
functions.
Funcargs a prime example of dependency injection
"Funcargs" a prime example of dependency injection
---------------------------------------------------
When injecting fixtures to test functions, pytest-2.0 introduced the
term "funcargs" or "funcarg mechanism" which continues to be present
also in pytest-2.3 docs. It now refers to the specific case of injecting
also in docs today. It now refers to the specific case of injecting
fixture values as arguments to test functions. With pytest-2.3 there are
more possibilities to use fixtures but "funcargs" probably will remain
as the main way of dealing with fixtures.
more possibilities to use fixtures but "funcargs" remain as the main way
as they allow to directly state the dependencies of a test function.
As the following examples show in more detail, funcargs allow test
functions to easily receive and work against specific pre-initialized
@@ -143,7 +148,7 @@ functions take the role of the *injector* and test functions are the
.. _smtpshared:
Working with a module-shared fixture
Sharing a fixture across tests in a module (or class/session)
-----------------------------------------------------------------
.. regendoc:wipe
@@ -154,10 +159,10 @@ can add a ``scope='module'`` parameter to the
:py:func:`@pytest.fixture <_pytest.python.fixture>` invocation
to cause the decorated ``smtp`` fixture function to only be invoked once
per test module. Multiple test functions in a test module will thus
each receive the same ``smtp`` fixture instance. The next example also
extracts the fixture function into a separate ``conftest.py`` file so
that all tests in test modules in the directory can access the fixture
function::
each receive the same ``smtp`` fixture instance. The next example puts
the fixture function into a separate ``conftest.py`` file so
that tests from multiple test modules in the directory can
access the fixture function::
# content of conftest.py
import pytest
@@ -189,7 +194,7 @@ inspect what is going on and can now run the tests::
$ py.test test_module.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 2 items
test_module.py FF
@@ -197,7 +202,7 @@ inspect what is going on and can now run the tests::
================================= FAILURES =================================
________________________________ test_ehlo _________________________________
smtp = <smtplib.SMTP instance at 0x1c9add0>
smtp = <smtplib.SMTP instance at 0x1af5440>
def test_ehlo(smtp):
response = smtp.ehlo()
@@ -209,7 +214,7 @@ inspect what is going on and can now run the tests::
test_module.py:6: AssertionError
________________________________ test_noop _________________________________
smtp = <smtplib.SMTP instance at 0x1c9add0>
smtp = <smtplib.SMTP instance at 0x1af5440>
def test_noop(smtp):
response = smtp.noop()
@@ -218,7 +223,7 @@ inspect what is going on and can now run the tests::
E assert 0
test_module.py:11: AssertionError
========================= 2 failed in 0.23 seconds =========================
========================= 2 failed in 0.17 seconds =========================
You see the two ``assert 0`` failing and more importantly you can also see
that the same (module-scoped) ``smtp`` object was passed into the two
@@ -229,28 +234,62 @@ quick as a single one because they reuse the same instance.
If you decide that you rather want to have a session-scoped ``smtp``
instance, you can simply declare it::
@pytest.fixture(scope=``session``)
@pytest.fixture(scope="session")
def smtp(...):
# the returned fixture value will be shared for
# all tests needing it
.. _`request-context`:
Fixtures can interact with the requesting test context
.. _`finalization`:
fixture finalization / executing teardown code
-------------------------------------------------------------
Fixture functions can themselves use other fixtures by naming
them as an input argument just like test functions do, see
:ref:`interdependent fixtures`. Moreover, pytest
provides a builtin :py:class:`request <FixtureRequest>` object,
which fixture functions can use to introspect the function, class or module
for which they are invoked or to register finalizing (cleanup)
functions which are called when the last test finished execution.
pytest supports execution of fixture specific finalization code
when the fixture goes out of scope. By accepting a ``request`` object
into your fixture function you can call its ``request.addfinalizer`` one
or multiple times::
# content of conftest.py
import smtplib
import pytest
@pytest.fixture(scope="module")
def smtp(request):
smtp = smtplib.SMTP("merlinux.eu")
def fin():
print ("teardown smtp")
smtp.close()
request.addfinalizer(fin)
return smtp # provide the fixture value
The ``fin`` function will execute when the last test using
the fixture in the module has finished execution.
Let's execute it::
$ py.test -s -q --tb=no
FFteardown smtp
2 failed in 0.17 seconds
We see that the ``smtp`` instance is finalized after the two
tests finished execution. Note that if we decorated our fixture
function with ``scope='function'`` then fixture setup and cleanup would
occur around each single test. In either case the test
module itself does not need to change or know about these details
of fixture setup.
.. _`request-context`:
Fixtures can introspect the requesting test context
-------------------------------------------------------------
Fixture function can accept the :py:class:`request <FixtureRequest>` object
to introspect the "requesting" test function, class or module context.
Further extending the previous ``smtp`` fixture example, let's
read an optional server URL from the module namespace and register
a finalizer that closes the smtp connection after the last
test in a module finished execution::
read an optional server URL from the test module which uses our fixture::
# content of conftest.py
import pytest
@@ -260,26 +299,23 @@ test in a module finished execution::
def smtp(request):
server = getattr(request.module, "smtpserver", "merlinux.eu")
smtp = smtplib.SMTP(server)
def fin():
print ("finalizing %s" % smtp)
print ("finalizing %s (%s)" % (smtp, server))
smtp.close()
request.addfinalizer(fin)
return smtp
return smtp
The registered ``fin`` function will be called when the last test
using it has executed::
We use the ``request.module`` attribute to optionally obtain an
``smtpserver`` attribute from the test module. If we just execute
again, nothing much has changed::
$ py.test -s -q --tb=no
FF
finalizing <smtplib.SMTP instance at 0x2720290>
We see that the ``smtp`` instance is finalized after the two
tests using it tests executed. If we had specified ``scope='function'``
then fixture setup and cleanup would occur around each single test.
Note that either case the test module itself does not need to change!
2 failed in 0.21 seconds
Let's quickly create another test module that actually sets the
server URL and has a test to verify the fixture picks it up::
server URL in its module namespace::
# content of test_anothersmtp.py
@@ -298,7 +334,8 @@ Running it::
> assert 0, smtp.helo()
E AssertionError: (250, 'mail.python.org')
.. _`request`: :py:class:`_pytest.python.FixtureRequest`
voila! The ``smtp`` fixture function picked up our mail server name
from the module namespace.
.. _`fixture-parametrize`:
@@ -315,7 +352,7 @@ configured in multiple ways.
Extending the previous example, we can flag the fixture to create two
``smtp`` fixture instances which will cause all tests using the fixture
to run twice. The fixture function gets access to each parameter
through the special `request`_ object::
through the special :py:class:`request <FixtureRequest>` object::
# content of conftest.py
import pytest
@@ -342,7 +379,7 @@ So let's just do another run::
================================= FAILURES =================================
__________________________ test_ehlo[merlinux.eu] __________________________
smtp = <smtplib.SMTP instance at 0x1dae368>
smtp = <smtplib.SMTP instance at 0x100ac20>
def test_ehlo(smtp):
response = smtp.ehlo()
@@ -354,7 +391,7 @@ So let's just do another run::
test_module.py:6: AssertionError
__________________________ test_noop[merlinux.eu] __________________________
smtp = <smtplib.SMTP instance at 0x1dae368>
smtp = <smtplib.SMTP instance at 0x100ac20>
def test_noop(smtp):
response = smtp.noop()
@@ -365,18 +402,20 @@ So let's just do another run::
test_module.py:11: AssertionError
________________________ test_ehlo[mail.python.org] ________________________
smtp = <smtplib.SMTP instance at 0x1dbc7a0>
smtp = <smtplib.SMTP instance at 0x105b638>
def test_ehlo(smtp):
response = smtp.ehlo()
assert response[0] == 250
> assert "merlinux" in response[1]
E assert 'merlinux' in 'mail.python.org\nSIZE 10240000\nETRN\nSTARTTLS\nENHANCEDSTATUSCODES\n8BITMIME\nDSN'
E assert 'merlinux' in 'mail.python.org\nSIZE 25600000\nETRN\nSTARTTLS\nENHANCEDSTATUSCODES\n8BITMIME\nDSN'
test_module.py:5: AssertionError
----------------------------- Captured stdout ------------------------------
finalizing <smtplib.SMTP instance at 0x100ac20>
________________________ test_noop[mail.python.org] ________________________
smtp = <smtplib.SMTP instance at 0x1dbc7a0>
smtp = <smtplib.SMTP instance at 0x105b638>
def test_noop(smtp):
response = smtp.noop()
@@ -385,6 +424,7 @@ So let's just do another run::
E assert 0
test_module.py:11: AssertionError
4 failed in 6.58 seconds
We see that our two test functions each ran twice, against the different
``smtp`` instances. Note also, that with the ``mail.python.org``
@@ -424,7 +464,7 @@ Here we declare an ``app`` fixture which receives the previously defined
$ py.test -v test_appsetup.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4 -- /home/hpk/p/pytest/.tox/regen/bin/python
platform linux2 -- Python 2.7.3 -- pytest-2.5.1 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 2 items
test_appsetup.py:12: test_smtp_exists[merlinux.eu] PASSED
@@ -470,8 +510,7 @@ to show the setup/teardown flow::
param = request.param
print "create", param
def fin():
print "fin", param
request.addfinalizer(fin)
print ("fin %s" % param)
return param
@pytest.fixture(scope="function", params=[1,2])
@@ -489,38 +528,36 @@ 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 linux2 -- Python 2.7.3 -- pytest-2.3.4 -- /home/hpk/p/pytest/.tox/regen/bin/python
platform linux2 -- Python 2.7.3 -- pytest-2.5.1 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 8 items
test_module.py:16: test_0[1] PASSED
test_module.py:16: test_0[2] PASSED
test_module.py:18: test_1[mod1] PASSED
test_module.py:20: test_2[1-mod1] PASSED
test_module.py:20: test_2[2-mod1] PASSED
test_module.py:18: test_1[mod2] PASSED
test_module.py:20: test_2[1-mod2] PASSED
test_module.py:20: test_2[2-mod2] PASSED
test_module.py:15: test_0[1] test0 1
PASSED
test_module.py:15: test_0[2] test0 2
PASSED
test_module.py:17: test_1[mod1] create mod1
test1 mod1
PASSED
test_module.py:19: test_2[1-mod1] test2 1 mod1
PASSED
test_module.py:19: test_2[2-mod1] test2 2 mod1
PASSED
test_module.py:17: test_1[mod2] create mod2
test1 mod2
PASSED
test_module.py:19: test_2[1-mod2] test2 1 mod2
PASSED
test_module.py:19: test_2[2-mod2] test2 2 mod2
PASSED
========================= 8 passed in 0.01 seconds =========================
test0 1
test0 2
create mod1
test1 mod1
test2 1 mod1
test2 2 mod1
fin mod1
create mod2
test1 mod2
test2 1 mod2
test2 2 mod2
fin mod2
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.
.. _`usefixtures`:
.. _`usefixtures`:
using fixtures from classes, modules or projects
----------------------------------------------------------------------
@@ -569,6 +606,7 @@ to verify our fixture is activated and the tests pass::
$ py.test -q
..
2 passed in 0.01 seconds
You can specify multiple fixtures like this::
@@ -588,6 +626,7 @@ into an ini-file::
usefixtures = cleandir
.. _`autouse`:
.. _`autouse fixtures`:
autouse fixtures (xUnit setup on steroids)
@@ -639,6 +678,7 @@ If we run it, we get two passing tests::
$ py.test -q
..
2 passed in 0.01 seconds
Here is how autouse fixtures work in other scopes:
@@ -687,3 +727,4 @@ to a :ref:`conftest.py <conftest.py>` file or even separately installable
fixtures functions starts at test classes, then test modules, then
``conftest.py`` files and finally builtin and third party plugins.

View File

@@ -157,7 +157,7 @@ several problems:
that are never needed because it only co-ordinates the test run
activities of the slave processes.
2. if you only perform a collection (with "--collectonly")
2. if you only perform a collection (with "--collect-only")
resource-setup will still be executed.
3. If a pytest_sessionstart is contained in some subdirectories
@@ -182,7 +182,7 @@ funcargs/fixture discovery now happens at collection time
pytest-2.3 takes care to discover fixture/funcarg factories
at collection time. This is more efficient especially for large test suites.
Moreover, a call to "py.test --collectonly" should be able to in the future
Moreover, a call to "py.test --collect-only" should be able to in the future
show a lot of setup-information and thus presents a nice method to get an
overview of fixture management in your project.

View File

@@ -1,7 +1,7 @@
Installation and Getting Started
===================================
**Pythons**: Python 2.4-3.3, Jython, PyPy
**Pythons**: Python 2.5-3.3, Jython, PyPy
**Platforms**: Unix/Posix and Windows
@@ -23,7 +23,7 @@ Installation options::
To check your installation has installed the correct version::
$ py.test --version
This is py.test version 2.3.4, imported from /home/hpk/p/pytest/.tox/regen/lib/python2.7/site-packages/pytest.pyc
This is py.test version 2.5.1, imported from /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/pytest.pyc
If you get an error checkout :ref:`installation issues`.
@@ -45,7 +45,7 @@ That's it. You can execute the test function now::
$ py.test
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.4
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 1 items
test_sample.py F
@@ -93,6 +93,7 @@ Running it with, this time in "quiet" reporting mode::
$ py.test -q test_sysexit.py
.
1 passed in 0.00 seconds
.. todo:: For further ways to assert exceptions see the `raises`
@@ -122,7 +123,7 @@ run the module by passing its filename::
================================= FAILURES =================================
____________________________ TestClass.test_two ____________________________
self = <test_class.TestClass instance at 0x2418e18>
self = <test_class.TestClass instance at 0x2b57dd0>
def test_two(self):
x = "hello"
@@ -130,6 +131,7 @@ run the module by passing its filename::
E assert hasattr('hello', 'check')
test_class.py:8: AssertionError
1 failed, 1 passed in 0.01 seconds
The first test passed, the second failed. Again we can easily see
the intermediate values used in the assertion, helping us to
@@ -157,7 +159,7 @@ before performing the test function call. Let's just run it::
================================= FAILURES =================================
_____________________________ test_needsfiles ______________________________
tmpdir = local('/tmp/pytest-539/test_needsfiles0')
tmpdir = local('/tmp/pytest-38/test_needsfiles0')
def test_needsfiles(tmpdir):
print tmpdir
@@ -166,7 +168,8 @@ before performing the test function call. Let's just run it::
test_tmpdir.py:3: AssertionError
----------------------------- Captured stdout ------------------------------
/tmp/pytest-539/test_needsfiles0
/tmp/pytest-38/test_needsfiles0
1 failed in 0.04 seconds
Before the test runs, a unique-per-test-invocation temporary directory
was created. More info at :ref:`tmpdir handling`.
@@ -198,11 +201,8 @@ easy_install or pip not found?
`Install pip`_ for a state of the art python package installer.
Or consult `distribute docs`_ to install the ``easy_install``
tool on your machine.
You may also use the older `setuptools`_ project but it lacks bug fixes
and does not work on Python3.
Install `setuptools`_ to get ``easy_install`` which allows to install
``.egg`` binary format packages in addition to source-based ones.
py.test not found on Windows despite installation?
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

View File

@@ -8,24 +8,149 @@ Good Integration Practises
Work with virtual environments
-----------------------------------------------------------
We recommend to use virtualenv_ environments and use easy_install_
(or pip_) for installing your application dependencies as well as
the ``pytest`` package itself. This way you will get a much more reproducible
environment. A good tool to help you automate test runs against multiple
dependency configurations or Python interpreters is `tox`_.
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::
virtualenv . # create a virtualenv directory in the current directory
source bin/activate # on unix
scripts/activate # on Windows
We can now install pytest::
pip install pytest
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
------------------------------------------
py.test supports two common test layouts:
* putting tests into an extra directory outside your actual application
code, useful if you have many functional tests or for other reasons
want to keep tests separate from actual application code (often a good
idea)::
setup.py # your distutils/setuptools Python package metadata
mypkg/
__init__.py
appmodule.py
tests/
test_app.py
...
* inlining test directories into your application package, useful if you
have direct relation between (unit-)test and application modules and
want to distribute your tests along with your application::
setup.py # your distutils/setuptools Python package metadata
mypkg/
__init__.py
appmodule.py
...
test/
test_app.py
...
Important notes relating to both schemes:
- **make sure that "mypkg" is importable**, for example by typing once::
pip install -e . # install package using setup.py in editable mode
- **avoid "__init__.py" files in your test directories**.
This way your tests can run easily against an installed version
of ``mypkg``, independently from if the installed package contains
the tests or not.
- With inlined tests you might put ``__init__.py`` into test
directories and make them installable as part of your application.
Using the ``py.test --pyargs mypkg`` invocation pytest will
discover where mypkg is installed and collect tests from there.
With the "external" test you can still distribute tests but they
will not be installed or become importable.
Typically you can run tests by pointing to test directories or modules::
py.test tests/test_app.py # for external test dirs
py.test mypkg/test/test_app.py # for inlined test dirs
py.test mypkg # run tests in all below test directories
py.test # run all tests below current dir
...
Because of the above ``editable install`` mode you can change your
source code (both tests and the app) and rerun tests at will.
Once you are done with your work, you can `use tox`_ to make sure
that the package is really correct and tests pass in all
required configurations.
.. note::
You can use Python3 namespace packages (PEP420) for your application
but pytest will still perform `test package name`_ discovery based on the
presence of ``__init__.py`` files. If you use one of the
two recommended file system layouts above but leave away the ``__init__.py``
files from your directories it should just work on Python3.3 and above. From
"inlined tests", however, you will need to use absolute imports for
getting at your application code.
.. _`test package name`:
.. note::
If py.test finds a "a/b/test_module.py" test file while
recursing into the filesystem it determines the import name
as follows:
* determine ``basedir``: this is the first "upward" (towards the root)
directory not containing an ``__init__.py``. If e.g. both ``a``
and ``b`` contain an ``__init__.py`` file then the parent directory
of ``a`` will become the ``basedir``.
* perform ``sys.path.insert(0, basedir)`` to make the test module
importable under the fully qualified import name.
* ``import a.b.test_module`` where the path is determined
by converting path separators ``/`` into "." characters. This means
you must follow the convention of having directory and file
names map directly to the import names.
The reason for this somewhat evolved importing technique is
that in larger projects multiple test modules might import
from each other and thus deriving a canonical import name helps
to avoid surprises such as a test modules getting imported twice.
.. _`virtualenv`: http://pypi.python.org/pypi/virtualenv
.. _`buildout`: http://www.buildout.org/
.. _pip: http://pypi.python.org/pypi/pip
.. _`use tox`:
Use tox and Continuous Integration servers
-------------------------------------------------
If you frequently release code to the public you
may want to look into `tox`_, the virtualenv test automation
tool and its `pytest support <http://codespeak.net/tox/example/pytest.html>`_.
The basic idea is to generate a JUnitXML file through the ``--junitxml=PATH`` option and have a continuous integration server like Jenkins_ pick it up
and generate reports.
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
virtualenv test automation tool and its `pytest support
<http://testrun.org/tox/latest/example/pytest.html>`_.
Tox helps you to setup virtualenv environments with pre-defined
dependencies and then executing a pre-configured test command with
options. It will run tests against the installed package and not
against your source code checkout, helping to detect packaging
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`:
@@ -33,23 +158,19 @@ and generate reports.
Create a py.test standalone script
-------------------------------------------
If you are a maintainer or application developer and want others
to easily run tests you can generate a completely standalone "py.test"
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 "py.test" script::
py.test --genscript=runtests.py
generates a ``runtests.py`` script which is a fully functional basic
This generates a ``runtests.py`` script which is a fully functional basic
``py.test`` 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
.. _`Distribute for installation`: http://pypi.python.org/pypi/distribute#installation-instructions
.. _`distribute installation`: http://pypi.python.org/pypi/distribute
Integrating with distutils / ``python setup.py test``
--------------------------------------------------------
@@ -92,12 +213,12 @@ arguments to the subprocess-calls such as your test directory or other
options.
Integration with setuptools/distribute test commands
Integration with setuptools test commands
----------------------------------------------------
Distribute/Setuptools support test requirements,
which means its really easy to extend its test command
to support running a pytest from test requirements::
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::
from setuptools.command.test import test as TestCommand
import sys
@@ -146,69 +267,4 @@ For examples of how to customize your test discovery :doc:`example/pythoncollect
Within Python modules, py.test also discovers tests using the standard
:ref:`unittest.TestCase <unittest.TestCase>` subclassing technique.
Choosing a test layout / import rules
------------------------------------------
py.test supports common test layouts:
* inlining test directories into your application package, useful if you want to
keep (unit) tests and actually tested code close together::
mypkg/
__init__.py
appmodule.py
...
test/
test_app.py
...
* putting tests into an extra directory outside your actual application
code, useful if you have many functional tests or want to keep
tests separate from actual application code::
mypkg/
__init__.py
appmodule.py
tests/
test_app.py
...
In both cases you usually need to make sure that ``mypkg`` is importable,
for example by using the setuptools ``python setup.py develop`` method.
You can run your tests by pointing to it::
py.test tests/test_app.py # for external test dirs
py.test mypkg/test/test_app.py # for inlined test dirs
py.test mypkg # run tests in all below test directories
py.test # run all tests below current dir
...
.. _`package name`:
.. note::
If py.test finds a "a/b/test_module.py" test file while
recursing into the filesystem it determines the import name
as follows:
* find ``basedir`` -- this is the first "upward" (towards the root)
directory not containing an ``__init__.py``. If both the ``a``
and ``b`` directories contain an ``__init__.py`` the basedir will
be the parent dir of ``a``.
* perform ``sys.path.insert(0, basedir)`` to make the test module
importable under the fully qualified import name.
* ``import a.b.test_module`` where the path is determined
by converting path separators ``/`` into "." characters. This means
you must follow the convention of having directory and file
names map directly to the import names.
The reason for this somewhat evolved importing technique is
that in larger projects multiple test modules might import
from each other and thus deriving a canonical import name helps
to avoid surprises such as a test modules getting imported twice.
.. include:: links.inc

BIN
doc/en/img/cramer2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
doc/en/img/gaynor3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
doc/en/img/keleshev.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
doc/en/img/theuni.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -1,56 +1,59 @@
.. _features:
.. second training: `professional testing with Python <http://www.python-academy.com/courses/specialtopics/python_course_testing.html>`_ , 25-27th November 2013, Leipzig.
pytest: helps you write better programs
=============================================
**a mature full-featured Python testing tool**
- runs on Posix/Windows, Python 2.4-3.3, PyPy and Jython-2.5.1
- runs on Posix/Windows, Python 2.5-3.3, PyPy and Jython-2.5.1
- **zero-reported-bugs** policy with >1000 tests against itself
- **strict backward compatibility policy** for safe pytest upgrades
- :ref:`comprehensive online <toc>` and `PDF documentation <pytest.pdf>`_
- used in :ref:`many projects and organisations <projects>`, in test
suites ranging from 10 to 10s of thousands of tests
- many :ref:`third party plugins <extplugins>` and :ref:`builtin helpers <pytest helpers>`,
- used in :ref:`many small and large projects and organisations <projects>`
- comes with many :ref:`tested examples <examples>`
**provides easy no-boilerplate testing**
- makes it :ref:`easy to get started <getstarted>`,
many :ref:`usage options <usage>`
has many :ref:`usage options <usage>`
- :ref:`assert with the assert statement`
- helpful :ref:`traceback and failing assertion reporting <tbreportdemo>`
- allows :ref:`print debugging <printdebugging>` and :ref:`the
- :ref:`print debugging <printdebugging>` and :ref:`the
capturing of standard output during test execution <captures>`
**scales from simple unit to complex functional testing**
- (new in 2.3) :ref:`modular parametrizeable fixtures <fixture>`
- :ref:`modular parametrizeable fixtures <fixture>` (new in 2.3,
continously improved)
- :ref:`parametrized test functions <parametrized test functions>`
- :ref:`mark`
- :ref:`skipping`
- can :ref:`distribute tests to multiple CPUs <xdistcpu>` through :ref:`xdist plugin <xdist>`
- can :ref:`continuously re-run failing tests <looponfailing>`
- many :ref:`builtin helpers <pytest helpers>` and :ref:`plugins <plugins>`
- :ref:`skipping` (improved in 2.4)
- :ref:`distribute tests to multiple CPUs <xdistcpu>` through :ref:`xdist plugin <xdist>`
- :ref:`continuously re-run failing tests <looponfailing>`
- flexible :ref:`Python test discovery`
**integrates many common testing methods**:
**integrates with other testing methods and tools**:
- multi-paradigm: pytest can run many ``nose``, ``unittest.py`` and
``doctest.py`` style test suites, including running testcases made for
- 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 extended :ref:`xUnit style setup <xunitsetup>`
- supports domain-specific :ref:`non-python tests`
- supports the generation of testing coverage reports
- `Javascript unit- and functional testing`_
- supports generating `test coverage reports
<https://pypi.python.org/pypi/pytest-cov>`_
- supports :pep:`8` compliant coding styles in tests
**extensive plugin and customization system**:
- all collection, reporting, running aspects are delegated to hook functions
- customizations can be per-directory, per-project or per PyPI released plugins
- it is easy to add command line options or do other kind of add-ons and customizations.
- customizations can be per-directory, per-project or per PyPI released plugin
- it is easy to add command line options or customize existing behaviour
.. _`Javascript unit- and functional testing`: http://pypi.python.org/pypi/oejskit
.. _`easy`: http://bruynooghe.blogspot.com/2009/12/skipping-slow-test-by-default-in-pytest.html

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