Compare commits

..

578 Commits
2.4.0 ... 2.6.2

Author SHA1 Message Date
holger krekel
7dad3cb157 prepare 2.6.2, release announcement, also add HOWTORELEASE.rst 2014-09-05 13:13:23 +02:00
holger krekel
745737e337 strike python2.5 from test code cc @flub 2014-09-05 09:50:40 +02:00
Floris Bruynooghe
eae1055fb0 Merged in nocoddemus/pytest/cx_freeze-support (pull request #189) 2014-09-03 21:55:46 +01:00
holger krekel
2e1c36bbb6 merged in flub/pytest (PR #195) 2014-09-02 12:33:19 +02:00
holger krekel
e3cea41dcd fix issue572 - python3 compat of tmpdir example in docs. 2014-09-02 12:20:16 +02:00
Benjamin Peterson
c0f091d540 remove debugging turd 2014-09-01 16:51:44 -04:00
Benjamin Peterson
d4cd1aad8e improvements to rewrite cache invalidation
- stat the source path before it is read.
- Validate the source size in addition to mtime.
2014-09-01 16:51:27 -04:00
Floris Bruynooghe
39a297afe6 Improve test by also matching expected output 2014-08-30 21:57:01 +01:00
Floris Bruynooghe
068548f7a9 Merged in flub/pytest (pull request #196) 2014-08-27 21:00:24 +01:00
Bruno Oliveira
0c163ce624 Updating plugins_index
Also fixed duplicated links when repository is not github or
bitbucket
2014-08-26 20:38:19 -03:00
Bruno Oliveira
fc95877622 Added changelog entry for freeze_includes()
--HG--
branch : cx_freeze-support
2014-08-23 10:10:32 -03:00
Bruno Oliveira
03b694a1d0 merging with default
--HG--
branch : cx_freeze-support
2014-08-23 10:05:40 -03:00
Floris Bruynooghe
79c2a47985 Improve the docstring further 2014-08-23 12:10:16 +02:00
Floris Bruynooghe
9289d77a80 Mention why no universal wheel in changelog
Fixes issue566.
2014-08-22 20:22:51 +02:00
Floris Bruynooghe
2eee2d0099 Merged in sontek/pytest/fix_universal (pull request #198)
Removed marking pytest as universal, py26 and py27 are not compatible.
2014-08-22 20:16:17 +02:00
sontek@gmail.com
0ea46e6aef Removed marking pytest as universal, py26 and py27 are not compatible.
--HG--
branch : fix_universal
2014-08-21 12:47:53 -07:00
Floris Bruynooghe
b0ac3581dd Mention doc fix in changelog 2014-08-19 21:00:46 +02:00
Floris Bruynooghe
e7ed45a5d4 Explain why this is important 2014-08-19 20:50:25 +02:00
Thomas Weißschuh
7cf859085e [doc] fix requests monkeypatch example 2014-08-18 19:44:34 +00:00
Floris Bruynooghe
424479cf0f Escape newlines in repr for assertion rewriting
The assertion formatting mini-language depends on newlines being
escaped.  Unfortunately if the repr of an object contained
newlines the rewriting module did not escape those, which is now
fixed.

Fixes issue453.
2014-08-18 20:07:38 +02:00
Floris Bruynooghe
98dcd764bc Depend on newer version of py
This fixes issue560, a test for which is included here as well.
2014-08-14 23:23:04 +01:00
Bruno Oliveira
ccd67733fb standard lib modules no longer required in freeze_includes() and updated docs
--HG--
branch : cx_freeze-support
2014-08-11 20:20:41 -03:00
Bruno Oliveira
5873ca5146 Merging with default
--HG--
branch : cx_freeze-support
2014-08-11 20:07:11 -03:00
Bruno Oliveira
d2903507d8 Moved freeze_includes() to genscript
--HG--
branch : cx_freeze-support
2014-08-11 20:03:14 -03:00
holger krekel
9232b88df3 actually regen fixture docs with python3.4 instead of python2.7 (doh) 2014-08-08 15:25:16 +02:00
holger krekel
e98f77037e fix issue561 example adapted to python3. 2014-08-08 15:20:37 +02:00
holger krekel
672e42e558 fix doc config for latex 2014-08-07 23:05:02 +02:00
holger krekel
36d7df4542 Added tag 2.6.1 for changeset a4f9639702ba 2014-08-07 22:55:17 +02:00
holger krekel
e5eaf02e19 finalize pytest-2.6.1 release, regen docs 2014-08-07 21:41:51 +02:00
holger krekel
1d7b574b31 fix issue555: just add "errors" attribute to internal Capture stream. 2014-08-07 11:05:42 +02:00
holger krekel
d16fdb378c merge PR192, streamline a bit. 2014-08-07 10:42:23 +02:00
Bruno Oliveira
cc092afd3b updated CHANGELOG and trace error message as requested in review
fixes issue #437

--HG--
branch : assertionrewrite-currupted-pyc
2014-08-04 20:38:50 -03:00
Bruno Oliveira
fd4485a540 Fixed assertionrewrite._read_pyc to handle corrupted pyc files properly
This seems to be the cause for issues #437 and #301.

--HG--
branch : assertionrewrite-currupted-pyc
2014-08-02 18:01:28 -03:00
Floris Bruynooghe
3b8779ad17 Mention change in -v output in changelog 2014-08-01 23:11:25 +01:00
Floris Bruynooghe
3e875178ad Merge default 2014-08-01 23:06:24 +01:00
holger krekel
97b671057d put some imports back to function-level and streamline py2/py3 compat in one place 2014-08-01 10:12:53 +02:00
holger krekel
d6fc489b2b Backed out changeset e57017ad86ce -- logging should not be imported 2014-08-01 08:13:44 +02:00
Floris Bruynooghe
4e8438afc8 Simply show the node ID for verbose output
This strips the line number, /@\d/, from the verbose output so it is
directly the node ID of the test.  This in turn means no special logic
for accepting the line number as part of the node ID is needed when
parsing the command line.
2014-08-01 00:29:35 +01:00
Bruno Oliveira
b1f8038abf Minor fixed in test_capture
This test failed only in py34. We don't import logging directly,
but it seems that one of the standard modules that are now
globally imported started including this on py34.

Just removed the assert as it doesn't seem central to the
test's objective.
2014-07-31 19:52:08 -03:00
Bruno Oliveira
5603a0cd4b Removing py.std usage from _pytest 2014-07-31 19:13:40 -03:00
Bruno Oliveira
3c649cf91d guarding scripts with __main__ and doc changes
tox-flakes environment tries to import the modules for
checking, and that may fail because of its dependencies

--HG--
branch : cx_freeze-support
2014-07-30 22:28:03 -03:00
Bruno Oliveira
82d573e391 fixed line endings for test_doctest.txt
--HG--
branch : cx_freeze-support
2014-07-30 21:57:19 -03:00
Bruno Oliveira
b7b96b24d8 Docs about cx_freeze support and minor adjustments
--HG--
branch : cx_freeze-support
2014-07-30 21:50:00 -03:00
Bruno Oliveira
990e7bf3b9 first implementation and tox environment for cx-freeze support
--HG--
branch : cx_freeze-support
2014-07-30 19:16:51 -03:00
holger krekel
bcdc3d0154 reorder talks, more talks from pelme and others 2014-07-29 15:06:37 +02:00
holger krekel
faea7e1407 add flub and pelme talks at EuroPython2014, remove one past training note 2014-07-29 15:00:45 +02:00
holger krekel
7c701948d5 Merged in nicoddemus/pytest/cx_freeze-docs (pull request #188)
Documentation example on how to integrate pytest and cx_freeze
2014-07-29 12:43:04 +02:00
Bruno Oliveira
899b804ec1 Improved the text a little
--HG--
branch : cx_freeze-docs
2014-07-28 22:46:57 -03:00
Bruno Oliveira
66bd4e485a Adding blurb about using pytest runner and cx_freeze
--HG--
branch : cx_freeze-docs
2014-07-28 22:40:23 -03:00
Anatoly Bubenkov
8ff8dd3ae9 Merged in fix_initial_parsing (pull request #186)
Fix issue544 and fix another issue with parsing ``::``
2014-07-28 14:16:02 +02:00
holger krekel
0d17dc1e19 add a comment for why we only consider .py files when removing @
--HG--
branch : fix_initial_parsing
2014-07-28 13:56:10 +02:00
holger krekel
83e0b52294 speedup @ replacement for the massive lists from pytest-bdd :)
--HG--
branch : fix_initial_parsing
2014-07-28 13:53:53 +02:00
holger krekel
1265612465 fix issue547 2.6 regression: capsys/capfd now work again when output capturing ("-s") is disabled. 2014-07-28 13:17:37 +02:00
holger krekel
40eed363e8 fix issue544 by only removing "@NUM" at the end of a part (parts are
separated by "::") and if the part has an .py extension.

--HG--
branch : fix_initial_parsing
2014-07-28 12:07:15 +02:00
holger krekel
5ccd3f2fc5 fix conftest detection if commandline arguments contain "::" syntax
--HG--
branch : fix_initial_parsing
2014-07-28 11:48:37 +02:00
holger krekel
ba878c6d9d add changelog entry and refactor unittest.mock.patch fix a bit 2014-07-28 10:34:01 +02:00
holger krekel
8792261df1 Merged in ticosax/pytest/mock-unittest-252 (pull request #184)
Injection of fixture doesn't work when decorated with unittest.mock.patch
2014-07-28 10:31:31 +02:00
holger krekel
c2ed29070a address issue170 by merging David Mohr's PR on adding "raises" as an optional
argument to pytest.mark.xfail.
2014-07-28 09:59:22 +02:00
holger krekel
38104dfc92 Merged in squisher/pytest/xfail-cause (pull request #183)
Add mark.xfail argument raises so that unexpected exceptions show up as test failures.
2014-07-28 09:55:02 +02:00
Nicolas Delaby
2e55c4ba61 unittest.mock from stdlib should come last
--HG--
branch : mock-unittest-252
2014-07-27 12:11:39 +02:00
Nicolas Delaby
e6ad6e02d2 Handle also unittest.mock
Move handling in dedicated function to isolate its logic

--HG--
branch : mock-unittest-252
2014-07-27 10:43:50 +02:00
david@mcbf.net
d08c4ce0ad Tiny example update for clarification
--HG--
branch : xfail-cause
2014-07-26 18:24:55 +02:00
david@mcbf.net
309e3d38a0 Directly pass multiple parameters with mark.parametrize()
--HG--
branch : xfail-cause
2014-07-26 18:19:27 +02:00
david@mcbf.net
91e2b23258 Update documentation
--HG--
branch : xfail-cause
2014-07-26 18:10:32 +02:00
david@mcbf.net
6a4492a22d isinstance() on exception value instead of comparing types, consolidate tests
--HG--
branch : xfail-cause
2014-07-26 17:46:50 +02:00
Nicolas Delaby
0dd378da30 Injection of fixture doesn't work when decorated with unittest.mock.patch
This is a continuation of #182

--HG--
branch : mock-unittest-252
2014-07-26 17:26:18 +02:00
david@mcbf.net
7b273b8577 Add mark.xfail argument raises so that unexpected exceptions show up as test failures.
--HG--
branch : xfail-cause
2014-07-26 15:11:05 +02:00
holger krekel
d98521b0d9 Added tag 2.6.0 for changeset 88af949b9611 2014-07-20 13:15:44 +02:00
holger krekel
52011e84d3 merge 2014-07-20 13:15:21 +02:00
holger krekel
de583ed7a3 some notes about upcoming trainings 2014-07-20 13:07:38 +02:00
holger krekel
f8480caae4 Added tag 2.6.0 for changeset 60725b17a9d1 2014-07-20 13:03:11 +02:00
holger krekel
47d9e6ca1f fix release announce 2014-07-20 11:07:18 +02:00
Bruno Oliveira
6f0a33dfdc Updating plugins_index 2014-07-18 21:10:50 -03:00
Floris Bruynooghe
1641d00cb1 Merge default 2014-07-18 01:34:08 +01:00
Floris Bruynooghe
2d7a32f7ea Add test case for using different modules 2014-07-18 01:30:29 +01:00
holger krekel
06acbb9f5e bump doc version 2014-07-17 17:22:44 +02:00
holger krekel
7b630d9080 mention next open trainings at EP2014 and in Freiburg in November 2014 2014-07-17 17:21:45 +02:00
holger krekel
98de64badc regen docs for pytest-2.6 2014-07-17 16:55:24 +02:00
holger krekel
1e241e1f2a mention py34 compatibility 2014-07-17 16:49:10 +02:00
holger krekel
ff2c18fedb bump version, preliminary announcement 2014-07-17 16:38:54 +02:00
holger krekel
bf64a800d6 pytest depends on the freshly released 1.4.22 to function properly 2014-07-17 10:55:52 +02:00
Jurko Gospodnetić
efc57391eb remove extra parentheses - stylistic code change 2014-07-17 08:35:36 +02:00
Benjamin Peterson
dc65aa1fea avoid importing old assertion interpretation code by default (fixes #537) 2014-07-16 17:21:18 -07:00
holger krekel
80ad3fb8ed Merged in wooparadog/pytest (pull request #163)
Also replace `report.longrepr` with `bin_xml_escape`ed skipreason
2014-07-15 22:17:15 +02:00
holger krekel
bc7110931a Merged in alex_gaynor/pytest/alex_gaynor/marked-pytest-as-being-a-universal-wheel-1405267754279 (pull request #179)
Marked pytest as being a universal wheel.
2014-07-15 11:26:08 +02:00
holger krekel
8c508612ec with pypy this tests leaks one FD but it's probably a residual effect of the testing machinery and Pypy's lazy GC. 2014-07-14 09:17:04 +02:00
Alex Gaynor
b7d046527e Marked pytest as being a universal wheel.
--HG--
branch : alex_gaynor/marked-pytest-as-being-a-universal-wheel-1405267754279
2014-07-13 16:09:24 +00:00
holger krekel
42804c52e8 Merged in bmcorser/pytest-1/bmcorser/scratching-an-itch-love-pytest-1404943207227 (pull request #178)
Scratching an itch. Love py.test!
2014-07-10 09:00:24 +02:00
bmcorser
d88a3712c5 Scratching an itch. Love py.test!
--HG--
branch : bmcorser/scratching-an-itch-love-pytest-1404943207227
2014-07-09 22:00:24 +00:00
Bruno Oliveira
abfedd692e Updating plugins_index 2014-07-08 20:08:19 -03:00
holger krekel
d0b048c86d Merged in msabramo/pytest/norecursedirs_add_star_dot_egg (pull request #177)
Add *.egg to default for norecursedirs
2014-07-03 16:51:17 +02:00
Marc Abramowitz
b6f069f4c3 Add *.egg to default for norecursedirs
--HG--
branch : norecursedirs_add_star_dot_egg
2014-07-03 07:49:03 -07:00
holger krekel
edc2e5ab82 Merged in msabramo/pytest/fix_norecursedirs_doc_typos (pull request #176)
doc/en/customize.txt: Fix norecursedirs typo
2014-07-03 16:47:58 +02:00
Marc Abramowitz
6da9a087f8 doc/en/customize.txt: Fix norecursedirs typo
and update documented default to match current default.

--HG--
branch : fix_norecursedirs_doc_typos
2014-07-03 07:36:07 -07:00
holger krekel
e19462d581 fix ordering of import line of last commit 2014-07-03 13:20:51 +02:00
holger krekel
a811fabb43 avoid importing "py.test" (an old alias module for "pytest") 2014-07-03 12:58:12 +02:00
holger krekel
07e76cbef2 fix issue364: shorten and enhance tracebacks representation by default.
The new "--tb=auto" option (default) will only display long tracebacks
for the first and last entry.  You can get the old behaviour of printing
all entries as long entries with "--tb=long".  Also short entries by
default are now printed very similarly to "--tb=native" ones.
2014-06-29 13:32:53 +02:00
holger krekel
76d5c9e4f4 Merged in c_henz/pytest/explicit-ini-filename (pull request #175)
Implement the "-c" command line switch that allows to explicitly specifiy the config file to load.
2014-06-29 12:08:29 +02:00
holger krekel
abcadc4202 colors as used on the live web page 2014-06-29 09:47:50 +02:00
Christian Henz
b7f6a9f3fd Cleaner implementation of early handling of the '-c' command line switch.
--HG--
branch : explicit-ini-filename
2014-06-28 12:03:55 +02:00
christian@christian-linux.sarrazin.local
c8264385ea Implement the "-c" command line switch that allows to explicitly specifiy the config file to load.
This feature was requested in issue #174.

--HG--
branch : explicit-ini-filename
2014-06-27 17:42:37 +02:00
Floris Bruynooghe
ad8131be9e Re-add accidentally removed changelog items 2014-06-24 22:46:57 +01:00
holger krekel
54c88a6cf3 fix flakes issues 2014-06-16 11:27:32 +02:00
Floris Bruynooghe
115f15600f Add test for inter-dependent fixtures
Together with cc0a46a13ac4 this fixes issue 467.
2014-06-15 19:57:52 +01:00
Bruno Oliveira
65a145e2a7 Updated plugins index 2014-06-10 19:04:52 -03:00
holger krekel
5719a72eeb Merged in zyegfryed/pytest/zyegfryed/fixed-typo-noticed-by-dcramer-httpstwitt-1401780587159 (pull request #172)
Fixed typo noticed by @zeeg (https://twitter.com/zeeg/status/473676721128886272)
2014-06-03 09:49:02 +02:00
Sébastien Fievet
4bc4495115 Fixed typo noticed by @dcramer (https://twitter.com/zeeg/status/473676721128886272)
--HG--
branch : zyegfryed/fixed-typo-noticed-by-dcramer-httpstwitt-1401780587159
2014-06-03 07:30:14 +00:00
holger krekel
c66e9f8f0f fix typo, thanks @dcramer 2014-06-03 07:21:02 +02:00
holger krekel
52eafdc21e also add favicon 2014-06-01 12:36:31 +02:00
holger krekel
85c0d5481b incorporate the new pytest logo 2014-06-01 12:01:40 +02:00
Benjamin Peterson
fd9055fd11 fix test on Python 3.2 2014-05-31 14:51:05 -07:00
Benjamin Peterson
780bdda95a assert reinterpretation: try mangling attributes that look like private class vars (fixes #514) 2014-05-31 14:37:02 -07:00
Bruno Oliveira
fd4b461290 Updated plugins_index and updated python 3 to 3.4 2014-05-28 23:16:49 -03:00
holger krekel
040062e40c improve example for pytest integration with "python setup.py test"
which now has a generic "-a" or "--pytest-args" option where you
can pass additional options as a quoted string.  Thanks Trevor Bekolay.
2014-05-19 20:32:09 +02:00
holger krekel
d853e9167a Merged in tbekolay/pytest/tbekolay/better-setuptools-integration-in-goodpra-1400520902689 (pull request #171)
Better setuptools integration in goodpractices
2014-05-19 20:30:54 +02:00
Trevor Bekolay
d50ad270f0 Better setuptools integration in goodpractices
--HG--
branch : tbekolay/better-setuptools-integration-in-goodpra-1400520902689
2014-05-19 17:35:28 +00:00
holger krekel
30c93701a7 fix issue516: tell in getting-started about current dependencies.
cleanup setup.py a bit and specify supported versions. Thanks Jurko
Gospodnetic for the PR.
2014-05-14 09:58:34 +02:00
holger krekel
b507e1754c fix issue512 : document dependencies in getting-started doc. 2014-05-14 09:14:40 +02:00
holger krekel
748fce94fd Merged in jurko/pytest/setup cleanup (pull request #169)
setup.py cleanup/update (updated from pull request #167)
2014-05-14 09:10:13 +02:00
holger krekel
d6281b4206 - restore compatibility to old getvalueorskip behaviour
- introduce a better NOTSET representation to improve docs
2014-05-14 07:36:31 +02:00
holger krekel
b61ed2cf7e Merged in jurko/pytest/python 3.1 fix (pull request #168)
use py.builtin.callable instead of raw callable in _pytest/runner.py
2014-05-13 22:41:12 +02:00
Jurko Gospodnetić
9263f30c88 use py.builtin.callable instead of raw callable in _pytest/runner.py
This is consistent with how callable() is called from the rest of pytest code
(see _pytest/nose.py & _pytest/python.py) plus, as a nice side-effect, it
makes pytest work correctly on Python 3.1.

--HG--
branch : python 3.1 fix
2014-05-13 21:05:53 +02:00
Jurko Gospodnetić
8f9a88ef7a update the list of officially supported Python versions in setup.py
Removed Python 3.0 & 3.1 off the list and added Python 3.4.

--HG--
branch : setup cleanup
2014-05-13 19:03:17 +02:00
Jurko Gospodnetić
c64af0d9ce stylistic setup.py code cleanup
--HG--
branch : setup cleanup
2014-05-13 19:02:02 +02:00
holger krekel
9181df42da Merged in msabramo/pytest/require_argparse_for_py3_lt_32 (pull request #166)
setup.py: Require argparse for PY3 < 3.2
2014-05-11 19:30:11 +02:00
holger krekel
74e1a49dd7 remove the idea that nose support should be moved to a plugin. 2014-05-10 14:00:09 +02:00
holger krekel
468b1241a5 fix issue512: show "<notset>" for arguments which might not be set
in monkeypatch plugin.  Improves output in documentation.
2014-05-10 13:49:24 +02:00
Marc Abramowitz
24744cf5cf setup.py: Require argparse for PY3 < 3.2
E.g.: python3.1; noticed py31 failure while running tox for six

--HG--
branch : require_argparse_for_py3_lt_32
2014-05-09 23:40:06 -07:00
Floris Bruynooghe
ffc969b6c2 Do not list python 2.5 as supported in the docs 2014-05-01 22:25:03 +01:00
WooParadog
0567a8ee77 Add test for skipped case with utf-8 message 2014-04-24 10:45:39 +08:00
WooParadog
580c8525f0 Use processced skipreason for generating skip Junit node 2014-04-23 15:38:40 +08:00
WooParadog
d6010aa0c9 Also replace report.longrepr with bin_xml_escapeed skipreason 2014-04-23 14:50:21 +08:00
Floris Bruynooghe
c46e2cbbc7 Cache exception raised in fixtures according to their scope
Without this if a session scoped fixture fails it's setup it will
be re-tried each time it is requested.  Especially in case of
skip or failure exceptions this can be undesirable, but caching
makes sense for all exceptions.
2014-04-15 22:22:41 -04:00
Floris Bruynooghe
c47835f5ec Merge pull request #158, fixes issue 504 2014-04-15 11:43:38 -04:00
Floris Bruynooghe
412b56f7cf Changelog for issue 475 2014-04-14 18:12:29 -04:00
Floris Bruynooghe
faba432996 Improve error message if pytest.raises is used wrongly
If the type is not checked then an incomprehensible error will occur
later.  This enforces the type and raies the same exception/msg as
CPython does in that case.

Docstring unmodified, just re-justified for pep8 compat.
2014-04-14 18:09:10 -04:00
Andy Freeland
2ba23e8d08 issue504: verbose output displays node IDs for each test
Replace the verbose per-test reporting format of `file:line test_name RESULT`
with the node ID of the test, i.e. `file@line::class::method[param] RESULT`.

This patch does not update the examples in the docs; @hpk42 has a script
to regenerate those.

--HG--
branch : issue504
2014-04-14 17:42:02 -04:00
Floris Bruynooghe
d74f852fd6 Merged in rouge8/pytest/issue499 (pull request #157)
fix issue499: document selecting tests by node ID
2014-04-14 14:31:07 -04:00
Andy Freeland
1728798e81 Interal link to node ID explanation
--HG--
branch : issue499
2014-04-14 14:24:13 -04:00
Andy Freeland
53a8d20d88 fix issue499: document selecting tests by node ID
--HG--
branch : issue499
2014-04-14 12:27:55 -04:00
Floris Bruynooghe
61446faa17 Update changelog with last change 2014-04-12 17:01:05 -04:00
Floris Bruynooghe
9711e335d9 Change XPASS colour to be yellow rather then red
Unfortunately I'm not sure how to test this.
2014-04-12 10:27:12 -04:00
Anatoly Bubenkov
080a9d2f12 Merged in hpk42/pytest-hpk/nose_test_attr (pull request #154)
support nose-style __test__ attribute to disable collection of test modules/classes/functions
2014-04-10 22:38:53 +02:00
holger krekel
15af7e1662 fix tests to properly fail on failed collectiosn (which was hiding an error)
and also implement __test__=False for test functions properly.

--HG--
branch : nose_test_attr
2014-04-10 13:37:39 +02:00
holger krekel
e42cbc714f fix wrong merge
--HG--
branch : nose_test_attr
2014-04-10 12:58:10 +02:00
holger krekel
5e26e6e553 fix typo in changelog
--HG--
branch : nose_test_attr
2014-04-10 12:56:14 +02:00
holger krekel
d0a4d348fe merge default
--HG--
branch : nose_test_attr
2014-04-10 12:53:33 +02:00
holger krekel
494be731e3 support nose-style `__test__` attribute on modules, classes and
functions, including unittest-style Classes.  If set to True, the
test will not be collected.

--HG--
branch : nose_test_attr
2014-04-10 12:46:27 +02:00
Ronny Pfannschmidt
8ae244a06a Merged in hpk42/pytest-hpk/issue473 (pull request #152)
fix issue473: work around mock putting an unbound method into a class
2014-04-09 06:46:01 +02:00
holger krekel
f91049cec9 fix issue473: work around mock putting an unbound method into a class
dict when double-patching.

--HG--
branch : issue473
2014-04-08 12:50:13 +02:00
Ronny Pfannschmidt
270d0f89ba Merged in hpk42/pytest-hpk/issue498 (pull request #151)
fix issue498: if a fixture finalizer fails, make sure that the fixture
2014-04-07 13:51:03 +02:00
holger krekel
e382ed4245 fix issue439: clarify that capsys/capfd capture output during
test execution, not test setup.
2014-04-07 13:42:48 +02:00
holger krekel
ef7cb47b1e fix issue498: if a fixture finalizer fails, make sure that the fixture
is still invalidated.

--HG--
branch : issue498
2014-04-07 13:29:57 +02:00
holger krekel
6efde60b8b Merged in jurko/pytest/jurko/tox_usage (pull request #150)
runtox.py cleanup
2014-04-06 08:16:24 +02:00
Jurko Gospodnetić
fd059359cc make runtox.py not import external modules unless run as a script
Was not needed since the script actually does nothing unless run as a script.

--HG--
branch : jurko/tox_usage
2014-04-05 08:51:00 +02:00
Jurko Gospodnetić
c2c504797e stylistic runtox.py code cleanup
--HG--
branch : jurko/tox_usage
2014-04-05 08:49:30 +02:00
Jurko Gospodnetić
84f9f45f98 fix runtox.py failure when 'tox' is not available on the current system path
Now just assumes that the tox module is available in the executing Python
environment.

--HG--
branch : jurko/tox_usage
2014-04-05 08:47:04 +02:00
holger krekel
28aa4c891e bump version to 2.6.0.dev1 because i think we are going for a 2.6.0 release next
and not just a 2.5 maintenance one.
2014-04-03 22:27:04 +02:00
holger krekel
6ff0fdb977 fix issue443: fix skip examples to use proper comparison. Thanks Alex
Groenholm.
2014-04-03 22:26:10 +02:00
holger krekel
b0837693d0 add some changelog entries 2014-04-03 10:04:09 +02:00
holger krekel
52851e4388 Merged in jurko/pytest/fix_capfd_fixture_docstring (pull request #149)
correct a capfd fixture docstring typo
2014-04-03 10:02:00 +02:00
holger krekel
cbe31f3748 Merged in msabramo/pytest/makepyfile_utf8 (pull request #134)
Make makepyfile accept UTF-8 so a few cookie tests in test_assertrewrite.py
2014-04-03 10:00:24 +02:00
Jurko Gospodnetić
c9bbdf4f10 correct a capfd fixture docstring typo
--HG--
branch : fix_capfd_fixture_docstring
2014-04-03 09:59:04 +02:00
holger krekel
f984e94fca Merged in jurko/pytest/break_ExceptionInfo_reference_cycles (pull request #144)
break reference cycles caused by storing local reference to exception info
2014-04-03 09:47:41 +02:00
Anatoly Bubenkov
b4fe91943d Merged in hpk42/pytest-hpk/conftest-clean (pull request #148)
cleanup internal conftest handling and avoid the strange None entry in the conftest cache.
2014-04-03 09:38:47 +02:00
Anatoly Bubenkov
7d6317802e Merged in hpk42/pytest-hpk/issue486 (pull request #147)
fix issue486: better reporting and handling of early conftest loading failures
2014-04-03 00:19:05 +02:00
holger krekel
0365e5c3a0 cleanup internal conftest handling and avoid the strange None entry in the conftest cache.
(There is basically no reason to ask for conftestmodules without specifying a path.)

--HG--
branch : conftest-clean
2014-04-02 22:30:45 +02:00
holger krekel
e6859406f1 fix test on py33, thanks msabramo1 2014-04-02 20:49:57 +02:00
holger krekel
51cff6f106 fix issue486: better reporting and handling of early conftest loading failures
--HG--
branch : issue486
2014-04-02 20:42:41 +02:00
holger krekel
68e58e1493 add a test for robustness of capturing when a test closes FD1/2 2014-04-02 20:29:10 +02:00
holger krekel
9f7eac0ba1 Merged in msabramo/pytest/add_drone_io_to_README_rst (pull request #143)
README.rst: Add drone.io link
2014-04-02 19:13:26 +02:00
Floris Bruynooghe
b0e31dca86 Mention fix for issue 453 in changelog 2014-04-02 18:00:25 +01:00
holger krekel
7d10a57514 Merged in msabramo/pytest/remove_unused_import (pull request #146)
testing/test_capture.py: Remove unused stuff for PEP8 compliance
2014-04-02 18:56:46 +02:00
Marc Abramowitz
2c0f6207e9 test_capture.py: More PEP8. Remove unused cap in with.
--HG--
branch : remove_unused_import
2014-04-02 09:48:08 -07:00
Floris Bruynooghe
adb12d0d4f Escape newlines in result from assertrepr hook
The result from the pytest_assertrepr_compare hook should not include
any newlines since that will confuse the mini-formatting language used
by assertion.util.format_explanation.  So simply escape the included
newlines, this way hook writers do not have to worry about this at
all.

Fixes issue 453.
2014-04-02 17:35:22 +01:00
Floris Bruynooghe
844c141d10 Style fixes for pep8
Includes a quotation change for consistent style.
2014-04-02 17:16:37 +01:00
Marc Abramowitz
02d94e69f0 testing/test_capture.py: Remove unused import tempfile
--HG--
branch : remove_unused_import
2014-04-02 09:14:16 -07:00
Marc Abramowitz
1bc56f9838 README.rst: Remove drone.io build badge; keep link
hpk had concerns about the build badge confusing folks when shown on PyPI.

--HG--
branch : add_drone_io_to_README_rst
2014-04-02 06:41:33 -07:00
Jurko Gospodnetić
98ea8fae32 break reference cycles caused by storing local reference to exception info
Such reference cycles unnecessarily cause Python interpreter not to garbage
collect the objects referenced in those cycles as soon they could be collected,
and in turn cause the tests to use more memory than is strictly necessary.

--HG--
branch : break_ExceptionInfo_reference_cycles
2014-04-02 15:34:36 +02:00
holger krekel
36288c5134 fix issue493: don't run tests in doc directory with `python setup.py test`
(use tox -e doctesting for that)
2014-04-02 12:48:35 +02:00
holger krekel
83a3cc9c94 fix issue492: avoid leak in test_writeorg. Thanks Marc Abramowitz. 2014-04-02 12:32:30 +02:00
holger krekel
0c04b44919 fix issue492: avoid leak in test_writeorg 2014-04-02 12:32:21 +02:00
Marc Abramowitz
a5e8860feb README.rst: Add drone.io link and build badge image
--HG--
branch : add_drone_io_to_README_rst
2014-04-02 03:11:35 -07:00
holger krekel
8d95f89a6a fix issue496: add pytest-sugar github repo override and regen index pages, upload it. 2014-04-02 12:03:08 +02:00
holger krekel
3bca62e9e4 fix issue436: improved finding of initial conftest files from command
line arguments by using the result of parse_known_args rather than
the previous flaky heuristics.  Thanks Marc Abramowitz for tests
and initial fixing approaches in this area.
2014-04-02 11:29:23 +02:00
holger krekel
72b4534a0c add changelog entry for some PRs: improvements to pytest's own
test-suite leakage detection, courtesy of PRs from Marc Abramowitz
2014-04-02 09:51:24 +02:00
holger krekel
21b4280126 Merged in msabramo/pytest/test_writeorg_close_tempfile (pull request #136)
test_writeorg: Close a tempfile
2014-04-02 09:30:55 +02:00
holger krekel
30a9debaf1 Merged in msabramo/pytest/refactor_LsofFdLeakChecker (pull request #138)
testing/conftest.py: Refactor lsof fd leak checking
2014-04-02 09:24:16 +02:00
holger krekel
4c5718c78d Merged in msabramo/pytest/doc_plugins_pytest_sugar (pull request #141)
doc/en/plugins_index/index.txt: Update pytest-sugar
2014-04-02 09:20:42 +02:00
Marc Abramowitz
c93b949878 Update entry for pytest-sugar; GitHub icon instead of BitBucket, correct description
--HG--
branch : doc_plugins_pytest_sugar
2014-04-01 18:59:42 -07:00
Marc Abramowitz
cf34adb75f doc/en/plugins_index/index.txt: Update pytest-sugar
--HG--
branch : doc_plugins_pytest_sugar
2014-04-01 18:52:36 -07:00
Marc Abramowitz
f824a73143 Remove cast of fd to int and sorting
Casting of fd can break for non-numeric fd (e.g.: "rtd" on Linux) and isn't
necessary since we don't need to sort.

--HG--
branch : refactor_LsofFdLeakChecker
2014-04-01 15:36:54 -07:00
Marc Abramowitz
e45a33f029 testing/conftest.py: Reintialize config._openfiles for each test
And no longer need getopenfiles or config._numfiles

--HG--
branch : refactor_LsofFdLeakChecker
2014-04-01 14:13:11 -07:00
Marc Abramowitz
064e79761c Improve LsofFdLeakChecker; more reliable and useful leak checking
* Make it invoke lsof with options for machine-readable output
* Parse out file descriptor and filename from lsof output
* Draw attention to file descriptors now open that weren't open before

--HG--
branch : refactor_LsofFdLeakChecker
2014-04-01 13:41:35 -07:00
Marc Abramowitz
f7713c47e8 testing/conftest.py: Refactor lsof fd leak checking
Isolate the logic into one class to make easier to understand, more maintainable.
This may aid in later plugging in an alternative implementation, such as one
that uses psutil
(https://bitbucket.org/hpk42/pytest/pull-request/137/use-psutil-to-detect-open-files-in-tests/diff)

--HG--
branch : refactor_LsofFdLeakChecker
2014-04-01 10:15:27 -07:00
holger krekel
8e4e2ba244 merge main 2014-04-01 15:06:44 +02:00
holger krekel
3b8935c533 remove dupped_stdout logic and related changes, also simplify pytest_runtest_* calls to not use a contextlib with-decorator anymore. 2014-04-01 15:03:17 +02:00
holger krekel
ce8678e6d5 remove non-documented per-conftest capturing option and simplify/refactor all code accordingly. Also make capturing more robust against tests closing FD1/2 and against pdb.set_trace() calls. 2014-04-01 14:32:12 +02:00
holger krekel
2e1f6c85f6 introduce resume/suspend functionality for FDCapture and SysCapture,
fixing problems with early bailouts (from argparse's parse() function e.g.)
that wrote to stdout.
2014-04-01 14:19:58 +02:00
holger krekel
ca5e6830c6 avoid some redundancy by using SysCapture from FDCapture for manipulating sys.std{out,in,err} 2014-04-01 14:19:55 +02:00
holger krekel
69cbac8fb5 rename StdCaptureBase to MultiCapture 2014-04-01 14:19:52 +02:00
Marc Abramowitz
7301981f32 test_writeorg: Close a tempfile
--HG--
branch : test_writeorg_close_tempfile
2014-03-31 08:25:35 -07:00
Marc Abramowitz
555ba4159d Simplify tests; don't use u literal not supported in py32
--HG--
branch : makepyfile_utf8
2014-03-28 12:11:33 -07:00
Marc Abramowitz
f47ae74981 Make makepyfile accept UTF-8 so a few cookie tests in test_assertrewrite.py
don't need to be dedented.

--HG--
branch : makepyfile_utf8
2014-03-28 09:44:18 -07:00
holger krekel
e061ace099 fix toxenv spec for py33-pexpect, thanks Daniel Grana. 2014-03-28 14:51:00 +01:00
holger krekel
85d52481b1 fix issue483: trial/py33 works now properly. Thanks Daniel Grana for PR. 2014-03-28 14:47:34 +01:00
holger krekel
47379d4a79 Merged in dangra/pytest (pull request #132)
no need for im_func and it does not exists in python3
2014-03-28 14:46:34 +01:00
Daniel Grana
0cb9d26d83 add py33-trial tox environment 2014-03-28 10:44:51 -03:00
holger krekel
95cc114b34 add changelog entry:
fix issue412: messing with stdout/stderr FD-level streams is now
captured without crashes.
2014-03-28 11:29:29 +01:00
holger krekel
9d716a39d6 fix issue412 and other encoding issues. Streamline dupfile() into
a new more thoughtful safe_text_dupfile helper.
2014-03-28 11:27:02 +01:00
holger krekel
923dcfd620 cleanup and refine issue412 test (still failing on py33) 2014-03-28 09:46:38 +01:00
holger krekel
b5467645d3 merge 2014-03-28 09:27:44 +01:00
holger krekel
a65380941d Merged in msabramo/pytest/sys_meta_path_remove_hook_only_if_present (pull request #133)
Only try to remove hook from sys.meta_path if it's present
2014-03-28 09:10:58 +01:00
Marc Abramowitz
17d7c60735 test_sys_meta_path_munged: Simplify with makepyfile
--HG--
branch : sys_meta_path_remove_hook_only_if_present
2014-03-28 01:03:52 -07:00
Marc Abramowitz
81f822d528 Only try to remove hook from sys.meta_path if it's present
Prevent error on exit if some code messes with sys.meta_path and removes the
assertionrewrite hook (CaptureMock seems to do this):

      File "/Users/marca/dev/hg-repos/pytest/_pytest/assertion/__init__.py", line 64, in pytest_unconfigure
        sys.meta_path.remove(hook)
    ValueError: list.remove(x): x not in list

--HG--
branch : sys_meta_path_remove_hook_only_if_present
2014-03-28 00:33:12 -07:00
holger krekel
0b340aa1f6 simplify some capturing tests 2014-03-28 07:55:07 +01:00
holger krekel
859915dc5e simplify capturing funcarg handling 2014-03-28 07:13:08 +01:00
holger krekel
a8f4f49a82 simplify reset/stop_capturing and fix capturing wrt to capturing simple os.write() calls 2014-03-28 07:11:25 +01:00
holger krekel
e18c3ed494 unify and normalize Sys/FD Capturing classes
* * *
more unification
2014-03-28 07:03:37 +01:00
holger krekel
2263fcf6b7 remove unused "suspend/resume" on capturing, some formatting cleanup 2014-03-28 07:03:34 +01:00
Daniel Grana
b96d552dbd no need for im_func and it does not exists in python3 2014-03-27 23:57:18 -03:00
Ronny Pfannschmidt
2b2c1e5b7b condition for python3 of the xfailing test for 412 2014-03-27 14:01:38 +01:00
holger krekel
7b63fa5966 merge in current default 2014-03-27 13:57:54 +01:00
Ronny Pfannschmidt
b18040337a xfailing test for issue 412 2014-03-27 13:53:59 +01:00
holger krekel
42e0d7970c Merged in nloadholtes/pytest/nloadholtes/slight-change-to-the-wording-for-readabi-1395890493571 (pull request #131)
Slight change to the wording for readability
2014-03-27 06:40:35 +01:00
Nick Loadholtes
ed0a4fe23b Slight change to the wording for readability
--HG--
branch : nloadholtes/slight-change-to-the-wording-for-readabi-1395890493571
2014-03-27 03:21:58 +00:00
holger krekel
bb0632c7ad extend test 2014-03-26 19:37:49 +01:00
holger krekel
84ab194516 add changelog: merge PR123: improved integration with mock.patch decorator on tests. 2014-03-26 19:06:56 +01:00
holger krekel
530cae9204 Merged in cgilling/pytest (pull request #123)
Fix to work properly when @patch is used with new not equal to DEFAULT
2014-03-26 19:05:46 +01:00
holger krekel
9dc43e84dc Merged in msabramo/pytest/help_show_args_with_equals_instead_of_space (pull request #130)
_pytest/config.py: In --help, show args with `=` instead of space.
2014-03-26 19:04:35 +01:00
Marc Abramowitz
8af265da04 _pytest/config.py: In --help, show args with = instead of space.
The `=` is better because it encourages folks to use the form that doesn't
suffer from issue #436 (https://bitbucket.org/hpk42/pytest/issue/436), which
can cause the arg to be treated as an "anchor" and used as the (unexpected)
path for searching for conftest.py files.

--HG--
branch : help_show_args_with_equals_instead_of_space
2014-03-26 10:47:30 -07:00
Chris Gilling
e3b9382122 use sys.modules.get correctly and reference DEFAULT with respect to it 2014-03-26 09:36:02 -07:00
Chris Gilling
c3f4eb6d57 change try/except to sys.module.get and a conditional 2014-03-26 09:27:33 -07:00
holger krekel
892aa457be fix issue472: clarify that `pytest.config.getvalue()` cannot work
if it's triggered ahead of command line parsing.
2014-03-26 07:15:54 +01:00
holger krekel
8f7b53e55b fix issue490: include pytest_load_initial_conftests in documentation
and improve docstring.
2014-03-25 14:43:58 +01:00
holger krekel
d27c377817 tentatively fix py33 and py25 compat 2014-03-14 15:58:16 +01:00
holger krekel
50abe43216 fix issue #479: properly handle nose/unittest(2) SkipTest exceptions
during collection/loading of test modules.  Thanks to Marc Schlaich
for the complete PR.
2014-03-14 15:44:39 +01:00
holger krekel
ddc67ca13a Merged in schlamar/pytest (pull request #129)
Fixed race condition with SkipTest when module not in sys.modules on collection.
2014-03-14 15:39:01 +01:00
holger krekel
a1d3da4027 Merged in nicoddemus/pytest/integrate-plugin-index-docs (pull request #120)
Integrating plugin_index docs and misc improvements
2014-03-14 15:34:03 +01:00
schlamar
85e7b11ef5 Removed unnecessary iteration in nose.pytest_runtest_makereport. 2014-03-14 15:29:42 +01:00
schlamar
77e1f93ca1 Fixed pyflakes errors. 2014-03-14 14:25:36 +01:00
schlamar
94b1ce65c6 Fixed race condition with SkipTest when module not in sys.modules on collection. 2014-03-14 14:04:54 +01:00
holger krekel
f5b992f68a Merged in jurko/pytest/doc_typo_correction (pull request #122)
fix documentation typo
2014-03-14 13:10:33 +01:00
holger krekel
24a458b4c8 Merged in graingert/pytest/graingert/pep8-good-practices-code-1394196858258 (pull request #125)
pep8 good practices code
2014-03-14 13:06:53 +01:00
holger krekel
ac1d277225 simplify pdb disabling of capturing, also accomodate the new semantics
that capturing is always on during a test session.
2014-03-14 12:49:37 +01:00
holger krekel
9777703e03 - turn on capturing before early conftest loading and make terminal writer
use the original stream.

- avoid resetting capture FDs/sys.stdout for each test by keeping capturing
  always turned on and looking at snapshotted capturing data during runtest
  and collection phases.
2014-03-14 12:49:36 +01:00
holger krekel
f43cda9681 implement a new hook type: hook wrappers using a "yield" to distinguish
between working at the front and at the end of a hook call chain.
The idea is to make it easier for a plugin to "wrap" a certain hook
call and use context managers, in particular allow a major cleanup of
capturing.
2014-03-14 12:49:35 +01:00
holger krekel
b47fdbe0a7 remove externally setting and dealing with "item.outerr" from capturing in favor of a direct interface for adding reporting sections to items.
* * *
refactor makereport implementation to avoid recursion with __multicall__
2014-03-14 12:49:34 +01:00
holger krekel
cde970be69 remove unneccessary indirections and options 2014-03-14 12:49:34 +01:00
holger krekel
cfd43a9b02 add changelog for warning system, bump internal version 2014-03-14 08:15:38 +01:00
holger krekel
ebd10aa6b4 shrink and merge the somewhat obscure and undocumented internal hinting
system with the new warnings one
2014-03-11 22:10:51 +01:00
holger krekel
24db492f53 warn if instances are callable and have a test name 2014-03-11 22:10:18 +01:00
holger krekel
1b387bea62 introduce warning system with this API:
- node.warn() for a node-specific warning
- config.warn() for a global non-node specific warning

Each warning is accompanied by a "warning number" so that we can later
introduce mechanisms for surpressing them.

Each warning will trigger a call to pytest_report_warn(number, node, message)
which is by default implemented by the TerminalReporter which introduces
a new option "-rw" to show details about warnings.
2014-03-11 22:10:17 +01:00
Thomas Grainger
9528b64f7f pep8 good practices code
--HG--
branch : graingert/pep8-good-practices-code-1394196858258
2014-03-07 12:51:51 +00:00
holger krekel
b96559149c Merged in jurko/pytest/pdb_doc_update (pull request #118)
update PDB related pytest docs
2014-03-06 08:36:42 +01:00
Chris Gilling
3388d82c1c Fix to work properly when @patch is used with new not equal to DEFAULT
also updated test_mock to include this situation
2014-03-03 10:36:59 -08:00
Jurko Gospodnetić
9985a7cdca fix documentation typo
--HG--
branch : doc_typo_correction
2014-03-02 22:52:38 +01:00
Bruno Oliveira
1d00c5e109 Using github and bitbucket icons on plugins_index page
--HG--
branch : integrate-plugin-index-docs
2014-02-18 22:40:36 -03:00
Bruno Oliveira
0559f11aa5 Improved text on plugins_index
- Removed "beta" status from it;
- Added links to pytest-plugs app;

--HG--
branch : integrate-plugin-index-docs
2014-02-18 21:33:13 -03:00
Bruno Oliveira
2893cddb68 plugins_index no longer has the package names hard-coded
small number of changes:
- removed hard-coded links to package release versions, using
  a placeholder "latest" instead which is understood by pytest-plugs
- testing against pytest-2.5.2

This changes were implemented so this page only needs
to be updated after pytest releases, not after each package version changes;.
2014-02-11 23:38:30 -02:00
Jurko Gospodnetić
6910641266 reword PDB usage documentation
Documented that since pytest 2.4.0 you can use the raw pdb.set_trace() call
directly without the pytest.set_trace() wrapper or explicitly disabling pytest's
output capture using 'py.test -s'.

Clearly stated how pytest (since version 2.0.0.) automatically disables its
output capture when entering an interactive PDB debugger. This avoids confusing
new users because their tests display different output when running with or
without entering an interactive debugger (even if user does nothing in that
interactive debugger session other than exit it and continue with the regular
test execution).

--HG--
branch : pdb_doc_update
2014-02-01 10:19:09 +01:00
Jurko Gospodnetić
4b81a07303 make all 'PDB' doc references be recognized as links
--HG--
branch : pdb_doc_update
2014-02-01 10:11:42 +01:00
Jurko Gospodnetić
7eb765578a correct inconsistent comment wording
Once the 'the' article was used before 'first' and once it was not, all within
the same code example.

--HG--
branch : pdb_doc_update
2014-02-01 10:07:54 +01:00
holger krekel
e2cf3e0932 Added tag 2.5.2 for changeset 421d3b4d150d 2014-01-29 14:09:33 +01:00
holger krekel
1830de2c13 make it clear that this xfail is an unimplemented feature, nothing more. 2014-01-29 13:56:24 +01:00
holger krekel
25ab906b8b add release announcement, bump version to 2.5.2,
add links to plugins index, regenerate doc examples.
2014-01-29 13:47:11 +01:00
holger krekel
8a3b4b9c37 require py>=1.2.20 2014-01-29 13:11:40 +01:00
holger krekel
2b8a54d5d9 refine skipif to use direct booleans, to help with flakes 2014-01-29 11:46:36 +01:00
holger krekel
4eabfed651 refactor lsof checking and fix an lsof leak in pypy 2014-01-29 11:18:15 +01:00
holger krekel
505a34bb85 fix flakes failures 2014-01-29 10:20:13 +01:00
holger krekel
2dade6ed00 add changelog entry about issue429, adapt README 2014-01-29 09:00:14 +01:00
Floris Bruynooghe
825ea9bfa1 Fix assertrepr for mojibake
If the compared text was in bytes and not actually valid text
(i.e. could not be encoded to text/unicode using the default encoding)
then the assertrepr would fail with an EncodingError.  This ensures
that the internal string is always valid unicode, converting any bytes
safely to valid unicode.  This is done using repr() which then needs
post-processing to fix the encompassing quotes and un-escape newlines.

This fixes issue 429.
2014-01-29 00:42:58 +00:00
Floris Bruynooghe
cc1186242c Avoid wasted string concatenation and improve english 2014-01-29 00:39:04 +00:00
holger krekel
86284689a3 simplify loop which turns direct funcarg parametrization to indirect 2014-01-27 12:53:44 +01:00
holger krekel
c70d020bf3 allow positional args to tox invocation 2014-01-27 12:42:06 +01:00
holger krekel
4622c28ffd setupstate.addfinalizer(): fix docstring and remove related unit test not covering functional reality 2014-01-26 12:44:21 +01:00
holger krekel
899998cf9c Merged in hpk42/pytest-capsimple/capsimple1 (pull request #115)
some simplifications in capturing code
2014-01-26 12:07:45 +01:00
holger krekel
6f385fb4ea remove "mixed" capturing mode which is not used by pytest
--HG--
branch : capsimple1
2014-01-25 19:56:27 +01:00
holger krekel
18e12cbd67 remove "StdCapture*.call" classmethod because pytest does not use it.
--HG--
branch : capsimple1
2014-01-25 19:43:57 +01:00
holger krekel
3cf4e133cc remove now parameter because pytest only used now==False everywhere
--HG--
branch : capsimple1
2014-01-25 19:42:45 +01:00
holger krekel
d53bfe0aa7 Merged in RonnyPfannschmidt/pytest/multi-usageerror (pull request #113)
Make one usage error for every argument that fails instead of bailing at the first only
2014-01-25 11:04:53 +01:00
Ronny Pfannschmidt
97da43d909 merge from default
--HG--
branch : multi-usageerror
2014-01-25 10:42:21 +01:00
holger krekel
ee080ce8a5 Merged in jurko/pytest/remove_github_references (pull request #112)
remove references to the no longer used github mirror
2014-01-25 10:30:31 +01:00
Jurko Gospodnetić
741a3e8602 remove references to the no longer used github mirror
--HG--
branch : remove_github_references
2014-01-25 09:11:55 +01:00
Ronny Pfannschmidt
177637bfb9 Redo the Capture integration propperly 2014-01-24 21:22:19 +01:00
Piotr Banaszkiewicz
ae64221c34 Contribution guide: added pull request button image 2014-01-24 20:01:04 +01:00
Piotr Banaszkiewicz
22017f11d0 Contribution guide: removed confusion regarding git 2014-01-24 19:37:44 +01:00
Piotr Banaszkiewicz
c1eaad7d57 Contribution guide: added "what is pull request" section 2014-01-24 19:21:21 +01:00
holger krekel
8e6d538a57 address issue416: clarify docs as to conftest.py loading semantics 2014-01-23 15:25:01 +01:00
holger krekel
0836d74ae7 add Jurko to authors/contributors file 2014-01-23 15:08:24 +01:00
holger krekel
a9e5bd52d3 remove github links 2014-01-23 14:50:58 +01:00
Anatoly Bubenkov
b4b5e358c3 merge with mainline 2014-01-23 13:21:00 +01:00
Anatoly Bubenkov
4190809d1a remove the github contribution section 2014-01-23 13:19:49 +01:00
holger krekel
4e2a820c6a add Daniel Greensfeld 3-part blog series about pytest 2014-01-23 13:07:28 +01:00
holger krekel
14a43fffee have travis use the devpi index to get the pylib dependency 2014-01-23 12:18:20 +01:00
holger krekel
245d39f52d Merged in jurko/pytest/document_ExceptionInfo_ref_cycle (pull request #109)
document explicitly clearing local references to pytest.raises() results (redo of pull request #98)
2014-01-23 11:42:51 +01:00
holger krekel
ac0b862f8f speak about "pytest" rather than "py.test". Thanks Jurko. 2014-01-23 11:42:20 +01:00
holger krekel
c41db8ebbb refine contributing text in several places 2014-01-23 11:38:05 +01:00
Jurko Gospodnetić
75c124ea17 reword note on explicitly clearing local references to pytest.raises() results
Made it clearer that clearing such references is not mandatory and is only an
optional step which may help the Python interpreter speed up its garbage
collection.

--HG--
branch : document_ExceptionInfo_ref_cycle
2014-01-23 11:36:04 +01:00
Anatoly Bubenkov
ab13c3f362 add explicit branch creation note 2014-01-23 10:51:45 +01:00
holger krekel
c9af19dae1 rename, refine and link to new contributing doc from several places. 2014-01-23 10:21:06 +01:00
Jurko Gospodnetić
ffffac27f9 document explicitly clearing local references to pytest.raises() results
pytest.raises() returns an ExceptionInfo object which, if a local reference is
made to it, forms a reference cycle:
  ExceptionInfo
  --> exception
  --> stack frame raising the exception
  --> current stack frame
  --> current local variables
  --> Exception Info

Such a reference cycle would then prevent any local variables in the current
stack frame, or any of its child stack frames involved in the same reference
cycle, from being garbage collected until the next reference cycle garbage
collection phase. This unnecessarily increases the program's memory footprint
and potentially slows it down.

This situation is based on a similar one described in the official 'try'
statement Python documentation for locally stored exception references.

--HG--
branch : document_ExceptionInfo_ref_cycle
2014-01-23 09:46:36 +01:00
Anatoly Bubenkov
618bd56acf add Piotr Banaszkiewicz 2014-01-23 00:54:25 +01:00
Anatoly Bubenkov
402232e60f merge pbanaszkiewicz/contributiondocs 2014-01-23 00:52:49 +01:00
holger krekel
400b51caf6 mark encoding test as xfail also on py2 2014-01-22 22:18:33 +01:00
holger krekel
9aaf0fd340 backing out Ronny's PR because it was merged too early (still has failing tests) 2014-01-22 22:15:40 +01:00
Ronny Pfannschmidt
8976b3ee0e stop exposing capsys/capfd.capture 2014-01-22 21:52:32 +01:00
Ronny Pfannschmidt
ac2f2b1deb add notes on the copied pylib version 2014-01-22 21:50:07 +01:00
Ronny Pfannschmidt
0be961a0f3 capture tests: move imports and declarations to the top 2014-01-22 21:46:35 +01:00
Ronny Pfannschmidt
b4a397d153 kill ancient capture devnullpath, os.devnull exists since py 2.4 2014-01-22 21:37:59 +01:00
Ronny Pfannschmidt
d1a9ab3df0 small cleanp 2014-01-22 21:04:00 +01:00
Ronny Pfannschmidt
0ede968ec0 kill the str magic of Encodedfile 2014-01-22 21:03:49 +01:00
Ronny Pfannschmidt
5f21abc3a3 move imports and declarations to the top 2014-01-22 20:48:17 +01:00
Ronny Pfannschmidt
e2bb81124c simplify StdCaptureFD snapshot reading 2014-01-22 19:48:10 +01:00
Ronny Pfannschmidt
ea18e9656b rewrite all _pytest.capture uses of py.io to _pytest.capture 2014-01-22 19:44:20 +01:00
Ronny Pfannschmidt
3cc58c2f78 rewrite all testing uses of py.io to _pytest.capture 2014-01-22 19:32:23 +01:00
Ronny Pfannschmidt
0ac94134f5 initial code import for capture transfer 2014-01-22 19:04:38 +01:00
Ronny Pfannschmidt
c142f2551d xfailing test for captire encoding issues with binary stdio 2014-01-22 18:07:54 +01:00
holger krekel
cccfaa81fb fix issue413: exceptions with unicode attributes are now printed
correctly also on python2 and with pytest-xdist runs. (the fix
requires py-1.4.20)
2014-01-22 17:48:56 +01:00
Piotr Banaszkiewicz
d960fb78fc Contribution guide: moved tox inst. instructions up
--HG--
branch : contributiondocs
2014-01-22 17:05:11 +01:00
holger krekel
d02d0bb7b7 Merged in pelme/pytest/py2_pkg_skip (pull request #107)
fixed issue428: Skip test for packages without __init__.py on Python 2
2014-01-22 14:46:22 +01:00
Andreas Pelme
fe4cdd8a90 fixed issue428: Skip test for packages without __init__.py on Python 2
--HG--
branch : py2_pkg_skip
2014-01-22 14:32:22 +01:00
Jurko Gospodnetić
54a143e6a8 add test: '--markers' listing info from plugins in current folder
When pytest is called with the '--markers' option, it should collect marker
information from the current folder, and they should get loaded and used
correctly before the '--markers' output is constructed.
2014-01-22 14:16:39 +01:00
holger krekel
3a4f69734a remove superflous line 2014-01-22 13:54:25 +01:00
Piotr Banaszkiewicz
fbdc6e8cc0 Addressed contribution guide issues: virtualenv, links, Github mirror, RST rendering
--HG--
branch : contributiondocs
2014-01-22 12:19:33 +01:00
Piotr Banaszkiewicz
c2c44f0ffc Moved contribution guide to the rootdir/CONTRIBUTING.txt
--HG--
branch : contributiondocs
2014-01-22 11:37:02 +01:00
Floris Bruynooghe
e12fe64b54 Include py version in the terminal output
This can help to reproduce bugs when looking at the output pasted into
bug reports.
2014-01-22 11:27:15 +01:00
Piotr Banaszkiewicz
5240252164 New enthusiastic contribution guide based on Audreyr's cookiecutter-pypackage
Audrey's code is BSD, so there should be no problem with licensing.

I've covered:
* contribution types (with hints)
* steps to start with pytest development
* testing pytest
* basics of hg

--HG--
branch : contributiondocs
2014-01-22 11:24:58 +01:00
holger krekel
1ffc006363 fixed circular imports by reverting a few py.test -> pytest substitions. 2014-01-22 11:17:25 +01:00
holger krekel
836232e544 fix issue425: mention at end of "py.test -h" that --markers
and --fixtures work according to specified test path (or current dir)
2014-01-22 10:24:22 +01:00
holger krekel
2539e5a352 Merged in derdon/pytest/no-p-option (pull request #102)
added docs about the `no:` syntax for the -p option
2014-01-20 13:22:31 +01:00
Jurko Gospodnetić
8e457338ee fix handling MarkDecorators called with a single positional plus keyword args
When a MarkDecorator instance is called it does the following:
  1. If called with a single class as its only positional argument and no
     additional keyword arguments, it attaches itself to the class so it gets
     applied automatically to all test cases found in that class.
  2. If called with a single function as its only positional argument and no
     additional keyword arguments, it attaches a MarkInfo object to the
     function, containing all the arguments already stored internally in the
     MarkDecorator.
  3. When called in any other case, it performs a 'fake construction' call, i.e.
     it returns a new MarkDecorator instance with the original MarkDecorator's
     content updated with the arguments passed to this call.

When Python applies a function decorator it always passes the target class/
function to the decorator as its positional argument with no additional
positional or keyword arguments. However, when MarkDecorator was deciding
whether it was being called to decorate a target function/class (cases 1. & 2.
as documented above) or to return an updated MarkDecorator (case 3. as
documented above), it only checked that it received a single callable positional
argument and did not take into consideration whether additional keyword
arguments were being passed in as well.

With this change, it is now possible to create a pytest mark storing a function/
class parameter passed as its only positional argument and accompanied by one or
more additional keyword arguments. Before, it was only possible to do so if the
function/class parameter argument was accompanied by at least one other
positional argument.

Added a related unit test.

Updated MarkDecorator doc-string.
2014-01-20 01:27:33 +01:00
Simon Liedtke
d92ee8c3c3 added docs about the no: syntax for the -p option
--HG--
branch : no-p-option
2014-01-19 22:05:14 +01:00
Jurko Gospodnetić
492c60c202 fix doc typo 2014-01-19 10:26:55 +01:00
holger krekel
86ef81454f fix name 2014-01-19 09:13:06 +01:00
holger krekel
675669c52e added changelog: fixed docs and code to use "pytest" instead of "py.test"
everywhere.  Thanks Jurko Gspodnetic for the complete PR.
2014-01-18 14:53:04 +01:00
holger krekel
92c5d395a2 Merged in jurko/pytest (pull request #100)
update README.rst docs - pytest is compatible with Python 2.5 instead of 2.4
2014-01-18 14:51:53 +01:00
Jurko Gospodnetić
fa4ea1d9e8 update README.rst docs - pytest is compatible with Python 2.5 instead of 2.4 2014-01-18 13:11:17 +01:00
Jurko Gospodnetić
657a395839 fix comment typos 2014-01-18 12:39:16 +01:00
Jurko Gospodnetić
9fb2079458 replace py.test module references with pytest
The only remaining 'py.test' references are:
 * those referring to the 'py.test' executable
 * those in code explicitly testing py.test/pytest module compatibility
 * those in old CHANGES documentation
 * those in documentation generated based on external data
 * those in seemingly unfinished & unmaintained Japanese documentation

Minor stylistic changes and typo corrections made to documentation next to
several applied py.test --> pytest content changes.
2014-01-18 12:31:33 +01:00
Jurko Gospodnetić
83620ced2e trim trailing spaces 2014-01-18 10:40:20 +01:00
Bruno Oliveira
1fb824cd28 plugins_index: status images now link to actual tox output 2014-01-15 20:48:11 -02:00
Bruno Oliveira
0d35994fb8 Updated plugins_index.txt after changes in plugin_index.py 2014-01-15 20:32:11 -02:00
Bruno Oliveira
3a37f33d99 Improvements in plugins_index.txt
- Removed author and email in favor of repository link.
  Repository link is obtained from the 'home_page' field
  obtained from pypi. For plugins that don't have
  that field set, function obtain_override_repositories
  is used to override it in favor of known repositories url.

- Shortened "Python 2.7" in favor of "py27" to save space;
  Same for "Python 3.3".
2014-01-15 20:26:05 -02:00
Bruno Oliveira
ddfb2d5f3a Removing test_plugins_index
This test seems unnecessary now, since after every change we have
to generate plugins_index.txt and manually check to ensure it is
correct.
2014-01-15 19:20:53 -02:00
holger krekel
d81c0e9a92 remove build status that shows on pypi -- doesn't make sense because it shows the current trunk, not the released version. And start a new doc/en/status.txt 2014-01-11 10:46:07 +01:00
holger krekel
048cb71bf6 Merged in lakka/pytest//minor-doc-fix-in-skippingtxt-also-submi-1387492852421 (pull request #94)
Minor doc fix in skipping.txt.  Also submitted at Github before I realised that this was the master repo.  Will close over there.
2014-01-10 10:44:12 +01:00
Daniel Hahler
903fd144ff doc: fix desc for parametrize
- the parameter is called `expected`, not `output`
 - s/that that/that/
2014-01-09 22:27:23 +01:00
Jurko Gospodnetić
d51e27a5cb correct documentation typo 2013-12-28 17:05:17 +01:00
holger krekel
064290a606 Merge pull request #11 from pmuller/patch-1
Fix minor typo in special.txt
2013-12-20 11:37:39 -08:00
Philippe Muller
e6ae68c0cc Fix minor typo in special.txt 2013-12-20 14:01:17 +01:00
lakka
43f970ab6b Minor doc fix in skipping.txt. Also submitted at Github before I realised that this was the master repo. Will close over there.
--HG--
branch : /minor-doc-fix-in-skippingtxt-also-submi-1387492852421
2013-12-19 22:41:07 +00:00
holger krekel
49b9f9091a Merge pull request #9 from lukaszb/mark-error-message
Updated error message to be more helpful.
2013-12-19 07:13:13 -08:00
Lukasz Balcerzak
99277be25f Updated error message to be more helpful
Also, added misssing test
2013-12-19 14:29:57 +01:00
holger krekel
699892bd03 fix issue409 -- better interoperate with cx_freeze by not
trying to import from collections.abc which causes problems for py27/cx_freeze.
2013-12-18 14:56:45 +01:00
Bruno Oliveira
70c1503afc updated plugins_index for pytest 2.5.1 release 2013-12-17 21:52:26 -02:00
Bruno Oliveira
ee5d2eb696 plugins_index.py and test run under py33
changed a message a bit since issue405 has actually been resolved
2013-12-17 21:42:51 -02:00
Bruno Oliveira
2058f11931 Formatted plugins_index files to use a 80 char width
- Formatted code to follow pep-8 more closely;
- Got rid of header comments to better follow py.test coding style;
2013-12-17 19:34:07 -02:00
Bruno Oliveira
53a9ee21d4 Fixed test_plugins_index to work on python 2.6
- Expected and obtained modules now use .rst to avoid being picked as doctests
- Fixed test_plugins_index.expected to use the real py.test version
2013-12-17 19:14:57 -02:00
holger krekel
04118a5761 just use "sans-serif" as the default font, thankfully recommended by hynek. 2013-12-17 14:59:29 +01:00
holger krekel
c101c30690 change to the non-serif version of "Deja Vu" 2013-12-17 13:48:59 +01:00
holger krekel
0e664d3471 don't use guidea as font as it appears to give troubles to windows/chrome
users, see also here: http://stackoverflow.com/questions/11487427/is-there-any-font-smoothing-in-google-chrome
2013-12-17 13:21:39 +01:00
holger krekel
fcb1749f10 a few minor fixes and removing google+ stuff. 2013-12-17 11:24:58 +01:00
holger krekel
180eb098f1 Added tag 2.5.1 for changeset 039d543d1ca0 2013-12-17 10:52:03 +01:00
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
Ronny Pfannschmidt
cf37c477bb output errors for all failures of specific collection
when issueing a command with many specific items to collect,
print all collect failures instead of just the first one

--HG--
branch : multi-usageerror
2013-09-08 22:26:51 +02:00
178 changed files with 8631 additions and 3507 deletions

1
.gitignore vendored
View File

@@ -13,7 +13,6 @@ include/
*.pyc
*.pyo
*.swp
*.html
*.class
*.orig
*~

View File

@@ -9,6 +9,7 @@ lib/
bin/
include/
.Python/
.env/
# These lines are suggested according to the svn:ignore property
# Feel free to enable them by uncommenting them
@@ -27,6 +28,7 @@ dist/
*.egg-info
issue/
env/
env3/
3rdparty/
.tox
.cache

10
.hgtags
View File

@@ -60,3 +60,13 @@ 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
039d543d1ca02a716c0b0de9a7131beb8021e8a2 2.5.1
421d3b4d150d901de24b1cbeb8955547b1420483 2.5.2
60725b17a9d1af4100abb8be3f9f4ddf6262bf34 2.6.0
60725b17a9d1af4100abb8be3f9f4ddf6262bf34 2.6.0
88af949b9611494e2c65d528f9e565b00fb7e8ca 2.6.0
a4f9639702baa3eb4f3b16e162f74f7b69f3f9e1 2.6.1

View File

@@ -2,7 +2,8 @@ language: python
# command to install dependencies
install: "pip install -U detox"
# # command to run tests
script: detox --recreate
script: detox --recreate -i ALL=https://devpi.net/hpk/dev/
notifications:
irc:
- "chat.freenode.net#pytest-dev"

20
AUTHORS
View File

@@ -1,7 +1,7 @@
Holger Krekel, holger at merlinux eu
merlinux GmbH, Germany, office at merlinux eu
Contributors include::
Contributors include::
Ronny Pfannschmidt
Benjamin Peterson
@@ -9,21 +9,21 @@ Floris Bruynooghe
Jason R. Coombs
Wouter van Ackooy
Samuele Pedroni
Anatoly Bubenkoff
Anatoly Bubenkoff
Brianna Laugher
Carl Friedrich Bolz
Armin Rigo
Maho
Jaap Broekhuizen
Jaap Broekhuizen
Maciek Fijalkowski
Guido Wesdorp
Brian Dorsey
Ross Lawley
Ralf Schmitt
Chris Lamb
Chris Lamb
Harald Armin Massa
Martijn Faassen
Ian Bicking
Ian Bicking
Jan Balster
Grig Gheorghiu
Bob Ippolito
@@ -35,3 +35,13 @@ Brian Okken
Katarzyna Jachim
Christian Theunert
Anthon van der Neut
Mark Abramowitz
Piotr Banaszkiewicz
Jurko Gospodnetić
Marc Schlaich
Christopher Gilling
Daniel Grana
Andy Freeland
Trevor Bekolay
David Mohr
Nicolas Delaby

546
CHANGELOG
View File

@@ -1,4 +1,398 @@
Changes between 2.3.5 and 2.4
2.6.2
-----------
- Added function pytest.freeze_includes(), which makes it easy to embed
pytest into executables using tools like cx_freeze.
See docs for examples and rationale. Thanks Bruno Oliveira.
- Improve assertion rewriting cache invalidation precision.
- fixed issue561: adapt autouse fixture example for python3.
- fixed issue453: assertion rewriting issue with __repr__ containing
"\n{", "\n}" and "\n~".
- fix issue560: correctly display code if an "else:" or "finally:" is
followed by statements on the same line.
- Fix example in monkeypatch documentation, thanks t-8ch.
- fix issue572: correct tmpdir doc example for python3.
- Do not mark as universal wheel because Python 2.6 is different from
other builds due to the extra argparse dependency. Fixes issue566.
Thanks sontek.
2.6.1
-----------------------------------
- No longer show line numbers in the --verbose output, the output is now
purely the nodeid. The line number is still shown in failure reports.
Thanks Floris Bruynooghe.
- fix issue437 where assertion rewriting could cause pytest-xdist slaves
to collect different tests. Thanks Bruno Oliveira.
- fix issue555: add "errors" attribute to capture-streams to satisfy
some distutils and possibly other code accessing sys.stdout.errors.
- fix issue547 capsys/capfd also work when output capturing ("-s") is disabled.
- address issue170: allow pytest.mark.xfail(...) to specify expected exceptions via
an optional "raises=EXC" argument where EXC can be a single exception
or a tuple of exception classes. Thanks David Mohr for the complete
PR.
- fix integration of pytest with unittest.mock.patch decorator when
it uses the "new" argument. Thanks Nicolas Delaby for test and PR.
- fix issue with detecting conftest files if the arguments contain
"::" node id specifications (copy pasted from "-v" output)
- fix issue544 by only removing "@NUM" at the end of "::" separated parts
and if the part has an ".py" extension
- don't use py.std import helper, rather import things directly.
Thanks Bruno Oliveira.
2.6
-----------------------------------
- Cache exceptions from fixtures according to their scope (issue 467).
- fix issue537: Avoid importing old assertion reinterpretation code by default.
- fix issue364: shorten and enhance tracebacks representation by default.
The new "--tb=auto" option (default) will only display long tracebacks
for the first and last entry. You can get the old behaviour of printing
all entries as long entries with "--tb=long". Also short entries by
default are now printed very similarly to "--tb=native" ones.
- fix issue514: teach assertion reinterpretation about private class attributes
- change -v output to include full node IDs of tests. Users can copy
a node ID from a test run, including line number, and use it as a
positional argument in order to run only a single test.
- fix issue 475: fail early and comprehensible if calling
pytest.raises with wrong exception type.
- fix issue516: tell in getting-started about current dependencies.
- cleanup setup.py a bit and specify supported versions. Thanks Jurko
Gospodnetic for the PR.
- change XPASS colour to yellow rather then red when tests are run
with -v.
- fix issue473: work around mock putting an unbound method into a class
dict when double-patching.
- fix issue498: if a fixture finalizer fails, make sure that
the fixture is still invalidated.
- fix issue453: the result of the pytest_assertrepr_compare hook now gets
it's newlines escaped so that format_exception does not blow up.
- internal new warning system: pytest will now produce warnings when
it detects oddities in your test collection or execution.
Warnings are ultimately sent to a new pytest_logwarning hook which is
currently only implemented by the terminal plugin which displays
warnings in the summary line and shows more details when -rw (report on
warnings) is specified.
- change skips into warnings for test classes with an __init__ and
callables in test modules which look like a test but are not functions.
- fix issue436: improved finding of initial conftest files from command
line arguments by using the result of parse_known_args rather than
the previous flaky heuristics. Thanks Marc Abramowitz for tests
and initial fixing approaches in this area.
- fix issue #479: properly handle nose/unittest(2) SkipTest exceptions
during collection/loading of test modules. Thanks to Marc Schlaich
for the complete PR.
- fix issue490: include pytest_load_initial_conftests in documentation
and improve docstring.
- fix issue472: clarify that ``pytest.config.getvalue()`` cannot work
if it's triggered ahead of command line parsing.
- merge PR123: improved integration with mock.patch decorator on tests.
- fix issue412: messing with stdout/stderr FD-level streams is now
captured without crashes.
- fix issue483: trial/py33 works now properly. Thanks Daniel Grana for PR.
- improve example for pytest integration with "python setup.py test"
which now has a generic "-a" or "--pytest-args" option where you
can pass additional options as a quoted string. Thanks Trevor Bekolay.
- simplified internal capturing mechanism and made it more robust
against tests or setups changing FD1/FD2, also better integrated
now with pytest.pdb() in single tests.
- improvements to pytest's own test-suite leakage detection, courtesy of PRs
from Marc Abramowitz
- fix issue492: avoid leak in test_writeorg. Thanks Marc Abramowitz.
- fix issue493: don't run tests in doc directory with ``python setup.py test``
(use tox -e doctesting for that)
- fix issue486: better reporting and handling of early conftest loading failures
- some cleanup and simplification of internal conftest handling.
- work a bit harder to break reference cycles when catching exceptions.
Thanks Jurko Gospodnetic.
- fix issue443: fix skip examples to use proper comparison. Thanks Alex
Groenholm.
- support nose-style ``__test__`` attribute on modules, classes and
functions, including unittest-style Classes. If set to False, the
test will not be collected.
- fix issue512: show "<notset>" for arguments which might not be set
in monkeypatch plugin. Improves output in documentation.
2.5.2
-----------------------------------
- fix issue409 -- better interoperate with cx_freeze by not
trying to import from collections.abc which causes problems
for py27/cx_freeze. Thanks Wolfgang L. for reporting and tracking it down.
- fixed docs and code to use "pytest" instead of "py.test" almost everywhere.
Thanks Jurko Gospodnetic for the complete PR.
- fix issue425: mention at end of "py.test -h" that --markers
and --fixtures work according to specified test path (or current dir)
- fix issue413: exceptions with unicode attributes are now printed
correctly also on python2 and with pytest-xdist runs. (the fix
requires py-1.4.20)
- copy, cleanup and integrate py.io capture
from pylib 1.4.20.dev2 (rev 13d9af95547e)
- address issue416: clarify docs as to conftest.py loading semantics
- fix issue429: comparing byte strings with non-ascii chars in assert
expressions now work better. Thanks Floris Bruynooghe.
- make capfd/capsys.capture private, its unused and shouldnt be exposed
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.
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:
@@ -45,12 +439,12 @@ new features:
- 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
- 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
- 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
@@ -68,7 +462,7 @@ new features:
phase of a node.
- simplify pytest.mark.parametrize() signature: allow to pass a
CSV-separated string to specify argnames. For example:
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"), ...)``.
@@ -93,10 +487,10 @@ new features:
Bug fixes:
- fix issue358 - capturing options are now parsed more properly
- 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
- 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.
@@ -137,7 +531,7 @@ Bug fixes:
- 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
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
@@ -145,7 +539,7 @@ Bug fixes:
- 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 --
- 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.
@@ -155,7 +549,7 @@ Bug fixes:
mode. Thanks Jason R. Coombs.
- fix junitxml generation when test output contains control characters,
addressing issue267, thanks Jaap Broekhuizen
addressing issue267, thanks Jaap Broekhuizen
- fix issue338: honor --tb style for setup/teardown errors as well. Thanks Maho.
@@ -164,10 +558,10 @@ Bug fixes:
- better parametrize error messages, thanks Brianna Laugher
- pytest_terminal_summary(terminalreporter) hooks can now use
".section(title)" and ".line(msg)" methods to print extra
".section(title)" and ".line(msg)" methods to print extra
information at the end of a test run.
Changes between 2.3.4 and 2.3.5
v2.3.5
-----------------------------------
- fix issue169: respect --tb=style with setup/teardown errors as well.
@@ -218,7 +612,7 @@ Changes between 2.3.4 and 2.3.5
- 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
- allow to specify prefixes starting with "_" when
customizing python_functions test discovery. (thanks Graham Horler)
- improve PYTEST_DEBUG tracing output by puting
@@ -232,10 +626,10 @@ Changes between 2.3.4 and 2.3.5
- fix issue266 - accept unicode in MarkEvaluator expressions
Changes between 2.3.3 and 2.3.4
v2.3.4
-----------------------------------
- yielded test functions will now have autouse-fixtures active but
- 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
@@ -250,9 +644,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.
@@ -284,10 +678,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
@@ -317,7 +711,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
@@ -330,7 +724,7 @@ 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
@@ -371,7 +765,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.
@@ -402,14 +796,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
@@ -426,17 +820,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.
@@ -451,11 +845,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
@@ -463,25 +857,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
@@ -490,12 +884,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
@@ -503,7 +897,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
@@ -512,7 +906,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
@@ -522,7 +916,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
@@ -535,7 +929,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
@@ -555,7 +949,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
@@ -575,7 +969,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
@@ -587,17 +981,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
@@ -620,14 +1014,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
@@ -636,7 +1030,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)
@@ -666,17 +1060,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
@@ -721,7 +1115,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
@@ -730,7 +1124,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
@@ -745,7 +1139,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
@@ -820,7 +1214,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
@@ -892,7 +1286,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
@@ -957,7 +1351,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"::
@@ -996,7 +1390,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
@@ -1080,7 +1474,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'
@@ -1099,7 +1493,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
@@ -1184,7 +1578,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,
@@ -1192,7 +1586,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,
@@ -1226,13 +1620,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
@@ -1266,7 +1660,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
@@ -1295,7 +1689,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
@@ -1320,7 +1714,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
@@ -1337,7 +1731,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,
@@ -1361,7 +1755,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,
@@ -1394,10 +1788,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]

171
CONTRIBUTING.rst Normal file
View File

@@ -0,0 +1,171 @@
============
Contributing
============
Contributions are highly welcomed and appreciated. Every little help counts,
so do not hesitate!
Types of contributions
======================
Report bugs
-----------
Report bugs at https://bitbucket.org/hpk42/pytest/issues.
If you are reporting a bug, please include:
* Your operating system name and version.
* Any details about your local setup that might be helpful in troubleshooting,
specifically Python interpreter version,
installed libraries and pytest version.
* Detailed steps to reproduce the bug.
Submit feedback for developers
------------------------------
Do you like pytest? Share some love on Twitter or in your blog posts!
We'd also like to hear about your propositions and suggestions. Feel free to
`submit them as issues <https://bitbucket.org/hpk42/pytest/issues>`__ and:
* Set the "kind" to "enhancement" or "proposal" so that we can quickly find
about them.
* Explain in detail how they should work.
* Keep the scope as narrow as possible. This will make it easier to implement.
* If you have required skills and/or knowledge, we are very happy for
:ref:`pull requests <pull-requests>`.
Fix bugs
--------
Look through the BitBucket issues for bugs. Here is sample filter you can use:
https://bitbucket.org/hpk42/pytest/issues?status=new&status=open&kind=bug
:ref:`Talk <contact>` to developers to find out how you can fix specific bugs.
Implement features
------------------
Look through the BitBucket issues for enhancements. Here is sample filter you
can use:
https://bitbucket.org/hpk42/pytest/issues?status=new&status=open&kind=enhancement
:ref:`Talk <contact>` to developers to find out how you can implement specific
features.
Write documentation
-------------------
pytest could always use more documentation. What exactly is needed?
* More complementary documentation. Have you perhaps found something unclear?
* Documentation translations. We currently have English and Japanese versions.
* Docstrings. There's never too much of them.
* Blog posts, articles and such -- they're all very appreciated.
.. _pull-requests:
Preparing Pull Requests on Bitbucket
=====================================
.. note::
What is a "pull request"? It informs project's core developers about the
changes you want to review and merge. Pull requests are stored on
`BitBucket servers <https://bitbucket.org/hpk42/pytest/pull-requests>`__.
Once you send pull request, we can discuss it's potential modifications and
even add more commits to it later on.
The primary development platform for pytest is BitBucket. You can find all
the issues there and submit your pull requests.
1. Fork the
`pytest BitBucket repository <https://bitbucket.org/hpk42/pytest>`__. It's
fine to use ``pytest`` as your fork repository name because it will live
under your user.
.. _virtualenvactivate:
2. Create and activate a fork-specific virtualenv
(http://www.virtualenv.org/en/latest/)::
$ virtualenv pytest-venv
$ source pytest-venv/bin/activate
.. _checkout:
3. Clone your fork locally using `Mercurial <http://mercurial.selenic.com/>`_
(``hg``) and create a branch::
$ hg clone ssh://hg@bitbucket.org/YOUR_BITBUCKET_USERNAME/pytest
$ cd pytest
$ hg branch your-branch-name
If you need some help with Mercurial, follow this quick start
guide: http://mercurial.selenic.com/wiki/QuickStart
.. _testing-pytest:
4. You can now edit your local working copy. To test you need to
install the "tox" tool into your virtualenv::
$ pip install tox
You need to have Python 2.7 and 3.3 available in your system. Now
running tests is as simple as issuing this command::
$ python runtox.py -e py27,py33,flakes
This command will run tests via the "tox" tool against Python 2.7 and 3.3
and also perform "flakes" coding-style checks. ``runtox.py`` is
a thin wrapper around ``tox`` which installs from a development package
index where newer (not yet released to pypi) versions of dependencies
(especially ``py``) might be present.
To run tests on py27 and pass options (e.g. enter pdb on failure)
to pytest you can do::
$ python runtox.py -e py27 -- --pdb
or to only run tests in a particular test module on py33::
$ python runtox.py -e py33 -- testing/test_config.py
5. Commit and push once your tests pass and you are happy with your change(s)::
$ hg commit -m"<commit message>"
$ hg push -b .
6. Finally, submit a pull request through the BitBucket website:
.. image:: img/pullrequest.png
:width: 700px
:align: center
::
source: YOUR_BITBUCKET_USERNAME/pytest
branch: your-branch-name
target: hpk42/pytest
branch: default
.. _contribution-using-git:
What about git (and so GitHub)?
-------------------------------
There used to be the pytest GitHub mirror. It was removed in favor of the
Mercurial one, to remove confusion of people not knowing where it's better to
put their issues and pull requests. Also it wasn't easily possible to automate
the mirroring process.
However, it's still possible to use git to contribute to pytest using tools
like `gitifyhg <https://github.com/buchuki/gitifyhg>`_ which allows you to
clone and work with Mercurial repo still using git.
.. warning::
Remember that git is **not** a default version control system for pytest and
you need to be careful using it.

44
HOWTORELEASE.rst Normal file
View File

@@ -0,0 +1,44 @@
How to release pytest (draft)
--------------------------------------------
1. bump version numbers in setup.py and pytest/__init__.py
2. check and finalize CHANGELOG
3. write doc/en/announce/pytest-VERSION.txt and include
it in doc/en/announce/index.txt
4. use devpi for uploading a release tarball to a staging area:
- ``devpi use https://devpi.net/USER/dev``
- ``devpi upload``
5. run from multiple machines:
- ``devpi use https://devpi.net/USER/dev``
- ``devpi test pytest-VERSION``
6. check that tests pass for relevant combinations with
``devpi list pytest``
or look at failures with "devpi list -f pytest".
There will be some failed environments like e.g. the py33-trial
or py27-pexpect tox environments on Win32 platforms
which is ok (tox does not support skipping on
per-platform basis yet).
7. go to "doc/en" and upload docs with "make install"
(the latter requires ssh-login permissions on pytest.org
because it uses rsync). Note that the "install" target of
doc/en/Makefile defines where the rsync goes to, typically
to the "latest" section of pytest.org.
8. publish to pypi "devpi push pytest-2.6.2 pypi:NAME" where NAME
is the name of pypi.python.org as configured in your
~/.pypirc file -- it's the same you would use with
"setup.py upload -r NAME"
9. send release announcement to mailing lists:
pytest-dev
testing-in-python
python-announce-list@python.org

View File

@@ -79,15 +79,6 @@ style tests, i.e. it leverages existing test suites without needing
to rewrite them. Together with the previously mentioned setup_test()
maybe the setupfunc could be ommitted?
checks / deprecations for next release
---------------------------------------------------------------
tags: bug 2.4 core xdist
* check oejskit plugin compatibility
* move pytest_nose out of pylib because it implicitely extends
the protocol now - setup/teardown is called at module level.
consider making calling of setup/teardown configurable
optimizations
---------------------------------------------------------------
tags: 2.4 core
@@ -122,8 +113,8 @@ customize test function collection
-------------------------------------------------------
tags: feature
- introduce py.test.mark.nocollect for not considering a function for
test collection at all. maybe also introduce a py.test.mark.test to
- introduce pytest.mark.nocollect for not considering a function for
test collection at all. maybe also introduce a pytest.mark.test to
explicitely mark a function to become a tested one. Lookup JUnit ways
of tagging tests.
@@ -135,18 +126,18 @@ in addition to the imperative pytest.importorskip also introduce
a pytest.mark.importorskip so that the test count is more correct.
introduce py.test.mark.platform
introduce pytest.mark.platform
-------------------------------------------------------
tags: feature
Introduce nice-to-spell platform-skipping, examples:
@py.test.mark.platform("python3")
@py.test.mark.platform("not python3")
@py.test.mark.platform("win32 and not python3")
@py.test.mark.platform("darwin")
@py.test.mark.platform("not (jython and win32)")
@py.test.mark.platform("not (jython and win32)", xfail=True)
@pytest.mark.platform("python3")
@pytest.mark.platform("not python3")
@pytest.mark.platform("win32 and not python3")
@pytest.mark.platform("darwin")
@pytest.mark.platform("not (jython and win32)")
@pytest.mark.platform("not (jython and win32)", xfail=True)
etc. Idea is to allow Python expressions which can operate
on common spellings for operating systems and python
@@ -181,8 +172,8 @@ tags: feature
allow to name conftest.py files (in sub directories) that should
be imported early, as to include command line options.
improve central py.test ini file
----------------------------------
improve central pytest ini file
-------------------------------
tags: feature
introduce more declarative configuration options:
@@ -196,7 +187,7 @@ new documentation
----------------------------------
tags: feature
- logo py.test
- logo pytest
- examples for unittest or functional testing
- resource management for functional testing
- patterns: page object
@@ -205,17 +196,17 @@ have imported module mismatch honour relative paths
--------------------------------------------------------
tags: bug
With 1.1.1 py.test fails at least on windows if an import
With 1.1.1 pytest fails at least on windows if an import
is relative and compared against an absolute conftest.py
path. Normalize.
consider globals: py.test.ensuretemp and config
consider globals: pytest.ensuretemp and config
--------------------------------------------------------------
tags: experimental-wish
consider deprecating py.test.ensuretemp and py.test.config
to further reduce py.test globality. Also consider
having py.test.config and ensuretemp coming from
consider deprecating pytest.ensuretemp and pytest.config
to further reduce pytest globality. Also consider
having pytest.config and ensuretemp coming from
a plugin rather than being there from the start.
@@ -223,7 +214,7 @@ consider pytest_addsyspath hook
-----------------------------------------
tags: wish
py.test could call a new pytest_addsyspath() in order to systematically
pytest could call a new pytest_addsyspath() in order to systematically
allow manipulation of sys.path and to inhibit it via --no-addsyspath
in order to more easily run against installed packages.
@@ -232,11 +223,11 @@ and pytest_configure.
deprecate global py.test.config usage
deprecate global pytest.config usage
----------------------------------------------------------------
tags: feature
py.test.ensuretemp and py.test.config are probably the last
pytest.ensuretemp and pytest.config are probably the last
objects containing global state. Often using them is not
neccessary. This is about trying to get rid of them, i.e.
deprecating them and checking with PyPy's usages as well

View File

@@ -1,4 +1,13 @@
The ``py.test`` testing tool makes it easy to write small tests, yet
Documentation: http://pytest.org/latest/
Changelog: http://pytest.org/latest/changelog.html
Issues: https://bitbucket.org/hpk42/pytest/issues?status=open
CI: https://drone.io/bitbucket.org/hpk42/pytest
The ``pytest`` testing tool makes it easy to write small tests, yet
scales to support complex functional testing. It provides
- `auto-discovery
@@ -7,17 +16,14 @@ scales to support complex functional testing. It provides
- 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
- multi-paradigm support: you can use ``pytest`` to run test suites based
on `unittest <http://pytest.org/latest/unittest.html>`_ (or trial),
`nose <http://pytest.org/latest/nose.html>`_
- single-source compatibility to Python2.4 all the way up to Python3.3,
PyPy-1.9 and Jython-2.5.1.
- single-source compatibility to Python2.5 all the way up to Python3.4,
PyPy-2.3 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
@@ -35,11 +41,10 @@ and report bugs at:
http://bitbucket.org/hpk42/pytest/issues/
and checkout repos at:
and checkout or fork repo at:
http://github.com/hpk42/pytest/ (mirror)
http://bitbucket.org/hpk42/pytest/
Copyright Holger Krekel and others, 2004-2013
Copyright Holger Krekel and others, 2004-2014
Licensed under the MIT license.

View File

@@ -1,2 +1,2 @@
#
__version__ = '2.4.0'
__version__ = '2.6.2'

View File

@@ -74,9 +74,13 @@ class FastFilesCompleter:
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 += '*'
for x in sorted(glob(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)

View File

@@ -3,24 +3,35 @@ support for presenting detailed information in failing assertions.
"""
import py
import sys
import pytest
from _pytest.monkeypatch import monkeypatch
from _pytest.assertion import util
def pytest_addoption(parser):
group = parser.getgroup("debugconfig")
group.addoption('--assert', action="store", dest="assertmode",
group.addoption('--assert',
action="store",
dest="assertmode",
choices=("rewrite", "reinterp", "plain",),
default="rewrite", metavar="MODE",
help="""control assertion debugging tools.
'plain' performs no assertion debugging.
'reinterp' reinterprets assert statements after they failed to provide assertion expression information.
'rewrite' (the default) rewrites assert statements in test modules on import
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', '--no-magic', action="store_true",
default=False, help="DEPRECATED equivalent to --assert=plain")
default="rewrite",
metavar="MODE",
help="""control assertion debugging tools. 'plain'
performs no assertion debugging. 'reinterp'
reinterprets assert statements after they failed
to provide assertion expression information.
'rewrite' (the default) rewrites assert
statements in test modules on import 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', '--no-magic',
action="store_true",
default=False,
help="DEPRECATED equivalent to --assert=plain")
class AssertionState:
"""State for the assertion plugin."""
@@ -29,41 +40,44 @@ class AssertionState:
self.mode = mode
self.trace = config.trace.root.get("assertion")
def pytest_configure(config):
mode = config.getvalue("assertmode")
if config.getvalue("noassert") or config.getvalue("nomagic"):
mode = "plain"
if mode == "rewrite":
try:
import ast
import ast # noqa
except ImportError:
mode = "reinterp"
else:
# 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)):
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)
config._assertstate.hook = hook
config._assertstate.trace("configured with mode set to %r" % (mode,))
def pytest_unconfigure(config):
hook = config._assertstate.hook
if hook is not None:
if hook is not None and hook in sys.meta_path:
sys.meta_path.remove(hook)
def pytest_collection(session):
# this hook is only called when test modules are collected
# so for example not in the master process of pytest-xdist
@@ -72,39 +86,62 @@ def pytest_collection(session):
if hook is not None:
hook.set_session(session)
def pytest_runtest_setup(item):
"""Setup the pytest_assertrepr_compare hook
The newinterpret and rewrite modules will use util._reprcompare if
it exists to use custom reporting via the
pytest_assertrepr_compare hook. This sets up this custom
comparison for the test.
"""
def callbinrepr(op, left, right):
"""Call the pytest_assertrepr_compare hook and prepare the result
This uses the first result from the hook and then ensures the
following:
* Overly verbose explanations are dropped unles -vv was used.
* Embedded newlines are escaped to help util.format_explanation()
later.
* If the rewrite mode is used embedded %-characters are replaced
to protect later % formatting.
The result can be formatted by util.format_explanation() for
pretty printing.
"""
hook_result = item.ihook.pytest_assertrepr_compare(
config=item.config, op=op, left=left, right=right)
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 truncated, use "-vv" to see']
res = '\n~'.join(new_expl)
if (sum(len(p) for p in new_expl[1:]) > 80*8
and item.config.option.verbose < 2):
new_expl[1:] = [py.builtin._totext(
'Detailed information truncated, use "-vv" to show')]
new_expl = [line.replace("\n", "\\n") for line in new_expl]
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
# '%'s in the string. Escape them here.
res = res.replace("%", "%%")
return res
util._reprcompare = callbinrepr
def pytest_runtest_teardown(item):
util._reprcompare = None
def pytest_sessionfinish(session):
hook = session.config._assertstate.hook
if hook is not None:
hook.session = None
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:
@@ -119,8 +156,10 @@ def warn_about_missing_assertion(mode):
specifically = "failing tests may report as passing"
sys.stderr.write("WARNING: " + specifically +
" because assert statements are not executed "
"by the underlying Python interpreter "
"(are you using python -O?)\n")
" because assert statements are not executed "
"by the underlying Python interpreter "
"(are you using python -O?)\n")
# Expose this plugin's implementation for the pytest_assertrepr_compare hook
pytest_assertrepr_compare = util.assertrepr_compare

View File

@@ -286,7 +286,19 @@ class DebugInterpreter(ast.NodeVisitor):
source = "__exprinfo_expr.%s" % (attr.attr,)
co = self._compile(source)
try:
result = self.frame.eval(co, __exprinfo_expr=source_result)
try:
result = self.frame.eval(co, __exprinfo_expr=source_result)
except AttributeError:
# Maybe the attribute name needs to be mangled?
if not attr.attr.startswith("__") or attr.attr.endswith("__"):
raise
source = "getattr(__exprinfo_expr.__class__, '__name__', '')"
co = self._compile(source)
class_name = self.frame.eval(co, __exprinfo_expr=source_result)
mangled_attr = "_" + class_name + attr.attr
source = "__exprinfo_expr.%s" % (mangled_attr,)
co = self._compile(source)
result = self.frame.eval(co, __exprinfo_expr=source_result)
except Exception:
raise Failure(explanation)
explanation = "%s\n{%s = %s.%s\n}" % (self.frame.repr(result),

View File

@@ -1,3 +1,5 @@
import traceback
import types
import py
import sys, inspect
from compiler import parse, ast, pycodegen
@@ -355,7 +357,18 @@ class Getattr(Interpretable):
expr.eval(frame)
source = '__exprinfo_expr.%s' % self.attrname
try:
self.result = frame.eval(source, __exprinfo_expr=expr.result)
try:
self.result = frame.eval(source, __exprinfo_expr=expr.result)
except AttributeError:
# Maybe the attribute name needs to be mangled?
if (not self.attrname.startswith("__") or
self.attrname.endswith("__")):
raise
source = "getattr(__exprinfo_expr.__class__, '__name__', '')"
class_name = frame.eval(source, __exprinfo_expr=expr.result)
mangled_attr = "_" + class_name + self.attrname
source = "__exprinfo_expr.%s" % (mangled_attr,)
self.result = frame.eval(source, __exprinfo_expr=expr.result)
except passthroughex:
raise
except:
@@ -466,7 +479,7 @@ def check(s, frame=None):
def interpret(source, frame, should_fail=False):
module = Interpretable(parse(source, 'exec').node)
#print "got module", module
if isinstance(frame, py.std.types.FrameType):
if isinstance(frame, types.FrameType):
frame = py.code.Frame(frame)
try:
module.run(frame)
@@ -476,7 +489,6 @@ def interpret(source, frame, should_fail=False):
except passthroughex:
raise
except:
import traceback
traceback.print_exc()
if should_fail:
return ("(assertion failed, but when it was re-run for "

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:
@@ -37,10 +45,8 @@ class AssertionError(BuiltinAssertionError):
if sys.version_info > (3, 0):
AssertionError.__module__ = "builtins"
reinterpret_old = "old reinterpretation not available for py3"
else:
from _pytest.assertion.oldinterpret import interpret as reinterpret_old
if sys.version_info >= (2, 6) or (sys.platform.startswith("java")):
if sys.version_info >= (2, 6) or sys.platform.startswith("java"):
from _pytest.assertion.newinterpret import interpret as reinterpret
else:
reinterpret = reinterpret_old
from _pytest.assertion.oldinterpret import interpret as reinterpret

View File

@@ -15,7 +15,7 @@ import py
from _pytest.assertion import util
# py.test caches rewritten pycs in __pycache__.
# pytest caches rewritten pycs in __pycache__.
if hasattr(imp, "get_tag"):
PYTEST_TAG = imp.get_tag() + "-PYTEST"
else:
@@ -41,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")
@@ -55,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)
@@ -97,7 +102,7 @@ class AssertionRewritingHook(object):
# the most magical part of the process: load the source, rewrite the
# asserts, and load the rewritten source. We also cache the rewritten
# module code in a special pyc. We must be aware of the possibility of
# concurrent py.test processes rewriting and loading pycs. To avoid
# concurrent pytest processes rewriting and loading pycs. To avoid
# tricky race conditions, we maintain the following invariant: The
# cached pyc is always a complete, valid pyc. Operations on it must be
# atomic. POSIX's atomic rename comes in handy.
@@ -126,15 +131,15 @@ class AssertionRewritingHook(object):
pyc = os.path.join(cache_dir, cache_name)
# Notice that even if we're in a read-only directory, I'm going
# to check for a cached pyc. This may not be optimal...
co = _read_pyc(fn_pypath, pyc)
co = _read_pyc(fn_pypath, pyc, state.trace)
if co is None:
state.trace("rewriting %r" % (fn,))
co = _rewrite_test(state, fn_pypath)
source_stat, co = _rewrite_test(state, fn_pypath)
if co is None:
# Probably a SyntaxError in the test.
return None
if write:
_make_rewritten_pyc(state, fn_pypath, pyc, co)
_make_rewritten_pyc(state, source_stat, pyc, co)
else:
state.trace("found cached rewritten pyc for %r" % (fn,))
self.modules[name] = co, pyc
@@ -169,13 +174,30 @@ class AssertionRewritingHook(object):
tp = desc[2]
return tp == imp.PKG_DIRECTORY
def _write_pyc(state, co, source_path, pyc):
@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_stat, 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
# sometime to be able to use imp.load_compiled to load them. (See
# the comment in load_module above.)
mtime = int(source_path.mtime())
try:
fp = open(pyc, "wb")
except IOError:
@@ -187,7 +209,9 @@ def _write_pyc(state, co, source_path, pyc):
return False
try:
fp.write(imp.get_magic())
fp.write(struct.pack("<l", mtime))
mtime = int(source_stat.mtime)
size = source_stat.size & 0xFFFFFFFF
fp.write(struct.pack("<ll", mtime, size))
marshal.dump(co, fp)
finally:
fp.close()
@@ -196,15 +220,16 @@ def _write_pyc(state, co, source_path, pyc):
RN = "\r\n".encode("utf-8")
N = "\n".encode("utf-8")
cookie_re = re.compile("coding[:=]\s*[-\w.]+")
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:
stat = fn.stat()
source = fn.read("rb")
except EnvironmentError:
return None
return None, 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
@@ -220,17 +245,18 @@ def _rewrite_test(state, fn):
end1 = source.find("\n")
end2 = source.find("\n", end1 + 1)
if (not source.startswith(BOM_UTF8) and
(not cookie_re.match(source[0:end1]) or
not cookie_re.match(source[end1:end2]))):
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
# encodings imported us again, so don't rewrite.
return None, None
state._indecode = True
try:
try:
source.decode("ascii")
except UnicodeDecodeError:
# Let it fail in real import.
return None
return None, None
finally:
del state._indecode
# On Python versions which are not 2.7 and less than or equal to 3.1, the
@@ -242,7 +268,7 @@ def _rewrite_test(state, fn):
except SyntaxError:
# Let this pop up again in the real import.
state.trace("failed to parse: %r" % (fn,))
return None
return None, None
rewrite_asserts(tree)
try:
co = compile(tree, fn.strpath, "exec")
@@ -250,24 +276,24 @@ def _rewrite_test(state, fn):
# It's possible that this error is from some bug in the
# assertion rewriting, but I don't know of a fast way to tell.
state.trace("failed to compile: %r" % (fn,))
return None
return co
return None, None
return stat, co
def _make_rewritten_pyc(state, fn, pyc, co):
def _make_rewritten_pyc(state, source_stat, pyc, co):
"""Try to dump rewritten code to *pyc*."""
if sys.platform.startswith("win"):
# Windows grants exclusive access to open files and doesn't have atomic
# rename, so just write into the final file.
_write_pyc(state, co, fn, pyc)
_write_pyc(state, co, source_stat, pyc)
else:
# When not on windows, assume rename is atomic. Dump the code object
# into a file specific to this process and atomically replace it.
proc_pyc = pyc + "." + str(os.getpid())
if _write_pyc(state, co, fn, proc_pyc):
if _write_pyc(state, co, source_stat, proc_pyc):
os.rename(proc_pyc, pyc)
def _read_pyc(source, pyc):
"""Possibly read a py.test pyc containing rewritten code.
def _read_pyc(source, pyc, trace=lambda x: None):
"""Possibly read a pytest pyc containing rewritten code.
Return rewritten code if successful or None if not.
"""
@@ -275,23 +301,28 @@ def _read_pyc(source, pyc):
fp = open(pyc, "rb")
except IOError:
return None
try:
with fp:
try:
mtime = int(source.mtime())
data = fp.read(8)
except EnvironmentError:
size = source.size()
data = fp.read(12)
except EnvironmentError as e:
trace('_read_pyc(%s): EnvironmentError %s' % (source, e))
return None
# Check for invalid or out of date pyc file.
if (len(data) != 8 or data[:4] != imp.get_magic() or
struct.unpack("<l", data[4:])[0] != mtime):
if (len(data) != 12 or data[:4] != imp.get_magic() or
struct.unpack("<ll", data[4:]) != (mtime, size)):
trace('_read_pyc(%s): invalid or out of date pyc' % source)
return None
try:
co = marshal.load(fp)
except Exception as e:
trace('_read_pyc(%s): marshal.load error %s' % (source, e))
return None
co = marshal.load(fp)
if not isinstance(co, types.CodeType):
# That's interesting....
trace('_read_pyc(%s): not a code object' % source)
return None
return co
finally:
fp.close()
def rewrite_asserts(mod):
@@ -299,8 +330,26 @@ def rewrite_asserts(mod):
AssertionRewriter().run(mod)
_saferepr = py.io.saferepr
from _pytest.assertion.util import format_explanation as _format_explanation
def _saferepr(obj):
"""Get a safe repr of an object for assertion error messages.
The assertion formatting (util.format_explanation()) requires
newlines to be escaped since they are a special character for it.
Normally assertion.util.format_explanation() does this but for a
custom repr it is possible to contain one of the special escape
sequences, especially '\n{' and '\n}' are likely to be present in
JSON reprs.
"""
repr = py.io.saferepr(obj)
if py.builtin._istext(repr):
t = py.builtin.text
else:
t = py.builtin.bytes
return repr.replace(t("\n"), t("\\n"))
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)
@@ -538,7 +587,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)
@@ -631,7 +681,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,16 +1,14 @@
"""Utilities for assertion debugging"""
import pprint
import py
try:
from collections.abc import Sequence
from collections import Sequence
except ImportError:
try:
from collections import Sequence
except ImportError:
Sequence = list
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
@@ -29,7 +27,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)
@@ -51,28 +60,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()
@@ -80,9 +109,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
@@ -97,7 +126,7 @@ def assertrepr_compare(config, op, left, right):
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, Sequence))
and not isinstance(x, basestring))
@@ -120,13 +149,11 @@ def assertrepr_compare(config, op, left, right):
elif op == 'not in':
if istext(left) and istext(right):
explanation = _notin_text(left, right, verbose)
except py.builtin._sysex:
raise
except:
excinfo = py.code.ExceptionInfo()
except Exception:
explanation = [
'(pytest_assertion plugin: representation of details failed. '
'Probably an object has a faulty __repr__.)', str(excinfo)]
u('(pytest_assertion plugin: representation of details failed. '
'Probably an object has a faulty __repr__.)'),
u(py.code.ExceptionInfo())]
if not explanation:
return None
@@ -135,12 +162,19 @@ def assertrepr_compare(config, op, left, right):
def _diff_text(left, right, verbose=False):
"""Return the explanation for the diff between text
"""Return the explanation for the diff between text or bytes
Unless --verbose is used this will skip leading and trailing
characters which are identical to keep the diff minimal.
If the input are bytes they will be safely converted to text.
"""
from difflib import ndiff
explanation = []
if isinstance(left, py.builtin.bytes):
left = u(repr(left)[1:-1]).replace(r'\n', '\n')
if isinstance(right, py.builtin.bytes):
right = u(repr(right)[1:-1]).replace(r'\n', '\n')
if not verbose:
i = 0 # just in case left or right has zero length
for i in range(min(len(left), len(right))):
@@ -148,8 +182,8 @@ def _diff_text(left, right, verbose=False):
break
if i > 42:
i -= 10 # Provide some context
explanation = ['Skipping %s identical leading '
'characters in diff, use -v to show' % i]
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):
@@ -158,13 +192,13 @@ def _diff_text(left, right, verbose=False):
break
if i > 42:
i -= 10 # Provide some context
explanation += ['Skipping %s identical trailing '
'characters in diff, use -v to show' % i]
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())]
for line in ndiff(left.splitlines(),
right.splitlines())]
return explanation
@@ -172,19 +206,18 @@ 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' %
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))
return explanation # + _diff_text(pprint.pformat(left),
# pprint.pformat(right))
def _compare_eq_set(left, right, verbose=False):
@@ -192,11 +225,11 @@ def _compare_eq_set(left, right, verbose=False):
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
@@ -207,26 +240,26 @@ def _compare_eq_dict(left, right, verbose=False):
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 += ['Omitting %s identical items, use -v to show' %
explanation += [u('Omitting %s identical items, use -v to show') %
len(same)]
elif same:
explanation += ['Common items:']
explanation += py.std.pprint.pformat(same).splitlines()
explanation += [u('Common items:')]
explanation += pprint.pformat(same).splitlines()
diff = set(k for k in common if left[k] != right[k])
if diff:
explanation += ['Differing items:']
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('Left contains more items:')
explanation.extend(py.std.pprint.pformat(
explanation.append(u('Left contains more items:'))
explanation.extend(pprint.pformat(
dict((k, left[k]) for k in extra_left)).splitlines())
extra_right = set(right) - set(left)
if extra_right:
explanation.append('Right contains more items:')
explanation.extend(py.std.pprint.pformat(
explanation.append(u('Right contains more items:'))
explanation.extend(pprint.pformat(
dict((k, right[k]) for k in extra_right)).splitlines())
return explanation
@@ -237,14 +270,14 @@ def _notin_text(term, text, verbose=False):
tail = text[index+len(term):]
correct_text = head + tail
diff = _diff_text(correct_text, text, verbose)
newdiff = ['%s is contained here:' % py.io.saferepr(term, maxsize=42)]
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,42 +1,52 @@
""" per-test stdout/stderr capturing mechanisms, ``capsys`` and ``capfd`` function arguments. """
"""
per-test stdout/stderr capturing mechanism.
"""
from __future__ import with_statement
import pytest, py
import sys
import os
from tempfile import TemporaryFile
import py
import pytest
from py.io import TextIO
unicode = py.builtin.text
patchsysdict = {0: 'stdin', 1: 'stdout', 2: 'stderr'}
def pytest_addoption(parser):
group = parser.getgroup("general")
group._addoption('--capture', action="store", default=None,
group._addoption(
'--capture', action="store",
default="fd" if hasattr(os, "dup") else "sys",
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="per-test capturing method: one of fd|sys|no.")
group._addoption(
'-s', action="store_const", const="no", dest="capture",
help="shortcut for --capture=no.")
@pytest.mark.tryfirst
def pytest_load_initial_conftests(early_config, parser, args, __multicall__):
ns = parser.parse_known_args(args)
method = ns.capture
if not method:
method = "fd"
if method == "fd" and not hasattr(os, "dup"):
method = "sys"
capman = CaptureManager(method)
early_config.pluginmanager.register(capman, "capturemanager")
ns = early_config.known_args_namespace
pluginmanager = early_config.pluginmanager
capman = CaptureManager(ns.capture)
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 if it is imported
pluginmanager.add_shutdown(capman.reset_capturings)
# 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)
pluginmanager.add_shutdown(silence_logging_at_shutdown)
# finally trigger conftest loading but while capturing (issue93)
capman.resumecapture()
capman.init_capturings()
try:
try:
return __multicall__.execute()
@@ -47,202 +57,388 @@ def pytest_load_initial_conftests(early_config, parser, args, __multicall__):
sys.stderr.write(err)
raise
def addouterr(rep, outerr):
for secname, content in zip(["out", "err"], outerr):
if content:
rep.sections.append(("Captured std%s" % secname, content))
class NoCapture:
def startall(self):
pass
def resume(self):
pass
def reset(self):
pass
def suspend(self):
return "", ""
class CaptureManager:
def __init__(self, defaultmethod=None):
self._method2capture = {}
self._defaultmethod = defaultmethod
def _maketempfile(self):
f = py.std.tempfile.TemporaryFile()
newf = py.io.dupfile(f, encoding="UTF-8")
f.close()
return newf
def _makestringio(self):
return py.io.TextIO()
def __init__(self, method):
self._method = method
def _getcapture(self, method):
if method == "fd":
return py.io.StdCaptureFD(now=False,
out=self._maketempfile(), err=self._maketempfile()
)
return MultiCapture(out=True, err=True, Capture=FDCapture)
elif method == "sys":
return py.io.StdCapture(now=False,
out=self._makestringio(), err=self._makestringio()
)
return MultiCapture(out=True, err=True, Capture=SysCapture)
elif method == "no":
return NoCapture()
return MultiCapture(out=False, err=False, in_=False)
else:
raise ValueError("unknown capturing method: %r" % method)
def _getmethod(self, config, fspath):
if config.option.capture:
method = config.option.capture
else:
try:
method = config._conftest.rget("option_capture", path=fspath)
except KeyError:
method = "fd"
if method == "fd" and not hasattr(os, 'dup'): # e.g. jython
method = "sys"
return method
def init_capturings(self):
assert not hasattr(self, "_capturing")
self._capturing = self._getcapture(self._method)
self._capturing.start_capturing()
def reset_capturings(self):
for name, cap in self._method2capture.items():
cap.reset()
cap = self.__dict__.pop("_capturing", None)
if cap is not None:
cap.pop_outerr_to_orig()
cap.stop_capturing()
def resumecapture_item(self, item):
method = self._getmethod(item.config, item.fspath)
if not hasattr(item, 'outerr'):
item.outerr = ('', '') # we accumulate outerr on the item
return self.resumecapture(method)
def resumecapture(self):
self._capturing.resume_capturing()
def resumecapture(self, method=None):
if hasattr(self, '_capturing'):
raise ValueError("cannot resume, already capturing with %r" %
(self._capturing,))
if method is None:
method = self._defaultmethod
cap = self._method2capture.get(method)
self._capturing = method
if cap is None:
self._method2capture[method] = cap = self._getcapture(method)
cap.startall()
else:
cap.resume()
def suspendcapture(self, item=None):
def suspendcapture(self, in_=False):
self.deactivate_funcargs()
if hasattr(self, '_capturing'):
method = self._capturing
cap = self._method2capture.get(method)
if cap is not None:
outerr = cap.suspend()
del self._capturing
if item:
outerr = (item.outerr[0] + outerr[0],
item.outerr[1] + outerr[1])
cap = getattr(self, "_capturing", None)
if cap is not None:
outerr = cap.readouterr()
cap.suspend_capturing(in_=in_)
return outerr
if hasattr(item, 'outerr'):
return item.outerr
return "", ""
def activate_funcargs(self, pyfuncitem):
funcargs = getattr(pyfuncitem, "funcargs", None)
if funcargs is not None:
for name, capfuncarg in funcargs.items():
if name in ('capsys', 'capfd'):
assert not hasattr(self, '_capturing_funcarg')
self._capturing_funcarg = capfuncarg
capfuncarg._start()
capfuncarg = pyfuncitem.__dict__.pop("_capfuncarg", None)
if capfuncarg is not None:
capfuncarg._start()
self._capfuncarg = capfuncarg
def deactivate_funcargs(self):
capturing_funcarg = getattr(self, '_capturing_funcarg', None)
if capturing_funcarg:
outerr = capturing_funcarg._finalize()
del self._capturing_funcarg
return outerr
capfuncarg = self.__dict__.pop("_capfuncarg", None)
if capfuncarg is not None:
capfuncarg.close()
@pytest.mark.tryfirst
def pytest_make_collect_report(self, __multicall__, collector):
method = self._getmethod(collector.config, collector.fspath)
try:
self.resumecapture(method)
except ValueError:
return # recursive collect, XXX refactor capturing
# to allow for more lightweight recursive capturing
if not isinstance(collector, pytest.File):
return
self.resumecapture()
try:
rep = __multicall__.execute()
finally:
outerr = self.suspendcapture()
addouterr(rep, outerr)
out, err = self.suspendcapture()
if out:
rep.sections.append(("Captured stdout", out))
if err:
rep.sections.append(("Captured stderr", err))
return rep
@pytest.mark.tryfirst
@pytest.mark.hookwrapper
def pytest_runtest_setup(self, item):
self.resumecapture_item(item)
self.resumecapture()
yield
self.suspendcapture_item(item, "setup")
@pytest.mark.tryfirst
@pytest.mark.hookwrapper
def pytest_runtest_call(self, item):
self.resumecapture_item(item)
self.resumecapture()
self.activate_funcargs(item)
yield
#self.deactivate_funcargs() called from suspendcapture()
self.suspendcapture_item(item, "call")
@pytest.mark.tryfirst
@pytest.mark.hookwrapper
def pytest_runtest_teardown(self, item):
self.resumecapture_item(item)
def pytest_keyboard_interrupt(self, excinfo):
if hasattr(self, '_capturing'):
self.suspendcapture()
self.resumecapture()
yield
self.suspendcapture_item(item, "teardown")
@pytest.mark.tryfirst
def pytest_runtest_makereport(self, __multicall__, item, call):
funcarg_outerr = self.deactivate_funcargs()
rep = __multicall__.execute()
outerr = self.suspendcapture(item)
if funcarg_outerr is not None:
outerr = (outerr[0] + funcarg_outerr[0],
outerr[1] + funcarg_outerr[1])
addouterr(rep, outerr)
if not rep.passed or rep.when == "teardown":
outerr = ('', '')
item.outerr = outerr
return rep
def pytest_keyboard_interrupt(self, excinfo):
self.reset_capturings()
@pytest.mark.tryfirst
def pytest_internalerror(self, excinfo):
self.reset_capturings()
def suspendcapture_item(self, item, when):
out, err = self.suspendcapture()
item.add_report_section(when, "out", out)
item.add_report_section(when, "err", err)
error_capsysfderror = "cannot use capsys and capfd at the same time"
def pytest_funcarg__capsys(request):
@pytest.fixture
def capsys(request):
"""enables capturing of writes to sys.stdout/sys.stderr and makes
captured output available via ``capsys.readouterr()`` method calls
which return a ``(out, err)`` tuple.
"""
if "capfd" in request._funcargs:
raise request.raiseerror(error_capsysfderror)
return CaptureFixture(py.io.StdCapture)
request.node._capfuncarg = c = CaptureFixture(SysCapture)
return c
def pytest_funcarg__capfd(request):
@pytest.fixture
def capfd(request):
"""enables capturing of writes to file descriptors 1 and 2 and makes
captured output available via ``capsys.readouterr()`` method calls
captured output available via ``capfd.readouterr()`` method calls
which return a ``(out, err)`` tuple.
"""
if "capsys" in request._funcargs:
request.raiseerror(error_capsysfderror)
if not hasattr(os, 'dup'):
pytest.skip("capfd funcarg needs os.dup")
return CaptureFixture(py.io.StdCaptureFD)
request.node._capfuncarg = c = CaptureFixture(FDCapture)
return c
class CaptureFixture:
def __init__(self, captureclass):
self.capture = captureclass(now=False)
self.captureclass = captureclass
def _start(self):
self.capture.startall()
self._capture = MultiCapture(out=True, err=True, in_=False,
Capture=self.captureclass)
self._capture.start_capturing()
def _finalize(self):
if hasattr(self, 'capture'):
outerr = self._outerr = self.capture.reset()
del self.capture
return outerr
def close(self):
cap = self.__dict__.pop("_capture", None)
if cap is not None:
self._outerr = cap.pop_outerr_to_orig()
cap.stop_capturing()
def readouterr(self):
try:
return self.capture.readouterr()
return self._capture.readouterr()
except AttributeError:
return self._outerr
def safe_text_dupfile(f, mode, default_encoding="UTF8"):
""" return a open text file object that's a duplicate of f on the
FD-level if possible.
"""
encoding = getattr(f, "encoding", None)
try:
fd = f.fileno()
except Exception:
if "b" not in getattr(f, "mode", "") and hasattr(f, "encoding"):
# we seem to have a text stream, let's just use it
return f
else:
newfd = os.dup(fd)
if "b" not in mode:
mode += "b"
f = os.fdopen(newfd, mode, 0) # no buffering
return EncodedFile(f, encoding or default_encoding)
class EncodedFile(object):
errors = "strict" # possibly needed by py3 code (issue555)
def __init__(self, buffer, encoding):
self.buffer = buffer
self.encoding = encoding
def write(self, obj):
if isinstance(obj, unicode):
obj = obj.encode(self.encoding, "replace")
self.buffer.write(obj)
def writelines(self, linelist):
data = ''.join(linelist)
self.write(data)
def __getattr__(self, name):
return getattr(self.buffer, name)
class MultiCapture(object):
out = err = in_ = None
def __init__(self, out=True, err=True, in_=True, Capture=None):
if in_:
self.in_ = Capture(0)
if out:
self.out = Capture(1)
if err:
self.err = Capture(2)
def start_capturing(self):
if self.in_:
self.in_.start()
if self.out:
self.out.start()
if self.err:
self.err.start()
def pop_outerr_to_orig(self):
""" pop current snapshot out/err capture and flush to orig streams. """
out, err = self.readouterr()
if out:
self.out.writeorg(out)
if err:
self.err.writeorg(err)
return out, err
def suspend_capturing(self, in_=False):
if self.out:
self.out.suspend()
if self.err:
self.err.suspend()
if in_ and self.in_:
self.in_.suspend()
self._in_suspended = True
def resume_capturing(self):
if self.out:
self.out.resume()
if self.err:
self.err.resume()
if hasattr(self, "_in_suspended"):
self.in_.resume()
del self._in_suspended
def stop_capturing(self):
""" stop capturing and reset capturing streams """
if hasattr(self, '_reset'):
raise ValueError("was already stopped")
self._reset = True
if self.out:
self.out.done()
if self.err:
self.err.done()
if self.in_:
self.in_.done()
def readouterr(self):
""" return snapshot unicode value of stdout/stderr capturings. """
return (self.out.snap() if self.out is not None else "",
self.err.snap() if self.err is not None else "")
class NoCapture:
__init__ = start = done = suspend = resume = lambda *args: None
class FDCapture:
""" Capture IO to/from a given os-level filedescriptor. """
def __init__(self, targetfd, tmpfile=None):
self.targetfd = targetfd
try:
self.targetfd_save = os.dup(self.targetfd)
except OSError:
self.start = lambda: None
self.done = lambda: None
else:
if targetfd == 0:
assert not tmpfile, "cannot set tmpfile with stdin"
tmpfile = open(os.devnull, "r")
self.syscapture = SysCapture(targetfd)
else:
if tmpfile is None:
f = TemporaryFile()
with f:
tmpfile = safe_text_dupfile(f, mode="wb+")
if targetfd in patchsysdict:
self.syscapture = SysCapture(targetfd, tmpfile)
else:
self.syscapture = NoCapture()
self.tmpfile = tmpfile
self.tmpfile_fd = tmpfile.fileno()
def __repr__(self):
return "<FDCapture %s oldfd=%s>" % (self.targetfd, self.targetfd_save)
def start(self):
""" Start capturing on targetfd using memorized tmpfile. """
try:
os.fstat(self.targetfd_save)
except (AttributeError, OSError):
raise ValueError("saved filedescriptor not valid anymore")
os.dup2(self.tmpfile_fd, self.targetfd)
self.syscapture.start()
def snap(self):
f = self.tmpfile
f.seek(0)
res = f.read()
if res:
enc = getattr(f, "encoding", None)
if enc and isinstance(res, bytes):
res = py.builtin._totext(res, enc, "replace")
f.truncate(0)
f.seek(0)
return res
return ''
def done(self):
""" stop capturing, restore streams, return original capture file,
seeked to position zero. """
targetfd_save = self.__dict__.pop("targetfd_save")
os.dup2(targetfd_save, self.targetfd)
os.close(targetfd_save)
self.syscapture.done()
self.tmpfile.close()
def suspend(self):
self.syscapture.suspend()
os.dup2(self.targetfd_save, self.targetfd)
def resume(self):
self.syscapture.resume()
os.dup2(self.tmpfile_fd, self.targetfd)
def writeorg(self, data):
""" write to original file descriptor. """
if py.builtin._istext(data):
data = data.encode("utf8") # XXX use encoding of original stream
os.write(self.targetfd_save, data)
class SysCapture:
def __init__(self, fd, tmpfile=None):
name = patchsysdict[fd]
self._old = getattr(sys, name)
self.name = name
if tmpfile is None:
if name == "stdin":
tmpfile = DontReadFromInput()
else:
tmpfile = TextIO()
self.tmpfile = tmpfile
def start(self):
setattr(sys, self.name, self.tmpfile)
def snap(self):
f = self.tmpfile
res = f.getvalue()
f.truncate(0)
f.seek(0)
return res
def done(self):
setattr(sys, self.name, self._old)
del self._old
self.tmpfile.close()
def suspend(self):
setattr(sys, self.name, self._old)
def resume(self):
setattr(sys, self.name, self.tmpfile)
def writeorg(self, data):
self._old.write(data)
self._old.flush()
class DontReadFromInput:
"""Temporary stub class. Ideally when stdin is accessed, the
capturing should be turned off, with possibly all data captured
so far sent to the screen. This should be configurable, though,
because in automated test runs it is better to crash than
hang indefinitely.
"""
def read(self, *args):
raise IOError("reading from stdin while output is captured")
readline = read
readlines = read
__iter__ = read
def fileno(self):
raise ValueError("redirected Stdin is pseudofile, has no fileno()")
def isatty(self):
return False
def close(self):
self._finalize()
pass

View File

@@ -1,11 +1,24 @@
""" command line options, ini-file and conftest.py processing. """
import argparse
import shlex
import traceback
import types
import warnings
import py
# DON't import pytest here because it causes import cycle troubles
import sys, os
from _pytest import hookspec # the extension point definitions
from _pytest.core import PluginManager
# pytest startup
#
class ConftestImportFailure(Exception):
def __init__(self, path, excinfo):
Exception.__init__(self, path, excinfo)
self.path = path
self.excinfo = excinfo
def main(args=None, plugins=None):
""" return exit code, after performing an in-process test run.
@@ -15,15 +28,23 @@ def main(args=None, plugins=None):
: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
try:
config = _prepareconfig(args, plugins)
except ConftestImportFailure:
e = sys.exc_info()[1]
tw = py.io.TerminalWriter(sys.stderr)
for line in traceback.format_exception(*e.excinfo):
tw.line(line.rstrip(), red=True)
tw.line("ERROR: could not load %s\n" % (e.path), red=True)
return 4
else:
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"""
""" error in pytest usage or invocation"""
_preinit = []
@@ -41,7 +62,7 @@ def get_plugin_manager():
return _preinit.pop(0)
# subsequent calls to main will create a fresh instance
pluginmanager = PytestPluginManager()
pluginmanager.config = config = Config(pluginmanager) # XXX attr needed?
pluginmanager.config = Config(pluginmanager) # XXX attr needed?
for spec in default_plugins:
pluginmanager.import_plugin(spec)
return pluginmanager
@@ -54,13 +75,17 @@ def _prepareconfig(args=None, plugins=None):
elif not isinstance(args, (tuple, list)):
if not isinstance(args, str):
raise ValueError("not a string or argument list: %r" % (args,))
args = py.std.shlex.split(args)
args = 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)
try:
if plugins:
for plugin in plugins:
pluginmanager.register(plugin)
return pluginmanager.hook.pytest_cmdline_parse(
pluginmanager=pluginmanager, args=args)
except Exception:
pluginmanager.ensure_shutdown()
raise
class PytestPluginManager(PluginManager):
def __init__(self, hookspecs=[hookspec]):
@@ -82,6 +107,8 @@ class PytestPluginManager(PluginManager):
config.addinivalue_line("markers",
"trylast: mark a hook implementation function such that the "
"plugin machinery will try to call it last/as late as possible.")
for warning in self._warnings:
config.warn(code="I1", message=warning)
class Parser:
@@ -94,7 +121,6 @@ class Parser:
self._usage = usage
self._inidict = {}
self._ininames = []
self.hints = []
def processoption(self, option):
if self._processopt:
@@ -129,8 +155,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
@@ -159,8 +185,7 @@ class Parser:
a = option.attrs()
arggroup.add_argument(*n, **a)
# bash like autocompletion for dirs (appending '/')
optparser.add_argument(FILE_OR_DIR, nargs='*'
).completer=filescompleter
optparser.add_argument(FILE_OR_DIR, nargs='*').completer=filescompleter
return optparser
def parse_setoption(self, args, option):
@@ -207,7 +232,7 @@ class ArgumentError(Exception):
class Argument:
"""class that mimics the necessary behaviour of py.std.optparse.Option """
"""class that mimics the necessary behaviour of optparse.Option """
_typ_map = {
'int': int,
'string': str,
@@ -225,8 +250,8 @@ class Argument:
try:
help = attrs['help']
if '%default' in help:
py.std.warnings.warn(
'py.test now uses argparse. "%default" should be'
warnings.warn(
'pytest now uses argparse. "%default" should be'
' changed to "%(default)s" ',
FutureWarning,
stacklevel=3)
@@ -238,10 +263,10 @@ class Argument:
pass
else:
# this might raise a keyerror as well, don't want to catch that
if isinstance(typ, str):
if isinstance(typ, py.builtin._basestring):
if typ == 'choice':
if self.TYPE_WARN:
py.std.warnings.warn(
warnings.warn(
'type argument to addoption() is a string %r.'
' For parsearg this is optional and when supplied '
' should be a type.'
@@ -253,7 +278,7 @@ class Argument:
attrs['type'] = type(attrs['choices'][0])
else:
if self.TYPE_WARN:
py.std.warnings.warn(
warnings.warn(
'type argument to addoption() is a string %r.'
' For parsearg this should be a type.'
' (options: %s)' % (typ, names),
@@ -373,32 +398,24 @@ class OptionGroup:
self.options.append(option)
class MyOptionParser(py.std.argparse.ArgumentParser):
class MyOptionParser(argparse.ArgumentParser):
def __init__(self, parser):
self._parser = parser
py.std.argparse.ArgumentParser.__init__(self, usage=parser._usage,
argparse.ArgumentParser.__init__(self, usage=parser._usage,
add_help=False, formatter_class=DropShorterLongHelpFormatter)
def format_epilog(self, formatter):
hints = self._parser.hints
if hints:
s = "\n".join(["hint: " + x for x in hints]) + "\n"
s = "\n" + s + "\n"
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')
msg = argparse._('unrecognized arguments: %s')
self.error(msg % ' '.join(argv))
getattr(args, FILE_OR_DIR).extend(argv)
return args
class DropShorterLongHelpFormatter(py.std.argparse.HelpFormatter):
class DropShorterLongHelpFormatter(argparse.HelpFormatter):
"""shorten help for long options that differ only in extra hyphens
- collapse **long** options that are the same except for extra hyphens
@@ -408,7 +425,7 @@ class DropShorterLongHelpFormatter(py.std.argparse.HelpFormatter):
- 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)
orgstr = argparse.HelpFormatter._format_action_invocation(self, action)
if orgstr and orgstr[0] != '-': # only optional arguments
return orgstr
res = getattr(action, '_formatted_action_invocation', None)
@@ -442,47 +459,41 @@ class DropShorterLongHelpFormatter(py.std.argparse.HelpFormatter):
if len(option) == 2 or option[2] == ' ':
return_list.append(option)
if option[2:] == short_long.get(option.replace('-', '')):
return_list.append(option)
return_list.append(option.replace(' ', '='))
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.
towards conftest modules from pytest objects.
"""
def __init__(self, onimport=None, confcutdir=None):
def __init__(self, onimport=None):
self._path2confmods = {}
self._onimport = onimport
self._conftestpath2mod = {}
self._confcutdir = confcutdir
self._confcutdir = None
def setinitial(self, args):
""" try to find a first anchor path for looking up global values
from conftests. This function is usually called _before_
argument parsing. conftest files may add command line options
and we thus have no completely safe way of determining
which parts of the arguments are actually related to options
and which are file system paths. We just try here to get
bootstrapped ...
def setinitial(self, namespace):
""" load initial conftest files given a preparsed "namespace".
As conftest files may add their own command line options
which have arguments ('--my-opt somepath') we might get some
false positives. All builtin and 3rd party plugins will have
been loaded, however, so common options will not confuse our logic
here.
"""
current = py.path.local()
opt = '--confcutdir'
for i in range(len(args)):
opt1 = str(args[i])
if opt1.startswith(opt):
if opt1 == opt:
if len(args) > i:
p = current.join(args[i+1], abs=True)
elif opt1.startswith(opt + "="):
p = current.join(opt1[len(opt)+1:], abs=1)
self._confcutdir = p
break
self._confcutdir = current.join(namespace.confcutdir, abs=True) \
if namespace.confcutdir else None
testpaths = namespace.file_or_dir
foundanchor = False
for arg in args:
if hasattr(arg, 'startswith') and arg.startswith("--"):
continue
anchor = current.join(arg, abs=1)
for path in testpaths:
path = str(path)
# remove node-id syntax
i = path.find("::")
if i != -1:
path = path[:i]
anchor = current.join(path, abs=1)
if exists(anchor): # we found some file object
self._try_load_conftest(anchor)
foundanchor = True
@@ -490,7 +501,7 @@ class Conftest(object):
self._try_load_conftest(current)
def _try_load_conftest(self, anchor):
self._path2confmods[None] = self.getconftestmodules(anchor)
self.getconftestmodules(anchor)
# let's also consider test* subdirs
if anchor.check(dir=1):
for x in anchor.listdir("test*"):
@@ -499,28 +510,22 @@ class Conftest(object):
def getconftestmodules(self, path):
try:
clist = self._path2confmods[path]
return self._path2confmods[path]
except KeyError:
if path is None:
raise ValueError("missing default conftest.")
clist = []
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))
mod = self.importconftest(conftestpath)
clist.append(mod)
self._path2confmods[path] = clist
return clist
return clist
def rget(self, name, path=None):
mod, value = self.rget_with_confmod(name, path)
return value
def rget_with_confmod(self, name, path=None):
def rget_with_confmod(self, name, path):
modules = self.getconftestmodules(path)
modules.reverse()
for mod in modules:
for mod in reversed(modules):
try:
return mod, getattr(mod, name)
except AttributeError:
@@ -528,27 +533,27 @@ class Conftest(object):
raise KeyError(name)
def importconftest(self, conftestpath):
assert conftestpath.check(), conftestpath
try:
return self._conftestpath2mod[conftestpath]
except KeyError:
pkgpath = conftestpath.pypkgpath()
if pkgpath is None:
_ensure_removed_sysmodule(conftestpath.purebasename)
self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport()
try:
mod = conftestpath.pyimport()
except Exception:
raise ConftestImportFailure(conftestpath, sys.exc_info())
self._conftestpath2mod[conftestpath] = mod
dirpath = conftestpath.dirpath()
if dirpath in self._path2confmods:
for path, mods in self._path2confmods.items():
if path and path.relto(dirpath) or path == dirpath:
assert mod not in mods
mods.append(mod)
self._postimport(mod)
if self._onimport:
self._onimport(mod)
return mod
def _postimport(self, mod):
if self._onimport:
self._onimport(mod)
return mod
def _ensure_removed_sysmodule(modname):
try:
@@ -563,6 +568,11 @@ class CmdOptions(object):
def __repr__(self):
return "<CmdOptions %r>" %(self.__dict__,)
class Notset:
def __repr__(self):
return "<NOTSET>"
notset = Notset()
FILE_OR_DIR = 'file_or_dir'
class Config(object):
""" access to configuration values, pluginmanager and plugin hooks. """
@@ -613,6 +623,14 @@ class Config(object):
self.hook.pytest_unconfigure(config=self)
self.pluginmanager.ensure_shutdown()
def warn(self, code, message):
""" generate a warning for this test session. """
self.hook.pytest_logwarning(code=code, message=message,
fslocation=None, nodeid=None)
def get_terminal_writer(self):
return self.pluginmanager.getplugin("terminalreporter")._tw
def pytest_cmdline_parse(self, pluginmanager, args):
assert self == pluginmanager.config, (self, pluginmanager.config)
self.parse(args)
@@ -670,12 +688,20 @@ class Config(object):
plugins += self._conftest.getconftestmodules(fspath)
return plugins
def pytest_load_initial_conftests(self, parser, args):
self._conftest.setinitial(args)
def pytest_load_initial_conftests(self, early_config):
self._conftest.setinitial(early_config.known_args_namespace)
pytest_load_initial_conftests.trylast = True
def _initini(self, args):
self.inicfg = getcfg(args, ["pytest.ini", "tox.ini", "setup.cfg"])
parsed_args = self._parser.parse_known_args(args)
if parsed_args.inifilename:
iniconfig = py.iniconfig.IniConfig(parsed_args.inifilename)
if 'pytest' in iniconfig.sections:
self.inicfg = iniconfig['pytest']
else:
self.inicfg = {}
else:
self.inicfg = getcfg(args, ["pytest.ini", "tox.ini", "setup.cfg"])
self._parser.addini('addopts', 'extra command line options', 'args')
self._parser.addini('minversion', 'minimally required pytest version')
@@ -687,8 +713,19 @@ class Config(object):
self.pluginmanager.consider_preparse(args)
self.pluginmanager.consider_setuptools_entrypoints()
self.pluginmanager.consider_env()
self.hook.pytest_load_initial_conftests(early_config=self,
args=args, parser=self._parser)
self.known_args_namespace = ns = self._parser.parse_known_args(args)
try:
self.hook.pytest_load_initial_conftests(early_config=self,
args=args, parser=self._parser)
except ConftestImportFailure:
e = sys.exc_info()[1]
if ns.help or ns.version:
# we don't want to prevent --help/--version to work
# so just let is pass and print a warning at the end
self.pluginmanager._warnings.append(
"could not load initial conftests (%s)\n" % e.path)
else:
raise
def _checkversion(self):
import pytest
@@ -704,17 +741,15 @@ class Config(object):
def parse(self, args):
# parse given cmdline arguments into this config object.
# Note that this can only be called once per testing process.
assert not hasattr(self, 'args'), (
"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:
args.append(py.std.os.getcwd())
args.append(os.getcwd())
self.args = args
def addinivalue_line(self, name, line):
@@ -752,18 +787,18 @@ class Config(object):
if type == "pathlist":
dp = py.path.local(self.inicfg.config.path).dirpath()
l = []
for relpath in py.std.shlex.split(value):
for relpath in shlex.split(value):
l.append(dp.join(relpath, abs=True))
return l
elif type == "args":
return py.std.shlex.split(value)
return shlex.split(value)
elif type == "linelist":
return [t for t in map(lambda x: x.strip(), value.split("\n")) if t]
else:
assert type is None
return value
def _getconftest_pathlist(self, name, path=None):
def _getconftest_pathlist(self, name, path):
try:
mod, relroots = self._conftest.rget_with_confmod(name, path)
except KeyError:
@@ -777,47 +812,36 @@ class Config(object):
l.append(relroot)
return l
def _getconftest(self, name, path=None, check=False):
if check:
self._checkconftest(name)
return self._conftest.rget(name, path)
def getoption(self, name):
def getoption(self, name, default=notset, skip=False):
""" return command line option value.
:arg name: name of the option. You may also specify
the literal ``--OPT`` option instead of the "dest" option name.
:arg default: default value if no option of that name exists.
:arg skip: if True raise pytest.skip if option does not exists
or has a None value.
"""
name = self._opt2dest.get(name, name)
try:
return getattr(self.option, name)
val = getattr(self.option, name)
if val is None and skip:
raise AttributeError(name)
return val
except AttributeError:
if default is not notset:
return default
if skip:
import pytest
pytest.skip("no %r option found" %(name,))
raise ValueError("no option named %r" % (name,))
def getvalue(self, name, path=None):
""" return command line option value.
:arg name: name of the command line option
(deprecated) if we can't find the option also lookup
the name in a matching conftest file.
"""
try:
return getattr(self.option, name)
except AttributeError:
return self._getconftest(name, path, check=False)
""" (deprecated, use getoption()) """
return self.getoption(name)
def getvalueorskip(self, name, path=None):
""" (deprecated) return getvalue(name) or call
py.test.skip if no value exists. """
__tracebackhide__ = True
try:
val = self.getvalue(name, path)
if val is None:
raise KeyError(name)
return val
except KeyError:
py.test.skip("no %r value found" %(name,))
""" (deprecated, use getoption(skip=True)) """
return self.getoption(name, skip=True)
def exists(path, ignore=EnvironmentError):
try:
@@ -848,7 +872,7 @@ def setns(obj, dic):
mod = getattr(obj, name, None)
if mod is None:
modname = "pytest.%s" % name
mod = py.std.types.ModuleType(modname)
mod = types.ModuleType(modname)
sys.modules[modname] = mod
mod.__all__ = []
setattr(obj, name, mod)

View File

@@ -1,9 +1,11 @@
"""
pytest PluginManager, basic initialization and tracing.
"""
import sys, os
import os
import sys
import inspect
import py
# don't import pytest to avoid circular imports
assert py.__version__.split(".")[:2] >= ['1', '4'], ("installation problem: "
"%s is too old, remove or upgrade 'py'" % (py.__version__))
@@ -70,12 +72,16 @@ class PluginManager(object):
self._name2plugin = {}
self._listattrcache = {}
self._plugins = []
self._hints = []
self._warnings = []
self.trace = TagTracer().get("pluginmanage")
self._plugin_distinfo = []
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
@@ -132,7 +138,8 @@ class PluginManager(object):
def skipifmissing(self, name):
if not self.hasplugin(name):
py.test.skip("plugin %r is missing" % name)
import pytest
pytest.skip("plugin %r is missing" % name)
def hasplugin(self, name):
return bool(self.getplugin(name))
@@ -148,7 +155,7 @@ class PluginManager(object):
# API for bootstrapping
#
def _envlist(self, varname):
val = py.std.os.environ.get(varname, None)
val = os.environ.get(varname, None)
if val is not None:
return val.split(',')
return ()
@@ -215,12 +222,11 @@ class PluginManager(object):
return self.import_plugin(modname[7:])
raise
except:
e = py.std.sys.exc_info()[1]
if not hasattr(py.test, 'skip'):
e = sys.exc_info()[1]
import pytest
if not hasattr(pytest, 'skip') or not isinstance(e, pytest.skip.Exception):
raise
elif not isinstance(e, py.test.skip.Exception):
raise
self._hints.append("skipped plugin %r: %s" %((modname, e.msg)))
self._warnings.append("skipped plugin %r: %s" %((modname, e.msg)))
else:
self.register(mod, modname)
self.consider_module(mod)
@@ -235,18 +241,22 @@ class PluginManager(object):
pass
l = []
last = []
wrappers = []
for plugin in plugins:
try:
meth = getattr(plugin, attrname)
if hasattr(meth, 'tryfirst'):
last.append(meth)
elif hasattr(meth, 'trylast'):
l.insert(0, meth)
else:
l.append(meth)
except AttributeError:
continue
if hasattr(meth, 'hookwrapper'):
wrappers.append(meth)
elif hasattr(meth, 'tryfirst'):
last.append(meth)
elif hasattr(meth, 'trylast'):
l.insert(0, meth)
else:
l.append(meth)
l.extend(last)
l.extend(wrappers)
self._listattrcache[key] = list(l)
return l
@@ -259,23 +269,22 @@ def importplugin(importspec):
name = importspec
try:
mod = "_pytest." + name
#print >>sys.stderr, "tryimport", mod
__import__(mod)
return sys.modules[mod]
except ImportError:
#e = py.std.sys.exc_info()[1]
#if str(e).find(name) == -1:
# raise
pass #
try:
#print >>sys.stderr, "tryimport", importspec
__import__(importspec)
except ImportError:
raise ImportError(importspec)
return sys.modules[importspec]
return sys.modules[importspec]
class MultiCall:
""" execute a call into multiple python functions/methods. """
class WrongHookWrapper(Exception):
""" a hook wrapper does not behave correctly. """
def __init__(self, func, message):
Exception.__init__(self, func, message)
self.func = func
self.message = message
def __init__(self, methods, kwargs, firstresult=False):
self.methods = list(methods)
self.kwargs = kwargs
@@ -287,16 +296,39 @@ class MultiCall:
return "<MultiCall %s, kwargs=%r>" %(status, self.kwargs)
def execute(self):
while self.methods:
method = self.methods.pop()
kwargs = self.getkwargs(method)
res = method(**kwargs)
if res is not None:
self.results.append(res)
if self.firstresult:
return res
if not self.firstresult:
return self.results
next_finalizers = []
try:
while self.methods:
method = self.methods.pop()
kwargs = self.getkwargs(method)
if hasattr(method, "hookwrapper"):
it = method(**kwargs)
next = getattr(it, "next", None)
if next is None:
next = getattr(it, "__next__", None)
if next is None:
raise self.WrongHookWrapper(method,
"wrapper does not contain a yield")
res = next()
next_finalizers.append((method, next))
else:
res = method(**kwargs)
if res is not None:
self.results.append(res)
if self.firstresult:
return res
if not self.firstresult:
return self.results
finally:
for method, fin in reversed(next_finalizers):
try:
fin()
except StopIteration:
pass
else:
raise self.WrongHookWrapper(method,
"wrapper contain more than one yield")
def getkwargs(self, method):
kwargs = {}
@@ -309,19 +341,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:

View File

@@ -1,5 +1,6 @@
""" discover and run doctests in modules and test files."""
from __future__ import absolute_import
import traceback
import pytest, py
from _pytest.python import FixtureRequest, FuncFixtureInfo
from py._code.code import TerminalRepr, ReprFileLocation
@@ -43,7 +44,7 @@ class DoctestItem(pytest.Item):
self.runner.run(self.dtest)
def repr_failure(self, excinfo):
doctest = py.std.doctest
import doctest
if excinfo.errisinstance((doctest.DocTestFailure,
doctest.UnexpectedException)):
doctestfailure = excinfo.value
@@ -56,8 +57,8 @@ class DoctestItem(pytest.Item):
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
checker = doctest.OutputChecker()
REPORT_UDIFF = doctest.REPORT_UDIFF
filelines = py.path.local(filename).readlines(cr=0)
lines = []
if lineno is not None:
@@ -78,7 +79,7 @@ class DoctestItem(pytest.Item):
inner_excinfo = py.code.ExceptionInfo(excinfo.value.exc_info)
lines += ["UNEXPECTED EXCEPTION: %s" %
repr(inner_excinfo.value)]
lines += py.std.traceback.format_exception(*excinfo.value.exc_info)
lines += traceback.format_exception(*excinfo.value.exc_info)
return ReprFailDoctest(reprlocation, lines)
else:
return super(DoctestItem, self).repr_failure(excinfo)
@@ -88,11 +89,16 @@ class DoctestItem(pytest.Item):
class DoctestTextfile(DoctestItem, pytest.File):
def runtest(self):
doctest = py.std.doctest
import doctest
# satisfy `FixtureRequest` constructor...
self.funcargs = {}
self._fixtureinfo = FuncFixtureInfo((), [], {})
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,
@@ -101,7 +107,7 @@ class DoctestTextfile(DoctestItem, pytest.File):
class DoctestModule(pytest.File):
def collect(self):
doctest = py.std.doctest
import doctest
if self.fspath.basename == "conftest.py":
module = self.config._conftest.importconftest(self.fspath)
else:

View File

@@ -1,9 +1,16 @@
""" generate a single-file self-contained version of py.test """
import py
""" generate a single-file self-contained version of pytest """
import os
import sys
import pkgutil
import py
import _pytest
def find_toplevel(name):
for syspath in py.std.sys.path:
for syspath in sys.path:
base = py.path.local(syspath)
lib = base/name
if lib.check(dir=1):
@@ -29,9 +36,10 @@ def pkg_to_mapping(name):
return name2src
def compress_mapping(mapping):
data = py.std.pickle.dumps(mapping, 2)
data = py.std.zlib.compress(data, 9)
data = py.std.base64.encodestring(data)
import base64, pickle, zlib
data = pickle.dumps(mapping, 2)
data = zlib.compress(data, 9)
data = base64.encodestring(data)
data = data.decode('ascii')
return data
@@ -55,7 +63,7 @@ def pytest_addoption(parser):
group = parser.getgroup("debugconfig")
group.addoption("--genscript", action="store", default=None,
dest="genscript", metavar="path",
help="create standalone py.test script at given target path.")
help="create standalone pytest script at given target path.")
def pytest_cmdline_main(config):
genscript = config.getvalue("genscript")
@@ -64,13 +72,13 @@ def pytest_cmdline_main(config):
deps = ['py', '_pytest', 'pytest']
if sys.version_info < (2,7):
deps.append("argparse")
tw.line("generated script will run on python2.5-python3.3++")
tw.line("generated script will run on python2.6-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)
"due to 'argparse' dependency. Use python2.6 "
"to generate a python2.6 compatible script", red=True)
script = generate_script(
'import py; raise SystemExit(py.test.cmdline.main())',
'import pytest; raise SystemExit(pytest.cmdline.main())',
deps,
)
genscript = py.path.local(genscript)
@@ -78,3 +86,42 @@ def pytest_cmdline_main(config):
tw.line("generated pytest standalone script: %s" % genscript,
bold=True)
return 0
def pytest_namespace():
return {'freeze_includes': freeze_includes}
def freeze_includes():
"""
Returns a list of module names used by py.test that should be
included by cx_freeze.
"""
result = list(_iter_all_modules(py))
result += list(_iter_all_modules(_pytest))
return result
def _iter_all_modules(package, prefix=''):
"""
Iterates over the names of all modules that can be found in the given
package, recursively.
Example:
_iter_all_modules(_pytest) ->
['_pytest.assertion.newinterpret',
'_pytest.capture',
'_pytest.core',
...
]
"""
if type(package) is not str:
path, prefix = package.__path__[0], package.__name__ + '.'
else:
path = package
for _, name, is_package in pkgutil.iter_modules([path]):
if is_package:
for m in _iter_all_modules(os.path.join(path, name), prefix=name + '.'):
yield prefix + m
else:
yield prefix + name

View File

@@ -12,7 +12,9 @@ def pytest_addoption(parser):
help="show help message and configuration info")
group._addoption('-p', action="append", dest="plugins", default = [],
metavar="name",
help="early-load given plugin (multi-allowed).")
help="early-load given plugin (multi-allowed). "
"To avoid loading of plugins, use the `no:` prefix, e.g. "
"`no:doctest`.")
group.addoption('--traceconfig', '--trace-config',
action="store_true", default=False,
help="trace considerations of conftest.py files."),
@@ -46,7 +48,7 @@ def pytest_unconfigure(config):
def pytest_cmdline_main(config):
if config.option.version:
p = py.path.local(pytest.__file__)
sys.stderr.write("This is py.test version %s, imported from %s\n" %
sys.stderr.write("This is pytest version %s, imported from %s\n" %
(pytest.__version__, p))
plugininfo = getpluginversioninfo(config)
if plugininfo:
@@ -62,7 +64,6 @@ def pytest_cmdline_main(config):
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")
@@ -82,16 +83,12 @@ def showhelp(config):
#tw.sep("=")
tw.line("to see available markers type: py.test --markers")
tw.line("to see available fixtures type: py.test --fixtures")
tw.line("(shown according to specified file_or_dir or current dir "
"if not specified)")
for warning in config.pluginmanager._warnings:
tw.line("warning: %s" % (warning,), red=True)
return
tw.line("conftest.py options:")
tw.line()
conftestitems = sorted(config._parser._conftestdict.items())
for name, help in conftest_options + conftestitems:
line = " %-15s %s" %(name, help)
tw.line(line[:tw.fullwidth])
tw.line()
#tw.sep( "=")
conftest_options = [
('pytest_plugins', 'list of plugin names to load'),
@@ -120,7 +117,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

@@ -11,8 +11,8 @@ def pytest_addhooks(pluginmanager):
def pytest_namespace():
"""return dict of name->object to be made globally available in
the py.test/pytest namespace. This hook is called before command
line options are parsed.
the pytest namespace. This hook is called before command line options
are parsed.
"""
def pytest_cmdline_parse(pluginmanager, args):
@@ -53,8 +53,8 @@ def pytest_cmdline_main(config):
pytest_cmdline_main.firstresult = True
def pytest_load_initial_conftests(args, early_config, parser):
""" implements loading initial conftests.
"""
""" implements the loading of initial conftest files ahead
of command line option parsing. """
def pytest_configure(config):
""" called after command line options have been parsed
@@ -152,9 +152,9 @@ def pytest_runtest_protocol(item, nextitem):
:arg item: test item for which the runtest protocol is performed.
:arg nexitem: the scheduled-to-be-next test item (or None if this
is the end my friend). This argument is passed on to
:py:func:`pytest_runtest_teardown`.
:arg nextitem: the scheduled-to-be-next test item (or None if this
is the end my friend). This argument is passed on to
:py:func:`pytest_runtest_teardown`.
:return boolean: True if no further hook implementations should be invoked.
"""
@@ -172,10 +172,10 @@ def pytest_runtest_call(item):
def pytest_runtest_teardown(item, nextitem):
""" called after ``pytest_runtest_call``.
:arg nexitem: the scheduled-to-be-next test item (None if no further
test item is scheduled). This argument can be used to
perform exact teardowns, i.e. calling just enough finalizers
so that nextitem only needs to call setup-functions.
:arg nextitem: the scheduled-to-be-next test item (None if no further
test item is scheduled). This argument can be used to
perform exact teardowns, i.e. calling just enough finalizers
so that nextitem only needs to call setup-functions.
"""
def pytest_runtest_makereport(item, call):
@@ -227,6 +227,11 @@ pytest_report_teststatus.firstresult = True
def pytest_terminal_summary(terminalreporter):
""" add additional section in terminal summary reporting. """
def pytest_logwarning(message, code, nodeid, fslocation):
""" process a warning specified by a message, a code string,
a nodeid and fslocation (both of which may be None
if the warning is not tied to a partilar node/location)."""
# -------------------------------------------------------------------------
# doctest hooks
# -------------------------------------------------------------------------

View File

@@ -2,29 +2,20 @@
Based on initial code from Ross Lawley.
"""
import py
import os
import re
import sys
import time
# Python 2.X and 3.X compatibility
try:
unichr(65)
except NameError:
if sys.version_info[0] < 3:
from codecs import open
else:
unichr = chr
try:
unicode('A')
except NameError:
unicode = str
try:
long(1)
except NameError:
long = int
class Junit(py.xml.Namespace):
pass
@@ -109,12 +100,14 @@ class LogXML(object):
))
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)))
for capname in ('out', 'err'):
allcontent = ""
for name, content in report.get_sections("Captured std%s" %
capname):
allcontent += content
if allcontent:
tag = getattr(Junit, 'system-'+capname)
self.append(tag(bin_xml_escape(allcontent)))
def append(self, obj):
self.tests[-1].append(obj)
@@ -131,38 +124,38 @@ class LogXML(object):
self.skipped += 1
else:
fail = Junit.failure(message="test failure")
fail.append(str(report.longrepr))
fail.append(bin_xml_escape(report.longrepr))
self.append(fail)
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,
Junit.skipped("%s:%s: %s" % (filename, lineno, skipreason),
type="pytest.skip",
message=skipreason
))
@@ -194,22 +187,18 @@ 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__):
if py.std.sys.version_info[0] < 3:
logfile = py.std.codecs.open(self.logfile, 'w', encoding='utf-8')
else:
logfile = open(self.logfile, 'w', encoding='utf-8')
def pytest_sessionfinish(self):
logfile = open(self.logfile, 'w', encoding='utf-8')
suite_stop_time = time.time()
suite_time_delta = suite_stop_time - self.suite_start_time
numtests = self.passed + self.failed

View File

@@ -1,16 +1,15 @@
""" core implementation of testing process: init, session, runtest loop. """
import re
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
from _pytest.runner import collect_one_node
tracebackcutdir = py.path.local(_pytest.__file__).dirpath()
@@ -21,11 +20,11 @@ EXIT_INTERRUPTED = 2
EXIT_INTERNALERROR = 3
EXIT_USAGEERROR = 4
name_re = py.std.re.compile("^[a-zA-Z_]\w*$")
name_re = re.compile("^[a-zA-Z_]\w*$")
def pytest_addoption(parser):
parser.addini("norecursedirs", "directory patterns to avoid for recursion",
type="args", default=('.*', 'CVS', '_darcs', '{arch}'))
type="args", default=('.*', 'CVS', '_darcs', '{arch}', '*.egg'))
#parser.addini("dirpatterns",
# "patterns specifying possible locations of test files",
# type="linelist", default=["**/test_*.txt",
@@ -40,6 +39,8 @@ def pytest_addoption(parser):
help="exit after first num failures or errors.")
group._addoption('--strict', action="store_true",
help="run pytest in strict mode, warnings become errors.")
group._addoption("-c", metavar="file", type=str, dest="inifilename",
help="load configuration from `file` instead of trying to locate one of the implicit configuration files.")
group = parser.getgroup("collect", "collection")
group.addoption('--collectonly', '--collect-only', action="store_true",
@@ -65,7 +66,7 @@ def pytest_namespace():
return dict(collect=collect)
def pytest_configure(config):
py.test.config = config # compatibiltiy
pytest.config = config # compatibiltiy
if config.option.exitfirst:
config.option.maxfail = 1
@@ -82,8 +83,9 @@ def wrap_session(config, doit):
initstate = 2
doit(config, session)
except pytest.UsageError:
msg = sys.exc_info()[1].args[0]
sys.stderr.write("ERROR: %s\n" %(msg,))
args = sys.exc_info()[1].args
for msg in args:
sys.stderr.write("ERROR: %s\n" %(msg,))
session.exitstatus = EXIT_USAGEERROR
except KeyboardInterrupt:
excinfo = py.code.ExceptionInfo()
@@ -99,6 +101,7 @@ def wrap_session(config, doit):
if session._testsfailed:
session.exitstatus = EXIT_TESTSFAILED
finally:
excinfo = None # Explicitly break reference cycle.
session.startdir.chdir()
if initstate >= 2:
config.hook.pytest_sessionfinish(
@@ -145,7 +148,7 @@ def pytest_ignore_collect(path, config):
p = path.dirpath()
ignore_paths = config._getconftest_pathlist("collect_ignore", path=p)
ignore_paths = ignore_paths or []
excludeopt = config.getvalue("ignore")
excludeopt = config.getoption("ignore")
if excludeopt:
ignore_paths.extend([py.path.local(x) for x in excludeopt])
return path in ignore_paths
@@ -172,30 +175,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.
@@ -223,6 +235,9 @@ class Node(object):
#: 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
@@ -253,6 +268,20 @@ class Node(object):
return "<%s %r>" %(self.__class__.__name__,
getattr(self, 'name', None))
def warn(self, code, message):
""" generate a warning with the given code and message for this
item. """
assert isinstance(code, str)
fslocation = getattr(self, "location", None)
if fslocation is None:
fslocation = getattr(self, "fspath", None)
else:
fslocation = "%s:%s" % fslocation[:2]
self.ihook.pytest_logwarning(code=code, message=message,
nodeid=self.nodeid,
fslocation=fslocation)
# methods for ordering nodes
@property
def nodeid(self):
@@ -263,21 +292,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
@@ -297,7 +316,7 @@ class Node(object):
except py.builtin._sysex:
raise
except:
failure = py.std.sys.exc_info()
failure = sys.exc_info()
setattr(self, exattrname, failure)
raise
setattr(self, attrname, res)
@@ -314,6 +333,27 @@ 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()
@@ -337,6 +377,8 @@ class Node(object):
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
@@ -349,20 +391,24 @@ class Node(object):
fm = self.session._fixturemanager
if excinfo.errisinstance(fm.FixtureLookupError):
return excinfo.value.formatrepr()
tbfilter = True
if self.config.option.fulltrace:
style="long"
else:
self._prunetraceback(excinfo)
# XXX should excinfo.getrepr record all data and toterminal()
# process it?
tbfilter = False # prunetraceback already does it
if style == "auto":
style = "long"
# XXX should excinfo.getrepr record all data and toterminal() process it?
if style is None:
if self.config.option.tbstyle == "short":
style = "short"
else:
style = "long"
return excinfo.getrepr(funcargs=True,
showlocals=self.config.option.showlocals,
style=style)
style=style, tbfilter=tbfilter)
repr_failure = _repr_failure_py
@@ -371,10 +417,6 @@ class Collector(Node):
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. """
@@ -397,7 +439,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:
@@ -433,6 +474,14 @@ class Item(Node):
"""
nextitem = None
def __init__(self, name, parent=None, config=None, session=None):
super(Item, self).__init__(name, parent, config, session)
self._report_sections = []
def add_report_section(self, when, key, content):
if content:
self._report_sections.append((when, key, content))
def reportinfo(self):
return self.fspath, None, ""
@@ -517,9 +566,12 @@ class Session(FSCollector):
self.ihook.pytest_collectreport(report=rep)
self.trace.root.indent -= 1
if self._notfound:
errors = []
for arg, exc in self._notfound:
line = "(no name %r in any of %r)" % (arg, exc.args[0])
raise pytest.UsageError("not found: %s\n%s" %(arg, line))
errors.append("not found: %s\n%s" % (arg, line))
#XXX: test this
raise pytest.UsageError(*errors)
if not genitems:
return rep.result
else:
@@ -540,8 +592,7 @@ class Session(FSCollector):
# we are inside a make_report hook so
# we cannot directly pass through the exception
self._notfound.append((arg, sys.exc_info()[1]))
self.trace.root.indent -= 1
break
self.trace.root.indent -= 1
def _collect(self, arg):
@@ -670,11 +721,4 @@ class Session(FSCollector):
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

@@ -81,11 +81,8 @@ def pytest_collection_modifyitems(items, config):
class MarkMapping:
"""Provides a local mapping for markers.
Only the marker names from the given :class:`NodeKeywords` will be mapped,
so the names are taken only from :class:`MarkInfo` or
:class:`MarkDecorator` items.
"""
"""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():
@@ -93,8 +90,8 @@ class MarkMapping:
mymarks.add(key)
self._mymarks = mymarks
def __getitem__(self, markname):
return markname in self._mymarks
def __getitem__(self, name):
return name in self._mymarks
class KeywordMapping:
@@ -139,10 +136,17 @@ def matchkeyword(colitem, keywordexpr):
mapped_names.add(name)
# Add the names attached to the current function through direct assignment
for name in colitem.function.__dict__:
mapped_names.add(name)
if hasattr(colitem, 'function'):
for name in colitem.function.__dict__:
mapped_names.add(name)
return eval(keywordexpr, {}, KeywordMapping(mapped_names))
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):
@@ -153,10 +157,10 @@ def pytest_configure(config):
class MarkGenerator:
""" Factory for :class:`MarkDecorator` objects - exposed as
a ``py.test.mark`` singleton instance. Example::
a ``pytest.mark`` singleton instance. Example::
import py
@py.test.mark.slowtest
@pytest.mark.slowtest
def test_function():
pass
@@ -165,7 +169,7 @@ class MarkGenerator:
def __getattr__(self, name):
if name[0] == "_":
raise AttributeError(name)
raise AttributeError("Marker name must NOT start with underscore")
if hasattr(self, '_config'):
self._check(name)
return MarkDecorator(name)
@@ -184,6 +188,9 @@ 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
@@ -191,32 +198,54 @@ class MarkDecorator:
:ref:`retrieved by hooks as item keywords <excontrolskip>`.
MarkDecorator instances are often created like this::
mark1 = py.test.mark.NAME # simple MarkDecorator
mark2 = py.test.mark.NAME(name1=value) # parametrized MarkDecorator
mark1 = pytest.mark.NAME # simple MarkDecorator
mark2 = pytest.mark.NAME(name1=value) # parametrized MarkDecorator
and can then be applied as decorators to test functions::
@mark2
def test_function():
pass
When a MarkDecorator instance is called it does the following:
1. If called with a single class as its only positional argument and no
additional keyword arguments, it attaches itself to the class so it
gets applied automatically to all test cases found in that class.
2. If called with a single function as its only positional argument and
no additional keyword arguments, it attaches a MarkInfo object to the
function, containing all the arguments already stored internally in
the MarkDecorator.
3. When called in any other case, it performs a 'fake construction' call,
i.e. it returns a new MarkDecorator instance with the original
MarkDecorator's content updated with the arguments passed to this
call.
Note: The rules above prevent MarkDecorator objects from storing only a
single function or class reference as their positional argument with no
additional keyword or positional arguments.
"""
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')
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:
if args and not kwargs:
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
@@ -227,19 +256,19 @@ class MarkDecorator:
else:
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
self.name, self.args, self.kwargs
)
setattr(func, self.markname, holder)
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:

View File

@@ -1,7 +1,7 @@
""" monkeypatching and mocking functionality. """
import os, sys, inspect
import pytest
import os, sys
from py.builtin import _basestring
def pytest_funcarg__monkeypatch(request):
"""The returned ``monkeypatch`` funcarg provides these
@@ -28,7 +28,8 @@ def pytest_funcarg__monkeypatch(request):
def derive_importpath(import_path):
if not isinstance(import_path, str) or "." not in 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 = []
@@ -58,7 +59,11 @@ def derive_importpath(import_path):
notset = object()
class Notset:
def __repr__(self):
return "<notset>"
notset = Notset()
class monkeypatch:
""" object keeping a record of setattr/item/env/syspath changes. """
@@ -82,9 +87,10 @@ class monkeypatch:
which means it will raise).
"""
__tracebackhide__ = True
import inspect
if value is notset:
if not isinstance(target, str):
if not isinstance(target, _basestring):
raise TypeError("use setattr(target, name, value) or "
"setattr(target, value) with target being a dotted "
"import string")
@@ -114,7 +120,7 @@ class monkeypatch:
"""
__tracebackhide__ = True
if name is notset:
if not isinstance(target, str):
if not isinstance(target, _basestring):
raise TypeError("use delattr(target, name) or "
"delattr(target) with target being a dotted "
"import string")

View File

@@ -1,18 +1,27 @@
""" run test suites written for nose. """
import pytest, py
import inspect
import sys
import py
import pytest
from _pytest import unittest
def get_skip_exceptions():
skip_classes = set()
for module_name in ('unittest', 'unittest2', 'nose'):
mod = sys.modules.get(module_name)
if hasattr(mod, 'SkipTest'):
skip_classes.add(mod.SkipTest)
return tuple(skip_classes)
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:
pytest.skip(str(call.excinfo.value)), call.when)
call.excinfo = call2.excinfo
if call.excinfo and call.excinfo.errisinstance(get_skip_exceptions()):
# let's substitute the excinfo with a pytest.skip one
call2 = call.__class__(lambda:
pytest.skip(str(call.excinfo.value)), call.when)
call.excinfo = call2.excinfo
@pytest.mark.trylast
@@ -39,13 +48,8 @@ def teardown_nose(item):
# #call_optional(item._nosegensetup, 'teardown')
# 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')

View File

@@ -1,8 +1,12 @@
""" interactive debugging with PDB, the Python Debugger. """
import pytest, py
from __future__ import absolute_import
import pdb
import sys
import pytest
import py
def pytest_addoption(parser):
group = parser.getgroup("general")
group._addoption('--pdb',
@@ -16,50 +20,37 @@ def pytest_configure(config):
if config.getvalue("usepdb"):
config.pluginmanager.register(PdbInvoke(), 'pdbinvoke')
old_trace = py.std.pdb.set_trace
old = (pdb.set_trace, pytestPDB._pluginmanager)
def fin():
py.std.pdb.set_trace = old_trace
py.std.pdb.set_trace = pytest.set_trace
pdb.set_trace, pytestPDB._pluginmanager = old
pdb.set_trace = pytest.set_trace
pytestPDB._pluginmanager = config.pluginmanager
config._cleanup.append(fin)
class pytestPDB:
""" Pseudo PDB that defers to the real pdb. """
item = None
collector = None
_pluginmanager = None
def set_trace(self):
""" invoke PDB set_trace debugging, dropping any IO capturing. """
frame = sys._getframe().f_back
item = self.item or self.collector
if item is not None:
capman = item.config.pluginmanager.getplugin("capturemanager")
out, err = capman.suspendcapture()
if hasattr(item, 'outerr'):
item.outerr = (item.outerr[0] + out, item.outerr[1] + err)
capman = None
if self._pluginmanager is not None:
capman = self._pluginmanager.getplugin("capturemanager")
if capman:
capman.suspendcapture(in_=True)
tw = py.io.TerminalWriter()
tw.line()
tw.sep(">", "PDB set_trace (IO-capturing turned off)")
py.std.pdb.Pdb().set_trace(frame)
pdb.Pdb().set_trace(frame)
def pdbitem(item):
pytestPDB.item = item
pytest_runtest_setup = pytest_runtest_call = pytest_runtest_teardown = pdbitem
@pytest.mark.tryfirst
def pytest_make_collect_report(__multicall__, collector):
try:
pytestPDB.collector = collector
return __multicall__.execute()
finally:
pytestPDB.collector = None
def pytest_runtest_makereport():
pytestPDB.item = None
class PdbInvoke:
def pytest_exception_interact(self, node, call, report):
return _enter_pdb(node, call.excinfo, report)
capman = node.config.pluginmanager.getplugin("capturemanager")
if capman:
capman.suspendcapture(in_=True)
_enter_pdb(node, call.excinfo, report)
def pytest_internalerror(self, excrepr, excinfo):
for line in str(excrepr).split("\n"):
@@ -87,7 +78,8 @@ def _enter_pdb(node, excinfo, 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):
from doctest import UnexpectedException
if isinstance(excinfo.value, UnexpectedException):
return excinfo.value.exc_info[2]
else:
return excinfo._excinfo[2]
@@ -101,7 +93,6 @@ def _find_last_non_hidden_frame(stack):
def post_mortem(t):
pdb = py.std.pdb
class Pdb(pdb.Pdb):
def get_stack(self, f, t):
stack, i = pdb.Pdb.get_stack(self, f, t)

View File

@@ -1,16 +1,21 @@
""" (disabled by default) support for testing py.test and py.test plugins. """
import py, pytest
import sys, os
""" (disabled by default) support for testing pytest and pytest plugins. """
import inspect
import sys
import os
import codecs
import re
import inspect
import time
import platform
from fnmatch import fnmatch
from _pytest.main import Session, EXIT_OK
import subprocess
import py
import pytest
from py.builtin import print_
from _pytest.core import HookRelay
from _pytest.main import Session, EXIT_OK
def get_public_names(l):
"""Only return names from iterator l without a leading underscore."""
@@ -27,7 +32,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:
@@ -89,10 +93,10 @@ class HookRecorder:
def _makecallparser(self, method):
name = method.__name__
args, varargs, varkw, default = py.std.inspect.getargspec(method)
args, varargs, varkw, default = inspect.getargspec(method)
if not args or args[0] != "self":
args.insert(0, 'self')
fspec = py.std.inspect.formatargspec(args, varargs, varkw, default)
fspec = inspect.formatargspec(args, varargs, varkw, default)
# we use exec because we want to have early type
# errors on wrong input arguments, using
# *args/**kwargs delays this and gives errors
@@ -122,10 +126,9 @@ 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
backlocals = sys._getframe(1).f_locals
while entries:
name, check = entries.pop(0)
for ind, call in enumerate(self.calls[i:]):
@@ -140,7 +143,7 @@ class HookRecorder:
break
print_("NONAMEMATCH", name, "with", call)
else:
py.test.fail("could not find %r check %r" % (name, check))
pytest.fail("could not find %r check %r" % (name, check))
def popcall(self, name):
__tracebackhide__ = True
@@ -150,7 +153,7 @@ class HookRecorder:
return call
lines = ["could not find call %r, in:" % (name,)]
lines.extend([" %s" % str(x) for x in self.calls])
py.test.fail("\n".join(lines))
pytest.fail("\n".join(lines))
def getcall(self, name):
l = self.getcalls(name)
@@ -213,7 +216,7 @@ class TmpTestdir:
def finalize(self):
for p in self._syspathremove:
py.std.sys.path.remove(p)
sys.path.remove(p)
if hasattr(self, '_olddir'):
self._olddir.chdir()
# delete modules that have been loaded from tmpdir
@@ -249,8 +252,14 @@ class TmpTestdir:
ret = None
for name, value in items:
p = self.tmpdir.join(name).new(ext=ext)
source = py.builtin._totext(py.code.Source(value)).strip()
content = source.encode("utf-8") # + "\n"
source = py.code.Source(value)
def my_totext(s, encoding="utf-8"):
if py.builtin._isbytes(s):
s = py.builtin._totext(s, encoding=encoding)
return s
source_unicode = "\n".join([my_totext(line) for line in source.lines])
source = py.builtin._totext(source_unicode)
content = source.strip().encode("utf-8") # + "\n"
#content = content.rstrip() + "\n"
p.write(content, "wb")
if ret is None:
@@ -261,9 +270,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)
@@ -283,7 +289,7 @@ class TmpTestdir:
def syspathinsert(self, path=None):
if path is None:
path = self.tmpdir
py.std.sys.path.insert(0, str(path))
sys.path.insert(0, str(path))
self._syspathremove.append(str(path))
def mkdir(self, name):
@@ -426,9 +432,8 @@ class TmpTestdir:
env['PYTHONPATH'] = os.pathsep.join(filter(None, [
str(os.getcwd()), env.get('PYTHONPATH', '')]))
kw['env'] = env
#print "env", env
return py.std.subprocess.Popen(cmdargs,
stdout=stdout, stderr=stderr, **kw)
return subprocess.Popen(cmdargs,
stdout=stdout, stderr=stderr, **kw)
def run(self, *cmdargs):
return self._run(*cmdargs)
@@ -473,12 +478,12 @@ class TmpTestdir:
def _getpybinargs(self, scriptname):
if not self.request.config.getvalue("notoolsonpath"):
# 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,)
# XXX we rely on script referring to the correct environment
# we cannot use "(sys.executable,script)"
# because on windows the script is e.g. a py.test.exe
return (sys.executable, _pytest_fullpath,) # noqa
else:
py.test.skip("cannot run %r with --no-tools-on-path" % scriptname)
pytest.skip("cannot run %r with --no-tools-on-path" % scriptname)
def runpython(self, script, prepend=True):
if prepend:
@@ -496,7 +501,7 @@ class TmpTestdir:
def runpython_c(self, command):
command = self._getsysprepend() + command
return self.run(py.std.sys.executable, "-c", command)
return self.run(sys.executable, "-c", command)
def runpytest(self, *args):
p = py.path.local.make_numbered_dir(prefix="runpytest-",
@@ -515,22 +520,23 @@ class TmpTestdir:
def spawn_pytest(self, string, expect_timeout=10.0):
if self.request.config.getvalue("notoolsonpath"):
py.test.skip("--no-tools-on-path prevents running pexpect-spawn tests")
pytest.skip("--no-tools-on-path prevents running pexpect-spawn tests")
basetemp = self.tmpdir.mkdir("pexpect")
invoke = " ".join(map(str, self._getpybinargs("py.test")))
cmd = "%s --basetemp=%s %s" % (invoke, basetemp, string)
return self.spawn(cmd, expect_timeout=expect_timeout)
def spawn(self, cmd, expect_timeout=10.0):
pexpect = py.test.importorskip("pexpect", "2.4")
if hasattr(sys, 'pypy_version_info') and '64' in py.std.platform.machine():
pexpect = pytest.importorskip("pexpect", "3.0")
if hasattr(sys, 'pypy_version_info') and '64' in 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
@@ -661,9 +667,15 @@ 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)
py.builtin.print_(arg1, arg2, file=sys.stderr)
lines2 = self._getlines(lines2)
lines1 = self.lines[:]
nextline = None
@@ -687,4 +699,4 @@ class LineMatcher:
show(" and:", repr(nextline))
extralines.append(nextline)
else:
py.test.fail("remains unmatched: %r, see stderr" % (line,))
pytest.fail("remains unmatched: %r, see stderr" % (line,))

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +1,19 @@
""" recording warnings during test function execution. """
import py
import sys, os
import sys
import warnings
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.
"""
if sys.version_info >= (2,7):
import warnings
oldfilters = warnings.filters[:]
warnings.simplefilter('default')
def reset_filters():
@@ -30,26 +30,24 @@ def deprecated_call(func, *args, **kwargs):
""" assert that calling ``func(*args, **kwargs)``
triggers a DeprecationWarning.
"""
warningmodule = py.std.warnings
l = []
oldwarn_explicit = getattr(warningmodule, 'warn_explicit')
oldwarn_explicit = getattr(warnings, 'warn_explicit')
def warn_explicit(*args, **kwargs):
l.append(args)
oldwarn_explicit(*args, **kwargs)
oldwarn = getattr(warningmodule, 'warn')
oldwarn = getattr(warnings, 'warn')
def warn(*args, **kwargs):
l.append(args)
oldwarn(*args, **kwargs)
warningmodule.warn_explicit = warn_explicit
warningmodule.warn = warn
warnings.warn_explicit = warn_explicit
warnings.warn = warn
try:
ret = func(*args, **kwargs)
finally:
warningmodule.warn_explicit = warn_explicit
warningmodule.warn = warn
warnings.warn_explicit = warn_explicit
warnings.warn = warn
if not l:
#print warningmodule
__tracebackhide__ = True
raise AssertionError("%r did not produce DeprecationWarning" %(func,))
return ret
@@ -65,7 +63,6 @@ class RecordedWarning:
class WarningsRecorder:
def __init__(self):
warningmodule = py.std.warnings
self.list = []
def showwarning(message, category, filename, lineno, line=0):
self.list.append(RecordedWarning(
@@ -76,8 +73,8 @@ class WarningsRecorder:
except TypeError:
# < python2.6
self.old_showwarning(message, category, filename, lineno)
self.old_showwarning = warningmodule.showwarning
warningmodule.showwarning = showwarning
self.old_showwarning = warnings.showwarning
warnings.showwarning = showwarning
def pop(self, cls=Warning):
""" pop the first recorded warning, raise exception if not exists."""
@@ -88,7 +85,6 @@ class WarningsRecorder:
assert 0, "%r not found in %r" %(cls, self.list)
#def resetregistry(self):
# import warnings
# warnings.onceregistry.clear()
# warnings.__warningregistry__.clear()
@@ -96,4 +92,4 @@ class WarningsRecorder:
self.list[:] = []
def finalize(self):
py.std.warnings.showwarning = self.old_showwarning
warnings.showwarning = self.old_showwarning

View File

@@ -6,7 +6,7 @@ import py
def pytest_addoption(parser):
group = parser.getgroup("terminal reporting", "resultlog plugin options")
group.addoption('--resultlog', '--result-log', action="store",
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

@@ -1,7 +1,10 @@
""" basic collect and runtest protocol implementations """
import py, sys
import bdb
import sys
from time import time
import py
import pytest
from py._code.code import TerminalRepr
def pytest_namespace():
@@ -116,7 +119,7 @@ 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))
call.excinfo.errisinstance(bdb.BdbQuit))
def call_runtest_hook(item, when, **kwds):
hookname = "pytest_runtest_" + when
@@ -133,14 +136,13 @@ class CallInfo:
self.when = when
self.start = time()
try:
try:
self.result = func()
except KeyboardInterrupt:
raise
except:
self.excinfo = py.code.ExceptionInfo()
finally:
self.result = func()
except KeyboardInterrupt:
self.stop = time()
raise
except:
self.excinfo = py.code.ExceptionInfo()
self.stop = time()
def __repr__(self):
if self.excinfo:
@@ -176,6 +178,11 @@ class BaseReport(object):
except UnicodeEncodeError:
out.line("<unprintable longrepr>")
def get_sections(self, prefix):
for name, content in self.sections:
if name.startswith(prefix):
yield prefix, content
passed = property(lambda x: x.outcome == "passed")
failed = property(lambda x: x.outcome == "failed")
skipped = property(lambda x: x.outcome == "skipped")
@@ -189,15 +196,15 @@ def pytest_runtest_makereport(item, call):
duration = call.stop-call.start
keywords = dict([(x,1) for x in item.keywords])
excinfo = call.excinfo
sections = []
if not call.excinfo:
outcome = "passed"
longrepr = None
else:
excinfo = call.excinfo
if not isinstance(excinfo, py.code.ExceptionInfo):
outcome = "failed"
longrepr = excinfo
elif excinfo.errisinstance(py.test.skip.Exception):
elif excinfo.errisinstance(pytest.skip.Exception):
outcome = "skipped"
r = excinfo._getreprcrash()
longrepr = (str(r.path), r.lineno, r.message)
@@ -208,16 +215,18 @@ def pytest_runtest_makereport(item, call):
else: # exception in setup or teardown
longrepr = item._repr_failure_py(excinfo,
style=item.config.option.tbstyle)
for rwhen, key, content in item._report_sections:
sections.append(("Captured std%s %s" %(key, rwhen), content))
return TestReport(item.nodeid, item.location,
keywords, outcome, longrepr, when,
duration=duration)
sections, duration)
class TestReport(BaseReport):
""" Basic test report object (also used for setup and teardown calls if
they fail).
"""
def __init__(self, nodeid, location,
keywords, outcome, longrepr, when, sections=(), duration=0, **extra):
def __init__(self, nodeid, location, keywords, outcome,
longrepr, when, sections=(), duration=0, **extra):
#: normalized collection node id
self.nodeid = nodeid
@@ -266,7 +275,9 @@ def pytest_make_collect_report(collector):
if not call.excinfo:
outcome = "passed"
else:
if call.excinfo.errisinstance(collector.skip_exceptions):
from _pytest import nose
skip_exceptions = (Skipped,) + nose.get_skip_exceptions()
if call.excinfo.errisinstance(skip_exceptions):
outcome = "skipped"
r = collector._repr_failure_py(call.excinfo, "line").reprcrash
longrepr = (str(r.path), r.lineno, r.message)
@@ -283,7 +294,8 @@ def pytest_make_collect_report(collector):
class CollectReport(BaseReport):
def __init__(self, nodeid, outcome, longrepr, result, sections=(), **extra):
def __init__(self, nodeid, outcome, longrepr, result,
sections=(), **extra):
self.nodeid = nodeid
self.outcome = outcome
self.longrepr = longrepr
@@ -315,11 +327,10 @@ class SetupState(object):
""" attach a finalizer to the given colitem.
if colitem is None, this will add a finalizer that
is called at the end of teardown_all().
if colitem is a tuple, it will be used as a key
and needs an explicit call to _callfinalizers(key) later on.
"""
assert hasattr(finalizer, '__call__')
#assert colitem in self.stack
assert colitem and not isinstance(colitem, tuple)
assert py.builtin.callable(finalizer)
#assert colitem in self.stack # some unit tests don't setup stack :/
self._finalizers.setdefault(colitem, []).append(finalizer)
def _pop_and_teardown(self):
@@ -328,9 +339,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)
@@ -410,7 +430,7 @@ class Skipped(OutcomeException):
__module__ = 'builtins'
class Failed(OutcomeException):
""" raised from an explicit call to py.test.fail() """
""" raised from an explicit call to pytest.fail() """
__module__ = 'builtins'
class Exit(KeyboardInterrupt):
@@ -430,7 +450,7 @@ exit.Exception = Exit
def skip(msg=""):
""" skip an executing test with the given message. Note: it's usually
better to use the py.test.mark.skipif marker to declare a test to be
better to use the pytest.mark.skipif marker to declare a test to be
skipped under certain conditions like mismatching platforms or
dependencies. See the pytest_skipping plugin for details.
"""
@@ -450,25 +470,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

@@ -1,7 +1,10 @@
""" support for skip/xfail functions and markers. """
import py, pytest
import os
import sys
import traceback
import py
import pytest
def pytest_addoption(parser):
group = parser.getgroup("general")
@@ -10,6 +13,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 "
@@ -18,18 +29,20 @@ def pytest_configure(config):
"http://pytest.org/latest/skipping.html"
)
config.addinivalue_line("markers",
"xfail(condition, reason=None, run=True): mark the the test function "
"xfail(condition, reason=None, run=True, raises=None): mark the the test function "
"as an expected failure if eval(condition) has a True value. "
"Optionally specify a reason for better reporting and run=False if "
"you don't even want to execute the test function. See "
"http://pytest.org/latest/skipping.html"
"you don't even want to execute the test function. If only specific "
"exception(s) are expected, you can list them in raises, and if the test fails "
"in other ways, it will be reported as a true failure. "
"See http://pytest.org/latest/skipping.html"
)
def pytest_namespace():
return dict(xfail=xfail)
class XFailed(pytest.fail.Exception):
""" raised from an explicit call to py.test.xfail() """
""" raised from an explicit call to pytest.xfail() """
def xfail(reason=""):
""" xfail an executing test or setup functions with the given reason."""
@@ -52,6 +65,12 @@ class MarkEvaluator:
def wasvalid(self):
return not hasattr(self, 'exc')
def invalidraise(self, exc):
raises = self.get('raises')
if not raises:
return
return not isinstance(exc, raises)
def istrue(self):
try:
return self._istrue()
@@ -63,7 +82,7 @@ class MarkEvaluator:
msg = [" " * (self.exc[1].offset + 4) + "^",]
msg.append("SyntaxError: invalid syntax")
else:
msg = py.std.traceback.format_exception_only(*self.exc[:2])
msg = traceback.format_exception_only(*self.exc[:2])
pytest.fail("Error evaluating %r expression\n"
" %s\n"
"%s"
@@ -71,7 +90,7 @@ class MarkEvaluator:
pytrace=False)
def _getglobals(self):
d = {'os': py.std.os, 'sys': py.std.sys, 'config': self.item.config}
d = {'os': os, 'sys': sys, 'config': self.item.config}
func = self.item.obj
try:
d.update(func.__globals__)
@@ -121,7 +140,7 @@ def pytest_runtest_setup(item):
return
evalskip = MarkEvaluator(item, 'skipif')
if evalskip.istrue():
py.test.skip(evalskip.getexplanation())
pytest.skip(evalskip.getexplanation())
item._evalxfail = MarkEvaluator(item, 'xfail')
check_xfail_no_run(item)
@@ -133,7 +152,7 @@ def check_xfail_no_run(item):
evalxfail = item._evalxfail
if evalxfail.istrue():
if not evalxfail.get('run', True):
py.test.xfail("[NOTRUN] " + evalxfail.getexplanation())
pytest.xfail("[NOTRUN] " + evalxfail.getexplanation())
def pytest_runtest_makereport(__multicall__, item, call):
if not isinstance(item, pytest.Function):
@@ -142,16 +161,16 @@ def pytest_runtest_makereport(__multicall__, item, call):
if hasattr(item, '_unexpectedsuccess'):
rep = __multicall__.execute()
if rep.when == "call":
# we need to translate into how py.test encodes xpass
# we need to translate into how pytest encodes xpass
rep.wasxfail = "reason: " + repr(item._unexpectedsuccess)
rep.outcome = "failed"
return rep
if not (call.excinfo and
call.excinfo.errisinstance(py.test.xfail.Exception)):
call.excinfo.errisinstance(pytest.xfail.Exception)):
evalxfail = getattr(item, '_evalxfail', None)
if not evalxfail:
return
if call.excinfo and call.excinfo.errisinstance(py.test.xfail.Exception):
if call.excinfo and call.excinfo.errisinstance(pytest.xfail.Exception):
if not item.config.getvalue("runxfail"):
rep = __multicall__.execute()
rep.wasxfail = "reason: " + call.excinfo.value.msg
@@ -163,7 +182,11 @@ def pytest_runtest_makereport(__multicall__, item, call):
if not item.config.option.runxfail:
if evalxfail.wasvalid() and evalxfail.istrue():
if call.excinfo:
rep.outcome = "skipped"
if evalxfail.invalidraise(call.excinfo.value):
rep.outcome = "failed"
return rep
else:
rep.outcome = "skipped"
elif call.when == "call":
rep.outcome = "failed"
else:
@@ -178,7 +201,7 @@ def pytest_report_teststatus(report):
if report.skipped:
return "xfailed", "x", "xfail"
elif report.failed:
return "xpassed", "X", "XPASS"
return "xpassed", "X", ("XPASS", {'yellow': True})
# called by the terminalreporter instance/plugin
def pytest_terminal_summary(terminalreporter):
@@ -209,7 +232,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,7 +6,6 @@ sources = """
import sys
import base64
import zlib
import imp
class DictImporter(object):
def __init__(self, sources):
@@ -40,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):
@@ -64,4 +63,4 @@ if __name__ == "__main__":
sys.meta_path.insert(0, importer)
entry = "@ENTRY@"
do_exec(entry, locals())
do_exec(entry, locals()) # noqa

View File

@@ -5,7 +5,8 @@ This is a good source for looking at the various reporting hooks.
import pytest
import py
import sys
import os
import time
def pytest_addoption(parser):
group = parser.getgroup("terminal reporting", "reporting", after="general")
@@ -16,7 +17,7 @@ def pytest_addoption(parser):
group._addoption('-r',
action="store", dest="reportchars", default=None, metavar="chars",
help="show extra test summary info as specified by chars (f)ailed, "
"(E)error, (s)skipped, (x)failed, (X)passed.")
"(E)error, (s)skipped, (x)failed, (X)passed (w)warnings.")
group._addoption('-l', '--showlocals',
action="store_true", dest="showlocals", default=False,
help="show locals in tracebacks (disabled by default).")
@@ -24,31 +25,20 @@ def pytest_addoption(parser):
action="store", dest="report", default=None, metavar="opts",
help="(deprecated, use -r)")
group._addoption('--tb', metavar="style",
action="store", dest="tbstyle", default='long',
choices=['long', 'short', 'no', 'line', 'native'],
action="store", dest="tbstyle", default='auto',
choices=['auto', 'long', 'short', 'no', 'line', 'native'],
help="traceback print mode (long/short/line/native/no).")
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. (unless capturing is turned off)
stdout = py.std.sys.stdout
capture = config.option.capture != "no"
if capture and 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):
@@ -61,7 +51,7 @@ def getreportopt(config):
optvalue = config.option.report
if optvalue:
py.builtin.print_("DEPRECATED: use -r instead of --report option.",
file=py.std.sys.stderr)
file=sys.stderr)
if optvalue:
for setting in optvalue.split(","):
setting = setting.strip()
@@ -87,6 +77,14 @@ def pytest_report_teststatus(report):
letter = "f"
return report.outcome, letter, report.outcome.upper()
class WarningReport:
def __init__(self, code, message, nodeid=None, fslocation=None):
self.code = code
self.message = message
self.nodeid = nodeid
self.fslocation = fslocation
class TerminalReporter:
def __init__(self, config, file=None):
self.config = config
@@ -99,8 +97,12 @@ class TerminalReporter:
self.stats = {}
self.startdir = self.curdir = py.path.local()
if file is None:
file = py.std.sys.stdout
file = sys.stdout
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
@@ -136,7 +138,8 @@ class TerminalReporter:
self._tw.write(content, **markup)
def write_line(self, line, **markup):
line = str(line)
if not py.builtin._istext(line):
line = py.builtin.text(line, errors="replace")
self.ensure_newline()
self._tw.line(line, **markup)
@@ -155,10 +158,16 @@ class TerminalReporter:
self._tw.line(msg, **kw)
def pytest_internalerror(self, excrepr):
for line in str(excrepr).split("\n"):
for line in py.builtin.text(excrepr).split("\n"):
self.write_line("INTERNALERROR> " + line)
return 1
def pytest_logwarning(self, code, fslocation, message, nodeid):
warnings = self.stats.setdefault("warnings", [])
warning = WarningReport(code=code, fslocation=fslocation,
message=message, nodeid=nodeid)
warnings.append(warning)
def pytest_plugin_registered(self, plugin):
if self.config.option.traceconfig:
msg = "PLUGIN registered: %s" % (plugin,)
@@ -258,7 +267,7 @@ class TerminalReporter:
@pytest.mark.trylast
def pytest_sessionstart(self, session):
self._sessionstarttime = py.std.time.time()
self._sessionstarttime = time.time()
if not self.showheader:
return
self.write_sep("=", "test session starts", bold=True)
@@ -267,7 +276,7 @@ class TerminalReporter:
if hasattr(sys, 'pypy_version_info'):
verinfo = ".".join(map(str, sys.pypy_version_info[:3]))
msg += "[pypy-%s-%s]" % (verinfo, sys.pypy_version_info[3])
msg += " -- pytest-%s" % (py.test.__version__)
msg += " -- py-%s -- pytest-%s" % (py.__version__, pytest.__version__)
if self.verbosity > 0 or self.config.option.debug or \
getattr(self.config.option, 'pastebin', None):
msg += " -- " + str(sys.executable)
@@ -342,7 +351,7 @@ class TerminalReporter:
if exitstatus in (0, 1, 2, 4):
self.summary_errors()
self.summary_failures()
self.summary_hints()
self.summary_warnings()
self.config.hook.pytest_terminal_summary(terminalreporter=self)
if exitstatus == 2:
self._report_keyboardinterrupt()
@@ -373,11 +382,10 @@ class TerminalReporter:
fspath = "%s <- %s" % (collect_fspath, fspath)
if fspath:
line = str(fspath)
if lineno is not None:
lineno += 1
line += ":" + str(lineno)
if domain:
line += ": " + str(domain)
split = str(domain).split('[')
split[0] = split[0].replace('.', '::') # don't replace '.' in params
line += "::" + '['.join(split)
else:
line = "[location]"
return line + " "
@@ -408,10 +416,15 @@ 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_warnings(self):
if self.hasopt("w"):
warnings = self.stats.get("warnings")
if not warnings:
return
self.write_sep("=", "warning summary")
for w in warnings:
self._tw.line("W%s %s %s" % (w.code,
w.fslocation, w.message))
def summary_failures(self):
if self.config.option.tbstyle != "no":
@@ -455,9 +468,10 @@ class TerminalReporter:
self._tw.line(content)
def summary_stats(self):
session_duration = py.std.time.time() - self._sessionstarttime
session_duration = time.time() - self._sessionstarttime
keys = "failed passed skipped deselected xfailed xpassed".split()
keys = ("failed passed skipped deselected "
"xfailed xpassed warnings").split()
for key in self.stats.keys():
if key not in keys:
keys.append(key)

View File

@@ -1,7 +1,11 @@
""" support for providing temporary directories to test functions. """
import pytest, py
import re
import pytest
import py
from _pytest.monkeypatch import monkeypatch
class TempdirHandler:
def __init__(self, config):
self.config = config
@@ -63,6 +67,9 @@ def tmpdir(request):
path object.
"""
name = request.node.name
name = py.std.re.sub("[\W]", "_", name)
name = 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,25 @@
""" discovery and running of std-library "unittest" style tests. """
import pytest, py
import sys, pdb
from __future__ import absolute_import
import traceback
import sys
import pytest
import py
# for transfering markers
from _pytest.python import transfer_markers
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
try:
return issubclass(obj, unittest.TestCase)
except KeyboardInterrupt:
raise
except:
return False
def pytest_pycollect_makeitem(collector, name, obj):
if is_unittest(obj):
return UnitTestCase(name, parent=collector)
# has unittest been imported and is obj a subclass of its TestCase?
try:
if not issubclass(obj, sys.modules["unittest"].TestCase):
return
except Exception:
return
# yes, so let's collect it
return UnitTestCase(name, parent=collector)
class UnitTestCase(pytest.Class):
@@ -41,17 +39,18 @@ class UnitTestCase(pytest.Class):
super(UnitTestCase, self).setup()
def collect(self):
self.session._fixturemanager.parsefactories(self, unittest=True)
loader = py.std.unittest.TestLoader()
module = self.getparent(pytest.Module).obj
from unittest import TestLoader
cls = self.obj
if not getattr(cls, "__test__", True):
return
self.session._fixturemanager.parsefactories(self, unittest=True)
loader = TestLoader()
module = self.getparent(pytest.Module).obj
foundsomething = False
for name in loader.getTestCaseNames(self.obj):
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
@@ -70,10 +69,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"):
@@ -94,7 +89,7 @@ class TestCaseFunction(pytest.Function):
except TypeError:
try:
try:
l = py.std.traceback.format_exception(*rawexcinfo)
l = traceback.format_exception(*rawexcinfo)
l.insert(0, "NOTE: Incompatible Exception Representation, "
"displaying natively:\n\n")
pytest.fail("".join(l), pytrace=False)
@@ -160,7 +155,7 @@ def pytest_runtest_protocol(item, __multicall__):
if isinstance(item, TestCaseFunction):
if 'twisted.trial.unittest' in sys.modules:
ut = sys.modules['twisted.python.failure']
Failure__init__ = ut.Failure.__init__.im_func
Failure__init__ = ut.Failure.__init__
check_testcase_implements_trial_reporter()
def excstore(self, exc_value=None, exc_type=None, exc_tb=None,
captureVars=None):

View File

@@ -1,10 +1,12 @@
import sys
if __name__ == '__main__':
import cProfile
import py
import pytest
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('pytest.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))

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

@@ -12,7 +12,7 @@ PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
SITETARGET=dev
SITETARGET=latest
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest

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,13 +1,5 @@
{% extends "!layout.html" %}
{% block relbaritems %}
{{ super() }}
<g:plusone></g:plusone>
{% endblock %}
{% block footer %}
{{ super() }}
<script type="text/javascript">
@@ -23,5 +15,4 @@
})();
</script>
<script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script>
{% endblock %}

View File

@@ -0,0 +1,11 @@
<h3>Useful Links</h3>
<ul>
<li><a href="{{ pathto('index') }}">The pytest Website</a></li>
<li><a href="{{ pathto('contributing') }}">Contribution Guide</a></li>
<li><a href="https://pypi.python.org/pypi/pytest">pytest @ PyPI</a></li>
<li><a href="https://bitbucket.org/hpk42/pytest/">pytest @ Bitbucket</a></li>
<li><a href="http://pytest.org/latest/plugins_index/index.html">3rd party plugins</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>

View File

@@ -1,39 +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><tr><td>
<a href="{{ pathto('talks') }}">Talks/Posts</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,24 @@
{%- 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">
{% 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,556 @@
/*
* flasky.css_t
* ~~~~~~~~~~~~
*
* :copyright: Copyright 2010 by Armin Ronacher.
* :license: Flask Design License, see LICENSE for details.
*/
{% set page_width = '1020px' %}
{% set sidebar_width = '220px' %}
/* orange of logo is #d67c29 but we use black for links for now */
{% set link_color = '#000' %}
{% set link_hover_color = '#000' %}
{% set base_font = 'sans-serif' %}
{% set header_font = 'sans-serif' %}
@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,15 @@ Release announcements
.. toctree::
:maxdepth: 2
release-2.6.2
release-2.6.1
release-2.6.0
release-2.5.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

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

@@ -0,0 +1,64 @@
pytest-2.5.2: fixes
===========================================================================
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.2 release fixes a few bugs with two maybe-bugs remaining and
actively being worked on (and waiting for the bug reporter's input).
We also have a new contribution guide thanks to Piotr Banaszkiewicz
and others.
See docs at:
http://pytest.org
As usual, you can upgrade from pypi via::
pip install -U pytest
Thanks to the following people who contributed to this release:
Anatoly Bubenkov
Ronny Pfannschmidt
Floris Bruynooghe
Bruno Oliveira
Andreas Pelme
Jurko Gospodnetić
Piotr Banaszkiewicz
Simon Liedtke
lakka
Lukasz Balcerzak
Philippe Muller
Daniel Hahler
have fun,
holger krekel
2.5.2
-----------------------------------
- fix issue409 -- better interoperate with cx_freeze by not
trying to import from collections.abc which causes problems
for py27/cx_freeze. Thanks Wolfgang L. for reporting and tracking it down.
- fixed docs and code to use "pytest" instead of "py.test" almost everywhere.
Thanks Jurko Gospodnetic for the complete PR.
- fix issue425: mention at end of "py.test -h" that --markers
and --fixtures work according to specified test path (or current dir)
- fix issue413: exceptions with unicode attributes are now printed
correctly also on python2 and with pytest-xdist runs. (the fix
requires py-1.4.20)
- copy, cleanup and integrate py.io capture
from pylib 1.4.20.dev2 (rev 13d9af95547e)
- address issue416: clarify docs as to conftest.py loading semantics
- fix issue429: comparing byte strings with non-ascii chars in assert
expressions now work better. Thanks Floris Bruynooghe.
- make capfd/capsys.capture private, its unused and shouldnt be exposed

View File

@@ -0,0 +1,153 @@
pytest-2.6.0: shorter tracebacks, new warning system, test runner compat
===========================================================================
pytest is a mature Python testing tool with more than a 1000 tests
against itself, passing on many different interpreters and platforms.
The 2.6.0 release should be drop-in backward compatible to 2.5.2 and
fixes a number of bugs and brings some new features, mainly:
- shorter tracebacks by default: only the first (test function) entry
and the last (failure location) entry are shown, the ones between
only in "short" format. Use ``--tb=long`` to get back the old
behaviour of showing "long" entries everywhere.
- a new warning system which reports oddities during collection
and execution. For example, ignoring collecting Test* classes with an
``__init__`` now produces a warning.
- various improvements to nose/mock/unittest integration
Note also that 2.6.0 departs with the "zero reported bugs" policy
because it has been too hard to keep up with it, unfortunately.
Instead we are for now rather bound to work on "upvoted" issues in
the https://bitbucket.org/hpk42/pytest/issues?status=new&status=open&sort=-votes
issue tracker.
See docs at:
http://pytest.org
As usual, you can upgrade from pypi via::
pip install -U pytest
Thanks to all who contributed, among them:
Benjamin Peterson
Jurko Gospodnetić
Floris Bruynooghe
Marc Abramowitz
Marc Schlaich
Trevor Bekolay
Bruno Oliveira
Alex Groenholm
have fun,
holger krekel
2.6.0
-----------------------------------
- fix issue537: Avoid importing old assertion reinterpretation code by default.
Thanks Benjamin Peterson.
- fix issue364: shorten and enhance tracebacks representation by default.
The new "--tb=auto" option (default) will only display long tracebacks
for the first and last entry. You can get the old behaviour of printing
all entries as long entries with "--tb=long". Also short entries by
default are now printed very similarly to "--tb=native" ones.
- fix issue514: teach assertion reinterpretation about private class attributes
Thanks Benjamin Peterson.
- change -v output to include full node IDs of tests. Users can copy
a node ID from a test run, including line number, and use it as a
positional argument in order to run only a single test.
- fix issue 475: fail early and comprehensible if calling
pytest.raises with wrong exception type.
- fix issue516: tell in getting-started about current dependencies.
- cleanup setup.py a bit and specify supported versions. Thanks Jurko
Gospodnetic for the PR.
- change XPASS colour to yellow rather then red when tests are run
with -v.
- fix issue473: work around mock putting an unbound method into a class
dict when double-patching.
- fix issue498: if a fixture finalizer fails, make sure that
the fixture is still invalidated.
- fix issue453: the result of the pytest_assertrepr_compare hook now gets
it's newlines escaped so that format_exception does not blow up.
- internal new warning system: pytest will now produce warnings when
it detects oddities in your test collection or execution.
Warnings are ultimately sent to a new pytest_logwarning hook which is
currently only implemented by the terminal plugin which displays
warnings in the summary line and shows more details when -rw (report on
warnings) is specified.
- change skips into warnings for test classes with an __init__ and
callables in test modules which look like a test but are not functions.
- fix issue436: improved finding of initial conftest files from command
line arguments by using the result of parse_known_args rather than
the previous flaky heuristics. Thanks Marc Abramowitz for tests
and initial fixing approaches in this area.
- fix issue #479: properly handle nose/unittest(2) SkipTest exceptions
during collection/loading of test modules. Thanks to Marc Schlaich
for the complete PR.
- fix issue490: include pytest_load_initial_conftests in documentation
and improve docstring.
- fix issue472: clarify that ``pytest.config.getvalue()`` cannot work
if it's triggered ahead of command line parsing.
- merge PR123: improved integration with mock.patch decorator on tests.
- fix issue412: messing with stdout/stderr FD-level streams is now
captured without crashes.
- fix issue483: trial/py33 works now properly. Thanks Daniel Grana for PR.
- improve example for pytest integration with "python setup.py test"
which now has a generic "-a" or "--pytest-args" option where you
can pass additional options as a quoted string. Thanks Trevor Bekolay.
- simplified internal capturing mechanism and made it more robust
against tests or setups changing FD1/FD2, also better integrated
now with pytest.pdb() in single tests.
- improvements to pytest's own test-suite leakage detection, courtesy of PRs
from Marc Abramowitz
- fix issue492: avoid leak in test_writeorg. Thanks Marc Abramowitz.
- fix issue493: don't run tests in doc directory with ``python setup.py test``
(use tox -e doctesting for that)
- fix issue486: better reporting and handling of early conftest loading failures
- some cleanup and simplification of internal conftest handling.
- work a bit harder to break reference cycles when catching exceptions.
Thanks Jurko Gospodnetic.
- fix issue443: fix skip examples to use proper comparison. Thanks Alex
Groenholm.
- support nose-style ``__test__`` attribute on modules, classes and
functions, including unittest-style Classes. If set to False, the
test will not be collected.
- fix issue512: show "<notset>" for arguments which might not be set
in monkeypatch plugin. Improves output in documentation.
- avoid importing "py.test" (an old alias module for "pytest")

View File

@@ -0,0 +1,59 @@
pytest-2.6.1: fixes and new xfail feature
===========================================================================
pytest is a mature Python testing tool with more than a 1100 tests
against itself, passing on many different interpreters and platforms.
The 2.6.1 release is drop-in compatible to 2.5.2 and actually fixes some
regressions introduced with 2.6.0. It also brings a little feature
to the xfail marker which now recognizes expected exceptions,
see the CHANGELOG below.
See docs at:
http://pytest.org
As usual, you can upgrade from pypi via::
pip install -U pytest
Thanks to all who contributed, among them:
Floris Bruynooghe
Bruno Oliveira
Nicolas Delaby
have fun,
holger krekel
Changes 2.6.1
=================
- No longer show line numbers in the --verbose output, the output is now
purely the nodeid. The line number is still shown in failure reports.
Thanks Floris Bruynooghe.
- fix issue437 where assertion rewriting could cause pytest-xdist slaves
to collect different tests. Thanks Bruno Oliveira.
- fix issue555: add "errors" attribute to capture-streams to satisfy
some distutils and possibly other code accessing sys.stdout.errors.
- fix issue547 capsys/capfd also work when output capturing ("-s") is disabled.
- address issue170: allow pytest.mark.xfail(...) to specify expected exceptions via
an optional "raises=EXC" argument where EXC can be a single exception
or a tuple of exception classes. Thanks David Mohr for the complete
PR.
- fix integration of pytest with unittest.mock.patch decorator when
it uses the "new" argument. Thanks Nicolas Delaby for test and PR.
- fix issue with detecting conftest files if the arguments contain
"::" node id specifications (copy pasted from "-v" output)
- fix issue544 by only removing "@NUM" at the end of "::" separated parts
and if the part has an ".py" extension
- don't use py.std import helper, rather import things directly.
Thanks Bruno Oliveira.

View File

@@ -0,0 +1,52 @@
pytest-2.6.2: few fixes and cx_freeze support
===========================================================================
pytest is a mature Python testing tool with more than a 1100 tests
against itself, passing on many different interpreters and platforms.
This release is drop-in compatible to 2.5.2 and 2.6.X. It also
brings support for including pytest with cx_freeze or similar
freezing tools into your single-file app distribution. For details
see the CHANGELOG below.
See docs at:
http://pytest.org
As usual, you can upgrade from pypi via::
pip install -U pytest
Thanks to all who contributed, among them:
Floris Bruynooghe
Benjamin Peterson
Bruno Oliveira
have fun,
holger krekel
2.6.2
-----------
- Added function pytest.freeze_includes(), which makes it easy to embed
pytest into executables using tools like cx_freeze.
See docs for examples and rationale. Thanks Bruno Oliveira.
- Improve assertion rewriting cache invalidation precision.
- fixed issue561: adapt autouse fixture example for python3.
- fixed issue453: assertion rewriting issue with __repr__ containing
"\n{", "\n}" and "\n~".
- fix issue560: correctly display code if an "else:" or "finally:" is
followed by statements on the same line.
- Fix example in monkeypatch documentation, thanks t-8ch.
- fix issue572: correct tmpdir doc example for python3.
- Do not mark as universal wheel because Python 2.6 is different from
other builds due to the extra argparse dependency. Fixes issue566.
Thanks sontek.

View File

@@ -1,7 +1,7 @@
.. _apiref:
py.test reference documentation
pytest reference documentation
================================================
.. toctree::

View File

@@ -10,7 +10,7 @@ The writing and reporting of assertions in tests
Asserting with the ``assert`` statement
---------------------------------------------------------
``py.test`` allows you to use the standard python ``assert`` for verifying
``pytest`` allows you to use the standard python ``assert`` for verifying
expectations and values in Python tests. For example, you can write the
following::
@@ -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.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1
collected 1 items
test_assert1.py F
@@ -42,7 +42,7 @@ you will see the return value of the function call::
test_assert1.py:5: AssertionError
========================= 1 failed in 0.01 seconds =========================
py.test has support for showing the values of the most common subexpressions
``pytest`` has support for showing the values of the most common subexpressions
including calls, attributes, comparisons, and binary and unary
operators. (See :ref:`tbreportdemo`). This allows you to use the
idiomatic python constructs without boilerplate code while not losing
@@ -95,6 +95,22 @@ asserts that the given ``ExpectedException`` is raised. The reporter will
provide you with helpful output in case of failures such as *no
exception* or *wrong exception*.
Note that it is also possible to specify a "raises" argument to
``pytest.mark.xfail``, which checks that the test is failing in a more
specific way than just having any exception raised::
@pytest.mark.xfail(raises=IndexError)
def test_f():
f()
Using ``pytest.raises`` is likely to be better for cases where you are testing
exceptions your own code is deliberately raising, whereas using
``@pytest.mark.xfail`` with a check function is probably better for something
like documenting unfixed bugs (where the test describes what "should" happen)
or bugs in dependencies.
.. _newreport:
Making use of context-sensitive comparisons
@@ -102,7 +118,7 @@ Making use of context-sensitive comparisons
.. versionadded:: 2.0
py.test has rich support for providing context-sensitive information
``pytest`` has rich support for providing context-sensitive information
when it encounters comparisons. For example::
# content of test_assert2.py
@@ -116,7 +132,7 @@ if you run this module::
$ py.test test_assert2.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1
collected 1 items
test_assert2.py F
@@ -175,22 +191,23 @@ now, given this test module::
f2 = Foo(2)
assert f1 == f2
you can run the test module and get the custom output defined in
you can run the test module and get the custom output defined in
the conftest file::
$ py.test -q test_foocompare.py
F
================================= FAILURES =================================
_______________________________ test_compare _______________________________
def test_compare():
f1 = Foo(1)
f2 = Foo(2)
> assert f1 == f2
E assert Comparing Foo instances:
E vals: 1 != 2
test_foocompare.py:8: AssertionError
1 failed in 0.01 seconds
.. _assert-details:
.. _`assert introspection`:
@@ -204,33 +221,33 @@ Advanced assertion introspection
Reporting details about a failing assertion is achieved either by rewriting
assert statements before they are run or re-evaluating the assert expression and
recording the intermediate values. Which technique is used depends on the
location of the assert, py.test's configuration, and Python version being used
to run py.test. Note that for assert statements with a manually provided
location of the assert, ``pytest`` configuration, and Python version being used
to run ``pytest``. Note that for assert statements with a manually provided
message, i.e. ``assert expr, message``, no assertion introspection takes place
and the manually provided message will be rendered in tracebacks.
By default, if the Python version is greater than or equal to 2.6, py.test
By default, if the Python version is greater than or equal to 2.6, ``pytest``
rewrites assert statements in test modules. Rewritten assert statements put
introspection information into the assertion failure message. py.test only
introspection information into the assertion failure message. ``pytest`` only
rewrites test modules directly discovered by its test collection process, so
asserts in supporting modules which are not themselves test modules will not be
rewritten.
.. note::
py.test rewrites test modules on import. It does this by using an import hook
to write a new pyc files. Most of the time this works transparently. However,
if you are messing with import yourself, the import hook may interfere. If
this is the case, simply use ``--assert=reinterp`` or
``pytest`` rewrites test modules on import. It does this by using an import
hook to write a new pyc files. Most of the time this works transparently.
However, if you are messing with import yourself, the import hook may
interfere. If this is the case, simply use ``--assert=reinterp`` or
``--assert=plain``. Additionally, rewriting will fail silently if it cannot
write new pycs, i.e. in a read-only filesystem or a zipfile.
If an assert statement has not been rewritten or the Python version is less than
2.6, py.test falls back on assert reinterpretation. In assert reinterpretation,
py.test walks the frame of the function containing the assert statement to
discover sub-expression results of the failing assert statement. You can force
py.test to always use assertion reinterpretation by passing the
``--assert=reinterp`` option.
2.6, ``pytest`` falls back on assert reinterpretation. In assert
reinterpretation, ``pytest`` walks the frame of the function containing the
assert statement to discover sub-expression results of the failing assert
statement. You can force ``pytest`` to always use assertion reinterpretation by
passing the ``--assert=reinterp`` option.
Assert reinterpretation has a caveat not present with assert rewriting: If
evaluating the assert expression has side effects you may get a warning that the
@@ -249,7 +266,7 @@ easy to rewrite the assertion and avoid any trouble::
All assert introspection can be turned off by passing ``--assert=plain``.
For further information, Benjamin Peterson wrote up `Behind the scenes of py.test's new assertion rewriting <http://pybites.blogspot.com/2011/07/behind-scenes-of-pytests-new-assertion.html>`_.
For further information, Benjamin Peterson wrote up `Behind the scenes of pytest's new assertion rewriting <http://pybites.blogspot.com/2011/07/behind-scenes-of-pytests-new-assertion.html>`_.
.. versionadded:: 2.1
Add assert rewriting as an alternate introspection technique.

View File

@@ -1,186 +0,0 @@
**Test classes, modules or whole projects can make use of
one or more fixtures**. All required fixture functions will execute
before a test from the specifying context executes. As You can use this
to make tests operate from a pre-initialized directory or with
certain environment variables or with pre-configured global application
settings.
For example, the Django_ project requires database
initialization to be able to import from and use its model objects.
For that, the `pytest-django`_ plugin provides fixtures which your
project can then easily depend or extend on, simply by referencing the
name of the particular fixture.
Fixture functions have limited visilibity which depends on where they
are defined. If they are defined on a test class, only its test methods
may use it. A fixture defined in a module can only be used
from that test module. A fixture defined in a conftest.py file
can only be used by the tests below the directory of that file.
Lastly, plugins can define fixtures which are available across all
projects.
Python, Java and many other languages support a so called xUnit_ style
for providing a fixed state, `test fixtures`_, for running tests. It
typically involves calling a autouse function ahead and a teardown
function after test execute. In 2005 pytest introduced a scope-specific
model of automatically detecting and calling autouse and teardown
functions on a per-module, class or function basis. The Python unittest
package and nose have subsequently incorporated them. This model
remains supported by pytest as :ref:`classic xunit`.
One property of xunit fixture functions is that they work implicitely
by preparing global state or setting attributes on TestCase objects.
By contrast, pytest provides :ref:`funcargs` which allow to
dependency-inject application test state into test functions or
methods as function arguments. If your application is sufficiently modular
or if you are creating a new project, we recommend you now rather head over to
:ref:`funcargs` instead because many pytest users agree that using this
paradigm leads to better application and test organisation.
However, not all programs and frameworks work and can be tested in
a fully modular way. They rather require preparation of global state
like database autouse on which further fixtures like preparing application
specific tables or wrapping tests in transactions can take place. For those
needs, pytest-2.3 now supports new **fixture functions** which come with
a ton of improvements over classic xunit fixture writing. Fixture functions:
- allow to separate different autouse concerns into multiple modular functions
- can receive and fully interoperate with :ref:`funcargs <resources>`,
- are called multiple times if its funcargs are parametrized,
- don't need to be defined directly in your test classes or modules,
they can also be defined in a plugin or :ref:`conftest.py <conftest.py>` files and get called
- are called on a per-session, per-module, per-class or per-function basis
by means of a simple "scope" declaration.
- can access the :ref:`request <request>` object which allows to
introspect and interact with the (scoped) testcontext.
- can add cleanup functions which will be invoked when the last test
of the fixture test context has finished executing.
All of these features are now demonstrated by little examples.
test modules accessing a global resource
-------------------------------------------------------
.. note::
Relying on `global state is considered bad programming practise <http://en.wikipedia.org/wiki/Global_variable>`_ but when you work with an application
that relies on it you often have no choice.
If you want test modules to access a global resource,
you can stick the resource to the module globals in
a per-module autouse function. We use a :ref:`resource factory
<@pytest.fixture>` to create our global resource::
# content of conftest.py
import pytest
class GlobalResource:
def __init__(self):
pass
@pytest.fixture(scope="session")
def globresource():
return GlobalResource()
@pytest.fixture(scope="module")
def setresource(request, globresource):
request.module.globresource = globresource
Now any test module can access ``globresource`` as a module global::
# content of test_glob.py
def test_1():
print ("test_1 %s" % globresource)
def test_2():
print ("test_2 %s" % globresource)
Let's run this module without output-capturing::
$ py.test -qs test_glob.py
FF
================================= FAILURES =================================
__________________________________ test_1 __________________________________
def test_1():
> print ("test_1 %s" % globresource)
E NameError: global name 'globresource' is not defined
test_glob.py:3: NameError
__________________________________ test_2 __________________________________
def test_2():
> print ("test_2 %s" % globresource)
E NameError: global name 'globresource' is not defined
test_glob.py:5: NameError
The two tests see the same global ``globresource`` object.
Parametrizing the global resource
+++++++++++++++++++++++++++++++++++++++++++++++++
We extend the previous example and add parametrization to the globresource
factory and also add a finalizer::
# content of conftest.py
import pytest
class GlobalResource:
def __init__(self, param):
self.param = param
@pytest.fixture(scope="session", params=[1,2])
def globresource(request):
g = GlobalResource(request.param)
def fin():
print "finalizing", g
request.addfinalizer(fin)
return g
@pytest.fixture(scope="module")
def setresource(request, globresource):
request.module.globresource = globresource
And then re-run our test module::
$ py.test -qs test_glob.py
FF
================================= FAILURES =================================
__________________________________ test_1 __________________________________
def test_1():
> print ("test_1 %s" % globresource)
E NameError: global name 'globresource' is not defined
test_glob.py:3: NameError
__________________________________ test_2 __________________________________
def test_2():
> print ("test_2 %s" % globresource)
E NameError: global name 'globresource' is not defined
test_glob.py:5: NameError
We are now running the two tests twice with two different global resource
instances. Note that the tests are ordered such that only
one instance is active at any given time: the finalizer of
the first globresource instance is called before the second
instance is created and sent to the autouse functions.

View File

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

View File

@@ -80,7 +80,7 @@ You can ask for available builtin or project-custom
capfd
enables capturing of writes to file descriptors 1 and 2 and makes
captured output available via ``capsys.readouterr()`` method calls
captured output available via ``capfd.readouterr()`` method calls
which return a ``(out, err)`` tuple.
monkeypatch
@@ -120,3 +120,4 @@ You can ask for available builtin or project-custom
path object.
in 0.00 seconds

View File

@@ -23,7 +23,7 @@ a test.
Setting capturing methods or disabling capturing
-------------------------------------------------
There are two ways in which ``py.test`` can perform capturing:
There are two ways in which ``pytest`` can perform capturing:
* file descriptor (FD) level capturing (default): All writes going to the
operating system file descriptors 1 and 2 will be captured.
@@ -49,7 +49,7 @@ One primary benefit of the default capturing of stdout/stderr output
is that you can use print statements for debugging::
# content of test_module.py
def setup_function(function):
print ("setting up %s" % function)
@@ -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.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1
collected 2 items
test_module.py .F
@@ -77,18 +77,16 @@ of the failing function and hide the other one::
E assert False
test_module.py:9: AssertionError
----------------------------- Captured stdout ------------------------------
setting up <function test_func2 at 0x2d79f50>
-------------------------- Captured stdout setup ---------------------------
setting up <function test_func2 at 0x2b5d6a81c9d8>
==================== 1 failed, 1 passed in 0.01 seconds ====================
Accessing captured output from a test function
---------------------------------------------------
The :ref:`funcarg mechanism` allows test function a very easy
way to access the captured output by simply using the names
``capsys`` or ``capfd`` in the test function signature. Here
is an example test function that performs some output related
checks::
The ``capsys`` and ``capfd`` fixtures allow to access stdout/stderr
output created during test execution. Here is an example test function
that performs some output related checks::
def test_myoutput(capsys): # or use "capfd" for fd-level
print ("hello")
@@ -105,11 +103,13 @@ and capturing will be continued. After the test
function finishes the original streams will
be restored. Using ``capsys`` this way frees your
test from having to care about setting/resetting
output streams and also interacts well with py.test's
output streams and also interacts well with pytest's
own per-test capturing.
If you want to capture on ``fd`` level you can use
If you want to capture on filedescriptor level you can use
the ``capfd`` function argument which offers the exact
same interface.
same interface but allows to also capture output from
libraries or subprocesses that directly write to operating
system level output streams (FD1 and FD2).
.. include:: links.inc

View File

@@ -17,7 +17,8 @@
#
# The full version, including alpha/beta/rc tags.
# The short X.Y version.
version = release = "2.4.0.dev"
version = "2.6"
release = "2.6.2"
import sys, os
@@ -53,7 +54,7 @@ master_doc = 'contents'
# General information about the project.
project = u'pytest'
copyright = u'2012, holger krekel'
copyright = u'2014, holger krekel'
@@ -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 = []
@@ -125,12 +131,12 @@ html_short_title = "pytest-%s" % release
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
html_logo = "img/pytest1.png"
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
html_favicon = "img/pytest1favi.ico"
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
@@ -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 = {}
@@ -197,12 +220,12 @@ htmlhelp_basename = 'pytestdoc'
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('contents', 'pytest.tex', u'pytest Documentation',
u'holger krekel, http://merlinux.eu', 'manual'),
u'holger krekel, trainer and consultant, http://merlinux.eu', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
latex_logo = 'img/pytest1.png'
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
@@ -239,7 +262,7 @@ man_pages = [
epub_title = u'pytest'
epub_author = u'holger krekel at merlinux eu'
epub_publisher = u'holger krekel at merlinux eu'
epub_copyright = u'2012, holger krekel et alii'
epub_copyright = u'2013, holger krekel et alii'
# The language of the text. It defaults to the language option
# or en if the language is not set.

View File

@@ -18,6 +18,9 @@ Contact channels
- `pytest-commit at python.org (mailing list)`_: for commits and new issues
- :doc:`contribution guide <contributing>` for help on submitting pull
requests to bitbucket (including using git via gitifyhg).
- #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

View File

@@ -1,4 +1,10 @@
.. note::
`improving your automated testing with pytest <https://ep2014.europython.eu/en/schedule/sessions/92/>`_, July 25th 2014, Berlin, Germany
`professional testing with pytest and tox <http://www.python-academy.com/courses/specialtopics/python_course_testing.html>`_, 24-26th November 2014, Freiburg, Germany
.. _toc:
Full pytest documentation
@@ -14,9 +20,10 @@ Full pytest documentation
overview
apiref
plugins
plugins_index/index
example/index
talks
develop
contributing
funcarg_compare.txt
announce/index

3
doc/en/contributing.txt Normal file
View File

@@ -0,0 +1,3 @@
.. _contributing:
.. include:: ../../CONTRIBUTING.rst

View File

@@ -17,7 +17,7 @@ which were registered by installed plugins.
How test configuration is read from configuration INI-files
-------------------------------------------------------------
py.test searches for the first matching ini-style configuration file
``pytest`` searches for the first matching ini-style configuration file
in the directories of command line argument and the directories above.
It looks for file basenames in this order::
@@ -26,8 +26,8 @@ It looks for file basenames in this order::
setup.cfg
Searching stops when the first ``[pytest]`` section is found in any of
these files. There is no merging of configuration values from multiple
files. Example::
these files. There is no merging of configuration values from multiple
files. Example::
py.test path/to/testdir
@@ -41,7 +41,7 @@ will look in the following dirs for a config file::
path/to/setup.cfg
... # up until root of filesystem
If argument is provided to a py.test run, the current working directory
If argument is provided to a ``pytest`` run, the current working directory
is used to start the search.
.. _`how to change command line options defaults`:
@@ -51,7 +51,7 @@ How to change command line options defaults
------------------------------------------------
It can be tedious to type the same series of command line options
every time you use py.test . For example, if you always want to see
every time you use ``pytest``. For example, if you always want to see
detailed info on skipped and xfailed tests, as well as have terser "dot"
progress output, you can write it into a configuration file::
@@ -60,7 +60,7 @@ progress output, you can write it into a configuration file::
[pytest]
addopts = -rsxX -q
From now on, running ``py.test`` will add the specified options.
From now on, running ``pytest`` will add the specified options.
Builtin configuration file options
----------------------------------------------
@@ -97,15 +97,15 @@ Builtin configuration file options
[seq] matches any character in seq
[!seq] matches any char not in seq
Default patterns are ``.* _darcs CVS {args}``. Setting a ``norecursedir``
replaces the default. Here is an example of how to avoid
certain directories::
Default patterns are ``'.*', 'CVS', '_darcs', '{arch}', '*.egg'``.
Setting a ``norecursedirs`` replaces the default. Here is an example of
how to avoid certain directories::
# content of setup.cfg
[pytest]
norecursedirs = .svn _build tmp*
This would tell py.test to not look into typical subversion or
This would tell ``pytest`` to not look into typical subversion or
sphinx-build directories or into any ``tmp`` prefixed directory.
.. confval:: python_files
@@ -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

@@ -1,40 +0,0 @@
=================================================
Feedback and contribute to py.test
=================================================
.. toctree::
:maxdepth: 2
contact.txt
.. _checkout:
Working from version control or a tarball
=================================================
To follow development or start experiments, checkout the
complete code and documentation source with mercurial_::
hg clone https://bitbucket.org/hpk42/pytest/
You can also go to the python package index and
download and unpack a TAR file::
http://pypi.python.org/pypi/pytest/
Activating a checkout with setuptools
--------------------------------------------
With a working Distribute_ or setuptools_ installation you can type::
python setup.py develop
in order to work inline with the tools and the lib of your checkout.
If this command complains that it could not find the required version
of "py" then you need to use the development pypi repository::
python setup.py develop -i http://pypi.testrun.org
.. include:: links.inc

View File

@@ -44,15 +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.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1
collected 1 items
mymodule.py .
========================= 1 passed in 0.02 seconds =========================
========================= 1 passed in 0.06 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

@@ -1,4 +1,4 @@
from py.test import raises
from pytest import raises
import py
def otherfunc(a,b):

View File

@@ -21,6 +21,9 @@ You can "mark" a test function with custom metadata like this::
pass
def test_another():
pass
class TestClass:
def test_method(self):
pass
.. versionadded:: 2.2
@@ -28,26 +31,82 @@ 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.5 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 3 items
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1 -- /home/hpk/p/pytest/.tox/regen/bin/python3.4
collecting ... collected 4 items
test_server.py:3: test_send_http PASSED
test_server.py::test_send_http PASSED
=================== 2 tests deselected by "-m 'webtest'" ===================
================== 1 passed, 2 deselected in 0.01 seconds ==================
=================== 3 tests deselected by "-m 'webtest'" ===================
================== 1 passed, 3 deselected in 0.01 seconds ==================
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.5 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 3 items
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1 -- /home/hpk/p/pytest/.tox/regen/bin/python3.4
collecting ... collected 4 items
test_server.py:6: test_something_quick PASSED
test_server.py:8: test_another PASSED
test_server.py::test_something_quick PASSED
test_server.py::test_another PASSED
test_server.py::TestClass::test_method PASSED
================= 1 tests deselected by "-m 'not webtest'" =================
================== 2 passed, 1 deselected in 0.01 seconds ==================
================== 3 passed, 1 deselected in 0.01 seconds ==================
Selecing tests based on their node ID
-------------------------------------
You can provide one or more :ref:`node IDs <node-id>` as positional
arguments to select only specified tests. This makes it easy to select
tests based on their module, class, method, or function name::
$ py.test -v test_server.py::TestClass::test_method
=========================== test session starts ============================
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1 -- /home/hpk/p/pytest/.tox/regen/bin/python3.4
collecting ... collected 5 items
test_server.py::TestClass::test_method PASSED
========================= 1 passed in 0.01 seconds =========================
You can also select on the class::
$ py.test -v test_server.py::TestClass
=========================== test session starts ============================
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1 -- /home/hpk/p/pytest/.tox/regen/bin/python3.4
collecting ... collected 4 items
test_server.py::TestClass::test_method PASSED
========================= 1 passed in 0.01 seconds =========================
Or select multiple nodes::
$ py.test -v test_server.py::TestClass test_server.py::test_send_http
=========================== test session starts ============================
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1 -- /home/hpk/p/pytest/.tox/regen/bin/python3.4
collecting ... collected 8 items
test_server.py::TestClass::test_method PASSED
test_server.py::test_send_http PASSED
========================= 2 passed in 0.01 seconds =========================
.. _node-id:
.. note::
Node IDs are of the form ``module.py::class::method`` or
``module.py::function``. Node IDs control which tests are
collected, so ``module.py::class`` will select all test methods
on the class. Nodes are also created for each parameter of a
parametrized fixture or test, so selecting a parametrized test
must include the parameter value, e.g.
``module.py::function[param]``.
Node IDs for failing tests are displayed in the test summary info
when running py.test with the ``-rf`` option. You can also
construct Node IDs from the output of ``py.test --collectonly``.
Using ``-k expr`` to select tests based on their name
-------------------------------------------------------
@@ -61,39 +120,51 @@ 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.5 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 3 items
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1 -- /home/hpk/p/pytest/.tox/regen/bin/python3.4
collecting ... collected 4 items
test_server.py:3: test_send_http PASSED
test_server.py::test_send_http PASSED
====================== 2 tests deselected by '-khttp' ======================
================== 1 passed, 2 deselected in 0.01 seconds ==================
====================== 3 tests deselected by '-khttp' ======================
================== 1 passed, 3 deselected in 0.01 seconds ==================
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.5 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 3 items
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1 -- /home/hpk/p/pytest/.tox/regen/bin/python3.4
collecting ... collected 4 items
test_server.py:6: test_something_quick PASSED
test_server.py:8: test_another PASSED
test_server.py::test_something_quick PASSED
test_server.py::test_another PASSED
test_server.py::TestClass::test_method PASSED
================= 1 tests deselected by '-knot send_http' ==================
================== 2 passed, 1 deselected in 0.01 seconds ==================
================== 3 passed, 1 deselected in 0.01 seconds ==================
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.5 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 3 items
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1 -- /home/hpk/p/pytest/.tox/regen/bin/python3.4
collecting ... collected 4 items
test_server.py:3: test_send_http PASSED
test_server.py:6: test_something_quick PASSED
test_server.py::test_send_http PASSED
test_server.py::test_something_quick PASSED
================= 1 tests deselected by '-khttp or quick' ==================
================== 2 passed, 1 deselected in 0.01 seconds ==================
================= 2 tests deselected by '-khttp or quick' ==================
================== 2 passed, 2 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
-------------------------------------
@@ -116,9 +187,9 @@ You can ask which markers exist for your test suite - the list includes our just
@pytest.mark.skipif(condition): skip the given test function if eval(condition) results in a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform. see http://pytest.org/latest/skipping.html
@pytest.mark.xfail(condition, reason=None, run=True): 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.xfail(condition, reason=None, run=True, raises=None): mark the the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See http://pytest.org/latest/skipping.html
@pytest.mark.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
@@ -139,8 +210,8 @@ For an example on how to add and work with markers from a plugin, see
* asking for existing markers via ``py.test --markers`` gives good output
* typos in function markers are treated as an error if you use
the ``--strict`` option. Later versions of py.test are probably
going to treat non-registered markers as an error.
the ``--strict`` option. Future versions of ``pytest`` are probably
going to start treating non-registered markers as errors at some point.
.. _`scoped-marking`:
@@ -235,7 +306,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"):
@@ -255,18 +326,18 @@ the test needs::
$ py.test -E stage2
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1
collected 1 items
test_someenv.py s
======================== 1 skipped in 0.01 seconds =========================
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.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1
collected 1 items
test_someenv.py .
@@ -280,9 +351,9 @@ The ``--markers`` option always gives you a list of available markers::
@pytest.mark.skipif(condition): skip the given test function if eval(condition) results in a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform. see http://pytest.org/latest/skipping.html
@pytest.mark.xfail(condition, reason=None, run=True): 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.xfail(condition, reason=None, run=True, raises=None): mark the the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See http://pytest.org/latest/skipping.html
@pytest.mark.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
@@ -290,7 +361,7 @@ The ``--markers`` option always gives you a list of available markers::
@pytest.mark.trylast: mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
Reading markers which were set from multiple places
----------------------------------------------------
@@ -318,7 +389,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))
@@ -326,11 +397,12 @@ test function. From a conftest file we can read it like this::
Let's run this without capturing output and see what we get::
$ py.test -q -s
$ py.test -q -s
glob args=('function',) kwargs={'x': 3}
glob args=('class',) kwargs={'x': 2}
glob args=('module',) kwargs={'x': 1}
.
1 passed in 0.01 seconds
marking platform specific tests with pytest
--------------------------------------------------------------
@@ -340,7 +412,7 @@ marking platform specific tests with pytest
Consider you have a test suite which marks tests for particular platforms,
namely ``pytest.mark.osx``, ``pytest.mark.win32`` etc. and you
also have tests that run on all platforms and have no specific
marker. If you now want to have a way to only run the tests
marker. If you now want to have a way to only run the tests
for your particular platform, you could use the following plugin::
# content of conftest.py
@@ -353,7 +425,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))
@@ -383,28 +455,28 @@ 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.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1
collected 4 items
test_plat.py s.s.
test_plat.py sss.
========================= short test summary info ==========================
SKIP [2] /tmp/doc-exec-273/conftest.py:12: cannot run on platform linux2
SKIP [3] /tmp/doc-exec-238/conftest.py:12: cannot run on platform linux
=================== 2 passed, 2 skipped in 0.01 seconds ====================
=================== 1 passed, 3 skipped in 0.01 seconds ====================
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.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1
collected 4 items
test_plat.py .
test_plat.py s
=================== 3 tests deselected by "-m 'linux2'" ====================
================== 1 passed, 3 deselected in 0.01 seconds ==================
================= 1 skipped, 3 deselected in 0.01 seconds ==================
then the unmarked-tests will not be run. It is thus a way to restrict the run to the specific tests.
then the unmarked-tests will not be run. It is thus a way to restrict the run to the specific tests.
Automatically adding markers based on test names
--------------------------------------------------------
@@ -423,7 +495,7 @@ at this test module::
def test_interface_complex():
assert 0
def test_event_simple():
assert 0
@@ -434,20 +506,20 @@ We want to dynamically define two markers and can do it in a
``conftest.py`` plugin::
# content of conftest.py
import pytest
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.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1
collected 4 items
test_module.py FF
@@ -455,12 +527,12 @@ We can now use the ``-m option`` to select one set::
================================= FAILURES =================================
__________________________ test_interface_simple ___________________________
test_module.py:3: in test_interface_simple
> assert 0
E assert 0
assert 0
E assert 0
__________________________ test_interface_complex __________________________
test_module.py:6: in test_interface_complex
> assert 0
E assert 0
assert 0
E assert 0
================== 2 tests deselected by "-m 'interface'" ==================
================== 2 failed, 2 deselected in 0.01 seconds ==================
@@ -468,7 +540,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.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1
collected 4 items
test_module.py FFF
@@ -476,15 +548,15 @@ or to select both "event" and "interface" tests::
================================= FAILURES =================================
__________________________ test_interface_simple ___________________________
test_module.py:3: in test_interface_simple
> assert 0
E assert 0
assert 0
E assert 0
__________________________ test_interface_complex __________________________
test_module.py:6: in test_interface_complex
> assert 0
E assert 0
assert 0
E assert 0
____________________________ test_event_simple _____________________________
test_module.py:9: in test_event_simple
> assert 0
E assert 0
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

@@ -2,9 +2,10 @@
module containing a parametrized tests testing cross-python
serialization via the pickle module.
"""
import py, pytest
import py
import pytest
pythonlist = ['python2.4', 'python2.5', 'python2.6', 'python2.7', 'python2.8']
pythonlist = ['python2.6', 'python2.7', 'python3.4']
@pytest.fixture(params=pythonlist)
def python1(request, tmpdir):
picklefile = tmpdir.join("data.pickle")
@@ -18,7 +19,7 @@ class Python:
def __init__(self, version, picklefile):
self.pythonpath = py.path.local.sysfind(version)
if not self.pythonpath:
py.test.skip("%r not found" %(version,))
pytest.skip("%r not found" %(version,))
self.picklefile = picklefile
def dumps(self, obj):
dumpfile = self.picklefile.dirpath("dump.py")

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.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.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.05 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,28 +56,28 @@ consulted when reporting in ``verbose`` mode::
nonpython $ py.test -v
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.5 -- /home/hpk/p/pytest/.tox/regen/bin/python
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1 -- /home/hpk/p/pytest/.tox/regen/bin/python3.4
collecting ... collected 2 items
test_simple.yml:1: usecase: ok PASSED
test_simple.yml:1: usecase: hello FAILED
test_simple.yml::usecase: ok PASSED
test_simple.yml::usecase: hello FAILED
================================= FAILURES =================================
______________________________ usecase: hello ______________________________
usecase execution failed
spec failed: 'some': 'other'
no further details known at this point.
==================== 1 failed, 1 passed in 0.05 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 --collect-only
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1
collected 2 items
<YamlFile 'test_simple.yml'>
<YamlItem 'ok'>
<YamlItem 'hello'>
============================= in 0.05 seconds =============================
============================= in 0.03 seconds =============================

View File

@@ -6,7 +6,7 @@ Parametrizing tests
.. currentmodule:: _pytest.python
py.test allows to easily parametrize test functions.
``pytest`` allows to easily parametrize test functions.
For basic docs, see :ref:`parametrize-basics`.
In the following we provide some examples using
@@ -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::
@@ -54,14 +55,15 @@ let's run the full monty::
....F
================================= FAILURES =================================
_____________________________ test_compute[4] ______________________________
param1 = 4
def test_compute(param1):
> assert param1 < 4
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.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1
collected 4 items
test_scenarios.py ....
@@ -116,7 +118,7 @@ If you just collect tests you'll also nicely see 'advanced' and 'basic' as varia
$ py.test --collect-only test_scenarios.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1
collected 4 items
<Module 'test_scenarios.py'>
<Class 'TestSampleWithScenarios'>
@@ -180,7 +182,7 @@ Let's first see how it looks like at collection time::
$ py.test test_backends.py --collect-only
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.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 0x2038f80>
db = <conftest.DB2 object at 0x2b83684b5eb8>
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 0x1338f80>, a = 1, b = 2
self = <test_parametrize.TestClass object at 0x2ae94130e390>, 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
--------------------------------------------------------------
@@ -275,9 +279,12 @@ is to be run with different sets of arguments for its three arguments:
Running it results in some skips if we don't have all the python interpreters installed and otherwise runs all combinations (5 interpreters times 5 interpreters times 3 objects to serialize/deserialize)::
. $ py.test -rs -q multipython.py
............sss............sss............sss............ssssssssssssssssss
ssssssssssssssssssssssssssssssssssss......sssssssss......ssssssssssssssssss
========================= short test summary info ==========================
SKIP [27] /home/hpk/p/pytest/doc/en/example/multipython.py:21: 'python2.8' not found
SKIP [21] /home/hpk/p/pytest/doc/en/example/multipython.py:22: 'python2.5' not found
SKIP [21] /home/hpk/p/pytest/doc/en/example/multipython.py:22: 'python2.8' not found
SKIP [21] /home/hpk/p/pytest/doc/en/example/multipython.py:22: 'python2.4' not found
12 passed, 63 skipped in 0.65 seconds
Indirect parametrization of optional implementations/imports
--------------------------------------------------------------------
@@ -285,7 +292,7 @@ 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)
say we have a "base" implementation and the other (possibly optimized ones)
need to provide similar results::
# content of conftest.py
@@ -324,12 +331,12 @@ 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.3.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1
collected 2 items
test_module.py .s
========================= short test summary info ==========================
SKIP [1] /tmp/doc-exec-275/conftest.py:10: could not import 'opt2'
SKIP [1] /tmp/doc-exec-240/conftest.py:10: could not import 'opt2'
=================== 1 passed, 1 skipped in 0.01 seconds ====================
@@ -337,13 +344,13 @@ 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
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
parametrization on the test functions to parametrize input/output
values as well.

View File

@@ -10,7 +10,7 @@ You can set the :confval:`norecursedirs` option in an ini-file, for example your
[pytest]
norecursedirs = .svn _build tmp*
This would tell py.test to not recurse into typical subversion or sphinx-build directories or into any ``tmp`` prefixed directory.
This would tell ``pytest`` to not recurse into typical subversion or sphinx-build directories or into any ``tmp`` prefixed directory.
.. _`change naming conventions`:
@@ -28,7 +28,7 @@ the :confval:`python_files`, :confval:`python_classes` and
python_classes=Check
python_functions=check
This would make py.test look for ``check_`` prefixes in
This would make ``pytest`` look for ``check_`` prefixes in
Python filenames, ``Check`` prefixes in classes and ``check`` prefixes
in functions and classes. For example, if we have::
@@ -43,7 +43,7 @@ then the test collection looks like this::
$ py.test --collect-only
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1
collected 2 items
<Module 'check_myapp.py'>
<Class 'CheckMyApp'>
@@ -53,10 +53,16 @@ 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
-----------------------------------------------------
You can use the ``--pyargs`` option to make py.test try
You can use the ``--pyargs`` option to make ``pytest`` try
interpreting arguments as python package names, deriving
their file system path and then running the test. For
example if you have unittest2 installed you can type::
@@ -82,7 +88,7 @@ You can always peek at the collection tree without running tests like this::
. $ py.test --collect-only pythoncollection.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1
collected 3 items
<Module 'pythoncollection.py'>
<Function 'test_function'>
@@ -98,7 +104,7 @@ customizing test collection to find all .py files
.. regendoc:wipe
You can easily instruct py.test to discover tests from every python file::
You can easily instruct ``pytest`` to discover tests from every python file::
# content of pytest.ini
@@ -106,8 +112,8 @@ You can easily instruct py.test to discover tests from every python file::
python_files = *.py
However, many projects will have a ``setup.py`` which they don't want to be imported. Moreover, there may files only importable by a specific python version.
For such cases you can dynamically define files to be ignored by listing
them in a ``conftest.py`` file::
For such cases you can dynamically define files to be ignored by listing
them in a ``conftest.py`` file::
# content of conftest.py
import sys
@@ -130,15 +136,13 @@ and a setup.py dummy file like this::
# content of setup.py
0/0 # will raise exeption if imported
then a pytest run on python2 will find the one test when run with a python2
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 --collect-only
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.5
collected 1 items
<Module 'pkg/module_py2.py'>
<Function 'test_only_on_python2'>
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1
collected 0 items
============================= in 0.01 seconds =============================

View File

@@ -1,11 +1,11 @@
.. _`tbreportdemo`:
Demo of Python failure reports with py.test
Demo of Python failure reports with pytest
==================================================
Here is a nice run of several tens of failures
and how py.test presents things (unfortunately
and how ``pytest`` presents things (unfortunately
not showing the nice colors here in the HTML that you
get on the terminal - we are working on that):
@@ -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.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.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 0x1445e10>
self = <failure_demo.TestFailing object at 0x2aec3e52d470>
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 0x137c6e0>()
E + and 43 = <function g at 0x137c758>()
E + where 42 = <function TestFailing.test_simple.<locals>.f at 0x2aec3e47b158>()
E + and 43 = <function TestFailing.test_simple.<locals>.g at 0x2aec3e47b268>()
failure_demo.py:28: AssertionError
____________________ TestFailing.test_simple_multiline _____________________
self = <failure_demo.TestFailing object at 0x135a1d0>
self = <failure_demo.TestFailing object at 0x2aec3e474ac8>
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 0x1458ed0>
self = <failure_demo.TestFailing object at 0x2aec3e5156a0>
def test_not(self):
def f():
return 42
> assert not f()
E assert not 42
E + where 42 = <function f at 0x137caa0>()
E + where 42 = <function TestFailing.test_not.<locals>.f at 0x2aec3e47e620>()
failure_demo.py:38: AssertionError
_________________ TestSpecialisedExplanations.test_eq_text _________________
self = <failure_demo.TestSpecialisedExplanations object at 0x14451d0>
self = <failure_demo.TestSpecialisedExplanations object at 0x2aec3e50cba8>
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 0x1458c90>
self = <failure_demo.TestSpecialisedExplanations object at 0x2aec3e4e24e0>
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 0x1434390>
self = <failure_demo.TestSpecialisedExplanations object at 0x2aec3e4cc6d8>
def test_eq_multiline_text(self):
> assert 'foo\nspam\nbar' == 'foo\neggs\nbar'
@@ -115,7 +115,7 @@ 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 0x1459f50>
self = <failure_demo.TestSpecialisedExplanations object at 0x2aec3e501908>
def test_eq_long_text(self):
a = '1'*100 + 'a' + '2'*100
@@ -132,7 +132,7 @@ 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 0x135a790>
self = <failure_demo.TestSpecialisedExplanations object at 0x2aec3e3af048>
def test_eq_long_text_multiline(self):
a = '1\n'*100 + 'a' + '2\n'*100
@@ -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 0x138dfd0>
self = <failure_demo.TestSpecialisedExplanations object at 0x2aec3e474c50>
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 0x135a990>
self = <failure_demo.TestSpecialisedExplanations object at 0x2aec3e515dd8>
def test_eq_list_long(self):
a = [0]*100 + [1] + [3]*100
@@ -178,12 +178,12 @@ get on the terminal - we are working on that):
failure_demo.py:66: AssertionError
_________________ TestSpecialisedExplanations.test_eq_dict _________________
self = <failure_demo.TestSpecialisedExplanations object at 0x1459310>
self = <failure_demo.TestSpecialisedExplanations object at 0x2aec3e4a5ef0>
def test_eq_dict(self):
> 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 Hiding 1 identical items, use -v to show
E Omitting 1 identical items, use -v to show
E Differing items:
E {'b': 1} != {'b': 2}
E Left contains more items:
@@ -194,7 +194,7 @@ get on the terminal - we are working on that):
failure_demo.py:69: AssertionError
_________________ TestSpecialisedExplanations.test_eq_set __________________
self = <failure_demo.TestSpecialisedExplanations object at 0x1434310>
self = <failure_demo.TestSpecialisedExplanations object at 0x2aec3e4a2e48>
def test_eq_set(self):
> assert set([0, 10, 11, 12]) == set([0, 20, 21])
@@ -210,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 0x138ded0>
self = <failure_demo.TestSpecialisedExplanations object at 0x2aec3e4e0c50>
def test_eq_longer_list(self):
> assert [1,2] == [1,2,3]
@@ -220,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 0x1459e10>
self = <failure_demo.TestSpecialisedExplanations object at 0x2aec3e4950f0>
def test_in_list(self):
> assert 1 in [0, 2, 3, 4, 5]
@@ -229,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 0x1434950>
self = <failure_demo.TestSpecialisedExplanations object at 0x2aec3e474f98>
def test_not_in_text_multiline(self):
text = 'some multiline\ntext\nwhich\nincludes foo\nand a\ntail'
@@ -247,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 0x138dbd0>
self = <failure_demo.TestSpecialisedExplanations object at 0x2aec3e5333c8>
def test_not_in_text_single(self):
text = 'single foo line'
@@ -260,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 0x14593d0>
self = <failure_demo.TestSpecialisedExplanations object at 0x2aec3e4ccb70>
def test_not_in_text_single_long(self):
text = 'head ' * 50 + 'foo ' + 'tail ' * 20
@@ -273,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 0x1459650>
self = <failure_demo.TestSpecialisedExplanations object at 0x2aec3e502080>
def test_not_in_text_single_long_term(self):
text = 'head ' * 50 + 'f'*70 + 'tail ' * 20
@@ -292,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 0x1434850>.b
E + where 1 = <failure_demo.test_attribute.<locals>.Foo object at 0x2aec3e519c18>.b
failure_demo.py:101: AssertionError
_________________________ test_attribute_instance __________________________
@@ -302,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 0x1459dd0>.b
E + where <failure_demo.Foo object at 0x1459dd0> = <class 'failure_demo.Foo'>()
E + where 1 = <failure_demo.test_attribute_instance.<locals>.Foo object at 0x2aec3e52d898>.b
E + where <failure_demo.test_attribute_instance.<locals>.Foo object at 0x2aec3e52d898> = <class 'failure_demo.test_attribute_instance.<locals>.Foo'>()
failure_demo.py:107: AssertionError
__________________________ test_attribute_failure __________________________
@@ -319,7 +319,7 @@ get on the terminal - we are working on that):
failure_demo.py:116:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <failure_demo.Foo object at 0x1434150>
self = <failure_demo.test_attribute_failure.<locals>.Foo object at 0x2aec3e4e0b38>
def _get_b(self):
> raise Exception('Failed to get attrib')
@@ -335,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 0x14590d0>.b
E + where <failure_demo.Foo object at 0x14590d0> = <class 'failure_demo.Foo'>()
E + and 2 = <failure_demo.Bar object at 0x1459b10>.b
E + where <failure_demo.Bar object at 0x1459b10> = <class 'failure_demo.Bar'>()
E + where 1 = <failure_demo.test_attribute_multiple.<locals>.Foo object at 0x2aec3e4a5748>.b
E + where <failure_demo.test_attribute_multiple.<locals>.Foo object at 0x2aec3e4a5748> = <class 'failure_demo.test_attribute_multiple.<locals>.Foo'>()
E + and 2 = <failure_demo.test_attribute_multiple.<locals>.Bar object at 0x2aec3e4a51d0>.b
E + where <failure_demo.test_attribute_multiple.<locals>.Bar object at 0x2aec3e4a51d0> = <class 'failure_demo.test_attribute_multiple.<locals>.Bar'>()
failure_demo.py:124: AssertionError
__________________________ TestRaises.test_raises __________________________
self = <failure_demo.TestRaises instance at 0x13a0d88>
self = <failure_demo.TestRaises object at 0x2aec3e4a2d68>
def test_raises(self):
s = 'qwe'
@@ -355,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/local/lib/python2.7/site-packages/_pytest/python.py:858>:1: ValueError
<0-codegen /home/hpk/p/pytest/.tox/regen/lib/python3.4/site-packages/_pytest/python.py:1028>:1: ValueError
______________________ TestRaises.test_raises_doesnt _______________________
self = <failure_demo.TestRaises instance at 0x145fcf8>
self = <failure_demo.TestRaises object at 0x2aec3e4e2198>
def test_raises_doesnt(self):
> raises(IOError, "int('3')")
@@ -367,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 0x13a9ea8>
self = <failure_demo.TestRaises object at 0x2aec3e5017b8>
def test_raise(self):
> raise ValueError("demo error")
@@ -376,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 0x13843f8>
self = <failure_demo.TestRaises object at 0x2aec3e533160>
def test_tupleerror(self):
> a,b = [1]
@@ -385,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 0x14532d8>
self = <failure_demo.TestRaises object at 0x2aec3e4cc438>
def test_reinterpret_fails_with_print_for_the_fun_of_it(self):
l = [1,2,3]
@@ -394,15 +394,15 @@ get on the terminal - we are working on that):
E TypeError: 'int' object is not iterable
failure_demo.py:147: TypeError
----------------------------- Captured stdout ------------------------------
--------------------------- Captured stdout call ---------------------------
l is [1, 2, 3]
________________________ TestRaises.test_some_error ________________________
self = <failure_demo.TestRaises instance at 0x139d290>
self = <failure_demo.TestRaises object at 0x2aec3e5199e8>
def test_some_error(self):
> if namenotexi:
E NameError: global name 'namenotexi' is not defined
E NameError: name 'namenotexi' is not defined
failure_demo.py:150: NameError
____________________ test_dynamic_compile_shows_nicely _____________________
@@ -426,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 0x137d758>
self = <failure_demo.TestMoreErrors object at 0x2aec3e515cf8>
def test_complex_error(self):
def f():
@@ -437,13 +437,8 @@ get on the terminal - we are working on that):
failure_demo.py:175:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
x = 44, y = 43
def somefunc(x,y):
> otherfunc(x,y)
failure_demo.py:8:
failure_demo.py:8: in somefunc
otherfunc(x,y)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
a = 44, b = 43
@@ -455,7 +450,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 0x13a5200>
self = <failure_demo.TestMoreErrors object at 0x2aec3e4f7a58>
def test_z1_unpack_error(self):
l = []
@@ -465,7 +460,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 0x1395290>
self = <failure_demo.TestMoreErrors object at 0x2aec3e52db38>
def test_z2_type_error(self):
l = 3
@@ -475,19 +470,19 @@ get on the terminal - we are working on that):
failure_demo.py:183: TypeError
______________________ TestMoreErrors.test_startswith ______________________
self = <failure_demo.TestMoreErrors instance at 0x137f200>
self = <failure_demo.TestMoreErrors object at 0x2aec3e538a58>
def test_startswith(self):
s = "123"
g = "456"
> assert s.startswith(g)
E assert <built-in method startswith of str object at 0x143f288>('456')
E + where <built-in method startswith of str object at 0x143f288> = '123'.startswith
E assert <built-in method startswith of str object at 0x2aec3e501420>('456')
E + where <built-in method startswith of str object at 0x2aec3e501420> = '123'.startswith
failure_demo.py:188: AssertionError
__________________ TestMoreErrors.test_startswith_nested ___________________
self = <failure_demo.TestMoreErrors instance at 0x145fb00>
self = <failure_demo.TestMoreErrors object at 0x2aec3e4f1b00>
def test_startswith_nested(self):
def f():
@@ -495,15 +490,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 0x143f288>('456')
E + where <built-in method startswith of str object at 0x143f288> = '123'.startswith
E + where '123' = <function f at 0x13abaa0>()
E + and '456' = <function g at 0x13ab578>()
E assert <built-in method startswith of str object at 0x2aec3e501420>('456')
E + where <built-in method startswith of str object at 0x2aec3e501420> = '123'.startswith
E + where '123' = <function TestMoreErrors.test_startswith_nested.<locals>.f at 0x2aec3e5572f0>()
E + and '456' = <function TestMoreErrors.test_startswith_nested.<locals>.g at 0x2aec3e557268>()
failure_demo.py:195: AssertionError
_____________________ TestMoreErrors.test_global_func ______________________
self = <failure_demo.TestMoreErrors instance at 0x139cd40>
self = <failure_demo.TestMoreErrors object at 0x2aec3e495438>
def test_global_func(self):
> assert isinstance(globf(42), float)
@@ -513,18 +508,18 @@ get on the terminal - we are working on that):
failure_demo.py:198: AssertionError
_______________________ TestMoreErrors.test_instance _______________________
self = <failure_demo.TestMoreErrors instance at 0x13593b0>
self = <failure_demo.TestMoreErrors object at 0x2aec3e567240>
def test_instance(self):
self.x = 6*7
> assert self.x != 42
E assert 42 != 42
E + where 42 = <failure_demo.TestMoreErrors instance at 0x13593b0>.x
E + where 42 = <failure_demo.TestMoreErrors object at 0x2aec3e567240>.x
failure_demo.py:202: AssertionError
_______________________ TestMoreErrors.test_compare ________________________
self = <failure_demo.TestMoreErrors instance at 0x1465d40>
self = <failure_demo.TestMoreErrors object at 0x2aec3e502cc0>
def test_compare(self):
> assert globf(10) < 5
@@ -534,7 +529,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 0x1456ea8>
self = <failure_demo.TestMoreErrors object at 0x2aec3e5197f0>
def test_try_finally(self):
x = 1
@@ -543,4 +538,4 @@ get on the terminal - we are working on that):
E assert 1 == 0
failure_demo.py:210: AssertionError
======================== 39 failed in 0.21 seconds =========================
======================== 39 failed in 0.22 seconds =========================

View File

@@ -53,8 +53,9 @@ Let's run this without supplying our new option::
E assert 0
test_sample.py:6: AssertionError
----------------------------- Captured stdout ------------------------------
--------------------------- Captured stdout call ---------------------------
first
1 failed in 0.01 seconds
And now with supplying a command line option::
@@ -74,8 +75,9 @@ And now with supplying a command line option::
E assert 0
test_sample.py:6: AssertionError
----------------------------- Captured stdout ------------------------------
--------------------------- Captured stdout call ---------------------------
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.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.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.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1
collected 2 items
test_module.py .s
========================= short test summary info ==========================
SKIP [1] /tmp/doc-exec-278/conftest.py:9: need --runslow option to run
SKIP [1] /tmp/doc-exec-243/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.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1
collected 2 items
test_module.py ..
@@ -191,7 +193,7 @@ Example::
def test_something():
checkconfig(42)
The ``__tracebackhide__`` setting influences py.test showing
The ``__tracebackhide__`` setting influences ``pytest`` showing
of tracebacks: the ``checkconfig`` function will not be shown
unless the ``--fulltrace`` command line option is specified.
Let's run our little function::
@@ -200,14 +202,15 @@ Let's run our little function::
F
================================= FAILURES =================================
______________________________ test_something ______________________________
def test_something():
> checkconfig(42)
E Failed: not configured: 42
test_checkconfig.py:8: Failed
Detect if running from within a py.test run
test_checkconfig.py:8: Failed
1 failed in 0.01 seconds
Detect if running from within a pytest run
--------------------------------------------------------------
.. regendoc:wipe
@@ -242,10 +245,10 @@ Adding info to test report header
.. regendoc:wipe
It's easy to present extra information in a py.test run::
It's easy to present extra information in a ``pytest`` run::
# content of conftest.py
def pytest_report_header(config):
return "project deps: mylib-1.1"
@@ -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.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.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.5 -- /home/hpk/p/pytest/.tox/regen/bin/python
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1 -- /home/hpk/p/pytest/.tox/regen/bin/python3.4
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.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.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.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.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 setup test_some_are_slow.py::test_funcfast
0.00s setup test_some_are_slow.py::test_funcslow2
========================= 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.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.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 0x282b8c0>
self = <test_step.TestUserHandling object at 0x2b2ef2a4feb8>
def test_modification(self):
> assert 0
@@ -445,12 +448,12 @@ the ``db`` fixture::
# content of b/test_error.py
def test_root(db): # no db here, will error out
pass
We can run this::
$ py.test
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.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-278/b/test_error.py, line 1
file /tmp/doc-exec-243/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: tmpdir, monkeypatch, capsys, capfd, pytestconfig, recwarn
use 'py.test --fixtures [testpath]' for help on them.
/tmp/doc-exec-278/b/test_error.py:1
/tmp/doc-exec-243/b/test_error.py:1
================================= FAILURES =================================
____________________ TestUserHandling.test_modification ____________________
self = <test_step.TestUserHandling instance at 0x26145f0>
self = <test_step.TestUserHandling object at 0x2b63a7aec710>
def test_modification(self):
> assert 0
@@ -479,20 +482,20 @@ We can run this::
test_step.py:9: AssertionError
_________________________________ test_a1 __________________________________
db = <conftest.DB instance at 0x26211b8>
db = <conftest.DB object at 0x2b63a7b04470>
def test_a1(db):
> assert 0, db # to show value
E AssertionError: <conftest.DB instance at 0x26211b8>
E AssertionError: <conftest.DB object at 0x2b63a7b04470>
a/test_db.py:2: AssertionError
_________________________________ test_a2 __________________________________
db = <conftest.DB instance at 0x26211b8>
db = <conftest.DB object at 0x2b63a7b04470>
def test_a2(db):
> assert 0, db # to show value
E AssertionError: <conftest.DB instance at 0x26211b8>
E AssertionError: <conftest.DB object at 0x2b63a7b04470>
a/test_db2.py:2: AssertionError
========== 3 failed, 2 passed, 1 xfailed, 1 error in 0.03 seconds ==========
@@ -509,7 +512,7 @@ post-process test reports / failures
---------------------------------------
If you want to postprocess test reports and need access to the executing
environment you can implement a hook that gets called when the test
environment you can implement a hook that gets called when the test
"report" object is about to be created. Here we write out all failing
test calls and also access a fixture (if it was used by the test) in
case you want to query/look at it during your post processing. In our
@@ -526,7 +529,7 @@ case we just write some informations out to a ``failures`` file::
rep = __multicall__.execute()
# we only look at actual failing test calls, not setup/teardown
if rep.when == "call" and rep.failed:
if rep.when == "call" and rep.failed:
mode = "a" if os.path.exists("failures") else "w"
with open("failures", mode) as f:
# let's also access a fixture for the fun of it
@@ -534,7 +537,7 @@ case we just write some informations out to a ``failures`` file::
extra = " (%s)" % item.funcargs["tmpdir"]
else:
extra = ""
f.write(rep.nodeid + extra + "\n")
return rep
@@ -542,15 +545,15 @@ if you then have failing tests::
# content of test_module.py
def test_fail1(tmpdir):
assert 0
assert 0
def test_fail2():
assert 0
assert 0
and run them::
$ py.test test_module.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1
collected 2 items
test_module.py FF
@@ -558,7 +561,7 @@ and run them::
================================= FAILURES =================================
________________________________ test_fail1 ________________________________
tmpdir = local('/tmp/pytest-326/test_fail10')
tmpdir = local('/tmp/pytest-509/test_fail10')
def test_fail1(tmpdir):
> assert 0
@@ -577,7 +580,7 @@ and run them::
you will have a "failures" file which contains the failing test ids::
$ cat failures
test_module.py::test_fail1 (/tmp/pytest-326/test_fail10)
test_module.py::test_fail1 (/tmp/pytest-509/test_fail10)
test_module.py::test_fail2
Making test result information available in fixtures
@@ -620,61 +623,99 @@ here is a little example implemented via a local plugin::
if you then have failing tests::
# content of test_module.py
import pytest
@pytest.fixture
def other():
assert 0
def test_setup_fails(something, other):
pass
def test_call_fails(something):
assert 0
assert 0
def test_fail2():
assert 0
assert 0
and run it::
$ py.test -s test_module.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.5
collected 3 items
Traceback (most recent call last):
File "/home/hpk/p/pytest/.tox/regen/lib/python3.4/site-packages/_pytest/config.py", line 513, in getconftestmodules
return self._path2confmods[path]
KeyError: local('/tmp/doc-exec-243/test_module.py')
test_module.py EFF
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/hpk/p/pytest/.tox/regen/lib/python3.4/site-packages/_pytest/config.py", line 537, in importconftest
return self._conftestpath2mod[conftestpath]
KeyError: local('/tmp/doc-exec-243/conftest.py')
================================== ERRORS ==================================
____________________ ERROR at setup of test_setup_fails ____________________
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/hpk/p/pytest/.tox/regen/lib/python3.4/site-packages/_pytest/config.py", line 543, in importconftest
mod = conftestpath.pyimport()
File "/home/hpk/p/pytest/.tox/regen/lib/python3.4/site-packages/py/_path/local.py", line 620, in pyimport
__import__(modname)
File "/tmp/doc-exec-243/conftest.py", line 22
print "setting up a test failed!", request.node.nodeid
^
SyntaxError: invalid syntax
ERROR: could not load /tmp/doc-exec-243/conftest.py
@pytest.fixture
def other():
> assert 0
E assert 0
test_module.py:6: AssertionError
================================= FAILURES =================================
_____________________________ test_call_fails ______________________________
something = None
def test_call_fails(something):
> assert 0
E assert 0
test_module.py:12: AssertionError
________________________________ test_fail2 ________________________________
def test_fail2():
> assert 0
E assert 0
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.
Integrating pytest runner and cx_freeze
-----------------------------------------------------------
If you freeze your application using a tool like
`cx_freeze <http://cx-freeze.readthedocs.org>`_ in order to distribute it
to your end-users, it is a good idea to also package your test runner and run
your tests using the frozen application.
This way packaging errors such as dependencies not being
included into the executable can be detected early while also allowing you to
send test files to users so they can run them in their machines, which can be
invaluable to obtain more information about a hard to reproduce bug.
Unfortunately ``cx_freeze`` can't discover them
automatically because of ``pytest``'s use of dynamic module loading, so you
must declare them explicitly by using ``pytest.freeze_includes()``::
# contents of setup.py
from cx_Freeze import setup, Executable
import pytest
setup(
name="app_main",
executables=[Executable("app_main.py")],
options={"build_exe":
{
'includes': pytest.freeze_includes()}
},
# ... other options
)
If you don't want to ship a different executable just in order to run your tests,
you can make your program check for a certain flag and pass control
over to ``pytest`` instead. For example::
# contents of app_main.py
import sys
if len(sys.argv) > 1 and sys.argv[1] == '--pytest':
import pytest
sys.exit(pytest.main(sys.argv[2:]))
else:
# normal application execution: at this point argv can be parsed
# by your argument-parsing library of choice as usual
...
This makes it convenient to execute your tests from within your frozen
application, using standard ``py.test`` command-line options::
$ ./app_main --pytest --verbose --tb=long --junit-xml=results.xml test-suite/

View File

@@ -1,5 +1,4 @@
A sesssion-fixture which can look at all collected tests
A session-fixture which can look at all collected tests
----------------------------------------------------------------
A session-scoped fixture effectively has access to all
@@ -61,12 +60,26 @@ will be called ahead of running any tests::
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
Traceback (most recent call last):
File "/home/hpk/p/pytest/.tox/regen/lib/python3.4/site-packages/_pytest/config.py", line 513, in getconftestmodules
return self._path2confmods[path]
KeyError: local('/tmp/doc-exec-244/test_module.py')
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/hpk/p/pytest/.tox/regen/lib/python3.4/site-packages/_pytest/config.py", line 537, in importconftest
return self._conftestpath2mod[conftestpath]
KeyError: local('/tmp/doc-exec-244/conftest.py')
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/hpk/p/pytest/.tox/regen/lib/python3.4/site-packages/_pytest/config.py", line 543, in importconftest
mod = conftestpath.pyimport()
File "/home/hpk/p/pytest/.tox/regen/lib/python3.4/site-packages/py/_path/local.py", line 620, in pyimport
__import__(modname)
File "/tmp/doc-exec-244/conftest.py", line 6
print "callattr_ahead_of_alltests called"
^
SyntaxError: invalid syntax
ERROR: could not load /tmp/doc-exec-244/conftest.py

View File

@@ -23,3 +23,8 @@ def test_hello5():
def test_hello6():
pytest.xfail("reason")
@xfail(raises=IndexError)
def test_hello7():
x = []
x[1] = 1

View File

@@ -3,37 +3,38 @@ Some Issues and Questions
.. note::
If you don't find an answer here, you may checkout
`pytest Q&A at Stackoverflow <http://stackoverflow.com/search?q=pytest>`_
or other :ref:`contact channels` to get help.
This FAQ is here only mostly for historic reasons. Checkout
`pytest Q&A at Stackoverflow <http://stackoverflow.com/search?q=pytest>`_
for many questions and answers related to pytest and/or use
:ref:`contact channels` to get help.
On naming, nosetests, licensing and magic
------------------------------------------------
How does py.test relate to nose and unittest?
How does pytest relate to nose and unittest?
+++++++++++++++++++++++++++++++++++++++++++++++++
py.test and nose_ share basic philosophy when it comes
``pytest`` and nose_ share basic philosophy when it comes
to running and writing Python tests. In fact, you can run many tests
written for nose with py.test. nose_ was originally created
as a clone of ``py.test`` when py.test was in the ``0.8`` release
written for nose with ``pytest``. nose_ was originally created
as a clone of ``pytest`` when ``pytest`` was in the ``0.8`` release
cycle. Note that starting with pytest-2.0 support for running unittest
test suites is majorly improved.
how does py.test relate to twisted's trial?
how does pytest relate to twisted's trial?
++++++++++++++++++++++++++++++++++++++++++++++
Since some time py.test has builtin support for supporting tests
Since some time ``pytest`` has builtin support for supporting tests
written using trial. It does not itself start a reactor, however,
and does not handle Deferreds returned from a test in pytest style.
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
return deferreds from pytest-style tests, allowing to use
:ref:`fixtures` and other features.
how does py.test work with Django?
how does pytest work with Django?
++++++++++++++++++++++++++++++++++++++++++++++
In 2012, some work is going into the `pytest-django plugin <http://pypi.python.org/pypi/pytest-django>`_. It substitutes the usage of Django's
@@ -43,36 +44,36 @@ are not available from Django directly.
.. _features: features.html
What's this "magic" with py.test? (historic notes)
What's this "magic" with pytest? (historic notes)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Around 2007 (version ``0.8``) some people thought that py.test
Around 2007 (version ``0.8``) some people thought that ``pytest``
was using too much "magic". It had been part of the `pylib`_ which
contains a lot of unreleated python library code. Around 2010 there
was a major cleanup refactoring, which removed unused or deprecated code
was a major cleanup refactoring, which removed unused or deprecated code
and resulted in the new ``pytest`` PyPI package which strictly contains
only test-related code. This relese also brought a complete pluginification
only test-related code. This release also brought a complete pluginification
such that the core is around 300 lines of code and everything else is
implemented in plugins. Thus ``pytest`` today is a small, universally runnable
implemented in plugins. Thus ``pytest`` today is a small, universally runnable
and customizable testing framework for Python. Note, however, that
``pytest`` uses metaprogramming techniques and reading its source is
``pytest`` uses metaprogramming techniques and reading its source is
thus likely not something for Python beginners.
A second "magic" issue was the assert statement debugging feature.
Nowadays, py.test explicitely rewrites assert statements in test modules
A second "magic" issue was the assert statement debugging feature.
Nowadays, ``pytest`` explicitely rewrites assert statements in test modules
in order to provide more useful :ref:`assert feedback <assertfeedback>`.
This completely avoids previous issues of confusing assertion-reporting.
This completely avoids previous issues of confusing assertion-reporting.
It also means, that you can use Python's ``-O`` optimization without loosing
assertions in test modules.
py.test contains a second mostly obsolete assert debugging technique,
``pytest`` contains a second, mostly obsolete, assert debugging technique,
invoked via ``--assert=reinterpret``, activated by default on
Python-2.5: When an ``assert`` statement fails, py.test re-interprets
Python-2.5: When an ``assert`` statement fails, ``pytest`` re-interprets
the expression part to show intermediate values. This technique suffers
from a caveat that the rewriting does not: If your expression has side
effects (better to avoid them anyway!) the intermediate values may not
be the same, confusing the reinterpreter and obfuscating the initial
error (this is also explained at the command line if it happens).
error (this is also explained at the command line if it happens).
You can also turn off all assertion interaction using the
``--assertmode=off`` option.
@@ -84,7 +85,7 @@ You can also turn off all assertion interaction using the
Why a ``py.test`` instead of a ``pytest`` command?
++++++++++++++++++++++++++++++++++++++++++++++++++
Some of the reasons are historic, others are practical. ``py.test``
Some of the reasons are historic, others are practical. ``pytest``
used to be part of the ``py`` package which provided several developer
utilities, all starting with ``py.<TAB>``, thus providing nice
TAB-completion. If
@@ -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,30 +126,30 @@ 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.
You can also use the `pytest_generate_tests`_ hook to
You can also use the `pytest_generate_tests`_ hook to
implement the `parametrization scheme of your choice`_.
.. _`pytest_generate_tests`: test/funcargs.html#parametrizing-tests
.. _`parametrization scheme of your choice`: http://tetamap.wordpress.com/2009/05/13/parametrizing-python-tests-generalized/
py.test interaction with other packages
pytest interaction with other packages
---------------------------------------------------
Issues with py.test, multiprocess and setuptools?
Issues with pytest, multiprocess and setuptools?
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
On windows the multiprocess package will instantiate sub processes

View File

@@ -15,7 +15,7 @@ pytest fixtures: explicit, modular, scalable
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
offer dramatic improvements over the classic xUnit style of setup/teardown
functions:
* fixtures have explicit names and are activated by declaring their use
@@ -34,6 +34,12 @@ 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`:
@@ -44,7 +50,7 @@ Fixtures as Function arguments
-----------------------------------------
Test functions can receive fixture objects by naming them as an input
argument. For each argument name, a fixture function with that name provides
argument. For each argument name, a fixture function with that name provides
the fixture object. Fixture functions are registered by marking them with
:py:func:`@pytest.fixture <_pytest.python.fixture>`. Let's look at a simple
self-contained test module containing a fixture and a test function
@@ -64,14 +70,13 @@ using it::
assert "merlinux" in msg
assert 0 # for demo purposes
Here, the ``test_ehlo`` needs the ``smtp`` fixture value. pytest
will discover and call the :py:func:`@pytest.fixture <_pytest.python.fixture>`
Here, the ``test_ehlo`` needs the ``smtp`` fixture value. pytest
will discover and call the :py:func:`@pytest.fixture <_pytest.python.fixture>`
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.4.0.dev12
plugins: xdist, pep8, cov, cache, capturelog, instafail
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.2.dev1
collected 1 items
test_smtpsimple.py F
@@ -79,22 +84,21 @@ marked ``smtp`` fixture function. Running the test looks like this::
================================= FAILURES =================================
________________________________ test_ehlo _________________________________
smtp = <smtplib.SMTP instance at 0x22530e0>
smtp = <smtplib.SMTP object at 0x2ade77b37e48>
def test_ehlo(smtp):
response, msg = smtp.ehlo()
assert response == 250
assert "merlinux" in msg
> assert 0 # for demo purposes
E assert 0
> assert "merlinux" in msg
E TypeError: Type str doesn't support the buffer API
test_smtpsimple.py:12: AssertionError
========================= 1 failed in 0.17 seconds =========================
test_smtpsimple.py:11: TypeError
========================= 1 failed in 0.18 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
function. The test function fails on our deliberate ``assert 0``. Here is
an exact protocol of how py.test comes to call the test function this way:
the exact protocol used by ``pytest`` to call the test function this way:
1. pytest :ref:`finds <test discovery>` the ``test_ehlo`` because
of the ``test_`` prefix. The test function needs a function argument
@@ -120,7 +124,7 @@ with a list of available function arguments.
In versions prior to 2.3 there was no ``@pytest.fixture`` marker
and you had to use a magic ``pytest_funcarg__NAME`` prefix
for the fixture factory. This remains and will remain supported
for the fixture factory. This remains and will remain supported
but is not anymore advertised as the primary means of declaring fixture
functions.
@@ -148,15 +152,15 @@ Sharing a fixture across tests in a module (or class/session)
.. regendoc:wipe
Fixtures requiring network access depend on connectivity and are
Fixtures requiring network access depend on connectivity and are
usually time-expensive to create. Extending the previous example, we
can add a ``scope='module'`` parameter to the
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
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 puts
the fixture function into a separate ``conftest.py`` file so
that tests from multiple test modules in the directory can
that tests from multiple test modules in the directory can
access the fixture function::
# content of conftest.py
@@ -175,7 +179,7 @@ function (in or below the directory where ``conftest.py`` is located)::
def test_ehlo(smtp):
response = smtp.ehlo()
assert response[0] == 250
assert response[0] == 250
assert "merlinux" in response[1]
assert 0 # for demo purposes
@@ -189,8 +193,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.4.0.dev12
plugins: xdist, pep8, cov, cache, capturelog, instafail
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.2.dev1
collected 2 items
test_module.py FF
@@ -198,19 +201,18 @@ inspect what is going on and can now run the tests::
================================= FAILURES =================================
________________________________ test_ehlo _________________________________
smtp = <smtplib.SMTP instance at 0x165fa28>
smtp = <smtplib.SMTP object at 0x2b4b07e38e48>
def test_ehlo(smtp):
response = smtp.ehlo()
assert response[0] == 250
assert "merlinux" in response[1]
> assert 0 # for demo purposes
E assert 0
> assert "merlinux" in response[1]
E TypeError: Type str doesn't support the buffer API
test_module.py:6: AssertionError
test_module.py:5: TypeError
________________________________ test_noop _________________________________
smtp = <smtplib.SMTP instance at 0x165fa28>
smtp = <smtplib.SMTP object at 0x2b4b07e38e48>
def test_noop(smtp):
response = smtp.noop()
@@ -221,26 +223,26 @@ inspect what is going on and can now run the tests::
test_module.py:11: AssertionError
========================= 2 failed in 0.18 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
test functions because pytest shows the incoming argument values in the
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
test functions because pytest shows the incoming argument values in the
traceback. As a result, the two test functions using ``smtp`` run as
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
# the returned fixture value will be shared for
# all tests needing it
.. _`finalization`:
fixture finalization / executing teardown code
fixture finalization / executing teardown code
-------------------------------------------------------------
pytest supports execution of fixture specific finalization code
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::
@@ -265,19 +267,17 @@ the fixture in the module has finished execution.
Let's execute it::
$ py.test -s -q --tb=no
FF
2 failed in 0.20 seconds
FFteardown smtp
2 failed in 0.16 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
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.
Note that pytest-2.4 introduced an experimental alternative
:ref:`yield fixture mechanism <yieldfixture>` for easier context manager
integration and more linear writing of teardown code.
.. _`request-context`:
@@ -286,7 +286,7 @@ 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
Further extending the previous ``smtp`` fixture example, let's
read an optional server URL from the test module which uses our fixture::
# content of conftest.py
@@ -297,12 +297,12 @@ read an optional server URL from the test module which uses our fixture::
def smtp(request):
server = getattr(request.module, "smtpserver", "merlinux.eu")
smtp = smtplib.SMTP(server)
def fin():
print ("finalizing %s (%s)" % (smtp, server))
smtp.close()
return smtp
return smtp
We use the ``request.module`` attribute to optionally obtain an
``smtpserver`` attribute from the test module. If we just execute
@@ -310,13 +310,13 @@ again, nothing much has changed::
$ py.test -s -q --tb=no
FF
2 failed in 0.18 seconds
2 failed in 0.17 seconds
Let's quickly create another test module that actually sets the
server URL in its module namespace::
# content of test_anothersmtp.py
smtpserver = "mail.python.org" # will be read by smtp fixture
def test_showhelo(smtp):
@@ -329,8 +329,8 @@ Running it::
================================= FAILURES =================================
______________________________ test_showhelo _______________________________
test_anothersmtp.py:5: in test_showhelo
> assert 0, smtp.helo()
E AssertionError: (250, 'mail.python.org')
assert 0, smtp.helo()
E AssertionError: (250, b'mail.python.org')
voila! The ``smtp`` fixture function picked up our mail server name
from the module namespace.
@@ -344,7 +344,7 @@ Fixture functions can be parametrized in which case they will be called
multiple times, each time executing the set of dependent tests, i. e. the
tests that depend on this fixture. Test functions do usually not need
to be aware of their re-running. Fixture parametrization helps to
write exhaustive functional tests for components which themselves can be
write exhaustive functional tests for components which themselves can be
configured in multiple ways.
Extending the previous example, we can flag the fixture to create two
@@ -356,7 +356,7 @@ through the special :py:class:`request <FixtureRequest>` object::
import pytest
import smtplib
@pytest.fixture(scope="module",
@pytest.fixture(scope="module",
params=["merlinux.eu", "mail.python.org"])
def smtp(request):
smtp = smtplib.SMTP(request.param)
@@ -366,10 +366,10 @@ through the special :py:class:`request <FixtureRequest>` object::
request.addfinalizer(fin)
return smtp
The main change is the declaration of ``params`` with
The main change is the declaration of ``params`` with
:py:func:`@pytest.fixture <_pytest.python.fixture>`, a list of values
for each of which the fixture function will execute and can access
a value via ``request.param``. No test function code needs to change.
a value via ``request.param``. No test function code needs to change.
So let's just do another run::
$ py.test -q test_module.py
@@ -377,19 +377,18 @@ So let's just do another run::
================================= FAILURES =================================
__________________________ test_ehlo[merlinux.eu] __________________________
smtp = <smtplib.SMTP instance at 0x28d13b0>
smtp = <smtplib.SMTP object at 0x2b824acf3e80>
def test_ehlo(smtp):
response = smtp.ehlo()
assert response[0] == 250
assert "merlinux" in response[1]
> assert 0 # for demo purposes
E assert 0
> assert "merlinux" in response[1]
E TypeError: Type str doesn't support the buffer API
test_module.py:6: AssertionError
test_module.py:5: TypeError
__________________________ test_noop[merlinux.eu] __________________________
smtp = <smtplib.SMTP instance at 0x28d13b0>
smtp = <smtplib.SMTP object at 0x2b824acf3e80>
def test_noop(smtp):
response = smtp.noop()
@@ -400,18 +399,20 @@ So let's just do another run::
test_module.py:11: AssertionError
________________________ test_ehlo[mail.python.org] ________________________
smtp = <smtplib.SMTP instance at 0x28d8440>
smtp = <smtplib.SMTP object at 0x2b824b19fb38>
def test_ehlo(smtp):
response = smtp.ehlo()
assert response[0] == 250
> assert "merlinux" in response[1]
E assert 'merlinux' in 'mail.python.org\nSIZE 25600000\nETRN\nSTARTTLS\nENHANCEDSTATUSCODES\n8BITMIME\nDSN'
E TypeError: Type str doesn't support the buffer API
test_module.py:5: AssertionError
test_module.py:5: TypeError
-------------------------- Captured stdout setup ---------------------------
finalizing <smtplib.SMTP object at 0x2b824acf3e80>
________________________ test_noop[mail.python.org] ________________________
smtp = <smtplib.SMTP instance at 0x28d8440>
smtp = <smtplib.SMTP object at 0x2b824b19fb38>
def test_noop(smtp):
response = smtp.noop()
@@ -420,11 +421,11 @@ So let's just do another run::
E assert 0
test_module.py:11: AssertionError
4 failed in 6.47 seconds
4 failed in 6.37 seconds
We see that our two test functions each ran twice, against the different
``smtp`` instances. Note also, that with the ``mail.python.org``
connection the second test fails in ``test_ehlo`` because a
``smtp`` instances. Note also, that with the ``mail.python.org``
connection the second test fails in ``test_ehlo`` because a
different server string is expected than what arrived.
@@ -441,7 +442,7 @@ and instantiate an object ``app`` where we stick the already defined
``smtp`` resource into it::
# content of test_appsetup.py
import pytest
class App:
@@ -460,24 +461,22 @@ 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.4.0.dev12 -- /home/hpk/venv/0/bin/python
cachedir: /tmp/doc-exec-127/.cache
plugins: xdist, pep8, cov, cache, capturelog, instafail
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.2.dev1 -- /home/hpk/p/pytest/.tox/regen/bin/python3.4
collecting ... collected 2 items
test_appsetup.py:12: test_smtp_exists[mail.python.org] PASSED
test_appsetup.py:12: test_smtp_exists[merlinux.eu] PASSED
test_appsetup.py::test_smtp_exists[merlinux.eu] PASSED
test_appsetup.py::test_smtp_exists[mail.python.org] PASSED
========================= 2 passed in 6.07 seconds =========================
========================= 2 passed in 6.11 seconds =========================
Due to the parametrization of ``smtp`` the test will run twice with two
different ``App`` instances and respective smtp servers. There is no
need for the ``app`` fixture to be aware of the ``smtp`` parametrization
as pytest will fully analyse the fixture dependency graph.
need for the ``app`` fixture to be aware of the ``smtp`` parametrization
as pytest will fully analyse the fixture dependency graph.
Note, that the ``app`` fixture has a scope of ``module`` and uses a
module-scoped ``smtp`` fixture. The example would still work if ``smtp``
was cached on a ``session`` scope: it is fine for fixtures to use
was cached on a ``session`` scope: it is fine for fixtures to use
"broader" scoped fixtures but not the other way round:
A session-scoped fixture could not use a module-scoped one in a
meaningful way.
@@ -492,11 +491,11 @@ Automatic grouping of tests by fixture instances
pytest minimizes the number of active fixtures during test runs.
If you have a parametrized fixture, then all the tests using it will
first execute with one instance and then finalizers are called
first execute with one instance and then finalizers are called
before the next fixture instance is created. Among other things,
this eases testing of applications which create and use global state.
The following example uses two parametrized funcargs, one of which is
The following example uses two parametrized funcargs, one of which is
scoped on a per-module basis, and all the functions perform ``print`` calls
to show the setup/teardown flow::
@@ -506,7 +505,7 @@ to show the setup/teardown flow::
@pytest.fixture(scope="module", params=["mod1", "mod2"])
def modarg(request):
param = request.param
print "create", param
print ("create", param)
def fin():
print ("fin %s" % param)
return param
@@ -516,44 +515,42 @@ to show the setup/teardown flow::
return request.param
def test_0(otherarg):
print " test0", otherarg
print (" test0", otherarg)
def test_1(modarg):
print " test1", modarg
print (" test1", modarg)
def test_2(otherarg, modarg):
print " test2", otherarg, modarg
print (" test2", otherarg, modarg)
Let's run the tests in verbose mode and with looking at the print-output::
$ py.test -v -s test_module.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.4.0.dev12 -- /home/hpk/venv/0/bin/python
cachedir: /tmp/doc-exec-127/.cache
plugins: xdist, pep8, cov, cache, capturelog, instafail
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.2.dev1 -- /home/hpk/p/pytest/.tox/regen/bin/python3.4
collecting ... collected 8 items
test_module.py:15: test_0[1] test0 1
test_module.py::test_0[1] test0 1
PASSED
test_module.py:15: test_0[2] test0 2
test_module.py::test_0[2] test0 2
PASSED
test_module.py:17: test_1[mod1] create mod1
test_module.py::test_1[mod1] create mod1
test1 mod1
PASSED
test_module.py:19: test_2[1-mod1] test2 1 mod1
test_module.py::test_2[1-mod1] test2 1 mod1
PASSED
test_module.py:19: test_2[2-mod1] test2 2 mod1
test_module.py::test_2[2-mod1] test2 2 mod1
PASSED
test_module.py:17: test_1[mod2] create mod2
test_module.py::test_1[mod2] create mod2
test1 mod2
PASSED
test_module.py:19: test_2[1-mod2] test2 1 mod2
test_module.py::test_2[1-mod2] test2 1 mod2
PASSED
test_module.py:19: test_2[2-mod2] test2 2 mod2
test_module.py::test_2[2-mod2] test2 2 mod2
PASSED
========================= 8 passed in 0.02 seconds =========================
========================= 8 passed in 0.01 seconds =========================
You can see that the parametrized module-scoped ``modarg`` resource caused
an ordering of test execution that lead to the fewest possible "active" resources. The finalizer for the ``mod1`` parametrized resource was executed
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.
@@ -573,7 +570,7 @@ achieve it. We separate the creation of the fixture into a conftest.py
file::
# content of conftest.py
import pytest
import tempfile
import os
@@ -606,18 +603,18 @@ to verify our fixture is activated and the tests pass::
$ py.test -q
..
2 passed in 0.02 seconds
2 passed in 0.01 seconds
You can specify multiple fixtures like this::
@pytest.mark.usefixtures("cleandir", "anotherfixture")
and you may specify fixture usage at the test module level, using
and you may specify fixture usage at the test module level, using
a generic feature of the mark mechanism::
pytestmark = pytest.mark.usefixtures("cleandir")
Lastly you can put fixtures required by all tests in your project
Lastly you can put fixtures required by all tests in your project
into an ini-file::
# content of pytest.ini
@@ -626,6 +623,7 @@ into an ini-file::
usefixtures = cleandir
.. _`autouse`:
.. _`autouse fixtures`:
autouse fixtures (xUnit setup on steroids)
@@ -634,14 +632,14 @@ autouse fixtures (xUnit setup on steroids)
.. regendoc:wipe
Occasionally, you may want to have fixtures get invoked automatically
without a `usefixtures`_ or `funcargs`_ reference. As a practical
without a `usefixtures`_ or `funcargs`_ reference. As a practical
example, suppose we have a database fixture which has a
begin/rollback/commit architecture and we want to automatically surround
each test method by a transaction and a rollback. Here is a dummy
self-contained implementation of this idea::
# content of test_db_transact.py
import pytest
class DB:
@@ -677,15 +675,15 @@ If we run it, we get two passing tests::
$ py.test -q
..
2 passed in 0.02 seconds
2 passed in 0.01 seconds
Here is how autouse fixtures work in other scopes:
- if an autouse fixture is defined in a test module, all its test
functions automatically use it.
- if an autouse fixture is defined in a test module, all its test
functions automatically use it.
- if an autouse fixture is defined in a conftest.py file then all tests in
all test modules belows its directory will invoke the fixture.
- if an autouse fixture is defined in a conftest.py file then all tests in
all test modules belows its directory will invoke the fixture.
- lastly, and **please use that with care**: if you define an autouse
fixture in a plugin, it will be invoked for all tests in all projects
@@ -696,7 +694,7 @@ Here is how autouse fixtures work in other scopes:
Note that the above ``transact`` fixture may very well be a fixture that
you want to make available in your project without having it generally
active. The canonical way to do that is to put the transact definition
active. The canonical way to do that is to put the transact definition
into a conftest.py file **without** using ``autouse``::
# content of conftest.py
@@ -713,7 +711,7 @@ and then e.g. have a TestClass using it by declaring the need::
...
All test methods in this TestClass will use the transaction fixture while
other test classes or functions in the module will not use it unless
other test classes or functions in the module will not use it unless
they also add a ``transact`` reference.
Shifting (visibility of) fixture functions

View File

@@ -1,12 +1,16 @@
Installation and Getting Started
===================================
**Pythons**: Python 2.4-3.3, Jython, PyPy
**Pythons**: Python 2.6-3.4, Jython, PyPy-2.3
**Platforms**: Unix/Posix and Windows
**PyPI package name**: `pytest <http://pypi.python.org/pypi/pytest>`_
**dependencies**: `py <http://pypi.python.org/pypi/py>`_,
`colorama (Windows) <http://pypi.python.org/pypi/colorama>`_,
`argparse (py26) <http://pypi.python.org/pypi/argparse>`_.
**documentation as PDF**: `download latest <http://pytest.org/latest/pytest.pdf>`_
.. _`getstarted`:
@@ -23,7 +27,7 @@ Installation options::
To check your installation has installed the correct version::
$ py.test --version
This is py.test version 2.3.5, imported from /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/pytest.py
This is pytest version 2.6.1, imported from /home/hpk/p/pytest/.tox/regen/lib/python3.4/site-packages/pytest.py
If you get an error checkout :ref:`installation issues`.
@@ -45,23 +49,23 @@ That's it. You can execute the test function now::
$ py.test
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.5
platform linux -- Python 3.4.0 -- py-1.4.23 -- pytest-2.6.1
collected 1 items
test_sample.py F
================================= FAILURES =================================
_______________________________ test_answer ________________________________
def test_answer():
> assert func(3) == 5
E assert 4 == 5
E + where 4 = func(3)
test_sample.py:5: AssertionError
========================= 1 failed in 0.01 seconds =========================
py.test found the ``test_answer`` function by following :ref:`standard test discovery rules <test discovery>`, basically detecting the ``test_`` prefixes. We got a failure report because our little ``func(3)`` call did not return ``5``.
``pytest`` found the ``test_answer`` function by following :ref:`standard test discovery rules <test discovery>`, basically detecting the ``test_`` prefixes. We got a failure report because our little ``func(3)`` call did not return ``5``.
.. note::
@@ -93,6 +97,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`
@@ -121,15 +126,16 @@ run the module by passing its filename::
.F
================================= FAILURES =================================
____________________________ TestClass.test_two ____________________________
self = <test_class.TestClass instance at 0x315b488>
self = <test_class.TestClass object at 0x2ad4b005b710>
def test_two(self):
x = "hello"
> assert hasattr(x, 'check')
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
@@ -140,33 +146,31 @@ Going functional: requesting a unique temporary directory
For functional tests one often needs to create some files
and pass them to application objects. pytest provides
:ref:`builtinfixtures` which allow to request arbitrary
:ref:`builtinfixtures` which allow to request arbitrary
resources, for example a unique temporary directory::
# content of test_tmpdir.py
def test_needsfiles(tmpdir):
print tmpdir
print (tmpdir)
assert 0
We list the name ``tmpdir`` in the test function signature and
py.test will lookup and call a fixture factory to create the resource
``pytest`` will lookup and call a fixture factory to create the resource
before performing the test function call. Let's just run it::
$ py.test -q test_tmpdir.py
F
================================= FAILURES =================================
_____________________________ test_needsfiles ______________________________
tmpdir = local('/tmp/pytest-322/test_needsfiles0')
def test_needsfiles(tmpdir):
print tmpdir
> assert 0
E assert 0
test_tmpdir.py:3: AssertionError
----------------------------- Captured stdout ------------------------------
/tmp/pytest-322/test_needsfiles0
================================== ERRORS ==================================
_____________________ ERROR collecting test_tmpdir.py ______________________
/home/hpk/p/pytest/.tox/regen/lib/python3.4/site-packages/_pytest/python.py:463: in _importtestmodule
mod = self.fspath.pyimport(ensuresyspath=True)
/home/hpk/p/pytest/.tox/regen/lib/python3.4/site-packages/py/_path/local.py:620: in pyimport
__import__(modname)
E File "/tmp/doc-exec-187/test_tmpdir.py", line 2
E print tmpdir
E ^
E SyntaxError: invalid syntax
1 error in 0.03 seconds
Before the test runs, a unique-per-test-invocation temporary directory
was created. More info at :ref:`tmpdir handling`.
@@ -183,7 +187,7 @@ Here are a few suggestions where to go next:
* :ref:`cmdline` for command line invocation examples
* :ref:`good practises <goodpractises>` for virtualenv, test layout, genscript support
* :ref:`fixtures` for providing a functional baseline to your tests
* :ref:`apiref` for documentation and examples on using py.test
* :ref:`apiref` for documentation and examples on using ``pytest``
* :ref:`plugins` managing and writing plugins
.. _`installation issues`:
@@ -218,7 +222,7 @@ py.test not found on Windows despite installation?
- **Jython2.5.1 on Windows XP**: `Jython does not create command line launchers`_
so ``py.test`` will not work correctly. You may install py.test on
CPython and type ``py.test --genscript=mytest`` and then use
``jython mytest`` to run py.test for your tests to run with Jython.
``jython mytest`` to run your tests with Jython using ``pytest``.
:ref:`examples` for more complex examples

View File

@@ -1,4 +1,3 @@
.. highlightlang:: python
.. _`goodpractises`:
@@ -8,149 +7,51 @@ 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`: http://pypi.python.org/pypi/virtualenv
.. _`buildout`: http://www.buildout.org/
.. _pip: http://pypi.python.org/pypi/pip
virtualenv . # create a virtualenv directory in the current directory
Use tox and Continuous Integration servers
-------------------------------------------------
source bin/activate # on unix
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://testrun.org/tox/latest/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.
scripts/activate # on Windows
.. _standalone:
.. _`genscript method`:
We can now install pytest::
Create a py.test standalone script
-------------------------------------------
pip install pytest
If you are a maintainer or application developer and want others
to easily run tests you can generate a completely standalone "py.test"
script::
py.test --genscript=runtests.py
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
Integrating with distutils / ``python setup.py test``
--------------------------------------------------------
You can integrate test runs into your distutils or
setuptools based project. Use the `genscript method`_
to generate a standalone py.test script::
py.test --genscript=runtests.py
and make this script part of your distribution and then add
this to your ``setup.py`` file::
from distutils.core import setup, Command
# you can also import from setuptools
class PyTest(Command):
user_options = []
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
import sys,subprocess
errno = subprocess.call([sys.executable, 'runtests.py'])
raise SystemExit(errno)
setup(
#...,
cmdclass = {'test': PyTest},
#...,
)
If you now type::
python setup.py test
this will execute your tests using ``runtests.py``. As this is a
standalone version of ``py.test`` no prior installation whatsoever is
required for calling the test command. You can also pass additional
arguments to the subprocess-calls such as your test directory or other
options.
Integration with setuptools test commands
----------------------------------------------------
Setuptools supports writing our own Test command for invoking
pytest::
from setuptools.command.test import test as TestCommand
import sys
class PyTest(TestCommand):
def finalize_options(self):
TestCommand.finalize_options(self)
self.test_args = []
self.test_suite = True
def run_tests(self):
#import here, cause outside the eggs aren't loaded
import pytest
errno = pytest.main(self.test_args)
sys.exit(errno)
setup(
#...,
tests_require=['pytest'],
cmdclass = {'test': PyTest},
)
Now if you run::
python setup.py test
this will download py.test if needed and then run py.test
as you would expect it to.
.. _`test discovery`:
.. _`Python test discovery`:
Conventions for Python test discovery
-------------------------------------------------
``py.test`` implements the following standard test discovery:
* collection starts from the initial command line arguments
which may be directories, filenames or test ids.
* recurse into directories, unless they match :confval:`norecursedirs`
* ``test_*.py`` or ``*_test.py`` files, imported by their `package name`_.
* ``Test`` prefixed test classes (without an ``__init__`` method)
* ``test_`` prefixed test functions or methods are test items
For examples of how to customize your test discovery :doc:`example/pythoncollection`.
Within Python modules, py.test also discovers tests using the standard
:ref:`unittest.TestCase <unittest.TestCase>` subclassing technique.
Due to the ``activate`` step above the ``pip`` will come from
the virtualenv directory and install any package into the isolated
virtual environment.
Choosing a test layout / import rules
------------------------------------------
py.test supports common test layouts:
``pytest`` supports two common test layouts:
* inlining test directories into your application package, useful if you want to
keep (unit) tests and actually tested code close together::
* 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
@@ -159,21 +60,25 @@ py.test supports common test layouts:
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::
Important notes relating to both schemes:
mypkg/
__init__.py
appmodule.py
tests/
test_app.py
...
- **make sure that "mypkg" is importable**, for example by typing once::
In both cases you usually need to make sure that ``mypkg`` is importable,
for example by using the setuptools ``python setup.py develop`` method.
pip install -e . # install package using setup.py in editable mode
You can run your tests by pointing to it::
- **avoid "__init__.py" files in your test directories**.
This way your tests can run easily against an installed version
of ``mypkg``, independently from the installed package if it 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
@@ -181,18 +86,34 @@ You can run your tests by pointing to it::
py.test # run all tests below current dir
...
.. _`package name`:
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::
If py.test finds a "a/b/test_module.py" test file while
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 ``pytest`` 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``.
* 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.
@@ -208,4 +129,160 @@ You can run your tests by pointing to it::
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 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`:
Create a pytest standalone script
-------------------------------------------
If you are a maintainer or application developer and want people
who don't deal with python much to easily run tests you may generate
a standalone ``pytest`` script::
py.test --genscript=runtests.py
This generates a ``runtests.py`` script which is a fully functional basic
``pytest`` script, running unchanged under Python2 and Python3.
You can tell people to download the script and then e.g. run it like this::
python runtests.py
Integrating with distutils / ``python setup.py test``
--------------------------------------------------------
You can integrate test runs into your distutils or
setuptools based project. Use the `genscript method`_
to generate a standalone ``pytest`` script::
py.test --genscript=runtests.py
and make this script part of your distribution and then add
this to your ``setup.py`` file::
from distutils.core import setup, Command
# you can also import from setuptools
class PyTest(Command):
user_options = []
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
import sys,subprocess
errno = subprocess.call([sys.executable, 'runtests.py'])
raise SystemExit(errno)
setup(
#...,
cmdclass = {'test': PyTest},
#...,
)
If you now type::
python setup.py test
this will execute your tests using ``runtests.py``. As this is a
standalone version of ``pytest`` no prior installation whatsoever is
required for calling the test command. You can also pass additional
arguments to the subprocess-calls such as your test directory or other
options.
Integration with setuptools test commands
----------------------------------------------------
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::
import sys
from setuptools.command.test import test as TestCommand
class PyTest(TestCommand):
user_options = [('pytest-args=', 'a', "Arguments to pass to py.test")]
def initialize_options(self):
TestCommand.initialize_options(self)
self.pytest_args = None
def finalize_options(self):
TestCommand.finalize_options(self)
self.test_args = []
self.test_suite = True
def run_tests(self):
#import here, cause outside the eggs aren't loaded
import pytest
errno = pytest.main(self.pytest_args)
sys.exit(errno)
setup(
#...,
tests_require=['pytest'],
cmdclass = {'test': PyTest},
)
Now if you run::
python setup.py test
this will download ``pytest`` if needed and then run your tests
as you would expect it to. You can pass a single string of arguments
using the ``--pytest-args`` or ``-a`` command-line option. For example::
python setup.py test -a "--durations=5"
is equivalent to running ``py.test --durations=5``.
.. _`test discovery`:
.. _`Python test discovery`:
Conventions for Python test discovery
-------------------------------------------------
``pytest`` implements the following standard test discovery:
* collection starts from the initial command line arguments
which may be directories, filenames or test ids.
* recurse into directories, unless they match :confval:`norecursedirs`
* ``test_*.py`` or ``*_test.py`` files, imported by their `package name`_.
* ``Test`` prefixed test classes (without an ``__init__`` method)
* ``test_`` prefixed test functions or methods are test items
For examples of how to customize your test discovery :doc:`example/pythoncollection`.
Within Python modules, ``pytest`` also discovers tests using the standard
:ref:`unittest.TestCase <unittest.TestCase>` subclassing technique.
.. include:: links.inc

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

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