merge from default

--HG--
branch : multi-usageerror
This commit is contained in:
Ronny Pfannschmidt 2014-01-25 10:42:21 +01:00
commit 97da43d909
160 changed files with 7152 additions and 3027 deletions

1
.gitignore vendored
View File

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

View File

@ -60,3 +60,8 @@ 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

View File

@ -1,8 +1,9 @@
language: python
# command to install dependencies
install: "pip install 'virtualenv<1.10' -e . detox"
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"

13
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,6 @@ Brian Okken
Katarzyna Jachim
Christian Theunert
Anthon van der Neut
Mark Abramowitz
Piotr Banaszkiewicz
Jurko Gospodnetić

528
CHANGELOG
View File

@ -1,41 +1,344 @@
Changes between 2.3.5 and 2.4.DEV
UNRELEASED
-----------------------------------
- make "import pdb ; pdb.set_trace()" work natively wrt capturing (no "-s" needed
anymore), making ``pytest.set_trace()`` a mere shortcut.
- 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.
- 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 below).
- fixed docs and code to use "pytest" instead of "py.test" almost everywhere.
Thanks Jurko Gospodnetic for the complete PR.
- fix issue341: introduce new experimental hook for IDEs/terminals to
- 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
- 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:
- 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.
- new monkeypatch.replace() to avoid imports and provide a shorter
invocation for patching out classes/functions from modules:
monkeypatch.replace("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 also introduces a new method "node.addfinalizer()"
helper which can only be called during the setup phase of a node.
- fix issue355: junitxml puts name="pytest" attribute to testsuite tag.
- fix issue336: autouse fixture in plugins should work again.
- change to use 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 issue279: improve object comparisons on assertion failure
for standard datatypes and recognise collections.abc. Thanks to
Brianna Laugher and Mathieu Agopian.
@ -49,19 +352,9 @@ Changes between 2.3.5 and 2.4.DEV
- fix issue305: ignore any problems when writing pyc files.
- integrate tab-completion on options through use of "argcomplete".
Thanks Anthon van der Neut for the PR.
- 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.
- SO-17664702: call fixture finalizers even if the fixture function
partially failed (finalizers would not always be called before)
- color the last line red or green depending if failures/errors occured
or everything passed. thanks Christian Theunert.
- fix issue320 - fix class scope for fixtures when mixed with
module-level functions. Thanks Anatloy Bubenkoff.
@ -73,35 +366,16 @@ Changes between 2.3.5 and 2.4.DEV
- fix issue323 - sorting of many module-scoped arg parametrizations
- add support for setUpModule/tearDownModule detection, thanks Brian Okken.
- 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
- fix issue 308 - allow to mark/xfail/skip individual parameter sets
when parametrizing. Thanks Brianna Laugher.
- simplify parametrize() signature: allow to pass a CSV-separated string
to specify argnames. For example: ``pytest.mark.parametrize("input,expected", [(1,2), (2,3)])`` is possible now in addition to the prior
``pytest.mark.parametrize(("input", "expected"), ...)``.
- fix issue 306 - cleanup of -k/-m options to only match markers/test
names/keywords respectively. Thanks Wouter van Ackooy.
- (experimental) allow fixture functions to be
implemented as context managers. Thanks Andreas Pelme,
Vladimir Keleshev.
- (experimental) 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.
- 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.
@ -111,7 +385,7 @@ Changes between 2.3.5 and 2.4.DEV
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.
@ -119,15 +393,11 @@ Changes between 2.3.5 and 2.4.DEV
- 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.
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.
Changes between 2.3.4 and 2.3.5
v2.3.5
-----------------------------------
- fix issue169: respect --tb=style with setup/teardown errors as well.
@ -178,7 +448,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
@ -192,10 +462,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
@ -210,9 +480,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.
@ -244,10 +514,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
@ -277,7 +547,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
@ -290,7 +560,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
@ -331,7 +601,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.
@ -362,14 +632,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
@ -386,17 +656,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.
@ -411,11 +681,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
@ -423,25 +693,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
@ -450,12 +720,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
@ -463,7 +733,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
@ -472,7 +742,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
@ -482,7 +752,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
@ -495,7 +765,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
@ -515,7 +785,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
@ -535,7 +805,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
@ -547,17 +817,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
@ -580,14 +850,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
@ -596,7 +866,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)
@ -626,17 +896,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
@ -681,7 +951,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
@ -690,7 +960,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
@ -705,7 +975,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
@ -780,7 +1050,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
@ -852,7 +1122,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
@ -917,7 +1187,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"::
@ -956,7 +1226,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
@ -1040,7 +1310,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'
@ -1059,7 +1329,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
@ -1144,7 +1414,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,
@ -1152,7 +1422,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,
@ -1186,13 +1456,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
@ -1226,7 +1496,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
@ -1255,7 +1525,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
@ -1280,7 +1550,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
@ -1297,7 +1567,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,
@ -1321,7 +1591,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,
@ -1354,10 +1624,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.

View File

@ -122,8 +122,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 +135,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 +181,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 +196,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 +205,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 +223,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 +232,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,11 @@
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
The ``pytest`` testing tool makes it easy to write small tests, yet
scales to support complex functional testing. It provides
- `auto-discovery
@ -7,17 +14,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,
- single-source compatibility to Python2.5 all the way up to Python3.3,
PyPy-1.9 and Jython-2.5.1.
- many `external plugins <http://pytest.org/latest/plugins.html#installing-external-plugins-searching>`_.
.. image:: https://secure.travis-ci.org/hpk42/pytest.png
:target: http://travis-ci.org/hpk42/pytest
A simple example for a test::
# content of test_module.py

View File

@ -1,2 +1,2 @@
#
__version__ = '2.4.0.dev12'
__version__ = '2.5.2.dev1'

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,7 +3,6 @@ support for presenting detailed information in failing assertions.
"""
import py
import sys
import pytest
from _pytest.monkeypatch import monkeypatch
from _pytest.assertion import util
@ -19,7 +18,7 @@ def pytest_addoption(parser):
to provide assert expression information. """)
group.addoption('--no-assert', action="store_true", default=False,
dest="noassert", help="DEPRECATED equivalent to --assert=plain")
group.addoption('--nomagic', '--no-magic', action="store_true",
group.addoption('--nomagic', '--no-magic', action="store_true",
default=False, help="DEPRECATED equivalent to --assert=plain")
class AssertionState:
@ -35,7 +34,7 @@ def pytest_configure(config):
mode = "plain"
if mode == "rewrite":
try:
import ast
import ast # noqa
except ImportError:
mode = "reinterp"
else:
@ -49,10 +48,10 @@ def pytest_configure(config):
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)
@ -79,10 +78,13 @@ def pytest_runtest_setup(item):
for new_expl in hook_result:
if new_expl:
# Don't include pageloads of data unless we are very verbose (-vv)
if len(''.join(new_expl[1:])) > 80*8 and item.config.option.verbose < 2:
new_expl[1:] = ['Detailed information truncated, use "-vv" to see']
res = '\n~'.join(new_expl)
# Don't include pageloads of data unless we are very
# verbose (-vv)
if (len(py.builtin._totext('').join(new_expl[1:])) > 80*8
and item.config.option.verbose < 2):
new_expl[1:] = [py.builtin._totext(
'Detailed information truncated, use "-vv" to see')]
res = py.builtin._totext('\n~').join(new_expl)
if item.config.getvalue("assertmode") == "rewrite":
# The result will be fed back a python % formatting
# operation, which will fail if there are extraneous
@ -102,9 +104,9 @@ def pytest_sessionfinish(session):
def _load_modules(mode):
"""Lazily import assertion related code."""
global rewrite, reinterpret
from _pytest.assertion import reinterpret
from _pytest.assertion import reinterpret # noqa
if mode == "rewrite":
from _pytest.assertion import rewrite
from _pytest.assertion import rewrite # noqa
def warn_about_missing_assertion(mode):
try:

View File

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

View File

@ -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.
@ -169,6 +174,24 @@ class AssertionRewritingHook(object):
tp = desc[2]
return tp == imp.PKG_DIRECTORY
@classmethod
def _register_with_pkg_resources(cls):
"""
Ensure package resources can be loaded from this loader. May be called
multiple times, as the operation is idempotent.
"""
try:
import pkg_resources
# access an attribute in case a deferred importer is present
pkg_resources.__name__
except ImportError:
return
# Since pytest tests are always located in the file system, the
# DefaultProvider is appropriate.
pkg_resources.register_loader_type(cls, pkg_resources.DefaultProvider)
def _write_pyc(state, co, source_path, pyc):
# Technically, we don't have to have the same pyc format as
# (C)Python, since these "pycs" should never be seen by builtin
@ -196,7 +219,7 @@ 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):
@ -220,8 +243,8 @@ 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
state._indecode = True
@ -267,7 +290,7 @@ def _make_rewritten_pyc(state, fn, pyc, co):
os.rename(proc_pyc, pyc)
def _read_pyc(source, pyc):
"""Possibly read a py.test pyc containing rewritten code.
"""Possibly read a pytest pyc containing rewritten code.
Return rewritten code if successful or None if not.
"""
@ -300,7 +323,7 @@ def rewrite_asserts(mod):
_saferepr = py.io.saferepr
from _pytest.assertion.util import format_explanation as _format_explanation
from _pytest.assertion.util import format_explanation as _format_explanation # noqa
def _should_repr_global_name(obj):
return not hasattr(obj, "__name__") and not py.builtin.callable(obj)
@ -538,7 +561,8 @@ class AssertionRewriter(ast.NodeVisitor):
for i, v in enumerate(boolop.values):
if i:
fail_inner = []
self.on_failure.append(ast.If(cond, fail_inner, []))
# cond is set in a prior loop iteration below
self.on_failure.append(ast.If(cond, fail_inner, [])) # noqa
self.on_failure = fail_inner
self.push_format_context()
res, expl = self.visit(v)
@ -631,7 +655,7 @@ class AssertionRewriter(ast.NodeVisitor):
res_expr = ast.Compare(left_res, [op], [next_res])
self.statements.append(ast.Assign([store_names[i]], res_expr))
left_res, left_expl = next_res, next_expl
# Use py.code._reprcompare if that's available.
# Use pytest.assertion.util._reprcompare if that's available.
expl_call = self.helper("call_reprcompare",
ast.Tuple(syms, ast.Load()),
ast.Tuple(load_names, ast.Load()),

View File

@ -2,15 +2,12 @@
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 +26,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 +59,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 +108,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 +125,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 +148,12 @@ 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:
except Exception:
excinfo = py.code.ExceptionInfo()
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(excinfo)]
if not explanation:
return None
@ -148,8 +175,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,8 +185,8 @@ 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')
@ -172,16 +199,15 @@ 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))
@ -192,11 +218,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,25 +233,25 @@ 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 += [u('Common items:')]
explanation += py.std.pprint.pformat(same).splitlines()
diff = set(k for k in common if left[k] != right[k])
if diff:
explanation += ['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.append(u('Left contains more items:'))
explanation.extend(py.std.pprint.pformat(
dict((k, left[k]) for k in extra_left)).splitlines())
extra_right = set(right) - set(left)
if extra_right:
explanation.append('Right contains more items:')
explanation.append(u('Right contains more items:'))
explanation.extend(py.std.pprint.pformat(
dict((k, right[k]) for k in extra_right)).splitlines())
return explanation
@ -237,14 +263,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,43 +1,114 @@
""" per-test stdout/stderr capturing mechanisms, ``capsys`` and ``capfd`` function arguments. """
import pytest, py
"""
per-test stdout/stderr capturing mechanisms,
``capsys`` and ``capfd`` function arguments.
"""
# note: py.io capture was where copied from
# pylib 1.4.20.dev2 (rev 13d9af95547e)
import sys
import os
import tempfile
import py
import pytest
try:
from io import StringIO
except ImportError:
from StringIO import StringIO
try:
from io import BytesIO
except ImportError:
class BytesIO(StringIO):
def write(self, data):
if isinstance(data, unicode):
raise TypeError("not a byte value: %r" % (data,))
StringIO.write(self, data)
if sys.version_info < (3, 0):
class TextIO(StringIO):
def write(self, data):
if not isinstance(data, unicode):
enc = getattr(self, '_encoding', 'UTF-8')
data = unicode(data, enc, 'replace')
StringIO.write(self, data)
else:
TextIO = StringIO
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=None,
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",
group._addoption(
'-s', action="store_const", const="no", dest="capture",
help="shortcut for --capture=no.")
@pytest.mark.tryfirst
def pytest_cmdline_parse(pluginmanager, args):
# we want to perform capturing already for plugin/conftest loading
if '-s' in args or "--capture=no" in args:
method = "no"
elif hasattr(os, 'dup') and '--capture=sys' not in args:
def pytest_load_initial_conftests(early_config, parser, args, __multicall__):
ns = parser.parse_known_args(args)
method = ns.capture
if not method:
method = "fd"
else:
if method == "fd" and not hasattr(os, "dup"):
method = "sys"
capman = CaptureManager(method)
pluginmanager.register(capman, "capturemanager")
early_config.pluginmanager.register(capman, "capturemanager")
# make sure that capturemanager is properly reset at final shutdown
def teardown():
try:
capman.reset_capturings()
except ValueError:
pass
early_config.pluginmanager.add_shutdown(teardown)
# make sure logging does not raise exceptions at the end
def silence_logging_at_shutdown():
if "logging" in sys.modules:
sys.modules["logging"].raiseExceptions = False
early_config.pluginmanager.add_shutdown(silence_logging_at_shutdown)
# finally trigger conftest loading but while capturing (issue93)
capman.resumecapture()
try:
try:
return __multicall__.execute()
finally:
out, err = capman.suspendcapture()
except:
sys.stdout.write(out)
sys.stderr.write(err)
raise
def addouterr(rep, outerr):
for secname, content in zip(["out", "err"], outerr):
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 = {}
@ -45,21 +116,25 @@ class CaptureManager:
def _maketempfile(self):
f = py.std.tempfile.TemporaryFile()
newf = py.io.dupfile(f, encoding="UTF-8")
newf = dupfile(f, encoding="UTF-8")
f.close()
return newf
def _makestringio(self):
return py.io.TextIO()
return TextIO()
def _getcapture(self, method):
if method == "fd":
return py.io.StdCaptureFD(now=False,
out=self._maketempfile(), err=self._maketempfile()
return StdCaptureFD(
now=False,
out=self._maketempfile(),
err=self._maketempfile(),
)
elif method == "sys":
return py.io.StdCapture(now=False,
out=self._makestringio(), err=self._makestringio()
return StdCapture(
now=False,
out=self._makestringio(),
err=self._makestringio(),
)
elif method == "no":
return NoCapture()
@ -74,23 +149,24 @@ class CaptureManager:
method = config._conftest.rget("option_capture", path=fspath)
except KeyError:
method = "fd"
if method == "fd" and not hasattr(os, 'dup'): # e.g. jython
if method == "fd" and not hasattr(os, 'dup'): # e.g. jython
method = "sys"
return method
def reset_capturings(self):
for name, cap in self._method2capture.items():
for cap in self._method2capture.values():
cap.reset()
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
item.outerr = ('', '') # we accumulate outerr on the item
return self.resumecapture(method)
def resumecapture(self, method=None):
if hasattr(self, '_capturing'):
raise ValueError("cannot resume, already capturing with %r" %
raise ValueError(
"cannot resume, already capturing with %r" %
(self._capturing,))
if method is None:
method = self._defaultmethod
@ -139,8 +215,9 @@ class CaptureManager:
try:
self.resumecapture(method)
except ValueError:
return # recursive collect, XXX refactor capturing
# to allow for more lightweight recursive capturing
# recursive collect, XXX refactor capturing
# to allow for more lightweight recursive capturing
return
try:
rep = __multicall__.execute()
finally:
@ -181,6 +258,7 @@ class CaptureManager:
error_capsysfderror = "cannot use capsys and capfd at the same time"
def pytest_funcarg__capsys(request):
"""enables capturing of writes to sys.stdout/sys.stderr and makes
captured output available via ``capsys.readouterr()`` method calls
@ -188,7 +266,8 @@ def pytest_funcarg__capsys(request):
"""
if "capfd" in request._funcargs:
raise request.raiseerror(error_capsysfderror)
return CaptureFixture(py.io.StdCapture)
return CaptureFixture(StdCapture)
def pytest_funcarg__capfd(request):
"""enables capturing of writes to file descriptors 1 and 2 and makes
@ -199,26 +278,366 @@ def pytest_funcarg__capfd(request):
request.raiseerror(error_capsysfderror)
if not hasattr(os, 'dup'):
pytest.skip("capfd funcarg needs os.dup")
return CaptureFixture(py.io.StdCaptureFD)
return CaptureFixture(StdCaptureFD)
class CaptureFixture:
def __init__(self, captureclass):
self.capture = captureclass(now=False)
self._capture = captureclass(now=False)
def _start(self):
self.capture.startall()
self._capture.startall()
def _finalize(self):
if hasattr(self, 'capture'):
outerr = self._outerr = self.capture.reset()
del self.capture
if hasattr(self, '_capture'):
outerr = self._outerr = self._capture.reset()
del self._capture
return outerr
def readouterr(self):
try:
return self.capture.readouterr()
return self._capture.readouterr()
except AttributeError:
return self._outerr
def close(self):
self._finalize()
class FDCapture:
""" Capture IO to/from a given os-level filedescriptor. """
def __init__(self, targetfd, tmpfile=None, now=True, patchsys=False):
""" save targetfd descriptor, and open a new
temporary file there. If no tmpfile is
specified a tempfile.Tempfile() will be opened
in text mode.
"""
self.targetfd = targetfd
if tmpfile is None and targetfd != 0:
f = tempfile.TemporaryFile('wb+')
tmpfile = dupfile(f, encoding="UTF-8")
f.close()
self.tmpfile = tmpfile
self._savefd = os.dup(self.targetfd)
if patchsys:
self._oldsys = getattr(sys, patchsysdict[targetfd])
if now:
self.start()
def start(self):
try:
os.fstat(self._savefd)
except OSError:
raise ValueError(
"saved filedescriptor not valid, "
"did you call start() twice?")
if self.targetfd == 0 and not self.tmpfile:
fd = os.open(os.devnull, os.O_RDONLY)
os.dup2(fd, 0)
os.close(fd)
if hasattr(self, '_oldsys'):
setattr(sys, patchsysdict[self.targetfd], DontReadFromInput())
else:
os.dup2(self.tmpfile.fileno(), self.targetfd)
if hasattr(self, '_oldsys'):
setattr(sys, patchsysdict[self.targetfd], self.tmpfile)
def done(self):
""" unpatch and clean up, returns the self.tmpfile (file object)
"""
os.dup2(self._savefd, self.targetfd)
os.close(self._savefd)
if self.targetfd != 0:
self.tmpfile.seek(0)
if hasattr(self, '_oldsys'):
setattr(sys, patchsysdict[self.targetfd], self._oldsys)
return self.tmpfile
def writeorg(self, data):
""" write a string to the original file descriptor
"""
tempfp = tempfile.TemporaryFile()
try:
os.dup2(self._savefd, tempfp.fileno())
tempfp.write(data)
finally:
tempfp.close()
def dupfile(f, mode=None, buffering=0, raising=False, encoding=None):
""" return a new open file object that's a duplicate of f
mode is duplicated if not given, 'buffering' controls
buffer size (defaulting to no buffering) and 'raising'
defines whether an exception is raised when an incompatible
file object is passed in (if raising is False, the file
object itself will be returned)
"""
try:
fd = f.fileno()
mode = mode or f.mode
except AttributeError:
if raising:
raise
return f
newfd = os.dup(fd)
if sys.version_info >= (3, 0):
if encoding is not None:
mode = mode.replace("b", "")
buffering = True
return os.fdopen(newfd, mode, buffering, encoding, closefd=True)
else:
f = os.fdopen(newfd, mode, buffering)
if encoding is not None:
return EncodedFile(f, encoding)
return f
class EncodedFile(object):
def __init__(self, _stream, encoding):
self._stream = _stream
self.encoding = encoding
def write(self, obj):
if isinstance(obj, unicode):
obj = obj.encode(self.encoding)
self._stream.write(obj)
def writelines(self, linelist):
data = ''.join(linelist)
self.write(data)
def __getattr__(self, name):
return getattr(self._stream, name)
class Capture(object):
def call(cls, func, *args, **kwargs):
""" return a (res, out, err) tuple where
out and err represent the output/error output
during function execution.
call the given function with args/kwargs
and capture output/error during its execution.
"""
so = cls()
try:
res = func(*args, **kwargs)
finally:
out, err = so.reset()
return res, out, err
call = classmethod(call)
def reset(self):
""" reset sys.stdout/stderr and return captured output as strings. """
if hasattr(self, '_reset'):
raise ValueError("was already reset")
self._reset = True
outfile, errfile = self.done(save=False)
out, err = "", ""
if outfile and not outfile.closed:
out = outfile.read()
outfile.close()
if errfile and errfile != outfile and not errfile.closed:
err = errfile.read()
errfile.close()
return out, err
def suspend(self):
""" return current snapshot captures, memorize tempfiles. """
outerr = self.readouterr()
outfile, errfile = self.done()
return outerr
class StdCaptureFD(Capture):
""" This class allows to capture writes to FD1 and FD2
and may connect a NULL file to FD0 (and prevent
reads from sys.stdin). If any of the 0,1,2 file descriptors
is invalid it will not be captured.
"""
def __init__(self, out=True, err=True, mixed=False,
in_=True, patchsys=True, now=True):
self._options = {
"out": out,
"err": err,
"mixed": mixed,
"in_": in_,
"patchsys": patchsys,
"now": now,
}
self._save()
if now:
self.startall()
def _save(self):
in_ = self._options['in_']
out = self._options['out']
err = self._options['err']
mixed = self._options['mixed']
patchsys = self._options['patchsys']
if in_:
try:
self.in_ = FDCapture(
0, tmpfile=None, now=False,
patchsys=patchsys)
except OSError:
pass
if out:
tmpfile = None
if hasattr(out, 'write'):
tmpfile = out
try:
self.out = FDCapture(
1, tmpfile=tmpfile,
now=False, patchsys=patchsys)
self._options['out'] = self.out.tmpfile
except OSError:
pass
if err:
if out and mixed:
tmpfile = self.out.tmpfile
elif hasattr(err, 'write'):
tmpfile = err
else:
tmpfile = None
try:
self.err = FDCapture(
2, tmpfile=tmpfile,
now=False, patchsys=patchsys)
self._options['err'] = self.err.tmpfile
except OSError:
pass
def startall(self):
if hasattr(self, 'in_'):
self.in_.start()
if hasattr(self, 'out'):
self.out.start()
if hasattr(self, 'err'):
self.err.start()
def resume(self):
""" resume capturing with original temp files. """
self.startall()
def done(self, save=True):
""" return (outfile, errfile) and stop capturing. """
outfile = errfile = None
if hasattr(self, 'out') and not self.out.tmpfile.closed:
outfile = self.out.done()
if hasattr(self, 'err') and not self.err.tmpfile.closed:
errfile = self.err.done()
if hasattr(self, 'in_'):
self.in_.done()
if save:
self._save()
return outfile, errfile
def readouterr(self):
""" return snapshot value of stdout/stderr capturings. """
out = self._readsnapshot('out')
err = self._readsnapshot('err')
return out, err
def _readsnapshot(self, name):
if hasattr(self, name):
f = getattr(self, name).tmpfile
else:
return ''
f.seek(0)
res = f.read()
enc = getattr(f, "encoding", None)
if enc:
res = py.builtin._totext(res, enc, "replace")
f.truncate(0)
f.seek(0)
return res
class StdCapture(Capture):
""" This class allows to capture writes to sys.stdout|stderr "in-memory"
and will raise errors on tries to read from sys.stdin. It only
modifies sys.stdout|stderr|stdin attributes and does not
touch underlying File Descriptors (use StdCaptureFD for that).
"""
def __init__(self, out=True, err=True, in_=True, mixed=False, now=True):
self._oldout = sys.stdout
self._olderr = sys.stderr
self._oldin = sys.stdin
if out and not hasattr(out, 'file'):
out = TextIO()
self.out = out
if err:
if mixed:
err = out
elif not hasattr(err, 'write'):
err = TextIO()
self.err = err
self.in_ = in_
if now:
self.startall()
def startall(self):
if self.out:
sys.stdout = self.out
if self.err:
sys.stderr = self.err
if self.in_:
sys.stdin = self.in_ = DontReadFromInput()
def done(self, save=True):
""" return (outfile, errfile) and stop capturing. """
outfile = errfile = None
if self.out and not self.out.closed:
sys.stdout = self._oldout
outfile = self.out
outfile.seek(0)
if self.err and not self.err.closed:
sys.stderr = self._olderr
errfile = self.err
errfile.seek(0)
if self.in_:
sys.stdin = self._oldin
return outfile, errfile
def resume(self):
""" resume capturing with original temp files. """
self.startall()
def readouterr(self):
""" return snapshot value of stdout/stderr capturings. """
out = err = ""
if self.out:
out = self.out.getvalue()
self.out.truncate(0)
self.out.seek(0)
if self.err:
err = self.err.getvalue()
self.err.truncate(0)
self.err.seek(0)
return out, err
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):
pass

View File

@ -1,29 +1,88 @@
""" command line options, ini-file and conftest.py processing. """
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
import pytest
from _pytest._argcomplete import try_argcomplete, filescompleter
# enable after some grace period for plugin writers
TYPE_WARN = False
if TYPE_WARN:
import warnings
# pytest startup
def main(args=None, plugins=None):
""" return exit code, after performing an in-process test run.
def pytest_cmdline_parse(pluginmanager, args):
config = Config(pluginmanager)
config.parse(args)
return config
:arg args: list of command line arguments.
:arg plugins: list of plugin objects to be auto-registered during
initialization.
"""
config = _prepareconfig(args, plugins)
return config.hook.pytest_cmdline_main(config=config)
class cmdline: # compatibility namespace
main = staticmethod(main)
class UsageError(Exception):
""" error in pytest usage or invocation"""
_preinit = []
default_plugins = (
"mark main terminal runner python pdb unittest capture skipping "
"tmpdir monkeypatch recwarn pastebin helpconfig nose assertion genscript "
"junitxml resultlog doctest").split()
def _preloadplugins():
assert not _preinit
_preinit.append(get_plugin_manager())
def get_plugin_manager():
if _preinit:
return _preinit.pop(0)
# subsequent calls to main will create a fresh instance
pluginmanager = PytestPluginManager()
pluginmanager.config = Config(pluginmanager) # XXX attr needed?
for spec in default_plugins:
pluginmanager.import_plugin(spec)
return pluginmanager
def _prepareconfig(args=None, plugins=None):
if args is None:
args = sys.argv[1:]
elif isinstance(args, py.path.local):
args = [str(args)]
elif not isinstance(args, (tuple, list)):
if not isinstance(args, str):
raise ValueError("not a string or argument list: %r" % (args,))
args = py.std.shlex.split(args)
pluginmanager = get_plugin_manager()
if plugins:
for plugin in plugins:
pluginmanager.register(plugin)
return pluginmanager.hook.pytest_cmdline_parse(
pluginmanager=pluginmanager, args=args)
class PytestPluginManager(PluginManager):
def __init__(self, hookspecs=[hookspec]):
super(PytestPluginManager, self).__init__(hookspecs=hookspecs)
self.register(self)
if os.environ.get('PYTEST_DEBUG'):
err = sys.stderr
encoding = getattr(err, 'encoding', 'utf8')
try:
err = py.io.dupfile(err, encoding=encoding)
except Exception:
pass
self.trace.root.setwriter(err.write)
def pytest_configure(self, config):
config.addinivalue_line("markers",
"tryfirst: mark a hook implementation function such that the "
"plugin machinery will try to call it first/as early as possible.")
config.addinivalue_line("markers",
"trylast: mark a hook implementation function such that the "
"plugin machinery will try to call it last/as late as possible.")
def pytest_unconfigure(config):
while 1:
try:
fin = config._cleanup.pop()
except IndexError:
break
fin()
class Parser:
""" Parser for command line arguments and ini-file values. """
@ -70,8 +129,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
@ -82,7 +141,14 @@ class Parser:
self._anonymous.addoption(*opts, **attrs)
def parse(self, args):
self.optparser = optparser = MyOptionParser(self)
from _pytest._argcomplete import try_argcomplete
self.optparser = self._getparser()
try_argcomplete(self.optparser)
return self.optparser.parse_args([str(x) for x in args])
def _getparser(self):
from _pytest._argcomplete import filescompleter
optparser = MyOptionParser(self)
groups = self._groups + [self._anonymous]
for group in groups:
if group.options:
@ -93,16 +159,20 @@ class Parser:
a = option.attrs()
arggroup.add_argument(*n, **a)
# bash like autocompletion for dirs (appending '/')
optparser.add_argument(Config._file_or_dir, nargs='*'
optparser.add_argument(FILE_OR_DIR, nargs='*'
).completer=filescompleter
try_argcomplete(self.optparser)
return self.optparser.parse_args([str(x) for x in args])
return optparser
def parse_setoption(self, args, option):
parsedoption = self.parse(args)
for name, value in parsedoption.__dict__.items():
setattr(option, name, value)
return getattr(parsedoption, Config._file_or_dir)
return getattr(parsedoption, FILE_OR_DIR)
def parse_known_args(self, args):
optparser = self._getparser()
args = [str(x) for x in args]
return optparser.parse_known_args(args)[0]
def addini(self, name, help, type=None, default=None):
""" register an ini-file option.
@ -142,6 +212,8 @@ class Argument:
'int': int,
'string': str,
}
# enable after some grace period for plugin writers
TYPE_WARN = False
def __init__(self, *names, **attrs):
"""store parms in private vars for use in add_argument"""
@ -149,12 +221,12 @@ class Argument:
self._short_opts = []
self._long_opts = []
self.dest = attrs.get('dest')
if TYPE_WARN:
if self.TYPE_WARN:
try:
help = attrs['help']
if '%default' in help:
warnings.warn(
'py.test now uses argparse. "%default" should be'
py.std.warnings.warn(
'pytest now uses argparse. "%default" should be'
' changed to "%(default)s" ',
FutureWarning,
stacklevel=3)
@ -166,10 +238,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 TYPE_WARN:
warnings.warn(
if self.TYPE_WARN:
py.std.warnings.warn(
'type argument to addoption() is a string %r.'
' For parsearg this is optional and when supplied '
' should be a type.'
@ -180,8 +252,8 @@ class Argument:
# the type of the first element
attrs['type'] = type(attrs['choices'][0])
else:
if TYPE_WARN:
warnings.warn(
if self.TYPE_WARN:
py.std.warnings.warn(
'type argument to addoption() is a string %r.'
' For parsearg this should be a type.'
' (options: %s)' % (typ, names),
@ -323,22 +395,9 @@ class MyOptionParser(py.std.argparse.ArgumentParser):
if arg and arg[0] == '-':
msg = py.std.argparse._('unrecognized arguments: %s')
self.error(msg % ' '.join(argv))
getattr(args, Config._file_or_dir).extend(argv)
getattr(args, FILE_OR_DIR).extend(argv)
return args
# #pylib 2013-07-31
# (12:05:53) anthon: hynek: can you get me a list of preferred py.test
# long-options with '-' inserted at the right places?
# (12:08:29) hynek: anthon, hpk: generally I'd love the following, decide
# yourself which you agree and which not:
# (12:10:51) hynek: --exit-on-first --full-trace --junit-xml --junit-prefix
# --result-log --collect-only --conf-cut-dir --trace-config
# --no-magic
# (12:18:21) hpk: hynek,anthon: makes sense to me.
# (13:40:30) hpk: hynek: let's not change names, rather only deal with
# hyphens for now
# (13:40:50) hynek: then --exit-first *shrug*
class DropShorterLongHelpFormatter(py.std.argparse.HelpFormatter):
"""shorten help for long options that differ only in extra hyphens
@ -390,7 +449,7 @@ class DropShorterLongHelpFormatter(py.std.argparse.HelpFormatter):
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):
self._path2confmods = {}
@ -504,35 +563,88 @@ class CmdOptions(object):
def __repr__(self):
return "<CmdOptions %r>" %(self.__dict__,)
FILE_OR_DIR = 'file_or_dir'
class Config(object):
""" access to configuration values, pluginmanager and plugin hooks. """
_file_or_dir = 'file_or_dir'
def __init__(self, pluginmanager=None):
def __init__(self, pluginmanager):
#: access to command line option as attributes.
#: (deprecated), use :py:func:`getoption() <_pytest.config.Config.getoption>` instead
self.option = CmdOptions()
_a = self._file_or_dir
_a = FILE_OR_DIR
self._parser = Parser(
usage="%%(prog)s [options] [%s] [%s] [...]" % (_a, _a),
processopt=self._processopt,
)
#: a pluginmanager instance
self.pluginmanager = pluginmanager or PluginManager(load=True)
self.pluginmanager = pluginmanager
self.trace = self.pluginmanager.trace.root.get("config")
self._conftest = Conftest(onimport=self._onimportconftest)
self.hook = self.pluginmanager.hook
self._inicache = {}
self._opt2dest = {}
self._cleanup = []
self.pluginmanager.register(self, "pytestconfig")
self.pluginmanager.set_register_callback(self._register_plugin)
self._configured = False
def _register_plugin(self, plugin, name):
call_plugin = self.pluginmanager.call_plugin
call_plugin(plugin, "pytest_addhooks",
{'pluginmanager': self.pluginmanager})
self.hook.pytest_plugin_registered(plugin=plugin,
manager=self.pluginmanager)
dic = call_plugin(plugin, "pytest_namespace", {}) or {}
if dic:
import pytest
setns(pytest, dic)
call_plugin(plugin, "pytest_addoption", {'parser': self._parser})
if self._configured:
call_plugin(plugin, "pytest_configure", {'config': self})
def do_configure(self):
assert not self._configured
self._configured = True
self.hook.pytest_configure(config=self)
def do_unconfigure(self):
assert self._configured
self._configured = False
self.hook.pytest_unconfigure(config=self)
self.pluginmanager.ensure_shutdown()
def pytest_cmdline_parse(self, pluginmanager, args):
assert self == pluginmanager.config, (self, pluginmanager.config)
self.parse(args)
return self
def pytest_unconfigure(config):
while config._cleanup:
fin = config._cleanup.pop()
fin()
def notify_exception(self, excinfo, option=None):
if option and option.fulltrace:
style = "long"
else:
style = "native"
excrepr = excinfo.getrepr(funcargs=True,
showlocals=getattr(option, 'showlocals', False),
style=style,
)
res = self.hook.pytest_internalerror(excrepr=excrepr,
excinfo=excinfo)
if not py.builtin.any(res):
for line in str(excrepr).split("\n"):
sys.stderr.write("INTERNALERROR> %s\n" %line)
sys.stderr.flush()
@classmethod
def fromdictargs(cls, option_dict, args):
""" constructor useable for subprocesses. """
config = cls()
# XXX slightly crude way to initialize capturing
import _pytest.capture
_pytest.capture.pytest_cmdline_parse(config.pluginmanager, args)
pluginmanager = get_plugin_manager()
config = pluginmanager.config
config._preparse(args, addopts=False)
config.option.__dict__.update(option_dict)
for x in config.option.plugins:
@ -558,21 +670,9 @@ class Config(object):
plugins += self._conftest.getconftestmodules(fspath)
return plugins
def _setinitialconftest(self, args):
# capture output during conftest init (#issue93)
# XXX introduce load_conftest hook to avoid needing to know
# about capturing plugin here
capman = self.pluginmanager.getplugin("capturemanager")
capman.resumecapture()
try:
try:
self._conftest.setinitial(args)
finally:
out, err = capman.suspendcapture() # logging might have got it
except:
sys.stdout.write(out)
sys.stderr.write(err)
raise
def pytest_load_initial_conftests(self, parser, args):
self._conftest.setinitial(args)
pytest_load_initial_conftests.trylast = True
def _initini(self, args):
self.inicfg = getcfg(args, ["pytest.ini", "tox.ini", "setup.cfg"])
@ -587,12 +687,11 @@ class Config(object):
self.pluginmanager.consider_preparse(args)
self.pluginmanager.consider_setuptools_entrypoints()
self.pluginmanager.consider_env()
self._setinitialconftest(args)
self.pluginmanager.do_addoption(self._parser)
if addopts:
self.hook.pytest_cmdline_preparse(config=self, args=args)
self.hook.pytest_load_initial_conftests(early_config=self,
args=args, parser=self._parser)
def _checkversion(self):
import pytest
minver = self.inicfg.get('minversion', None)
if minver:
ver = minver.split(".")
@ -610,6 +709,8 @@ class Config(object):
"can only parse cmdline args at most once per Config object")
self._origargs = args
self._preparse(args)
# XXX deprecated hook:
self.hook.pytest_cmdline_preparse(config=self, args=args)
self._parser.hints.extend(self.pluginmanager._hints)
args = self._parser.parse_setoption(args, self.option)
if not args:
@ -708,7 +809,7 @@ class Config(object):
def getvalueorskip(self, name, path=None):
""" (deprecated) return getvalue(name) or call
py.test.skip if no value exists. """
pytest.skip if no value exists. """
__tracebackhide__ = True
try:
val = self.getvalue(name, path)
@ -739,3 +840,23 @@ def getcfg(args, inibasenames):
return iniconfig['pytest']
return {}
def setns(obj, dic):
import pytest
for name, value in dic.items():
if isinstance(value, dict):
mod = getattr(obj, name, None)
if mod is None:
modname = "pytest.%s" % name
mod = py.std.types.ModuleType(modname)
sys.modules[modname] = mod
mod.__all__ = []
setattr(obj, name, mod)
obj.__all__.append(name)
setns(mod, value)
else:
setattr(obj, name, value)
obj.__all__.append(name)
#if obj != pytest:
# pytest.__all__.append(name)
setattr(pytest, name, value)

View File

@ -1,20 +1,14 @@
"""
pytest PluginManager, basic initialization and tracing.
(c) Holger Krekel 2004-2010
"""
import sys, os
import sys
import inspect
import py
from _pytest import hookspec # the extension point definitions
# 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__))
default_plugins = (
"config mark main terminal runner python pdb unittest capture skipping "
"tmpdir monkeypatch recwarn pastebin helpconfig nose assertion genscript "
"junitxml resultlog doctest").split()
class TagTracer:
def __init__(self):
self._tag2proc = {}
@ -73,37 +67,36 @@ class TagTracerSub:
return self.__class__(self.root, self.tags + (name,))
class PluginManager(object):
def __init__(self, load=False):
def __init__(self, hookspecs=None):
self._name2plugin = {}
self._listattrcache = {}
self._plugins = []
self._hints = []
self.trace = TagTracer().get("pluginmanage")
self._plugin_distinfo = []
if os.environ.get('PYTEST_DEBUG'):
err = sys.stderr
encoding = getattr(err, 'encoding', 'utf8')
try:
err = py.io.dupfile(err, encoding=encoding)
except Exception:
pass
self.trace.root.setwriter(err.write)
self.hook = HookRelay([hookspec], pm=self)
self.register(self)
if load:
for spec in default_plugins:
self.import_plugin(spec)
self._shutdown = []
self.hook = HookRelay(hookspecs or [], pm=self)
def do_configure(self, config):
# backward compatibility
config.do_configure()
def set_register_callback(self, callback):
assert not hasattr(self, "_registercallback")
self._registercallback = callback
def register(self, plugin, name=None, prepend=False):
if self._name2plugin.get(name, None) == -1:
return
name = name or getattr(plugin, '__name__', str(id(plugin)))
if self.isregistered(plugin, name):
raise ValueError("Plugin already registered: %s=%s" %(name, plugin))
raise ValueError("Plugin already registered: %s=%s\n%s" %(
name, plugin, self._name2plugin))
#self.trace("registering", name, plugin)
self._name2plugin[name] = plugin
self.call_plugin(plugin, "pytest_addhooks", {'pluginmanager': self})
self.hook.pytest_plugin_registered(manager=self, plugin=plugin)
reg = getattr(self, "_registercallback", None)
if reg is not None:
reg(plugin, name)
if not prepend:
self._plugins.append(plugin)
else:
@ -114,11 +107,21 @@ class PluginManager(object):
if plugin is None:
plugin = self.getplugin(name=name)
self._plugins.remove(plugin)
self.hook.pytest_plugin_unregistered(plugin=plugin)
for name, value in list(self._name2plugin.items()):
if value == plugin:
del self._name2plugin[name]
def add_shutdown(self, func):
self._shutdown.append(func)
def ensure_shutdown(self):
while self._shutdown:
func = self._shutdown.pop()
func()
self._plugins = []
self._name2plugin.clear()
self._listattrcache.clear()
def isregistered(self, plugin, name=None):
if self.getplugin(name) is not None:
return True
@ -126,8 +129,8 @@ class PluginManager(object):
if plugin == val:
return True
def addhooks(self, spec):
self.hook._addhooks(spec, prefix="pytest_")
def addhooks(self, spec, prefix="pytest_"):
self.hook._addhooks(spec, prefix=prefix)
def getplugins(self):
return list(self._plugins)
@ -209,7 +212,6 @@ class PluginManager(object):
if self.getplugin(modname) is not None:
return
try:
#self.trace("importing", modname)
mod = importplugin(modname)
except KeyboardInterrupt:
raise
@ -228,83 +230,6 @@ class PluginManager(object):
self.register(mod, modname)
self.consider_module(mod)
def pytest_configure(self, config):
config.addinivalue_line("markers",
"tryfirst: mark a hook implementation function such that the "
"plugin machinery will try to call it first/as early as possible.")
config.addinivalue_line("markers",
"trylast: mark a hook implementation function such that the "
"plugin machinery will try to call it last/as late as possible.")
def pytest_plugin_registered(self, plugin):
import pytest
dic = self.call_plugin(plugin, "pytest_namespace", {}) or {}
if dic:
self._setns(pytest, dic)
if hasattr(self, '_config'):
self.call_plugin(plugin, "pytest_addoption",
{'parser': self._config._parser})
self.call_plugin(plugin, "pytest_configure",
{'config': self._config})
def _setns(self, obj, dic):
import pytest
for name, value in dic.items():
if isinstance(value, dict):
mod = getattr(obj, name, None)
if mod is None:
modname = "pytest.%s" % name
mod = py.std.types.ModuleType(modname)
sys.modules[modname] = mod
mod.__all__ = []
setattr(obj, name, mod)
obj.__all__.append(name)
self._setns(mod, value)
else:
setattr(obj, name, value)
obj.__all__.append(name)
#if obj != pytest:
# pytest.__all__.append(name)
setattr(pytest, name, value)
def pytest_terminal_summary(self, terminalreporter):
tw = terminalreporter._tw
if terminalreporter.config.option.traceconfig:
for hint in self._hints:
tw.line("hint: %s" % hint)
def do_addoption(self, parser):
mname = "pytest_addoption"
methods = reversed(self.listattr(mname))
MultiCall(methods, {'parser': parser}).execute()
def do_configure(self, config):
assert not hasattr(self, '_config')
self._config = config
config.hook.pytest_configure(config=self._config)
def do_unconfigure(self, config):
config = self._config
del self._config
config.hook.pytest_unconfigure(config=config)
config.pluginmanager.unregister(self)
def notify_exception(self, excinfo, option=None):
if option and option.fulltrace:
style = "long"
else:
style = "native"
excrepr = excinfo.getrepr(funcargs=True,
showlocals=getattr(option, 'showlocals', False),
style=style,
)
res = self.hook.pytest_internalerror(excrepr=excrepr,
excinfo=excinfo)
if not py.builtin.any(res):
for line in str(excrepr).split("\n"):
sys.stderr.write("INTERNALERROR> %s\n" %line)
sys.stderr.flush()
def listattr(self, attrname, plugins=None):
if plugins is None:
plugins = self._plugins
@ -342,15 +267,8 @@ def importplugin(importspec):
__import__(mod)
return sys.modules[mod]
except ImportError:
#e = py.std.sys.exc_info()[1]
#if str(e).find(name) == -1:
# raise
pass #
try:
__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. """
@ -387,19 +305,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:
@ -457,43 +392,3 @@ class HookCaller:
self.trace.root.indent -= 1
return res
_preinit = []
def _preloadplugins():
_preinit.append(PluginManager(load=True))
def _prepareconfig(args=None, plugins=None):
if args is None:
args = sys.argv[1:]
elif isinstance(args, py.path.local):
args = [str(args)]
elif not isinstance(args, (tuple, list)):
if not isinstance(args, str):
raise ValueError("not a string or argument list: %r" % (args,))
args = py.std.shlex.split(args)
if _preinit:
_pluginmanager = _preinit.pop(0)
else: # subsequent calls to main will create a fresh instance
_pluginmanager = PluginManager(load=True)
hook = _pluginmanager.hook
if plugins:
for plugin in plugins:
_pluginmanager.register(plugin)
return hook.pytest_cmdline_parse(
pluginmanager=_pluginmanager, args=args)
def main(args=None, plugins=None):
""" return exit code, after performing an in-process test run.
:arg args: list of command line arguments.
:arg plugins: list of plugin objects to be auto-registered during
initialization.
"""
config = _prepareconfig(args, plugins)
exitstatus = config.hook.pytest_cmdline_main(config=config)
return exitstatus
class UsageError(Exception):
""" error in py.test usage or invocation"""

View File

@ -91,8 +91,13 @@ class DoctestTextfile(DoctestItem, pytest.File):
doctest = py.std.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,

View File

@ -1,4 +1,4 @@
""" generate a single-file self-contained version of py.test """
""" generate a single-file self-contained version of pytest """
import py
import sys
@ -55,7 +55,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")
@ -70,7 +70,7 @@ def pytest_cmdline_main(config):
"or below due to 'argparse' dependency. Use python2.6 "
"to generate a python2.5/6 compatible script", red=True)
script = generate_script(
'import py; raise SystemExit(py.test.cmdline.main())',
'import pytest; raise SystemExit(pytest.cmdline.main())',
deps,
)
genscript = py.path.local(genscript)

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:
@ -54,9 +56,9 @@ def pytest_cmdline_main(config):
sys.stderr.write(line + "\n")
return 0
elif config.option.help:
config.pluginmanager.do_configure(config)
config.do_configure()
showhelp(config)
config.pluginmanager.do_unconfigure(config)
config.do_unconfigure()
return 0
def showhelp(config):
@ -82,6 +84,8 @@ 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)")
return
tw.line("conftest.py options:")
@ -120,7 +124,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):
@ -20,7 +20,7 @@ def pytest_cmdline_parse(pluginmanager, args):
pytest_cmdline_parse.firstresult = True
def pytest_cmdline_preparse(config, args):
"""modify command line arguments before option parsing. """
"""(deprecated) modify command line arguments before option parsing. """
def pytest_addoption(parser):
"""register argparse-style options and ini-style config values.
@ -52,6 +52,10 @@ def pytest_cmdline_main(config):
implementation will invoke the configure hooks and runtest_mainloop. """
pytest_cmdline_main.firstresult = True
def pytest_load_initial_conftests(args, early_config, parser):
""" implements loading initial conftests.
"""
def pytest_configure(config):
""" called after command line options have been parsed
and all plugins and initial conftest files been loaded.
@ -221,7 +225,7 @@ def pytest_report_teststatus(report):
pytest_report_teststatus.firstresult = True
def pytest_terminal_summary(terminalreporter):
""" add additional section in terminal summary reporting. """
""" add additional section in terminal summary reporting. """
# -------------------------------------------------------------------------
# doctest hooks
@ -236,10 +240,7 @@ pytest_doctest_prepare_content.firstresult = True
# -------------------------------------------------------------------------
def pytest_plugin_registered(plugin, manager):
""" a new py lib plugin got registered. """
def pytest_plugin_unregistered(plugin):
""" a py lib plugin got unregistered. """
""" a new pytest plugin got registered. """
def pytest_internalerror(excrepr, excinfo):
""" called for internal errors. """

View File

@ -9,7 +9,6 @@ import re
import sys
import time
# Python 2.X and 3.X compatibility
try:
unichr(65)
@ -62,8 +61,8 @@ def bin_xml_escape(arg):
def pytest_addoption(parser):
group = parser.getgroup("terminal reporting")
group.addoption('--junitxml', '--junit-xml', action="store",
dest="xmlpath", metavar="path", default=None,
group.addoption('--junitxml', '--junit-xml', action="store",
dest="xmlpath", metavar="path", default=None,
help="create junit-xml style report file at given path.")
group.addoption('--junitprefix', '--junit-prefix', action="store",
metavar="str", default=None,
@ -131,36 +130,36 @@ 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,
type="pytest.skip",
@ -194,17 +193,17 @@ class LogXML(object):
def pytest_internalerror(self, excrepr):
self.errors += 1
data = py.xml.escape(excrepr)
data = bin_xml_escape(excrepr)
self.tests.append(
Junit.testcase(
Junit.error(data, message="internal error"),
classname="pytest",
name="internal"))
def pytest_sessionstart(self, session):
def pytest_sessionstart(self):
self.suite_start_time = time.time()
def pytest_sessionfinish(self, session, exitstatus, __multicall__):
def pytest_sessionfinish(self):
if py.std.sys.version_info[0] < 3:
logfile = py.std.codecs.open(self.logfile, 'w', encoding='utf-8')
else:
@ -217,7 +216,7 @@ class LogXML(object):
logfile.write('<?xml version="1.0" encoding="utf-8"?>')
logfile.write(Junit.testsuite(
self.tests,
name="",
name="pytest",
errors=self.errors,
failures=self.failed,
skips=self.skipped,

View File

@ -2,14 +2,12 @@
import py
import pytest, _pytest
import inspect
import os, sys, imp
try:
from collections import MutableMapping as MappingMixin
except ImportError:
from UserDict import DictMixin as MappingMixin
from _pytest.mark import MarkInfo
from _pytest.runner import collect_one_node, Skipped
tracebackcutdir = py.path.local(_pytest.__file__).dirpath()
@ -65,7 +63,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
@ -76,7 +74,7 @@ def wrap_session(config, doit):
initstate = 0
try:
try:
config.pluginmanager.do_configure(config)
config.do_configure()
initstate = 1
config.hook.pytest_sessionstart(session=session)
initstate = 2
@ -92,7 +90,7 @@ def wrap_session(config, doit):
session.exitstatus = EXIT_INTERRUPTED
except:
excinfo = py.code.ExceptionInfo()
config.pluginmanager.notify_exception(excinfo, config.option)
config.notify_exception(excinfo, config.option)
session.exitstatus = EXIT_INTERNALERROR
if excinfo.errisinstance(SystemExit):
sys.stderr.write("mainloop: caught Spurious SystemExit!\n")
@ -106,7 +104,8 @@ def wrap_session(config, doit):
session=session,
exitstatus=session.exitstatus)
if initstate >= 1:
config.pluginmanager.do_unconfigure(config)
config.do_unconfigure()
config.pluginmanager.ensure_shutdown()
return session.exitstatus
def pytest_cmdline_main(config):
@ -172,30 +171,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 +231,8 @@ 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
@ -263,21 +273,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
@ -314,6 +314,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 +358,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
@ -397,7 +420,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:
@ -672,11 +694,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

@ -1,5 +1,5 @@
""" generic mechanism for marking and selecting python functions. """
import pytest, py
import py
def pytest_namespace():
@ -39,14 +39,14 @@ def pytest_addoption(parser):
def pytest_cmdline_main(config):
if config.option.markers:
config.pluginmanager.do_configure(config)
config.do_configure()
tw = py.io.TerminalWriter()
for line in config.getini("markers"):
name, rest = line.split(":", 1)
tw.write("@pytest.mark.%s:" % name, bold=True)
tw.line(rest)
tw.line()
config.pluginmanager.do_unconfigure(config)
config.do_unconfigure()
return 0
pytest_cmdline_main.tryfirst = True
@ -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:
@ -129,6 +126,7 @@ def matchkeyword(colitem, keywordexpr):
mapped_names = set()
# Add the names of the current item and any parent items
import pytest
for item in colitem.listchain():
if not isinstance(item, pytest.Instance):
mapped_names.add(item.name)
@ -138,23 +136,31 @@ 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):
import pytest
if config.option.strict:
pytest.mark._config = config
class MarkGenerator:
""" Factory for :class:`MarkDecorator` objects - exposed as
a ``py.test.mark`` singleton instance. Example::
a ``pytest.mark`` singleton instance. Example::
import py
@py.test.mark.slowtest
@pytest.mark.slowtest
def test_function():
pass
@ -163,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)
@ -182,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
@ -189,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
@ -225,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
@ -25,6 +25,40 @@ def pytest_funcarg__monkeypatch(request):
request.addfinalizer(mpatch.undo)
return mpatch
def derive_importpath(import_path):
import pytest
if not isinstance(import_path, _basestring) or "." not in import_path:
raise TypeError("must be absolute import path string, not %r" %
(import_path,))
rest = []
target = import_path
while target:
try:
obj = __import__(target, None, None, "__doc__")
except ImportError:
if "." not in target:
__tracebackhide__ = True
pytest.fail("could not import any sub part: %s" %
import_path)
target, name = target.rsplit(".", 1)
rest.append(name)
else:
assert rest
try:
while len(rest) > 1:
attr = rest.pop()
obj = getattr(obj, attr)
attr = rest[0]
getattr(obj, attr)
except AttributeError:
__tracebackhide__ = True
pytest.fail("object %r has no attribute %r" % (obj, attr))
return attr, obj
notset = object()
class monkeypatch:
@ -34,70 +68,67 @@ class monkeypatch:
self._setitem = []
self._cwd = None
def replace(self, import_path, value):
""" replace the object specified by a dotted ``import_path``
with the given ``value``.
def setattr(self, target, name, value=notset, raising=True):
""" set attribute value on target, memorizing the old value.
By default raise AttributeError if the attribute did not exist.
For example ``replace("os.path.abspath", value)`` will
trigger an ``import os.path`` and a subsequent
setattr(os.path, "abspath", value). Or to prevent
the requests library from performing requests you can call
``replace("requests.sessions.Session.request", None)``
which will lead to an import of ``requests.sessions`` and a call
to ``setattr(requests.sessions.Session, "request", None)``.
For convenience you can specify a string as ``target`` which
will be interpreted as a dotted import path, with the last part
being the attribute name. Example:
``monkeypatch.setattr("os.getcwd", lambda x: "/")``
would set the ``getcwd`` function of the ``os`` module.
The ``raising`` value determines if the setattr should fail
if the attribute is not already present (defaults to True
which means it will raise).
"""
if not isinstance(import_path, str) or "." not in import_path:
raise TypeError("must be absolute import path string, not %r" %
(import_path,))
rest = []
target = import_path
while target:
try:
obj = __import__(target, None, None, "__doc__")
except ImportError:
if "." not in target:
__tracebackhide__ = True
pytest.fail("could not import any sub part: %s" %
import_path)
target, name = target.rsplit(".", 1)
rest.append(name)
else:
assert rest
try:
while len(rest) > 1:
attr = rest.pop()
obj = getattr(obj, attr)
attr = rest[0]
getattr(obj, attr)
except AttributeError:
__tracebackhide__ = True
pytest.fail("object %r has no attribute %r" % (obj, attr))
return self.setattr(obj, attr, value)
__tracebackhide__ = True
import inspect
def setattr(self, obj, name, value, raising=True):
""" set attribute ``name`` on ``obj`` to ``value``, by default
raise AttributeEror if the attribute did not exist.
if value is notset:
if not isinstance(target, _basestring):
raise TypeError("use setattr(target, name, value) or "
"setattr(target, value) with target being a dotted "
"import string")
value = name
name, target = derive_importpath(target)
"""
oldval = getattr(obj, name, notset)
oldval = getattr(target, name, notset)
if raising and oldval is notset:
raise AttributeError("%r has no attribute %r" %(obj, name))
raise AttributeError("%r has no attribute %r" %(target, name))
# avoid class descriptors like staticmethod/classmethod
if inspect.isclass(obj):
oldval = obj.__dict__.get(name, notset)
self._setattr.insert(0, (obj, name, oldval))
setattr(obj, name, value)
if inspect.isclass(target):
oldval = target.__dict__.get(name, notset)
self._setattr.insert(0, (target, name, oldval))
setattr(target, name, value)
def delattr(self, obj, name, raising=True):
""" delete attribute ``name`` from ``obj``, by default raise
AttributeError it the attribute did not previously exist. """
if not hasattr(obj, name):
def delattr(self, target, name=notset, raising=True):
""" delete attribute ``name`` from ``target``, by default raise
AttributeError it the attribute did not previously exist.
If no ``name`` is specified and ``target`` is a string
it will be interpreted as a dotted import path with the
last part being the attribute name.
If raising is set to false, the attribute is allowed to not
pre-exist.
"""
__tracebackhide__ = True
if name is notset:
if not isinstance(target, _basestring):
raise TypeError("use delattr(target, name) or "
"delattr(target) with target being a dotted "
"import string")
name, target = derive_importpath(target)
if not hasattr(target, name):
if raising:
raise AttributeError(name)
else:
self._setattr.insert(0, (obj, name, getattr(obj, name, notset)))
delattr(obj, name)
self._setattr.insert(0, (target, name,
getattr(target, name, notset)))
delattr(target, name)
def setitem(self, dic, name, value):
""" set dictionary entry ``name`` to value. """

View File

@ -1,7 +1,6 @@
""" run test suites written for nose. """
import pytest, py
import inspect
import sys
from _pytest import unittest
@ -9,7 +8,7 @@ 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
# 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

View File

@ -1,10 +1,9 @@
""" (disabled by default) support for testing py.test and py.test plugins. """
""" (disabled by default) support for testing pytest and pytest plugins. """
import py, pytest
import sys, os
import codecs
import re
import inspect
import time
from fnmatch import fnmatch
from _pytest.main import Session, EXIT_OK
@ -27,7 +26,6 @@ def pytest_addoption(parser):
def pytest_configure(config):
# This might be called multiple times. Only take the first.
global _pytest_fullpath
import pytest
try:
_pytest_fullpath
except NameError:
@ -83,7 +81,8 @@ class HookRecorder:
def finish_recording(self):
for recorder in self._recorders.values():
self._pluginmanager.unregister(recorder)
if self._pluginmanager.isregistered(recorder):
self._pluginmanager.unregister(recorder)
self._recorders.clear()
def _makecallparser(self, method):
@ -121,7 +120,6 @@ class HookRecorder:
def contains(self, entries):
__tracebackhide__ = True
from py.builtin import print_
i = 0
entries = list(entries)
backlocals = py.std.sys._getframe(1).f_locals
@ -139,7 +137,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
@ -149,7 +147,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)
@ -260,9 +258,6 @@ class TmpTestdir:
def makefile(self, ext, *args, **kwargs):
return self._makefile(ext, args, kwargs)
def makeini(self, source):
return self.makefile('cfg', setup=source)
def makeconftest(self, source):
return self.makepyfile(conftest=source)
@ -361,7 +356,7 @@ class TmpTestdir:
if not plugins:
plugins = []
plugins.append(Collect())
ret = self.pytestmain(list(args), plugins=plugins)
ret = pytest.main(list(args), plugins=plugins)
reprec = rec[0]
reprec.ret = ret
assert len(rec) == 1
@ -374,23 +369,24 @@ class TmpTestdir:
break
else:
args.append("--basetemp=%s" % self.tmpdir.dirpath('basetemp'))
import _pytest.core
config = _pytest.core._prepareconfig(args, self.plugins)
# the in-process pytest invocation needs to avoid leaking FDs
# so we register a "reset_capturings" callmon the capturing manager
# and make sure it gets called
config._cleanup.append(
config.pluginmanager.getplugin("capturemanager").reset_capturings)
import _pytest.config
self.request.addfinalizer(
lambda: _pytest.config.pytest_unconfigure(config))
config = _pytest.config._prepareconfig(args, self.plugins)
# we don't know what the test will do with this half-setup config
# object and thus we make sure it gets unconfigured properly in any
# case (otherwise capturing could still be active, for example)
def ensure_unconfigure():
if hasattr(config.pluginmanager, "_config"):
config.pluginmanager.do_unconfigure(config)
config.pluginmanager.ensure_shutdown()
self.request.addfinalizer(ensure_unconfigure)
return config
def parseconfigure(self, *args):
config = self.parseconfig(*args)
config.pluginmanager.do_configure(config)
config.do_configure()
self.request.addfinalizer(lambda:
config.pluginmanager.do_unconfigure(config))
config.do_unconfigure())
return config
def getitem(self, source, funcname="test_func"):
@ -428,17 +424,6 @@ class TmpTestdir:
return py.std.subprocess.Popen(cmdargs,
stdout=stdout, stderr=stderr, **kw)
def pytestmain(self, *args, **kwargs):
class ResetCapturing:
@pytest.mark.trylast
def pytest_unconfigure(self, config):
capman = config.pluginmanager.getplugin("capturemanager")
capman.reset_capturings()
plugins = kwargs.setdefault("plugins", [])
rc = ResetCapturing()
plugins.append(rc)
return pytest.main(*args, **kwargs)
def run(self, *cmdargs):
return self._run(*cmdargs)
@ -482,12 +467,12 @@ class TmpTestdir:
def _getpybinargs(self, scriptname):
if not self.request.config.getvalue("notoolsonpath"):
# XXX we rely on script refering to the correct environment
# XXX we rely on script referring 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,)
# because on windows the script is e.g. a py.test.exe
return (py.std.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:
@ -524,22 +509,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")
pexpect = pytest.importorskip("pexpect", "3.0")
if hasattr(sys, 'pypy_version_info') and '64' in py.std.platform.machine():
pytest.skip("pypy-64 bit not supported")
if sys.platform == "darwin":
pytest.xfail("pexpect does not work reliably on darwin?!")
if sys.platform.startswith("freebsd"):
pytest.xfail("pexpect does not work reliably on freebsd")
logfile = self.tmpdir.join("spawn.out")
child = pexpect.spawn(cmd, logfile=logfile.open("w"))
logfile = self.tmpdir.join("spawn.out").open("wb")
child = pexpect.spawn(cmd, logfile=logfile)
self.request.addfinalizer(logfile.close)
child.timeout = expect_timeout
return child
@ -670,6 +656,12 @@ class LineMatcher:
else:
raise ValueError("line %r not found in output" % line)
def get_lines_after(self, fnline):
for i, line in enumerate(self.lines):
if fnline == line or fnmatch(line, fnline):
return self.lines[i+1:]
raise ValueError("line %r not found in output" % fnline)
def fnmatch_lines(self, lines2):
def show(arg1, arg2):
py.builtin.print_(arg1, arg2, file=py.std.sys.stderr)
@ -696,4 +688,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,14 +1,14 @@
""" recording warnings during test function execution. """
import py
import sys, os
import sys
def pytest_funcarg__recwarn(request):
"""Return a WarningsRecorder instance that provides these methods:
* ``pop(category=None)``: return last warning matching the category.
* ``clear()``: clear list of warnings
See http://docs.python.org/library/warnings.html for information
on warning categories.
"""

View File

@ -6,7 +6,7 @@ import py
def pytest_addoption(parser):
group = parser.getgroup("terminal reporting", "resultlog plugin options")
group.addoption('--resultlog', '--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,6 +1,8 @@
""" basic collect and runtest protocol implementations """
import py, sys
import py
import pytest
import sys
from time import time
from py._code.code import TerminalRepr
@ -193,11 +195,10 @@ def pytest_runtest_makereport(item, call):
outcome = "passed"
longrepr = None
else:
excinfo = call.excinfo
if not isinstance(excinfo, py.code.ExceptionInfo):
outcome = "failed"
longrepr = excinfo
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)
@ -328,9 +329,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 +420,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 +440,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 +460,25 @@ fail.Exception = Failed
def importorskip(modname, minversion=None):
""" return imported module if it has a higher __version__ than the
optionally specified 'minversion' - otherwise call py.test.skip()
with a message detailing the mismatch.
""" return imported module if it has at least "minversion" as its
__version__ attribute. If no minversion is specified the a skip
is only triggered if the module can not be imported.
Note that version comparison only works with simple version strings
like "1.2.3" but not "1.2.3.dev1" or others.
"""
__tracebackhide__ = True
compile(modname, '', 'eval') # to catch syntaxerrors
try:
__import__(modname)
except ImportError:
py.test.skip("could not import %r" %(modname,))
skip("could not import %r" %(modname,))
mod = sys.modules[modname]
if minversion is None:
return mod
verattr = getattr(mod, '__version__', None)
if isinstance(minversion, str):
minver = minversion.split(".")
else:
minver = list(minversion)
if verattr is None or verattr.split(".") < minver:
py.test.skip("module %r has __version__ %r, required is: %r" %(
modname, verattr, minversion))
def intver(verstring):
return [int(x) for x in verstring.split(".")]
if verattr is None or intver(verattr) < intver(minversion):
skip("module %r has __version__ %r, required is: %r" %(
modname, verattr, minversion))
return mod

View File

@ -10,6 +10,14 @@ def pytest_addoption(parser):
help="run tests even if they are marked xfail")
def pytest_configure(config):
if config.option.runxfail:
old = pytest.xfail
config._cleanup.append(lambda: setattr(pytest, "xfail", old))
def nop(*args, **kwargs):
pass
nop.Exception = XFailed
setattr(pytest, "xfail", nop)
config.addinivalue_line("markers",
"skipif(condition): skip the given test function if eval(condition) "
"results in a True value. Evaluation happens within the "
@ -29,7 +37,7 @@ 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."""
@ -121,7 +129,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 +141,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 +150,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
@ -209,7 +217,6 @@ def pytest_terminal_summary(terminalreporter):
tr._tw.line(line)
def show_simple(terminalreporter, lines, stat, format):
tw = terminalreporter._tw
failed = terminalreporter.stats.get(stat)
if failed:
for rep in failed:

View File

@ -6,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,6 @@ This is a good source for looking at the various reporting hooks.
import pytest
import py
import sys
import os
def pytest_addoption(parser):
group = parser.getgroup("terminal reporting", "reporting", after="general")
@ -30,24 +29,14 @@ def pytest_addoption(parser):
group._addoption('--fulltrace', '--full-trace',
action="store_true", default=False,
help="don't cut any tracebacks (default is to cut).")
group._addoption('--color', metavar="color",
action="store", dest="color", default='auto',
choices=['yes', 'no', 'auto'],
help="color terminal output (yes/no/auto).")
def pytest_configure(config):
config.option.verbose -= config.option.quiet
# we try hard to make printing resilient against
# later changes on FD level.
stdout = py.std.sys.stdout
if hasattr(os, 'dup') and hasattr(stdout, 'fileno'):
try:
newstdout = py.io.dupfile(stdout, buffering=1,
encoding=stdout.encoding)
except ValueError:
pass
else:
config._cleanup.append(lambda: newstdout.close())
assert stdout.encoding == newstdout.encoding
stdout = newstdout
reporter = TerminalReporter(config, stdout)
reporter = TerminalReporter(config, sys.stdout)
config.pluginmanager.register(reporter, 'terminalreporter')
if config.option.debug or config.option.traceconfig:
def mywriter(tags, args):
@ -99,7 +88,11 @@ class TerminalReporter:
self.startdir = self.curdir = py.path.local()
if file is None:
file = py.std.sys.stdout
self._tw = py.io.TerminalWriter(file)
self._tw = self.writer = py.io.TerminalWriter(file)
if self.config.option.color == 'yes':
self._tw.hasmarkup = True
if self.config.option.color == 'no':
self._tw.hasmarkup = False
self.currentfspath = None
self.reportchars = getreportopt(config)
self.hasmarkup = self._tw.hasmarkup
@ -147,6 +140,12 @@ class TerminalReporter:
self.ensure_newline()
self._tw.sep(sep, title, **markup)
def section(self, title, sep="=", **kw):
self._tw.sep(sep, title, **kw)
def line(self, msg, **kw):
self._tw.line(msg, **kw)
def pytest_internalerror(self, excrepr):
for line in str(excrepr).split("\n"):
self.write_line("INTERNALERROR> " + line)
@ -178,6 +177,7 @@ class TerminalReporter:
res = self.config.hook.pytest_report_teststatus(report=rep)
cat, letter, word = res
self.stats.setdefault(cat, []).append(rep)
self._tests_ran = True
if not letter and not word:
# probably passed setup/teardown
return
@ -259,7 +259,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)
@ -334,6 +334,7 @@ class TerminalReporter:
if exitstatus in (0, 1, 2, 4):
self.summary_errors()
self.summary_failures()
self.summary_hints()
self.config.hook.pytest_terminal_summary(terminalreporter=self)
if exitstatus == 2:
self._report_keyboardinterrupt()
@ -399,6 +400,11 @@ class TerminalReporter:
l.append(x)
return l
def summary_hints(self):
if self.config.option.traceconfig:
for hint in self.config.pluginmanager._hints:
self._tw.line("hint: %s" % hint)
def summary_failures(self):
if self.config.option.tbstyle != "no":
reports = self.getreports('failed')

View File

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

View File

@ -1,6 +1,6 @@
""" discovery and running of std-library "unittest" style tests. """
import pytest, py
import sys, pdb
import sys
# for transfering markers
from _pytest.python import transfer_markers
@ -50,8 +50,6 @@ class UnitTestCase(pytest.Class):
x = getattr(self.obj, name)
funcobj = getattr(x, 'im_func', x)
transfer_markers(funcobj, cls, module)
if hasattr(funcobj, 'todo'):
pytest.mark.xfail(reason=str(funcobj.todo))(funcobj)
yield TestCaseFunction(name, parent=self)
foundsomething = True
@ -70,10 +68,6 @@ class TestCaseFunction(pytest.Function):
def setup(self):
self._testcase = self.parent.obj(self.name)
self._obj = getattr(self._testcase, self.name)
if hasattr(self._testcase, 'skip'):
pytest.skip(self._testcase.skip)
if hasattr(self._obj, 'skip'):
pytest.skip(self._obj.skip)
if hasattr(self._testcase, 'setup_method'):
self._testcase.setup_method(self._obj)
if hasattr(self, "_request"):
@ -150,7 +144,10 @@ def pytest_runtest_makereport(item, call):
if isinstance(item, TestCaseFunction):
if item._excinfo:
call.excinfo = item._excinfo.pop(0)
del call.result
try:
del call.result
except AttributeError:
pass
# twisted trial support
def pytest_runtest_protocol(item, __multicall__):

View File

@ -1,10 +1,12 @@
import sys
if __name__ == '__main__':
import cProfile
import py
import 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,16 +1,5 @@
{% extends "!layout.html" %}
{% block relbaritems %}
{{ super() }}
<g:plusone></g:plusone>
<iframe style="border: 0; margin: 0; padding: 0;"
src="https://www.gittip.com/hpk42/widget.html"
width="48pt" height="22pt"></iframe>
{% endblock %}
{% block footer %}
{{ super() }}
<script type="text/javascript">
@ -26,5 +15,4 @@
})();
</script>
<script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script>
{% endblock %}

View File

@ -0,0 +1,10 @@
<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="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,555 @@
/*
* 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' %}
{% set link_color = '#490' %}
{% set link_hover_color = '#9c0' %}
{% 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,11 @@ Release announcements
.. toctree::
:maxdepth: 2
release-2.5.1
release-2.5.0
release-2.4.2
release-2.4.1
release-2.4.0
release-2.3.5
release-2.3.4
release-2.3.3

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

@ -1,7 +1,7 @@
.. _apiref:
py.test reference documentation
pytest reference documentation
================================================
.. toctree::
@ -11,6 +11,7 @@ py.test reference documentation
customize.txt
assert.txt
fixture.txt
yieldfixture.txt
parametrize.txt
xunit_setup.txt
capture.txt

View File

@ -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,23 +26,23 @@ 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 linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 1 items
test_assert1.py F
================================= FAILURES =================================
______________________________ test_function _______________________________
def test_function():
> assert f() == 4
E assert 3 == 4
E + where 3 = f()
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
@ -102,7 +102,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,14 +116,14 @@ if you run this module::
$ py.test test_assert2.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.5
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 1 items
test_assert2.py F
================================= FAILURES =================================
___________________________ test_set_comparison ____________________________
def test_set_comparison():
set1 = set("1308")
set2 = set("8035")
@ -133,7 +133,7 @@ if you run this module::
E '1'
E Extra items in the right set:
E '5'
test_assert2.py:5: AssertionError
========================= 1 failed in 0.01 seconds =========================
@ -175,22 +175,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 +205,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 +250,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

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

View File

@ -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

@ -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,21 +64,21 @@ 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 linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 2 items
test_module.py .F
================================= FAILURES =================================
________________________________ test_func2 ________________________________
def test_func2():
> assert False
E assert False
test_module.py:9: AssertionError
----------------------------- Captured stdout ------------------------------
setting up <function test_func2 at 0x2d79f50>
setting up <function test_func2 at 0x1eb37d0>
==================== 1 failed, 1 passed in 0.01 seconds ====================
Accessing captured output from a test function
@ -105,7 +105,7 @@ 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

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.5.1"
release = "2.5.1"
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'2013, 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 = []
@ -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,7 +220,7 @@ 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
@ -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

@ -16,7 +16,7 @@ Full pytest documentation
plugins
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
----------------------------------------------
@ -105,7 +105,7 @@ Builtin configuration file options
[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 linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 1 items
mymodule.py .
========================= 1 passed in 0.02 seconds =========================
========================= 1 passed in 0.01 seconds =========================
It is possible to use fixtures using the ``getfixture`` helper::
# content of example.rst
>>> tmp = getfixture('tmpdir')
>>> ...
>>>
Also, :ref:`usefixtures` and :ref:`autouse` fixtures are supported
when executing text doctest files.

View File

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

View File

@ -28,24 +28,24 @@ 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
platform linux2 -- Python 2.7.3 -- pytest-2.5.1 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 3 items
test_server.py:3: test_send_http PASSED
=================== 2 tests deselected by "-m 'webtest'" ===================
================== 1 passed, 2 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
platform linux2 -- Python 2.7.3 -- pytest-2.5.1 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 3 items
test_server.py:6: test_something_quick PASSED
test_server.py:8: test_another PASSED
================= 1 tests deselected by "-m 'not webtest'" =================
================== 2 passed, 1 deselected in 0.01 seconds ==================
@ -61,11 +61,11 @@ 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
platform linux2 -- Python 2.7.3 -- pytest-2.5.1 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 3 items
test_server.py:3: test_send_http PASSED
====================== 2 tests deselected by '-khttp' ======================
================== 1 passed, 2 deselected in 0.01 seconds ==================
@ -73,12 +73,12 @@ 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
platform linux2 -- Python 2.7.3 -- pytest-2.5.1 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 3 items
test_server.py:6: test_something_quick PASSED
test_server.py:8: test_another PASSED
================= 1 tests deselected by '-knot send_http' ==================
================== 2 passed, 1 deselected in 0.01 seconds ==================
@ -86,15 +86,26 @@ 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
platform linux2 -- Python 2.7.3 -- pytest-2.5.1 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 3 items
test_server.py:3: test_send_http PASSED
test_server.py:6: test_something_quick PASSED
================= 1 tests deselected by '-khttp or quick' ==================
================== 2 passed, 1 deselected in 0.01 seconds ==================
.. note::
If you are using expressions such as "X and Y" then both X and Y
need to be simple non-keyword names. For example, "pass" or "from"
will result in SyntaxErrors because "-k" evaluates the expression.
However, if the "-k" argument is a simple string, no such restrictions
apply. Also "-k 'not STRING'" has no restrictions. You can also
specify numbers like "-k 1.3" to match tests which are parametrized
with the float "1.3".
Registering markers
-------------------------------------
@ -113,19 +124,19 @@ You can ask which markers exist for your test suite - the list includes our just
$ py.test --markers
@pytest.mark.webtest: mark a test as a webtest.
@pytest.mark.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.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.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures
@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
@pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
@pytest.mark.trylast: mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
For an example on how to add and work with markers from a plugin, see
:ref:`adding a custom marker from a plugin`.
@ -139,8 +150,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 +246,7 @@ specifies via named environments::
"env(name): mark test to run only on named environment")
def pytest_runtest_setup(item):
envmarker = item.keywords.get("env", None)
envmarker = item.get_marker("env")
if envmarker is not None:
envname = envmarker.args[0]
if envname != item.config.getoption("-E"):
@ -255,42 +266,42 @@ the test needs::
$ py.test -E stage2
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.5
platform linux2 -- Python 2.7.3 -- pytest-2.5.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 linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 1 items
test_someenv.py .
========================= 1 passed in 0.01 seconds =========================
The ``--markers`` option always gives you a list of available markers::
$ py.test --markers
@pytest.mark.env(name): mark test to run only on named environment
@pytest.mark.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.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.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures
@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
@pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
@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 +329,7 @@ test function. From a conftest file we can read it like this::
import sys
def pytest_runtest_setup(item):
g = item.keywords.get("glob", None)
g = item.get_marker("glob")
if g is not None:
for info in g:
print ("glob args=%s kwargs=%s" %(info.args, info.kwargs))
@ -326,11 +337,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 +352,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 +365,7 @@ for your particular platform, you could use the following plugin::
def pytest_runtest_setup(item):
if isinstance(item, item.Function):
plat = sys.platform
if plat not in item.keywords:
if not item.get_marker(plat):
if ALL.intersection(item.keywords):
pytest.skip("cannot run on platform %s" %(plat))
@ -383,28 +395,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 linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 4 items
test_plat.py s.s.
========================= short test summary info ==========================
SKIP [2] /tmp/doc-exec-273/conftest.py:12: cannot run on platform linux2
SKIP [2] /tmp/doc-exec-63/conftest.py:12: cannot run on platform linux2
=================== 2 passed, 2 skipped in 0.01 seconds ====================
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 linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 4 items
test_plat.py .
=================== 3 tests deselected by "-m 'linux2'" ====================
================== 1 passed, 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 +435,7 @@ at this test module::
def test_interface_complex():
assert 0
def test_event_simple():
assert 0
@ -434,24 +446,24 @@ 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 linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 4 items
test_module.py FF
================================= FAILURES =================================
__________________________ test_interface_simple ___________________________
test_module.py:3: in test_interface_simple
@ -468,11 +480,11 @@ 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 linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 4 items
test_module.py FFF
================================= FAILURES =================================
__________________________ test_interface_simple ___________________________
test_module.py:3: in test_interface_simple
@ -487,4 +499,4 @@ or to select both "event" and "interface" tests::
> assert 0
E assert 0
============= 1 tests deselected by "-m 'interface or event'" ==============
================== 3 failed, 1 deselected in 0.02 seconds ==================
================== 3 failed, 1 deselected in 0.01 seconds ==================

View File

@ -2,7 +2,8 @@
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']
@pytest.fixture(params=pythonlist)
@ -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 linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 2 items
test_simple.yml .F
@ -37,7 +37,7 @@ now execute the test specification::
usecase execution failed
spec failed: 'some': 'other'
no further details known at this point.
==================== 1 failed, 1 passed in 0.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,7 +56,7 @@ consulted when reporting in ``verbose`` mode::
nonpython $ py.test -v
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.5 -- /home/hpk/p/pytest/.tox/regen/bin/python
platform linux2 -- Python 2.7.3 -- pytest-2.5.1 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 2 items
test_simple.yml:1: usecase: ok PASSED
@ -67,17 +67,17 @@ consulted when reporting in ``verbose`` mode::
usecase execution failed
spec failed: 'some': 'other'
no further details known at this point.
==================== 1 failed, 1 passed in 0.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 linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 2 items
<YamlFile 'test_simple.yml'>
<YamlItem 'ok'>
<YamlItem 'hello'>
============================= in 0.05 seconds =============================
============================= in 0.02 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,11 +106,11 @@ 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 linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 4 items
test_scenarios.py ....
========================= 4 passed in 0.01 seconds =========================
If you just collect tests you'll also nicely see 'advanced' and 'basic' as variants for the test function::
@ -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 linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 4 items
<Module 'test_scenarios.py'>
<Class 'TestSampleWithScenarios'>
@ -125,7 +127,7 @@ If you just collect tests you'll also nicely see 'advanced' and 'basic' as varia
<Function 'test_demo2[basic]'>
<Function 'test_demo1[advanced]'>
<Function 'test_demo2[advanced]'>
============================= in 0.01 seconds =============================
Note that we told ``metafunc.parametrize()`` that your scenario values
@ -180,12 +182,12 @@ 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 linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 2 items
<Module 'test_backends.py'>
<Function 'test_db_initialized[d1]'>
<Function 'test_db_initialized[d2]'>
============================= in 0.00 seconds =============================
And then when we run the test::
@ -194,16 +196,17 @@ And then when we run the test::
.F
================================= FAILURES =================================
_________________________ test_db_initialized[d2] __________________________
db = <conftest.DB2 instance at 0x2038f80>
db = <conftest.DB2 instance at 0x12d4128>
def test_db_initialized(db):
# a dummy test
if db.__class__.__name__ == "DB2":
> pytest.fail("deliberately failing for demo purposes")
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] ________________________
self = <test_parametrize.TestClass instance at 0x1338f80>, a = 1, b = 2
________________________ TestClass.test_equals[2-1] ________________________
self = <test_parametrize.TestClass instance at 0x14493f8>, a = 1, b = 2
def test_equals(self, a, b):
> assert a == b
E assert 1 == 2
test_parametrize.py:18: AssertionError
1 failed, 2 passed in 0.01 seconds
Indirect parametrization with multiple fixtures
--------------------------------------------------------------
@ -278,6 +282,7 @@ Running it results in some skips if we don't have all the python interpreters in
............sss............sss............sss............ssssssssssssssssss
========================= short test summary info ==========================
SKIP [27] /home/hpk/p/pytest/doc/en/example/multipython.py:21: 'python2.8' not found
48 passed, 27 skipped in 1.34 seconds
Indirect parametrization of optional implementations/imports
--------------------------------------------------------------------
@ -285,7 +290,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,26 +329,26 @@ 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 linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 2 items
test_module.py .s
========================= short test summary info ==========================
SKIP [1] /tmp/doc-exec-275/conftest.py:10: could not import 'opt2'
SKIP [1] /tmp/doc-exec-65/conftest.py:10: could not import 'opt2'
=================== 1 passed, 1 skipped in 0.01 seconds ====================
You'll see that we don't have a ``opt2`` module and thus the second test run
of our ``test_func1`` was skipped. A few notes:
- the fixture functions in the ``conftest.py`` file are "session-scoped" because we
don't need to import more than once
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,20 +43,26 @@ 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 linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 2 items
<Module 'check_myapp.py'>
<Class 'CheckMyApp'>
<Instance '()'>
<Function 'check_simple'>
<Function 'check_complex'>
============================= 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 linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 3 items
<Module 'pythoncollection.py'>
<Function 'test_function'>
@ -90,7 +96,7 @@ You can always peek at the collection tree without running tests like this::
<Instance '()'>
<Function 'test_method'>
<Function 'test_anothermethod'>
============================= in 0.01 seconds =============================
customizing test collection to find all .py files
@ -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,16 +136,16 @@ 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
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 1 items
<Module 'pkg/module_py2.py'>
<Function 'test_only_on_python2'>
============================= in 0.01 seconds =============================
If you run with a Python3 interpreter the moduled added through the conftest.py file will not be considered for test collection.

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,84 +13,84 @@ 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 linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 39 items
failure_demo.py FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
================================= FAILURES =================================
____________________________ test_generative[0] ____________________________
param1 = 3, param2 = 6
def test_generative(param1, param2):
> assert param1 * 2 < param2
E assert (3 * 2) < 6
failure_demo.py:15: AssertionError
_________________________ TestFailing.test_simple __________________________
self = <failure_demo.TestFailing object at 0x1445e10>
self = <failure_demo.TestFailing object at 0x12d9250>
def test_simple(self):
def f():
return 42
def g():
return 43
> 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 f at 0x1278b90>()
E + and 43 = <function g at 0x1278c08>()
failure_demo.py:28: AssertionError
____________________ TestFailing.test_simple_multiline _____________________
self = <failure_demo.TestFailing object at 0x135a1d0>
self = <failure_demo.TestFailing object at 0x1287210>
def test_simple_multiline(self):
otherfunc_multi(
42,
> 6*9)
failure_demo.py:33:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
failure_demo.py:33:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
a = 42, b = 54
def otherfunc_multi(a,b):
> assert (a ==
b)
E assert 42 == 54
failure_demo.py:11: AssertionError
___________________________ TestFailing.test_not ___________________________
self = <failure_demo.TestFailing object at 0x1458ed0>
self = <failure_demo.TestFailing object at 0x12c6e10>
def test_not(self):
def f():
return 42
> assert not f()
E assert not 42
E + where 42 = <function f at 0x137caa0>()
E + where 42 = <function f at 0x12861b8>()
failure_demo.py:38: AssertionError
_________________ TestSpecialisedExplanations.test_eq_text _________________
self = <failure_demo.TestSpecialisedExplanations object at 0x14451d0>
self = <failure_demo.TestSpecialisedExplanations object at 0x1290c50>
def test_eq_text(self):
> assert 'spam' == 'eggs'
E assert 'spam' == 'eggs'
E - spam
E + eggs
failure_demo.py:42: AssertionError
_____________ TestSpecialisedExplanations.test_eq_similar_text _____________
self = <failure_demo.TestSpecialisedExplanations object at 0x1458c90>
self = <failure_demo.TestSpecialisedExplanations object at 0x12877d0>
def test_eq_similar_text(self):
> assert 'foo 1 bar' == 'foo 2 bar'
E assert 'foo 1 bar' == 'foo 2 bar'
@ -98,12 +98,12 @@ get on the terminal - we are working on that):
E ? ^
E + foo 2 bar
E ? ^
failure_demo.py:45: AssertionError
____________ TestSpecialisedExplanations.test_eq_multiline_text ____________
self = <failure_demo.TestSpecialisedExplanations object at 0x1434390>
self = <failure_demo.TestSpecialisedExplanations object at 0x12de1d0>
def test_eq_multiline_text(self):
> assert 'foo\nspam\nbar' == 'foo\neggs\nbar'
E assert 'foo\nspam\nbar' == 'foo\neggs\nbar'
@ -111,12 +111,12 @@ get on the terminal - we are working on that):
E - spam
E + eggs
E bar
failure_demo.py:48: AssertionError
______________ TestSpecialisedExplanations.test_eq_long_text _______________
self = <failure_demo.TestSpecialisedExplanations object at 0x1459f50>
self = <failure_demo.TestSpecialisedExplanations object at 0x143b5d0>
def test_eq_long_text(self):
a = '1'*100 + 'a' + '2'*100
b = '1'*100 + 'b' + '2'*100
@ -128,12 +128,12 @@ get on the terminal - we are working on that):
E ? ^
E + 1111111111b222222222
E ? ^
failure_demo.py:53: AssertionError
_________ TestSpecialisedExplanations.test_eq_long_text_multiline __________
self = <failure_demo.TestSpecialisedExplanations object at 0x135a790>
self = <failure_demo.TestSpecialisedExplanations object at 0x1287810>
def test_eq_long_text_multiline(self):
a = '1\n'*100 + 'a' + '2\n'*100
b = '1\n'*100 + 'b' + '2\n'*100
@ -152,50 +152,50 @@ get on the terminal - we are working on that):
E 2
E 2
E 2
failure_demo.py:58: AssertionError
_________________ TestSpecialisedExplanations.test_eq_list _________________
self = <failure_demo.TestSpecialisedExplanations object at 0x138dfd0>
self = <failure_demo.TestSpecialisedExplanations object at 0x12900d0>
def test_eq_list(self):
> assert [0, 1, 2] == [0, 1, 3]
E assert [0, 1, 2] == [0, 1, 3]
E At index 2 diff: 2 != 3
failure_demo.py:61: AssertionError
______________ TestSpecialisedExplanations.test_eq_list_long _______________
self = <failure_demo.TestSpecialisedExplanations object at 0x135a990>
self = <failure_demo.TestSpecialisedExplanations object at 0x12c62d0>
def test_eq_list_long(self):
a = [0]*100 + [1] + [3]*100
b = [0]*100 + [2] + [3]*100
> assert a == b
E assert [0, 0, 0, 0, 0, 0, ...] == [0, 0, 0, 0, 0, 0, ...]
E At index 100 diff: 1 != 2
failure_demo.py:66: AssertionError
_________________ TestSpecialisedExplanations.test_eq_dict _________________
self = <failure_demo.TestSpecialisedExplanations object at 0x1459310>
self = <failure_demo.TestSpecialisedExplanations object at 0x12deb50>
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:
E {'c': 0}
E Right contains more items:
E {'d': 0}
failure_demo.py:69: AssertionError
_________________ TestSpecialisedExplanations.test_eq_set __________________
self = <failure_demo.TestSpecialisedExplanations object at 0x1434310>
self = <failure_demo.TestSpecialisedExplanations object at 0x128b4d0>
def test_eq_set(self):
> assert set([0, 10, 11, 12]) == set([0, 20, 21])
E assert set([0, 10, 11, 12]) == set([0, 20, 21])
@ -206,31 +206,31 @@ get on the terminal - we are working on that):
E Extra items in the right set:
E 20
E 21
failure_demo.py:72: AssertionError
_____________ TestSpecialisedExplanations.test_eq_longer_list ______________
self = <failure_demo.TestSpecialisedExplanations object at 0x138ded0>
self = <failure_demo.TestSpecialisedExplanations object at 0x12c6b10>
def test_eq_longer_list(self):
> assert [1,2] == [1,2,3]
E assert [1, 2] == [1, 2, 3]
E Right contains more items, first extra item: 3
failure_demo.py:75: AssertionError
_________________ TestSpecialisedExplanations.test_in_list _________________
self = <failure_demo.TestSpecialisedExplanations object at 0x1459e10>
self = <failure_demo.TestSpecialisedExplanations object at 0x143b650>
def test_in_list(self):
> assert 1 in [0, 2, 3, 4, 5]
E assert 1 in [0, 2, 3, 4, 5]
failure_demo.py:78: AssertionError
__________ TestSpecialisedExplanations.test_not_in_text_multiline __________
self = <failure_demo.TestSpecialisedExplanations object at 0x1434950>
self = <failure_demo.TestSpecialisedExplanations object at 0x128be10>
def test_not_in_text_multiline(self):
text = 'some multiline\ntext\nwhich\nincludes foo\nand a\ntail'
> assert 'foo' not in text
@ -243,12 +243,12 @@ get on the terminal - we are working on that):
E ? +++
E and a
E tail
failure_demo.py:82: AssertionError
___________ TestSpecialisedExplanations.test_not_in_text_single ____________
self = <failure_demo.TestSpecialisedExplanations object at 0x138dbd0>
self = <failure_demo.TestSpecialisedExplanations object at 0x12d9fd0>
def test_not_in_text_single(self):
text = 'single foo line'
> assert 'foo' not in text
@ -256,58 +256,58 @@ get on the terminal - we are working on that):
E 'foo' is contained here:
E single foo line
E ? +++
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 0x143bdd0>
def test_not_in_text_single_long(self):
text = 'head ' * 50 + 'foo ' + 'tail ' * 20
> assert 'foo' not in text
E assert 'foo' not in 'head head head head hea...ail tail tail tail tail '
E 'foo' is contained here:
E head head foo tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail
E head head foo tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail
E ? +++
failure_demo.py:90: AssertionError
______ TestSpecialisedExplanations.test_not_in_text_single_long_term _______
self = <failure_demo.TestSpecialisedExplanations object at 0x1459650>
self = <failure_demo.TestSpecialisedExplanations object at 0x12c6390>
def test_not_in_text_single_long_term(self):
text = 'head ' * 50 + 'f'*70 + 'tail ' * 20
> assert 'f'*70 not in text
E assert 'fffffffffff...ffffffffffff' not in 'head head he...l tail tail '
E 'ffffffffffffffffff...fffffffffffffffffff' is contained here:
E head head fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffftail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail
E head head fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffftail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail
E ? ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
failure_demo.py:94: AssertionError
______________________________ test_attribute ______________________________
def test_attribute():
class Foo(object):
b = 1
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.Foo object at 0x1287790>.b
failure_demo.py:101: AssertionError
_________________________ test_attribute_instance __________________________
def test_attribute_instance():
class Foo(object):
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.Foo object at 0x12c6bd0>.b
E + where <failure_demo.Foo object at 0x12c6bd0> = <class 'failure_demo.Foo'>()
failure_demo.py:107: AssertionError
__________________________ test_attribute_failure __________________________
def test_attribute_failure():
class Foo(object):
def _get_b(self):
@ -315,19 +315,19 @@ get on the terminal - we are working on that):
b = property(_get_b)
i = Foo()
> assert i.b == 2
failure_demo.py:116:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <failure_demo.Foo object at 0x1434150>
failure_demo.py:116:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <failure_demo.Foo object at 0x12daed0>
def _get_b(self):
> raise Exception('Failed to get attrib')
E Exception: Failed to get attrib
failure_demo.py:113: Exception
_________________________ test_attribute_multiple __________________________
def test_attribute_multiple():
class Foo(object):
b = 1
@ -335,78 +335,78 @@ 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.Foo object at 0x128bcd0>.b
E + where <failure_demo.Foo object at 0x128bcd0> = <class 'failure_demo.Foo'>()
E + and 2 = <failure_demo.Bar object at 0x128b050>.b
E + where <failure_demo.Bar object at 0x128b050> = <class 'failure_demo.Bar'>()
failure_demo.py:124: AssertionError
__________________________ TestRaises.test_raises __________________________
self = <failure_demo.TestRaises instance at 0x13a0d88>
self = <failure_demo.TestRaises instance at 0x145c7e8>
def test_raises(self):
s = 'qwe'
> raises(TypeError, "int(s)")
failure_demo.py:133:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
failure_demo.py:133:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
> 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/local/lib/python2.7/site-packages/_pytest/python.py:983>:1: ValueError
______________________ TestRaises.test_raises_doesnt _______________________
self = <failure_demo.TestRaises instance at 0x145fcf8>
self = <failure_demo.TestRaises instance at 0x1455f38>
def test_raises_doesnt(self):
> raises(IOError, "int('3')")
E Failed: DID NOT RAISE
failure_demo.py:136: Failed
__________________________ TestRaises.test_raise ___________________________
self = <failure_demo.TestRaises instance at 0x13a9ea8>
self = <failure_demo.TestRaises instance at 0x1453998>
def test_raise(self):
> raise ValueError("demo error")
E ValueError: demo error
failure_demo.py:139: ValueError
________________________ TestRaises.test_tupleerror ________________________
self = <failure_demo.TestRaises instance at 0x13843f8>
self = <failure_demo.TestRaises instance at 0x1465560>
def test_tupleerror(self):
> a,b = [1]
E ValueError: need more than 1 value to unpack
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 instance at 0x1465758>
def test_reinterpret_fails_with_print_for_the_fun_of_it(self):
l = [1,2,3]
print ("l is %r" % l)
> a,b = l.pop()
E TypeError: 'int' object is not iterable
failure_demo.py:147: TypeError
----------------------------- Captured stdout ------------------------------
l is [1, 2, 3]
________________________ TestRaises.test_some_error ________________________
self = <failure_demo.TestRaises instance at 0x139d290>
self = <failure_demo.TestRaises instance at 0x1468ab8>
def test_some_error(self):
> if namenotexi:
E NameError: global name 'namenotexi' is not defined
failure_demo.py:150: NameError
____________________ test_dynamic_compile_shows_nicely _____________________
def test_dynamic_compile_shows_nicely():
src = 'def foo():\n assert 1 == 0\n'
name = 'abc-123'
@ -415,132 +415,132 @@ get on the terminal - we are working on that):
py.builtin.exec_(code, module.__dict__)
py.std.sys.modules[name] = module
> module.foo()
failure_demo.py:165:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
failure_demo.py:165:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
def foo():
> assert 1 == 0
E assert 1 == 0
<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 instance at 0x1442908>
def test_complex_error(self):
def f():
return 44
def g():
return 43
> somefunc(f(), g())
failure_demo.py:175:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
failure_demo.py:175:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
x = 44, y = 43
def somefunc(x,y):
> otherfunc(x,y)
failure_demo.py:8:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
failure_demo.py:8:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
a = 44, b = 43
def otherfunc(a,b):
> assert a==b
E assert 44 == 43
failure_demo.py:5: AssertionError
___________________ TestMoreErrors.test_z1_unpack_error ____________________
self = <failure_demo.TestMoreErrors instance at 0x13a5200>
self = <failure_demo.TestMoreErrors instance at 0x145bab8>
def test_z1_unpack_error(self):
l = []
> a,b = l
E ValueError: need more than 0 values to unpack
failure_demo.py:179: ValueError
____________________ TestMoreErrors.test_z2_type_error _____________________
self = <failure_demo.TestMoreErrors instance at 0x1395290>
self = <failure_demo.TestMoreErrors instance at 0x1444368>
def test_z2_type_error(self):
l = 3
> a,b = l
E TypeError: 'int' object is not iterable
failure_demo.py:183: TypeError
______________________ TestMoreErrors.test_startswith ______________________
self = <failure_demo.TestMoreErrors instance at 0x137f200>
self = <failure_demo.TestMoreErrors instance at 0x146e4d0>
def test_startswith(self):
s = "123"
g = "456"
> assert s.startswith(g)
E assert <built-in method startswith of str object at 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 0x12dfa58>('456')
E + where <built-in method startswith of str object at 0x12dfa58> = '123'.startswith
failure_demo.py:188: AssertionError
__________________ TestMoreErrors.test_startswith_nested ___________________
self = <failure_demo.TestMoreErrors instance at 0x145fb00>
self = <failure_demo.TestMoreErrors instance at 0x143ed40>
def test_startswith_nested(self):
def f():
return "123"
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 0x12dfa58>('456')
E + where <built-in method startswith of str object at 0x12dfa58> = '123'.startswith
E + where '123' = <function f at 0x1286500>()
E + and '456' = <function g at 0x126db18>()
failure_demo.py:195: AssertionError
_____________________ TestMoreErrors.test_global_func ______________________
self = <failure_demo.TestMoreErrors instance at 0x139cd40>
self = <failure_demo.TestMoreErrors instance at 0x1453b90>
def test_global_func(self):
> assert isinstance(globf(42), float)
E assert isinstance(43, float)
E + where 43 = globf(42)
failure_demo.py:198: AssertionError
_______________________ TestMoreErrors.test_instance _______________________
self = <failure_demo.TestMoreErrors instance at 0x13593b0>
self = <failure_demo.TestMoreErrors instance at 0x146b128>
def test_instance(self):
self.x = 6*7
> assert self.x != 42
E assert 42 != 42
E + where 42 = <failure_demo.TestMoreErrors instance at 0x13593b0>.x
E + where 42 = <failure_demo.TestMoreErrors instance at 0x146b128>.x
failure_demo.py:202: AssertionError
_______________________ TestMoreErrors.test_compare ________________________
self = <failure_demo.TestMoreErrors instance at 0x1465d40>
self = <failure_demo.TestMoreErrors instance at 0x1469368>
def test_compare(self):
> assert globf(10) < 5
E assert 11 < 5
E + where 11 = globf(10)
failure_demo.py:205: AssertionError
_____________________ TestMoreErrors.test_try_finally ______________________
self = <failure_demo.TestMoreErrors instance at 0x1456ea8>
self = <failure_demo.TestMoreErrors instance at 0x12c4098>
def test_try_finally(self):
x = 1
try:
> assert x == 0
E assert 1 == 0
failure_demo.py:210: AssertionError
======================== 39 failed in 0.21 seconds =========================
======================== 39 failed in 0.20 seconds =========================

View File

@ -41,9 +41,9 @@ Let's run this without supplying our new option::
F
================================= FAILURES =================================
_______________________________ test_answer ________________________________
cmdopt = 'type1'
def test_answer(cmdopt):
if cmdopt == "type1":
print ("first")
@ -51,10 +51,11 @@ Let's run this without supplying our new option::
print ("second")
> assert 0 # to see what was printed
E assert 0
test_sample.py:6: AssertionError
----------------------------- Captured stdout ------------------------------
first
1 failed in 0.01 seconds
And now with supplying a command line option::
@ -62,9 +63,9 @@ And now with supplying a command line option::
F
================================= FAILURES =================================
_______________________________ test_answer ________________________________
cmdopt = 'type2'
def test_answer(cmdopt):
if cmdopt == "type1":
print ("first")
@ -72,10 +73,11 @@ And now with supplying a command line option::
print ("second")
> assert 0 # to see what was printed
E assert 0
test_sample.py:6: AssertionError
----------------------------- Captured stdout ------------------------------
second
1 failed in 0.01 seconds
You can see that the command line option arrived in our test. This
completes the basic pattern. However, one often rather wants to process
@ -106,9 +108,9 @@ directory with the above conftest.py::
$ py.test
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.5
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 0 items
============================= in 0.00 seconds =============================
.. _`excontrolskip`:
@ -150,24 +152,24 @@ 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 linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 2 items
test_module.py .s
========================= short test summary info ==========================
SKIP [1] /tmp/doc-exec-278/conftest.py:9: need --runslow option to run
SKIP [1] /tmp/doc-exec-68/conftest.py:9: need --runslow option to run
=================== 1 passed, 1 skipped in 0.01 seconds ====================
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 linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 2 items
test_module.py ..
========================= 2 passed in 0.01 seconds =========================
Writing well integrated assertion helpers
@ -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,10 +256,10 @@ 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 linux2 -- Python 2.7.3 -- pytest-2.5.1
project deps: mylib-1.1
collected 0 items
============================= in 0.00 seconds =============================
.. regendoc:wipe
@ -276,20 +279,20 @@ 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 linux2 -- Python 2.7.3 -- pytest-2.5.1 -- /home/hpk/p/pytest/.tox/regen/bin/python
info1: did you know that ...
did you?
collecting ... collected 0 items
============================= in 0.00 seconds =============================
and nothing when run plainly::
$ py.test
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.5
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 0 items
============================= in 0.00 seconds =============================
profiling test duration
@ -319,11 +322,11 @@ 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 linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 3 items
test_some_are_slow.py ...
========================= slowest 3 test durations =========================
0.20s call test_some_are_slow.py::test_funcslow2
0.10s call test_some_are_slow.py::test_funcslow1
@ -380,20 +383,20 @@ If we run this::
$ py.test -rx
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.5
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 4 items
test_step.py .Fx.
================================= FAILURES =================================
____________________ TestUserHandling.test_modification ____________________
self = <test_step.TestUserHandling instance at 0x282b8c0>
self = <test_step.TestUserHandling instance at 0x2758c20>
def test_modification(self):
> assert 0
E assert 0
test_step.py:9: AssertionError
========================= short test summary info ==========================
XFAIL test_step.py::TestUserHandling::()::test_deletion
@ -445,55 +448,55 @@ 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 linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 7 items
test_step.py .Fx.
a/test_db.py F
a/test_db2.py F
b/test_error.py E
================================== ERRORS ==================================
_______________________ ERROR at setup of test_root ________________________
file /tmp/doc-exec-278/b/test_error.py, line 1
file /tmp/doc-exec-68/b/test_error.py, line 1
def test_root(db): # no db here, will error out
fixture 'db' not found
available fixtures: pytestconfig, recwarn, monkeypatch, capfd, capsys, tmpdir
available fixtures: recwarn, capfd, pytestconfig, capsys, tmpdir, monkeypatch
use 'py.test --fixtures [testpath]' for help on them.
/tmp/doc-exec-278/b/test_error.py:1
/tmp/doc-exec-68/b/test_error.py:1
================================= FAILURES =================================
____________________ TestUserHandling.test_modification ____________________
self = <test_step.TestUserHandling instance at 0x26145f0>
self = <test_step.TestUserHandling instance at 0x131fc20>
def test_modification(self):
> assert 0
E assert 0
test_step.py:9: AssertionError
_________________________________ test_a1 __________________________________
db = <conftest.DB instance at 0x26211b8>
db = <conftest.DB instance at 0x1328878>
def test_a1(db):
> assert 0, db # to show value
E AssertionError: <conftest.DB instance at 0x26211b8>
E AssertionError: <conftest.DB instance at 0x1328878>
a/test_db.py:2: AssertionError
_________________________________ test_a2 __________________________________
db = <conftest.DB instance at 0x26211b8>
db = <conftest.DB instance at 0x1328878>
def test_a2(db):
> assert 0, db # to show value
E AssertionError: <conftest.DB instance at 0x26211b8>
E AssertionError: <conftest.DB instance at 0x1328878>
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,42 +545,42 @@ 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 linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 2 items
test_module.py FF
================================= FAILURES =================================
________________________________ test_fail1 ________________________________
tmpdir = local('/tmp/pytest-326/test_fail10')
tmpdir = local('/tmp/pytest-42/test_fail10')
def test_fail1(tmpdir):
> assert 0
E assert 0
test_module.py:2: AssertionError
________________________________ test_fail2 ________________________________
def test_fail2():
> assert 0
E assert 0
test_module.py:4: AssertionError
========================= 2 failed in 0.02 seconds =========================
========================= 2 failed in 0.01 seconds =========================
you will have a "failures" file which contains the failing test ids::
$ cat failures
test_module.py::test_fail1 (/tmp/pytest-326/test_fail10)
test_module.py::test_fail1 (/tmp/pytest-42/test_fail10)
test_module.py::test_fail2
Making test result information available in fixtures
@ -620,60 +623,60 @@ 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
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 3 items
test_module.py EFF
test_module.py Esetting up a test failed! test_module.py::test_setup_fails
Fexecuting test failed test_module.py::test_call_fails
F
================================== ERRORS ==================================
____________________ ERROR at setup of test_setup_fails ____________________
@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.

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,13 @@ 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
.test_method1 called
.test other
.test_unit1 method called
.
4 passed in 0.01 seconds

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,17 +34,23 @@ both styles, moving incrementally from classic to new style, as you
prefer. You can also start out from existing :ref:`unittest.TestCase
style <unittest.TestCase>` or :ref:`nose based <nosestyle>` projects.
.. note::
pytest-2.4 introduced an additional experimental
:ref:`yield fixture mechanism <yieldfixture>` for easier context manager
integration and more linear writing of teardown code.
.. _`funcargs`:
.. _`funcarg mechanism`:
.. _`fixture function`:
.. _`@pytest.fixture`:
.. _`pytest.fixture`:
Fixtures as Function arguments (funcargs)
Fixtures as Function arguments
-----------------------------------------
Test functions can receive fixture objects by naming them as an input
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,36 +70,36 @@ 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.3.5
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 1 items
test_smtpsimple.py F
================================= FAILURES =================================
________________________________ test_ehlo _________________________________
smtp = <smtplib.SMTP instance at 0x226cc20>
smtp = <smtplib.SMTP instance at 0x2ae3469203f8>
def test_ehlo(smtp):
response, msg = smtp.ehlo()
assert response == 250
assert "merlinux" in msg
> assert 0 # for demo purposes
E assert 0
test_smtpsimple.py:12: AssertionError
========================= 1 failed in 0.20 seconds =========================
========================= 1 failed in 0.21 seconds =========================
In the failure traceback we see that the test function was called with a
``smtp`` argument, the ``smtplib.SMTP()`` instance created by the fixture
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
@ -119,11 +125,11 @@ 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.
Funcargs a prime example of dependency injection
"Funcargs" a prime example of dependency injection
---------------------------------------------------
When injecting fixtures to test functions, pytest-2.0 introduced the
@ -142,20 +148,20 @@ functions take the role of the *injector* and test functions are the
.. _smtpshared:
Working with a module-shared fixture
Sharing a fixture across tests in a module (or class/session)
-----------------------------------------------------------------
.. regendoc:wipe
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
@ -174,7 +180,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
@ -188,107 +194,62 @@ inspect what is going on and can now run the tests::
$ py.test test_module.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.5
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 2 items
test_module.py FF
================================= FAILURES =================================
________________________________ test_ehlo _________________________________
smtp = <smtplib.SMTP instance at 0x18a6368>
smtp = <smtplib.SMTP instance at 0x1af5440>
def test_ehlo(smtp):
response = smtp.ehlo()
assert response[0] == 250
assert "merlinux" in response[1]
> assert 0 # for demo purposes
E assert 0
test_module.py:6: AssertionError
________________________________ test_noop _________________________________
smtp = <smtplib.SMTP instance at 0x18a6368>
smtp = <smtplib.SMTP instance at 0x1af5440>
def test_noop(smtp):
response = smtp.noop()
assert response[0] == 250
> assert 0 # for demo purposes
E assert 0
test_module.py:11: AssertionError
========================= 2 failed in 0.26 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
test_module.py:11: AssertionError
========================= 2 failed in 0.17 seconds =========================
You see the two ``assert 0`` failing and more importantly you can also see
that the same (module-scoped) ``smtp`` object was passed into the two
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
.. _`contextfixtures`:
fixture finalization / teardowns
.. _`finalization`:
fixture finalization / executing teardown code
-------------------------------------------------------------
pytest supports two styles of fixture finalization:
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::
- (new in pytest-2.4) by writing a contextmanager fixture
generator where a fixture value is "yielded" and the remainder
of the function serves as the teardown code. This integrates
very well with existing context managers.
- by making a fixture function accept a ``request`` argument
with which it can call ``request.addfinalizer(teardownfunction)``
to register a teardown callback function.
Both methods are strictly equivalent from pytest's view and will
remain supported in the future.
Because a number of people prefer the new contextmanager style
we describe it first::
# content of test_ctxfixture.py
import smtplib
import pytest
@pytest.fixture(scope="module")
def smtp():
smtp = smtplib.SMTP("merlinux.eu")
yield smtp # provide the fixture value
print ("teardown smtp")
smtp.close()
pytest detects that you are using a ``yield`` in your fixture function,
turns it into a generator and:
a) iterates once into it for producing the value
b) iterates a second time for tearing the fixture down, expecting
a StopIteration (which is produced automatically from the Python
runtime when the generator returns).
.. note::
The teardown will execute independently of the status of test functions.
You do not need to write the teardown code into a ``try-finally`` clause
like you would usually do with ``contextlib.contextmanager`` decorated
functions.
If the fixture generator yields a second value pytest will report
an error. Yielding cannot be used for parametrization. We'll describe
ways to implement parametrization further below.
Prior to pytest-2.4 you always needed to register a finalizer by accepting
a ``request`` object into your fixture function and calling
``request.addfinalizer`` with a teardown function::
# content of conftest.py
import smtplib
import pytest
@ -299,24 +260,36 @@ a ``request`` object into your fixture function and calling
def fin():
print ("teardown smtp")
smtp.close()
request.addfinalizer(fin)
return smtp # provide the fixture value
This method of registering a finalizer reads more indirect
than the new contextmanager style syntax because ``fin``
is a callback function.
The ``fin`` function will execute when the last test using
the fixture in the module has finished execution.
Let's execute it::
$ py.test -s -q --tb=no
FFteardown smtp
2 failed in 0.17 seconds
We see that the ``smtp`` instance is finalized after the two
tests finished execution. Note that if we decorated our fixture
function with ``scope='function'`` then fixture setup and cleanup would
occur around each single test. In either case the test
module itself does not need to change or know about these details
of fixture setup.
.. _`request-context`:
Fixtures can interact with the requesting test context
Fixtures can introspect the requesting test context
-------------------------------------------------------------
pytest provides a builtin :py:class:`request <FixtureRequest>` object,
which fixture functions can use to introspect the function, class or module
for which they are invoked.
Further extending the previous ``smtp`` fixture example, let's
read an optional server URL from the module namespace::
Fixture function can accept the :py:class:`request <FixtureRequest>` object
to introspect the "requesting" test function, class or module context.
Further extending the previous ``smtp`` fixture example, let's
read an optional server URL from the test module which uses our fixture::
# content of conftest.py
import pytest
@ -326,28 +299,26 @@ read an optional server URL from the module namespace::
def smtp(request):
server = getattr(request.module, "smtpserver", "merlinux.eu")
smtp = smtplib.SMTP(server)
yield smtp # provide the fixture
print ("finalizing %s" % smtp)
smtp.close()
The finalizing part after the ``yield smtp`` statement will execute
when the last test using the ``smtp`` fixture has executed::
def fin():
print ("finalizing %s (%s)" % (smtp, server))
smtp.close()
return smtp
We use the ``request.module`` attribute to optionally obtain an
``smtpserver`` attribute from the test module. If we just execute
again, nothing much has changed::
$ py.test -s -q --tb=no
FF
finalizing <smtplib.SMTP instance at 0x1e10248>
We see that the ``smtp`` instance is finalized after the two
tests which use it finished executin. If we rather specify
``scope='function'`` then fixture setup and cleanup occurs
around each single test. Note that in either case the test
module itself does not need to change!
2 failed in 0.21 seconds
Let's quickly create another test module that actually sets the
server URL in its module namespace::
# content of test_anothersmtp.py
smtpserver = "mail.python.org" # will be read by smtp fixture
def test_showhelo(smtp):
@ -366,7 +337,6 @@ Running it::
voila! The ``smtp`` fixture function picked up our mail server name
from the module namespace.
.. _`fixture-parametrize`:
Parametrizing a fixture
@ -376,7 +346,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
@ -388,72 +358,77 @@ 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)
yield smtp
print ("finalizing %s" % smtp)
smtp.close()
def fin():
print ("finalizing %s" % smtp)
smtp.close()
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
FFFF
================================= FAILURES =================================
__________________________ test_ehlo[merlinux.eu] __________________________
smtp = <smtplib.SMTP instance at 0x1b38a28>
smtp = <smtplib.SMTP instance at 0x100ac20>
def test_ehlo(smtp):
response = smtp.ehlo()
assert response[0] == 250
assert "merlinux" in response[1]
> assert 0 # for demo purposes
E assert 0
test_module.py:6: AssertionError
__________________________ test_noop[merlinux.eu] __________________________
smtp = <smtplib.SMTP instance at 0x1b38a28>
smtp = <smtplib.SMTP instance at 0x100ac20>
def test_noop(smtp):
response = smtp.noop()
assert response[0] == 250
> assert 0 # for demo purposes
E assert 0
test_module.py:11: AssertionError
________________________ test_ehlo[mail.python.org] ________________________
smtp = <smtplib.SMTP instance at 0x1b496c8>
smtp = <smtplib.SMTP instance at 0x105b638>
def test_ehlo(smtp):
response = smtp.ehlo()
assert response[0] == 250
> assert "merlinux" in response[1]
E assert 'merlinux' in 'mail.python.org\nSIZE 25600000\nETRN\nSTARTTLS\nENHANCEDSTATUSCODES\n8BITMIME\nDSN'
test_module.py:5: AssertionError
----------------------------- Captured stdout ------------------------------
finalizing <smtplib.SMTP instance at 0x100ac20>
________________________ test_noop[mail.python.org] ________________________
smtp = <smtplib.SMTP instance at 0x1b496c8>
smtp = <smtplib.SMTP instance at 0x105b638>
def test_noop(smtp):
response = smtp.noop()
assert response[0] == 250
> assert 0 # for demo purposes
E assert 0
test_module.py:11: AssertionError
4 failed in 6.58 seconds
We see that our two test functions each ran twice, against the different
``smtp`` instances. Note also, that with the ``mail.python.org``
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.
@ -470,7 +445,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:
@ -489,22 +464,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.3.5 -- /home/hpk/p/pytest/.tox/regen/bin/python
platform linux2 -- Python 2.7.3 -- pytest-2.5.1 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 2 items
test_appsetup.py:12: test_smtp_exists[merlinux.eu] PASSED
test_appsetup.py:12: test_smtp_exists[mail.python.org] PASSED
========================= 2 passed in 5.38 seconds =========================
========================= 2 passed in 5.95 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.
@ -519,11 +494,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::
@ -534,8 +509,9 @@ to show the setup/teardown flow::
def modarg(request):
param = request.param
print "create", param
yield param
print ("fin %s" % param)
def fin():
print ("fin %s" % param)
return param
@pytest.fixture(scope="function", params=[1,2])
def otherarg(request):
@ -552,34 +528,32 @@ Let's run the tests in verbose mode and with looking at the print-output::
$ py.test -v -s test_module.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.5 -- /home/hpk/p/pytest/.tox/regen/bin/python
platform linux2 -- Python 2.7.3 -- pytest-2.5.1 -- /home/hpk/p/pytest/.tox/regen/bin/python
collecting ... collected 8 items
test_module.py:16: test_0[1] PASSED
test_module.py:16: test_0[2] PASSED
test_module.py:18: test_1[mod1] PASSED
test_module.py:20: test_2[1-mod1] PASSED
test_module.py:20: test_2[2-mod1] PASSED
test_module.py:18: test_1[mod2] PASSED
test_module.py:20: test_2[1-mod2] PASSED
test_module.py:20: test_2[2-mod2] PASSED
========================= 8 passed in 0.01 seconds =========================
test0 1
test0 2
create mod1
test_module.py:15: test_0[1] test0 1
PASSED
test_module.py:15: test_0[2] test0 2
PASSED
test_module.py:17: test_1[mod1] create mod1
test1 mod1
test2 1 mod1
test2 2 mod1
fin mod1
create mod2
PASSED
test_module.py:19: test_2[1-mod1] test2 1 mod1
PASSED
test_module.py:19: test_2[2-mod1] test2 2 mod1
PASSED
test_module.py:17: test_1[mod2] create mod2
test1 mod2
test2 1 mod2
test2 2 mod2
fin mod2
PASSED
test_module.py:19: test_2[1-mod2] test2 1 mod2
PASSED
test_module.py:19: test_2[2-mod2] test2 2 mod2
PASSED
========================= 8 passed in 0.01 seconds =========================
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.
@ -599,7 +573,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
@ -632,17 +606,18 @@ to verify our fixture is activated and the tests pass::
$ py.test -q
..
2 passed in 0.01 seconds
You can specify multiple fixtures like this::
@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
@ -651,6 +626,7 @@ into an ini-file::
usefixtures = cleandir
.. _`autouse`:
.. _`autouse fixtures`:
autouse fixtures (xUnit setup on steroids)
@ -659,14 +635,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:
@ -702,14 +678,15 @@ If we run it, we get two passing tests::
$ py.test -q
..
2 passed in 0.01 seconds
Here is how autouse fixtures work in other scopes:
- 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
@ -720,7 +697,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
@ -737,7 +714,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
@ -750,3 +727,4 @@ to a :ref:`conftest.py <conftest.py>` file or even separately installable
fixtures functions starts at test classes, then test modules, then
``conftest.py`` files and finally builtin and third party plugins.

View File

@ -1,7 +1,7 @@
Installation and Getting Started
===================================
**Pythons**: Python 2.4-3.3, Jython, PyPy
**Pythons**: Python 2.5-3.3, Jython, PyPy
**Platforms**: Unix/Posix and Windows
@ -23,7 +23,7 @@ Installation options::
To check your installation has installed the correct version::
$ py.test --version
This is py.test version 2.3.5, imported from /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/pytest.py
This is pytest version 2.5.1, imported from /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/pytest.pyc
If you get an error checkout :ref:`installation issues`.
@ -45,23 +45,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 linux2 -- Python 2.7.3 -- pytest-2.5.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 +93,7 @@ Running it with, this time in "quiet" reporting mode::
$ py.test -q test_sysexit.py
.
1 passed in 0.00 seconds
.. todo:: For further ways to assert exceptions see the `raises`
@ -121,15 +122,16 @@ run the module by passing its filename::
.F
================================= FAILURES =================================
____________________________ TestClass.test_two ____________________________
self = <test_class.TestClass instance at 0x315b488>
self = <test_class.TestClass instance at 0x2b57dd0>
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,7 +142,7 @@ 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
@ -149,24 +151,25 @@ resources, for example a unique temporary directory::
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')
tmpdir = local('/tmp/pytest-38/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
/tmp/pytest-38/test_needsfiles0
1 failed in 0.04 seconds
Before the test runs, a unique-per-test-invocation temporary directory
was created. More info at :ref:`tmpdir handling`.
@ -183,7 +186,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 +221,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

@ -8,52 +8,175 @@ Good Integration Practises
Work with virtual environments
-----------------------------------------------------------
We recommend to use virtualenv_ environments and use easy_install_
(or pip_) for installing your application dependencies as well as
the ``pytest`` package itself. This way you will get a much more reproducible
environment. A good tool to help you automate test runs against multiple
dependency configurations or Python interpreters is `tox`_.
We recommend to use virtualenv_ environments and use pip_
(or easy_install_) for installing your application and any dependencies
as well as the ``pytest`` package itself. This way you will get an isolated
and reproducible environment. Given you have installed virtualenv_
and execute it from the command line, here is an example session for unix
or windows::
virtualenv . # create a virtualenv directory in the current directory
source bin/activate # on unix
scripts/activate # on Windows
We can now install pytest::
pip install pytest
Due to the ``activate`` step above the ``pip`` will come from
the virtualenv directory and install any package into the isolated
virtual environment.
Choosing a test layout / import rules
------------------------------------------
``pytest`` supports two common test layouts:
* putting tests into an extra directory outside your actual application
code, useful if you have many functional tests or for other reasons
want to keep tests separate from actual application code (often a good
idea)::
setup.py # your distutils/setuptools Python package metadata
mypkg/
__init__.py
appmodule.py
tests/
test_app.py
...
* inlining test directories into your application package, useful if you
have direct relation between (unit-)test and application modules and
want to distribute your tests along with your application::
setup.py # your distutils/setuptools Python package metadata
mypkg/
__init__.py
appmodule.py
...
test/
test_app.py
...
Important notes relating to both schemes:
- **make sure that "mypkg" is importable**, for example by typing once::
pip install -e . # install package using setup.py in editable mode
- **avoid "__init__.py" files in your test directories**.
This way your tests can run easily against an installed version
of ``mypkg``, independently from if the installed package contains
the tests or not.
- With inlined tests you might put ``__init__.py`` into test
directories and make them installable as part of your application.
Using the ``py.test --pyargs mypkg`` invocation pytest will
discover where mypkg is installed and collect tests from there.
With the "external" test you can still distribute tests but they
will not be installed or become importable.
Typically you can run tests by pointing to test directories or modules::
py.test tests/test_app.py # for external test dirs
py.test mypkg/test/test_app.py # for inlined test dirs
py.test mypkg # run tests in all below test directories
py.test # run all tests below current dir
...
Because of the above ``editable install`` mode you can change your
source code (both tests and the app) and rerun tests at will.
Once you are done with your work, you can `use tox`_ to make sure
that the package is really correct and tests pass in all
required configurations.
.. note::
You can use Python3 namespace packages (PEP420) for your application
but pytest will still perform `test package name`_ discovery based on the
presence of ``__init__.py`` files. If you use one of the
two recommended file system layouts above but leave away the ``__init__.py``
files from your directories it should just work on Python3.3 and above. From
"inlined tests", however, you will need to use absolute imports for
getting at your application code.
.. _`test package name`:
.. note::
If ``pytest`` finds a "a/b/test_module.py" test file while
recursing into the filesystem it determines the import name
as follows:
* determine ``basedir``: this is the first "upward" (towards the root)
directory not containing an ``__init__.py``. If e.g. both ``a``
and ``b`` contain an ``__init__.py`` file then the parent directory
of ``a`` will become the ``basedir``.
* perform ``sys.path.insert(0, basedir)`` to make the test module
importable under the fully qualified import name.
* ``import a.b.test_module`` where the path is determined
by converting path separators ``/`` into "." characters. This means
you must follow the convention of having directory and file
names map directly to the import names.
The reason for this somewhat evolved importing technique is
that in larger projects multiple test modules might import
from each other and thus deriving a canonical import name helps
to avoid surprises such as a test modules getting imported twice.
.. _`virtualenv`: http://pypi.python.org/pypi/virtualenv
.. _`buildout`: http://www.buildout.org/
.. _pip: http://pypi.python.org/pypi/pip
.. _`use tox`:
Use tox and Continuous Integration servers
-------------------------------------------------
If you frequently release code to the public you
may want to look into `tox`_, the virtualenv test automation
tool and its `pytest support <http://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.
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 py.test standalone script
Create a pytest standalone script
-------------------------------------------
If you are a maintainer or application developer and want others
to easily run tests you can generate a completely standalone "py.test"
script::
If you are a maintainer or application developer and want people
who don't deal with python much to easily run tests you may generate
a standalone ``pytest`` 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.
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 py.test script::
to generate a standalone ``pytest`` script::
py.test --genscript=runtests.py
@ -84,7 +207,7 @@ 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
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.
@ -93,8 +216,9 @@ options.
Integration with setuptools test commands
----------------------------------------------------
Setuptools supports writing our own Test command for invoking
pytest::
Setuptools supports writing our own Test command for invoking pytest.
Most often it is better to use tox_ instead, but here is how you can
get started with setuptools integration::
from setuptools.command.test import test as TestCommand
import sys
@ -120,7 +244,7 @@ Now if you run::
python setup.py test
this will download py.test if needed and then run py.test
this will download ``pytest`` if needed and then run your tests
as you would expect it to.
.. _`test discovery`:
@ -129,7 +253,7 @@ as you would expect it to.
Conventions for Python test discovery
-------------------------------------------------
``py.test`` implements the following standard 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.
@ -140,72 +264,7 @@ Conventions for Python test discovery
For examples of how to customize your test discovery :doc:`example/pythoncollection`.
Within Python modules, py.test also discovers tests using the standard
Within Python modules, ``pytest`` also discovers tests using the standard
:ref:`unittest.TestCase <unittest.TestCase>` subclassing technique.
Choosing a test layout / import rules
------------------------------------------
py.test supports common test layouts:
* inlining test directories into your application package, useful if you want to
keep (unit) tests and actually tested code close together::
mypkg/
__init__.py
appmodule.py
...
test/
test_app.py
...
* putting tests into an extra directory outside your actual application
code, useful if you have many functional tests or want to keep
tests separate from actual application code::
mypkg/
__init__.py
appmodule.py
tests/
test_app.py
...
In both cases you usually need to make sure that ``mypkg`` is importable,
for example by using the setuptools ``python setup.py develop`` method.
You can run your tests by pointing to it::
py.test tests/test_app.py # for external test dirs
py.test mypkg/test/test_app.py # for inlined test dirs
py.test mypkg # run tests in all below test directories
py.test # run all tests below current dir
...
.. _`package name`:
.. note::
If py.test finds a "a/b/test_module.py" test file while
recursing into the filesystem it determines the import name
as follows:
* find ``basedir`` -- this is the first "upward" (towards the root)
directory not containing an ``__init__.py``. If both the ``a``
and ``b`` directories contain an ``__init__.py`` the basedir will
be the parent dir of ``a``.
* perform ``sys.path.insert(0, basedir)`` to make the test module
importable under the fully qualified import name.
* ``import a.b.test_module`` where the path is determined
by converting path separators ``/`` into "." characters. This means
you must follow the convention of having directory and file
names map directly to the import names.
The reason for this somewhat evolved importing technique is
that in larger projects multiple test modules might import
from each other and thus deriving a canonical import name helps
to avoid surprises such as a test modules getting imported twice.
.. include:: links.inc

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

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

View File

@ -48,11 +48,28 @@ requests in all your tests, you can do::
import pytest
@pytest.fixture(autouse=True)
def no_requests(monkeypatch):
monkeypatch.replace("requests.session.Session.request", None)
monkeypatch.delattr("requests.session.Session.request")
This autouse fixture will be executed for each test function and it
will delete the method ``request.session.Session.request``
so that any attempts within tests to create http requests will fail.
example: setting an attribute on some class
------------------------------------------------------
If you need to patch out ``os.getcwd()`` to return an artifical
value::
def test_some_interaction(monkeypatch):
monkeypatch.setattr("os.getcwd", lambda: "/")
which is equivalent to the long form::
def test_some_interaction(monkeypatch):
import os
monkeypatch.setattr(os, "getcwd", lambda: "/")
This autouse fixture will be executed for all test functions and it
will replace the method ``request.session.Session.request`` with the
value None so that any attempts to create http requests will fail.
Method reference of the monkeypatch function argument
-----------------------------------------------------

View File

@ -3,7 +3,7 @@ Running tests written for nose
.. include:: links.inc
py.test has basic support for running tests written for nose_.
``pytest`` has basic support for running tests written for nose_.
.. _nosestyle:
@ -16,7 +16,7 @@ After :ref:`installation` type::
py.test # instead of 'nosetests'
and you should be able to run your nose style tests and
make use of py.test's capabilities.
make use of pytest's capabilities.
Supported nose Idioms
----------------------
@ -30,9 +30,25 @@ Supported nose Idioms
Unsupported idioms / known issues
----------------------------------
- unittest-style ``setUp, tearDown, setUpClass, tearDownClass``
are recognized only on ``unittest.TestCase`` classes but not
on plain classes. ``nose`` supports these methods also on plain
classes but pytest deliberately does not. As nose and pytest already
both support ``setup_class, teardown_class, setup_method, teardown_method``
it doesn't seem useful to duplicate the unittest-API like nose does.
If you however rather think pytest should support the unittest-spelling on
plain classes please post `to this issue
<https://bitbucket.org/hpk42/pytest/issue/377/>`_.
- nose imports test modules with the same import path (e.g.
``tests.test_mod``) but different file system paths
(e.g. ``tests/test_mode.py`` and ``other/tests/test_mode.py``)
by extending sys.path/import semantics. pytest does not do that
but there is discussion in `issue268 <https://bitbucket.org/hpk42/pytest/issue/268>`_ for adding some support. Note that
`nose2 choose to avoid this sys.path/import hackery <https://nose2.readthedocs.org/en/latest/differences.html#test-discovery-and-loading>`_.
- nose-style doctests are not collected and executed correctly,
also doctest fixtures don't work.
- no nose-configuration is recognized

View File

@ -15,10 +15,10 @@ pytest supports test parametrization in several well-integrated ways:
at the level of fixture functions <fixture-parametrize>`.
* `@pytest.mark.parametrize`_ allows to define parametrization at the
function or class level, provides multiple argument/fixture sets
function or class level, provides multiple argument/fixture sets
for a particular test function or class.
* `pytest_generate_tests`_ enables implementing your own custom
* `pytest_generate_tests`_ enables implementing your own custom
dynamic parametrization scheme or extensions.
.. _parametrizemark:
@ -47,23 +47,22 @@ to an expected output::
def test_eval(input, expected):
assert eval(input) == expected
Here, the ``@parametrize`` decorator defines three different ``(input,output)``
tuples so that that the ``test_eval`` function will run three times using
Here, the ``@parametrize`` decorator defines three different ``(input,expected)``
tuples so that the ``test_eval`` function will run three times using
them in turn::
$ py.test
============================= test session starts ==============================
platform linux2 -- Python 2.7.3 -- pytest-2.4.0.dev3
plugins: xdist, cache, cli, pep8, xprocess, cov, capturelog, bdd-splinter, rerunfailures, instafail, localserver
$ py.test
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 3 items
test_expectation.py ..F
=================================== FAILURES ===================================
______________________________ test_eval[6*9-42] _______________________________
================================= FAILURES =================================
____________________________ test_eval[6*9-42] _____________________________
input = '6*9', expected = 42
@pytest.mark.parametrize("input,expected", [
("3+5", 8),
("2+4", 6),
@ -73,9 +72,9 @@ them in turn::
> assert eval(input) == expected
E assert 54 == 42
E + where 54 = eval('6*9')
test_expectation.py:8: AssertionError
====================== 1 failed, 2 passed in 0.02 seconds ======================
==================== 1 failed, 2 passed in 0.01 seconds ====================
As designed in this example, only one pair of input/output values fails
the simple test function. And as usual with test function arguments,
@ -100,14 +99,13 @@ for example with the builtin ``mark.xfail``::
Let's run this::
$ py.test
============================= test session starts ==============================
platform linux2 -- Python 2.7.3 -- pytest-2.4.0.dev3
plugins: xdist, cache, cli, pep8, xprocess, cov, capturelog, bdd-splinter, rerunfailures, instafail, localserver
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 3 items
test_expectation.py ..x
===================== 2 passed, 1 xfailed in 0.02 seconds ======================
=================== 2 passed, 1 xfailed in 0.01 seconds ====================
The one parameter set which caused a failure previously now
shows up as an "xfailed (expected to fail)" test.
@ -116,8 +114,8 @@ shows up as an "xfailed (expected to fail)" test.
In versions prior to 2.4 one needed to specify the argument
names as a tuple. This remains valid but the simpler ``"name1,name2,..."``
comma-separated-string syntax is now advertised fist because
it's easier to write, produces less line noise.
comma-separated-string syntax is now advertised first because
it's easier to write and produces less line noise.
.. _`pytest_generate_tests`:
@ -126,14 +124,14 @@ Basic ``pytest_generate_tests`` example
Sometimes you may want to implement your own parametrization scheme
or implement some dynamism for determining the parameters or scope
of a fixture. For this, you can use the ``pytest_generate_tests`` hook
of a fixture. For this, you can use the ``pytest_generate_tests`` hook
which is called when collecting a test function. Through the passed in
`metafunc` object you can inspect the requesting test context and, most
importantly, you can call ``metafunc.parametrize()`` to cause
parametrization.
parametrization.
For example, let's say we want to run a test taking string inputs which
we want to set via a new py.test command line option. Let's first write
we want to set via a new ``pytest`` command line option. Let's first write
a simple test accepting a ``stringinput`` fixture function argument::
# content of test_strings.py
@ -141,7 +139,7 @@ a simple test accepting a ``stringinput`` fixture function argument::
def test_valid_string(stringinput):
assert stringinput.isalpha()
Now we add a ``conftest.py`` file containing the addition of a
Now we add a ``conftest.py`` file containing the addition of a
command line option and the parametrization of our test function::
# content of conftest.py
@ -152,40 +150,43 @@ command line option and the parametrization of our test function::
def pytest_generate_tests(metafunc):
if 'stringinput' in metafunc.fixturenames:
metafunc.parametrize("stringinput",
metafunc.parametrize("stringinput",
metafunc.config.option.stringinput)
If we now pass two stringinput values, our test will run twice::
$ py.test -q --stringinput="hello" --stringinput="world" test_strings.py
..
2 passed in 0.01 seconds
Let's also run with a stringinput that will lead to a failing test::
$ py.test -q --stringinput="!" test_strings.py
F
=================================== FAILURES ===================================
_____________________________ test_valid_string[!] _____________________________
================================= FAILURES =================================
___________________________ test_valid_string[!] ___________________________
stringinput = '!'
def test_valid_string(stringinput):
> assert stringinput.isalpha()
E assert <built-in method isalpha of str object at 0x7fd657390fd0>()
E + where <built-in method isalpha of str object at 0x7fd657390fd0> = '!'.isalpha
test_strings.py:3: AssertionError
E assert <built-in method isalpha of str object at 0x2b72934ca198>()
E + where <built-in method isalpha of str object at 0x2b72934ca198> = '!'.isalpha
As expected our test function fails.
test_strings.py:3: AssertionError
1 failed in 0.01 seconds
As expected our test function fails.
If you don't specify a stringinput it will be skipped because
``metafunc.parametrize()`` will be called with an empty parameter
listlist::
$ py.test -q -rs test_strings.py
$ py.test -q -rs test_strings.py
s
=========================== short test summary info ============================
SKIP [1] /home/hpk/p/pytest/_pytest/python.py:999: got empty parameter set, function test_valid_string at /tmp/doc-exec-2/test_strings.py:1
========================= short test summary info ==========================
SKIP [1] /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:1094: got empty parameter set, function test_valid_string at /tmp/doc-exec-24/test_strings.py:1
1 skipped in 0.01 seconds
For further examples, you might want to look at :ref:`more
parametrization examples <paramexamples>`.

View File

@ -3,9 +3,9 @@
Working with plugins and conftest files
=============================================
py.test implements all aspects of configuration, collection, running and reporting by calling `well specified hooks`_. Virtually any Python module can be registered as a plugin. It can implement any number of hook functions (usually two or three) which all have a ``pytest_`` prefix, making hook functions easy to distinguish and find. There are three basic location types:
``pytest`` implements all aspects of configuration, collection, running and reporting by calling `well specified hooks`_. Virtually any Python module can be registered as a plugin. It can implement any number of hook functions (usually two or three) which all have a ``pytest_`` prefix, making hook functions easy to distinguish and find. There are three basic location types:
* `builtin plugins`_: loaded from py.test's internal ``_pytest`` directory.
* `builtin plugins`_: loaded from pytest's internal ``_pytest`` directory.
* `external plugins`_: modules discovered through `setuptools entry points`_
* `conftest.py plugins`_: modules auto-discovered in test directories
@ -63,7 +63,7 @@ tool, for example::
pip install pytest-NAME
pip uninstall pytest-NAME
If a plugin is installed, py.test automatically finds and integrates it,
If a plugin is installed, ``pytest`` automatically finds and integrates it,
there is no need to activate it. Here is a initial list of known plugins:
.. _`django`: https://www.djangoproject.com/
@ -84,14 +84,14 @@ there is no need to activate it. Here is a initial list of known plugins:
* `pytest-xdist <http://pypi.python.org/pypi/pytest-xdist>`_:
to distribute tests to CPUs and remote hosts, to run in boxed
mode which allows to survive segmentation faults, to run in
looponfailing mode, automatically re-running failing tests
looponfailing mode, automatically re-running failing tests
on file changes, see also :ref:`xdist`
* `pytest-instafail <http://pypi.python.org/pypi/pytest-instafail>`_:
to report failures while the test run is happening.
* `pytest-bdd <http://pypi.python.org/pypi/pytest-bdd>`_ and
`pytest-konira <http://pypi.python.org/pypi/pytest-konira>`_
`pytest-konira <http://pypi.python.org/pypi/pytest-konira>`_
to write tests using behaviour-driven testing.
* `pytest-timeout <http://pypi.python.org/pypi/pytest-timeout>`_:
@ -122,7 +122,7 @@ If you want to write a plugin, there are many real-life examples
you can copy from:
* a custom collection example plugin: :ref:`yaml plugin`
* around 20 `builtin plugins`_ which provide py.test's own functionality
* around 20 `builtin plugins`_ which provide pytest's own functionality
* many `external plugins`_ providing additional features
All of these plugins implement the documented `well specified hooks`_
@ -135,10 +135,10 @@ Making your plugin installable by others
If you want to make your plugin externally available, you
may define a so-called entry point for your distribution so
that ``py.test`` finds your plugin module. Entry points are
that ``pytest`` finds your plugin module. Entry points are
a feature that is provided by `setuptools`_ or `Distribute`_.
py.test looks up the ``pytest11`` entrypoint to discover its
plugins and you can thus make your plugin available by definig
pytest looks up the ``pytest11`` entrypoint to discover its
plugins and you can thus make your plugin available by defining
it in your setuptools/distribute-based setup-invocation:
.. sourcecode:: python
@ -150,7 +150,7 @@ it in your setuptools/distribute-based setup-invocation:
name="myproject",
packages = ['myproject']
# the following makes a plugin available to py.test
# the following makes a plugin available to pytest
entry_points = {
'pytest11': [
'name_of_plugin = myproject.pluginmodule',
@ -158,7 +158,7 @@ it in your setuptools/distribute-based setup-invocation:
},
)
If a package is installed this way, py.test will load
If a package is installed this way, ``pytest`` will load
``myproject.pluginmodule`` as a plugin which can define
`well specified hooks`_.
@ -167,7 +167,7 @@ If a package is installed this way, py.test will load
Plugin discovery order at tool startup
--------------------------------------------
py.test loads plugin modules at tool startup in the following way:
``pytest`` loads plugin modules at tool startup in the following way:
* by loading all builtin plugins
@ -177,9 +177,15 @@ py.test loads plugin modules at tool startup in the following way:
and loading the specified plugin before actual command line parsing.
* by loading all :file:`conftest.py` files as inferred by the command line
invocation (test files and all of its *parent* directories).
Note that ``conftest.py`` files from *sub* directories are by default
not loaded at tool startup.
invocation:
- if no test paths are specified use current dir as a test path
- if exists, load ``conftest.py`` and ``test*/conftest.py`` relative
to the directory part of the first test path.
Note that pytest does not find ``conftest.py`` files in deeper nested
sub directories at tool startup. It is usually a good idea to keep
your conftest.py file in the top level test or project root directory.
* by recursively loading all plugins specified by the
``pytest_plugins`` variable in ``conftest.py`` files
@ -197,7 +203,7 @@ will be loaded as well. You can also use dotted path like this::
pytest_plugins = "myapp.testsupport.myplugin"
which will import the specified module as a py.test plugin.
which will import the specified module as a ``pytest`` plugin.
Accessing another plugin by name
@ -243,7 +249,7 @@ how to obtain the name of a plugin.
.. _`builtin plugins`:
py.test default plugin reference
pytest default plugin reference
====================================
@ -277,14 +283,14 @@ in the `pytest repository <http://bitbucket.org/hpk42/pytest/>`_.
.. _`well specified hooks`:
py.test hook reference
pytest hook reference
====================================
Hook specification and validation
-----------------------------------------
py.test calls hook functions to implement initialization, running,
test execution and reporting. When py.test loads a plugin it validates
``pytest`` calls hook functions to implement initialization, running,
test execution and reporting. When ``pytest`` loads a plugin it validates
that each hook function conforms to its respective hook specification.
Each hook function name and its argument names need to match a hook
specification. However, a hook function may accept *fewer* parameters
@ -327,7 +333,7 @@ the reporting hook to print information about a test run.
Collection hooks
------------------------------
py.test calls the following hooks for collecting files and directories:
``pytest`` calls the following hooks for collecting files and directories:
.. autofunction:: pytest_ignore_collect
.. autofunction:: pytest_collect_directory

View File

@ -0,0 +1,237 @@
"""
Script to generate the file `plugins_index.txt` with information about
pytest plugins taken directly from a live PyPI server.
Also includes plugin compatibility between different python and pytest versions,
obtained from http://pytest-plugs.herokuapp.com.
"""
from __future__ import print_function
from collections import namedtuple
import datetime
from distutils.version import LooseVersion
import itertools
from optparse import OptionParser
import os
import sys
import pytest
def get_proxy(url):
"""
wrapper function to obtain a xmlrpc proxy, taking in account import
differences between python 2.X and 3.X
:param url: url to bind the proxy to
:return: a ServerProxy instance
"""
if sys.version_info < (3, 0):
from xmlrpclib import ServerProxy
else:
from xmlrpc.client import ServerProxy
return ServerProxy(url)
def iter_plugins(client, search='pytest-'):
"""
Returns an iterator of (name, version) from PyPI.
:param client: ServerProxy
:param search: package names to search for
"""
for plug_data in client.search({'name': search}):
yield plug_data['name'], plug_data['version']
def get_latest_versions(plugins):
"""
Returns an iterator of (name, version) from the given list of (name,
version), but returning only the latest version of the package. Uses
distutils.LooseVersion to ensure compatibility with PEP386.
"""
plugins = [(name, LooseVersion(version)) for (name, version) in plugins]
for name, grouped_plugins in itertools.groupby(plugins, key=lambda x: x[0]):
name, loose_version = list(grouped_plugins)[-1]
yield name, str(loose_version)
def obtain_plugins_table(plugins, client):
"""
Returns information to populate a table of plugins, their versions,
authors, etc.
The returned information is a list of columns of `ColumnData`
namedtuples(text, link). Link can be None if the text for that column
should not be linked to anything.
:param plugins: list of (name, version)
:param client: ServerProxy
"""
rows = []
ColumnData = namedtuple('ColumnData', 'text link')
headers = ['Name', 'Py27', 'Py33', 'Repository', 'Summary']
pytest_version = pytest.__version__
repositories = obtain_override_repositories()
print('*** pytest-{0} ***'.format(pytest_version))
plugins = list(plugins)
for index, (package_name, version) in enumerate(plugins):
print(package_name, version, '...', end='')
release_data = client.release_data(package_name, version)
common_params = dict(
site='http://pytest-plugs.herokuapp.com',
name=package_name,
version=version)
# first row: name, images and simple links
url = '.. image:: {site}/status/{name}-{version}'
image_url = url.format(**common_params)
image_url += '?py={py}&pytest={pytest}'
row = (
ColumnData(package_name + '-' + version,
release_data['release_url']),
ColumnData(image_url.format(py='py27', pytest=pytest_version),
None),
ColumnData(image_url.format(py='py33', pytest=pytest_version),
None),
ColumnData(
repositories.get(package_name, release_data['home_page']),
None),
ColumnData(release_data['summary'], None),
)
assert len(row) == len(headers)
rows.append(row)
# second row: links for images (they should be in their own line)
url = ' :target: {site}/output/{name}-{version}'
output_url = url.format(**common_params)
output_url += '?py={py}&pytest={pytest}'
row = (
ColumnData('', None),
ColumnData(output_url.format(py='py27', pytest=pytest_version),
None),
ColumnData(output_url.format(py='py33', pytest=pytest_version),
None),
ColumnData('', None),
ColumnData('', None),
)
assert len(row) == len(headers)
rows.append(row)
print('OK (%d%%)' % ((index + 1) * 100 / len(plugins)))
return headers, rows
def obtain_override_repositories():
"""
Used to override the "home_page" obtained from pypi to known
package repositories. Used when the author didn't fill the "home_page"
field in setup.py.
:return: dict of {package_name: repository_url}
"""
return {
'pytest-blockage': 'https://github.com/rob-b/pytest-blockage',
'pytest-konira': 'http://github.com/alfredodeza/pytest-konira',
}
def generate_plugins_index_from_table(filename, headers, rows):
"""
Generates a RST file with the table data given.
:param filename: output filename
:param headers: see `obtain_plugins_table`
:param rows: see `obtain_plugins_table`
"""
# creates a list of rows, each being a str containing appropriate column
# text and link
table_texts = []
for row in rows:
column_texts = []
for i, col_data in enumerate(row):
text = '`%s <%s>`_' % (
col_data.text,
col_data.link) if col_data.link else col_data.text
column_texts.append(text)
table_texts.append(column_texts)
# compute max length of each column so we can build the rst table
column_lengths = [len(x) for x in headers]
for column_texts in table_texts:
for i, row_text in enumerate(column_texts):
column_lengths[i] = max(column_lengths[i], len(row_text) + 2)
def get_row_limiter(char):
return ' '.join(char * length for length in column_lengths)
with open(filename, 'w') as f:
# write welcome
print('.. _plugins_index:', file=f)
print(file=f)
print('List of Third-Party Plugins', file=f)
print('===========================', file=f)
print(file=f)
# table
print(get_row_limiter('='), file=f)
formatted_headers = [
'{0:^{fill}}'.format(header, fill=column_lengths[i])
for i, header in enumerate(headers)]
print(*formatted_headers, file=f)
print(get_row_limiter('='), file=f)
for column_texts in table_texts:
formatted_rows = [
'{0:^{fill}}'.format(row_text, fill=column_lengths[i])
for i, row_text in enumerate(column_texts)
]
print(*formatted_rows, file=f)
print(file=f)
print(get_row_limiter('='), file=f)
print(file=f)
today = datetime.date.today().strftime('%Y-%m-%d')
print('*(Updated on %s)*' % today, file=f)
def generate_plugins_index(client, filename):
"""
Generates an RST file with a table of the latest pytest plugins found in
PyPI.
:param client: ServerProxy
:param filename: output filename
"""
plugins = get_latest_versions(iter_plugins(client))
headers, rows = obtain_plugins_table(plugins, client)
generate_plugins_index_from_table(filename, headers, rows)
def main(argv):
"""
Script entry point. Configures an option parser and calls the appropriate
internal function.
"""
filename = os.path.join(os.path.dirname(__file__), 'plugins_index.txt')
url = 'http://pypi.python.org/pypi'
parser = OptionParser(
description='Generates a restructured document of pytest plugins from PyPI')
parser.add_option('-f', '--filename', default=filename,
help='output filename [default: %default]')
parser.add_option('-u', '--url', default=url,
help='url of PyPI server to obtain data from [default: %default]')
(options, _) = parser.parse_args(argv[1:])
client = get_proxy(options.url)
generate_plugins_index(client, options.filename)
print()
print('%s Updated.' % options.filename)
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv))

View File

@ -0,0 +1,112 @@
.. _plugins_index:
List of Third-Party Plugins
===========================
========================================================================================== ======================================================================================================== ======================================================================================================== ============================================================= =============================================================================================================================================
Name Py27 Py33 Repository Summary
========================================================================================== ======================================================================================================== ======================================================================================================== ============================================================= =============================================================================================================================================
`pytest-bdd-0.6.8 <http://pypi.python.org/pypi/pytest-bdd/0.6.8>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-0.6.8?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-0.6.8?py=py33&pytest=2.5.1 https://github.com/olegpidsadnyi/pytest-bdd BDD for pytest
:target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-0.6.8?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-0.6.8?py=py33&pytest=2.5.1
`pytest-bdd-splinter-0.5.98 <http://pypi.python.org/pypi/pytest-bdd-splinter/0.5.98>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-splinter-0.5.98?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-splinter-0.5.98?py=py33&pytest=2.5.1 https://github.com/olegpidsadnyi/pytest-bdd-splinter Splinter subplugin for Pytest BDD plugin
:target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-splinter-0.5.98?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-splinter-0.5.98?py=py33&pytest=2.5.1
`pytest-bench-0.2.5 <http://pypi.python.org/pypi/pytest-bench/0.2.5>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bench-0.2.5?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bench-0.2.5?py=py33&pytest=2.5.1 http://github.com/concordusapps/pytest-bench Benchmark utility that plugs into pytest.
:target: http://pytest-plugs.herokuapp.com/output/pytest-bench-0.2.5?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-bench-0.2.5?py=py33&pytest=2.5.1
`pytest-blockage-0.1 <http://pypi.python.org/pypi/pytest-blockage/0.1>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-blockage-0.1?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-blockage-0.1?py=py33&pytest=2.5.1 https://github.com/rob-b/pytest-blockage Disable network requests during a test run.
:target: http://pytest-plugs.herokuapp.com/output/pytest-blockage-0.1?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-blockage-0.1?py=py33&pytest=2.5.1
`pytest-browsermob-proxy-0.1 <http://pypi.python.org/pypi/pytest-browsermob-proxy/0.1>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-browsermob-proxy-0.1?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-browsermob-proxy-0.1?py=py33&pytest=2.5.1 https://github.com/davehunt/pytest-browsermob-proxy BrowserMob proxy plugin for py.test.
:target: http://pytest-plugs.herokuapp.com/output/pytest-browsermob-proxy-0.1?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-browsermob-proxy-0.1?py=py33&pytest=2.5.1
`pytest-bugzilla-0.2 <http://pypi.python.org/pypi/pytest-bugzilla/0.2>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bugzilla-0.2?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bugzilla-0.2?py=py33&pytest=2.5.1 http://github.com/nibrahim/pytest_bugzilla py.test bugzilla integration plugin
:target: http://pytest-plugs.herokuapp.com/output/pytest-bugzilla-0.2?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-bugzilla-0.2?py=py33&pytest=2.5.1
`pytest-cache-1.0 <http://pypi.python.org/pypi/pytest-cache/1.0>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cache-1.0?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cache-1.0?py=py33&pytest=2.5.1 http://bitbucket.org/hpk42/pytest-cache/ pytest plugin with mechanisms for caching across test runs
:target: http://pytest-plugs.herokuapp.com/output/pytest-cache-1.0?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-cache-1.0?py=py33&pytest=2.5.1
`pytest-capturelog-0.7 <http://pypi.python.org/pypi/pytest-capturelog/0.7>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-capturelog-0.7?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-capturelog-0.7?py=py33&pytest=2.5.1 http://bitbucket.org/memedough/pytest-capturelog/overview py.test plugin to capture log messages
:target: http://pytest-plugs.herokuapp.com/output/pytest-capturelog-0.7?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-capturelog-0.7?py=py33&pytest=2.5.1
`pytest-codecheckers-0.2 <http://pypi.python.org/pypi/pytest-codecheckers/0.2>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-codecheckers-0.2?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-codecheckers-0.2?py=py33&pytest=2.5.1 http://bitbucket.org/RonnyPfannschmidt/pytest-codecheckers/ pytest plugin to add source code sanity checks (pep8 and friends)
:target: http://pytest-plugs.herokuapp.com/output/pytest-codecheckers-0.2?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-codecheckers-0.2?py=py33&pytest=2.5.1
`pytest-contextfixture-0.1.1 <http://pypi.python.org/pypi/pytest-contextfixture/0.1.1>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-contextfixture-0.1.1?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-contextfixture-0.1.1?py=py33&pytest=2.5.1 http://github.com/pelme/pytest-contextfixture/ Define pytest fixtures as context managers.
:target: http://pytest-plugs.herokuapp.com/output/pytest-contextfixture-0.1.1?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-contextfixture-0.1.1?py=py33&pytest=2.5.1
`pytest-couchdbkit-0.5.1 <http://pypi.python.org/pypi/pytest-couchdbkit/0.5.1>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-couchdbkit-0.5.1?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-couchdbkit-0.5.1?py=py33&pytest=2.5.1 http://bitbucket.org/RonnyPfannschmidt/pytest-couchdbkit py.test extension for per-test couchdb databases using couchdbkit
:target: http://pytest-plugs.herokuapp.com/output/pytest-couchdbkit-0.5.1?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-couchdbkit-0.5.1?py=py33&pytest=2.5.1
`pytest-cov-1.6 <http://pypi.python.org/pypi/pytest-cov/1.6>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cov-1.6?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cov-1.6?py=py33&pytest=2.5.1 http://bitbucket.org/memedough/pytest-cov/overview py.test plugin for coverage reporting with support for both centralised and distributed testing, including subprocesses and multiprocessing
:target: http://pytest-plugs.herokuapp.com/output/pytest-cov-1.6?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-cov-1.6?py=py33&pytest=2.5.1
`pytest-dbfixtures-0.4.3 <http://pypi.python.org/pypi/pytest-dbfixtures/0.4.3>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-dbfixtures-0.4.3?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-dbfixtures-0.4.3?py=py33&pytest=2.5.1 https://github.com/clearcode/pytest-dbfixtures dbfixtures plugin for py.test.
:target: http://pytest-plugs.herokuapp.com/output/pytest-dbfixtures-0.4.3?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-dbfixtures-0.4.3?py=py33&pytest=2.5.1
`pytest-django-2.5 <http://pypi.python.org/pypi/pytest-django/2.5>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-2.5?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-2.5?py=py33&pytest=2.5.1 http://pytest-django.readthedocs.org/ A Django plugin for py.test.
:target: http://pytest-plugs.herokuapp.com/output/pytest-django-2.5?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-django-2.5?py=py33&pytest=2.5.1
`pytest-django-lite-0.1.0 <http://pypi.python.org/pypi/pytest-django-lite/0.1.0>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-lite-0.1.0?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-lite-0.1.0?py=py33&pytest=2.5.1 UNKNOWN The bare minimum to integrate py.test with Django.
:target: http://pytest-plugs.herokuapp.com/output/pytest-django-lite-0.1.0?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-django-lite-0.1.0?py=py33&pytest=2.5.1
`pytest-figleaf-1.0 <http://pypi.python.org/pypi/pytest-figleaf/1.0>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-figleaf-1.0?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-figleaf-1.0?py=py33&pytest=2.5.1 http://bitbucket.org/hpk42/pytest-figleaf py.test figleaf coverage plugin
:target: http://pytest-plugs.herokuapp.com/output/pytest-figleaf-1.0?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-figleaf-1.0?py=py33&pytest=2.5.1
`pytest-flakes-0.2 <http://pypi.python.org/pypi/pytest-flakes/0.2>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-flakes-0.2?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-flakes-0.2?py=py33&pytest=2.5.1 https://github.com/fschulze/pytest-flakes pytest plugin to check source code with pyflakes
:target: http://pytest-plugs.herokuapp.com/output/pytest-flakes-0.2?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-flakes-0.2?py=py33&pytest=2.5.1
`pytest-greendots-0.2 <http://pypi.python.org/pypi/pytest-greendots/0.2>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-greendots-0.2?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-greendots-0.2?py=py33&pytest=2.5.1 UNKNOWN Green progress dots
:target: http://pytest-plugs.herokuapp.com/output/pytest-greendots-0.2?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-greendots-0.2?py=py33&pytest=2.5.1
`pytest-growl-0.2 <http://pypi.python.org/pypi/pytest-growl/0.2>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-growl-0.2?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-growl-0.2?py=py33&pytest=2.5.1 UNKNOWN Growl notifications for pytest results.
:target: http://pytest-plugs.herokuapp.com/output/pytest-growl-0.2?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-growl-0.2?py=py33&pytest=2.5.1
`pytest-incremental-0.3.0 <http://pypi.python.org/pypi/pytest-incremental/0.3.0>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-incremental-0.3.0?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-incremental-0.3.0?py=py33&pytest=2.5.1 https://bitbucket.org/schettino72/pytest-incremental an incremental test runner (pytest plugin)
:target: http://pytest-plugs.herokuapp.com/output/pytest-incremental-0.3.0?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-incremental-0.3.0?py=py33&pytest=2.5.1
`pytest-instafail-0.1.1 <http://pypi.python.org/pypi/pytest-instafail/0.1.1>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-instafail-0.1.1?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-instafail-0.1.1?py=py33&pytest=2.5.1 https://github.com/jpvanhal/pytest-instafail py.test plugin to show failures instantly
:target: http://pytest-plugs.herokuapp.com/output/pytest-instafail-0.1.1?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-instafail-0.1.1?py=py33&pytest=2.5.1
`pytest-ipdb-0.1-prerelease <http://pypi.python.org/pypi/pytest-ipdb/0.1-prerelease>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-ipdb-0.1-prerelease?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-ipdb-0.1-prerelease?py=py33&pytest=2.5.1 https://github.com/mverteuil/pytest-ipdb A py.test plug-in to enable drop to ipdb debugger on test failure.
:target: http://pytest-plugs.herokuapp.com/output/pytest-ipdb-0.1-prerelease?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-ipdb-0.1-prerelease?py=py33&pytest=2.5.1
`pytest-jira-0.01 <http://pypi.python.org/pypi/pytest-jira/0.01>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-jira-0.01?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-jira-0.01?py=py33&pytest=2.5.1 http://github.com/jlaska/pytest_jira py.test JIRA integration plugin, using markers
:target: http://pytest-plugs.herokuapp.com/output/pytest-jira-0.01?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-jira-0.01?py=py33&pytest=2.5.1
`pytest-konira-0.2 <http://pypi.python.org/pypi/pytest-konira/0.2>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-konira-0.2?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-konira-0.2?py=py33&pytest=2.5.1 http://github.com/alfredodeza/pytest-konira Run Konira DSL tests with py.test
:target: http://pytest-plugs.herokuapp.com/output/pytest-konira-0.2?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-konira-0.2?py=py33&pytest=2.5.1
`pytest-localserver-0.3.2 <http://pypi.python.org/pypi/pytest-localserver/0.3.2>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-localserver-0.3.2?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-localserver-0.3.2?py=py33&pytest=2.5.1 http://bitbucket.org/basti/pytest-localserver/ py.test plugin to test server connections locally.
:target: http://pytest-plugs.herokuapp.com/output/pytest-localserver-0.3.2?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-localserver-0.3.2?py=py33&pytest=2.5.1
`pytest-marker-bugzilla-0.06 <http://pypi.python.org/pypi/pytest-marker-bugzilla/0.06>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marker-bugzilla-0.06?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marker-bugzilla-0.06?py=py33&pytest=2.5.1 http://github.com/eanxgeek/pytest_marker_bugzilla py.test bugzilla integration plugin, using markers
:target: http://pytest-plugs.herokuapp.com/output/pytest-marker-bugzilla-0.06?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-marker-bugzilla-0.06?py=py33&pytest=2.5.1
`pytest-markfiltration-0.8 <http://pypi.python.org/pypi/pytest-markfiltration/0.8>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-markfiltration-0.8?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-markfiltration-0.8?py=py33&pytest=2.5.1 https://github.com/adamgoucher/pytest-markfiltration UNKNOWN
:target: http://pytest-plugs.herokuapp.com/output/pytest-markfiltration-0.8?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-markfiltration-0.8?py=py33&pytest=2.5.1
`pytest-marks-0.4 <http://pypi.python.org/pypi/pytest-marks/0.4>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marks-0.4?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marks-0.4?py=py33&pytest=2.5.1 https://github.com/adamgoucher/pytest-marks UNKNOWN
:target: http://pytest-plugs.herokuapp.com/output/pytest-marks-0.4?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-marks-0.4?py=py33&pytest=2.5.1
`pytest-monkeyplus-1.1.0 <http://pypi.python.org/pypi/pytest-monkeyplus/1.1.0>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-monkeyplus-1.1.0?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-monkeyplus-1.1.0?py=py33&pytest=2.5.1 http://bitbucket.org/hsoft/pytest-monkeyplus/ pytest's monkeypatch subclass with extra functionalities
:target: http://pytest-plugs.herokuapp.com/output/pytest-monkeyplus-1.1.0?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-monkeyplus-1.1.0?py=py33&pytest=2.5.1
`pytest-mozwebqa-1.1.1 <http://pypi.python.org/pypi/pytest-mozwebqa/1.1.1>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-mozwebqa-1.1.1?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-mozwebqa-1.1.1?py=py33&pytest=2.5.1 https://github.com/davehunt/pytest-mozwebqa Mozilla WebQA plugin for py.test.
:target: http://pytest-plugs.herokuapp.com/output/pytest-mozwebqa-1.1.1?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-mozwebqa-1.1.1?py=py33&pytest=2.5.1
`pytest-oerp-0.2.0 <http://pypi.python.org/pypi/pytest-oerp/0.2.0>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-oerp-0.2.0?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-oerp-0.2.0?py=py33&pytest=2.5.1 http://github.com/santagada/pytest-oerp/ pytest plugin to test OpenERP modules
:target: http://pytest-plugs.herokuapp.com/output/pytest-oerp-0.2.0?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-oerp-0.2.0?py=py33&pytest=2.5.1
`pytest-osxnotify-0.1.4 <http://pypi.python.org/pypi/pytest-osxnotify/0.1.4>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-osxnotify-0.1.4?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-osxnotify-0.1.4?py=py33&pytest=2.5.1 https://github.com/dbader/pytest-osxnotify OS X notifications for py.test results.
:target: http://pytest-plugs.herokuapp.com/output/pytest-osxnotify-0.1.4?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-osxnotify-0.1.4?py=py33&pytest=2.5.1
`pytest-paste-config-0.1 <http://pypi.python.org/pypi/pytest-paste-config/0.1>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-paste-config-0.1?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-paste-config-0.1?py=py33&pytest=2.5.1 UNKNOWN Allow setting the path to a paste config file
:target: http://pytest-plugs.herokuapp.com/output/pytest-paste-config-0.1?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-paste-config-0.1?py=py33&pytest=2.5.1
`pytest-pep8-1.0.5 <http://pypi.python.org/pypi/pytest-pep8/1.0.5>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pep8-1.0.5?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pep8-1.0.5?py=py33&pytest=2.5.1 http://bitbucket.org/hpk42/pytest-pep8/ pytest plugin to check PEP8 requirements
:target: http://pytest-plugs.herokuapp.com/output/pytest-pep8-1.0.5?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-pep8-1.0.5?py=py33&pytest=2.5.1
`pytest-poo-0.2 <http://pypi.python.org/pypi/pytest-poo/0.2>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-poo-0.2?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-poo-0.2?py=py33&pytest=2.5.1 http://github.com/pelme/pytest-poo Visualize your crappy tests
:target: http://pytest-plugs.herokuapp.com/output/pytest-poo-0.2?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-poo-0.2?py=py33&pytest=2.5.1
`pytest-pydev-0.1 <http://pypi.python.org/pypi/pytest-pydev/0.1>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pydev-0.1?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pydev-0.1?py=py33&pytest=2.5.1 http://bitbucket.org/basti/pytest-pydev/ py.test plugin to connect to a remote debug server with PyDev or PyCharm.
:target: http://pytest-plugs.herokuapp.com/output/pytest-pydev-0.1?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-pydev-0.1?py=py33&pytest=2.5.1
`pytest-qt-1.0.2 <http://pypi.python.org/pypi/pytest-qt/1.0.2>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-qt-1.0.2?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-qt-1.0.2?py=py33&pytest=2.5.1 http://github.com/nicoddemus/pytest-qt pytest plugin that adds fixtures for testing Qt (PyQt and PySide) applications.
:target: http://pytest-plugs.herokuapp.com/output/pytest-qt-1.0.2?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-qt-1.0.2?py=py33&pytest=2.5.1
`pytest-quickcheck-0.8 <http://pypi.python.org/pypi/pytest-quickcheck/0.8>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-quickcheck-0.8?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-quickcheck-0.8?py=py33&pytest=2.5.1 http://bitbucket.org/t2y/pytest-quickcheck/ pytest plugin to generate random data inspired by QuickCheck
:target: http://pytest-plugs.herokuapp.com/output/pytest-quickcheck-0.8?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-quickcheck-0.8?py=py33&pytest=2.5.1
`pytest-rage-0.1 <http://pypi.python.org/pypi/pytest-rage/0.1>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rage-0.1?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rage-0.1?py=py33&pytest=2.5.1 http://github.com/santagada/pytest-rage/ pytest plugin to implement PEP712
:target: http://pytest-plugs.herokuapp.com/output/pytest-rage-0.1?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-rage-0.1?py=py33&pytest=2.5.1
`pytest-random-0.02 <http://pypi.python.org/pypi/pytest-random/0.02>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-random-0.02?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-random-0.02?py=py33&pytest=2.5.1 https://github.com/klrmn/pytest-random py.test plugin to randomize tests
:target: http://pytest-plugs.herokuapp.com/output/pytest-random-0.02?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-random-0.02?py=py33&pytest=2.5.1
`pytest-rerunfailures-0.03 <http://pypi.python.org/pypi/pytest-rerunfailures/0.03>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rerunfailures-0.03?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rerunfailures-0.03?py=py33&pytest=2.5.1 https://github.com/klrmn/pytest-rerunfailures py.test plugin to re-run tests to eliminate flakey failures
:target: http://pytest-plugs.herokuapp.com/output/pytest-rerunfailures-0.03?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-rerunfailures-0.03?py=py33&pytest=2.5.1
`pytest-runfailed-0.3 <http://pypi.python.org/pypi/pytest-runfailed/0.3>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runfailed-0.3?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runfailed-0.3?py=py33&pytest=2.5.1 http://github.com/dmerejkowsky/pytest-runfailed implement a --failed option for pytest
:target: http://pytest-plugs.herokuapp.com/output/pytest-runfailed-0.3?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-runfailed-0.3?py=py33&pytest=2.5.1
`pytest-runner-2.0 <http://pypi.python.org/pypi/pytest-runner/2.0>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runner-2.0?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runner-2.0?py=py33&pytest=2.5.1 https://bitbucket.org/jaraco/pytest-runner UNKNOWN
:target: http://pytest-plugs.herokuapp.com/output/pytest-runner-2.0?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-runner-2.0?py=py33&pytest=2.5.1
`pytest-sugar-0.2.2 <http://pypi.python.org/pypi/pytest-sugar/0.2.2>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-0.2.2?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-0.2.2?py=py33&pytest=2.5.1 http://pivotfinland.com/pytest-sugar/ py.test plugin that adds instafail, ETA and neat graphics
:target: http://pytest-plugs.herokuapp.com/output/pytest-sugar-0.2.2?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-sugar-0.2.2?py=py33&pytest=2.5.1
`pytest-timeout-0.3 <http://pypi.python.org/pypi/pytest-timeout/0.3>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-timeout-0.3?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-timeout-0.3?py=py33&pytest=2.5.1 http://bitbucket.org/flub/pytest-timeout/ pytest plugin to abort tests after a timeout
:target: http://pytest-plugs.herokuapp.com/output/pytest-timeout-0.3?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-timeout-0.3?py=py33&pytest=2.5.1
`pytest-twisted-1.4 <http://pypi.python.org/pypi/pytest-twisted/1.4>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-twisted-1.4?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-twisted-1.4?py=py33&pytest=2.5.1 https://github.com/schmir/pytest-twisted A twisted plugin for py.test.
:target: http://pytest-plugs.herokuapp.com/output/pytest-twisted-1.4?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-twisted-1.4?py=py33&pytest=2.5.1
`pytest-xdist-1.9 <http://pypi.python.org/pypi/pytest-xdist/1.9>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xdist-1.9?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xdist-1.9?py=py33&pytest=2.5.1 http://bitbucket.org/hpk42/pytest-xdist py.test xdist plugin for distributed testing and loop-on-failing modes
:target: http://pytest-plugs.herokuapp.com/output/pytest-xdist-1.9?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-xdist-1.9?py=py33&pytest=2.5.1
`pytest-xprocess-0.8 <http://pypi.python.org/pypi/pytest-xprocess/0.8>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xprocess-0.8?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xprocess-0.8?py=py33&pytest=2.5.1 http://bitbucket.org/hpk42/pytest-xprocess/ pytest plugin to manage external processes across test runs
:target: http://pytest-plugs.herokuapp.com/output/pytest-xprocess-0.8?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-xprocess-0.8?py=py33&pytest=2.5.1
`pytest-yamlwsgi-0.6 <http://pypi.python.org/pypi/pytest-yamlwsgi/0.6>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-yamlwsgi-0.6?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-yamlwsgi-0.6?py=py33&pytest=2.5.1 UNKNOWN Run tests against wsgi apps defined in yaml
:target: http://pytest-plugs.herokuapp.com/output/pytest-yamlwsgi-0.6?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-yamlwsgi-0.6?py=py33&pytest=2.5.1
`pytest-zap-0.1 <http://pypi.python.org/pypi/pytest-zap/0.1>`_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-zap-0.1?py=py27&pytest=2.5.1 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-zap-0.1?py=py33&pytest=2.5.1 https://github.com/davehunt/pytest-zap OWASP ZAP plugin for py.test.
:target: http://pytest-plugs.herokuapp.com/output/pytest-zap-0.1?py=py27&pytest=2.5.1 :target: http://pytest-plugs.herokuapp.com/output/pytest-zap-0.1?py=py33&pytest=2.5.1
========================================================================================== ======================================================================================================== ======================================================================================================== ============================================================= =============================================================================================================================================
*(Updated on 2014-01-15)*

View File

@ -20,10 +20,10 @@
Project examples
==========================
Here are some examples of projects using py.test (please send notes via :ref:`contact`):
Here are some examples of projects using ``pytest`` (please send notes via :ref:`contact`):
* `PyPy <http://pypy.org>`_, Python with a JIT compiler, running over
`16000 tests <http://buildbot.pypy.org/summary?branch=%3Ctrunk%3E>`_
* `PyPy <http://pypy.org>`_, Python with a JIT compiler, running over
`21000 tests <http://buildbot.pypy.org/summary?branch=%3Ctrunk%3E>`_
* the `MoinMoin <http://moinmo.in>`_ Wiki Engine
* `sentry <https://getsentry.com/welcome/>`_, realtime app-maintenance and exception tracking
* `tox <http://testrun.org/tox>`_, virtualenv/Hudson integration tool
@ -60,7 +60,7 @@ Here are some examples of projects using py.test (please send notes via :ref:`co
* `pytest-localserver <https://bitbucket.org/basti/pytest-localserver/>`_ a plugin for pytest that provides a httpserver and smtpserver
* `pytest-monkeyplus <http://pypi.python.org/pypi/pytest-monkeyplus/>`_ a plugin that extends monkeypatch
These projects help integrate py.test into other Python frameworks:
These projects help integrate ``pytest`` into other Python frameworks:
* `pytest-django <http://pypi.python.org/pypi/pytest-django/>`_ for Django
* `zope.pytest <http://packages.python.org/zope.pytest/>`_ for Zope and Grok
@ -68,7 +68,7 @@ These projects help integrate py.test into other Python frameworks:
* There is `some work <https://github.com/Kotti/Kotti/blob/master/kotti/testing.py>`_ underway for Kotti, a CMS built in Pyramid/Pylons
Some organisations using py.test
Some organisations using pytest
-----------------------------------
* `Square Kilometre Array, Cape Town <http://ska.ac.za/>`_

View File

@ -14,7 +14,7 @@ A *skip* means that you expect your test to pass unless the environment
And *xfail* means that your test can run but you expect it to fail
because there is an implementation problem.
py.test counts and lists *skip* and *xfail* tests separately. Detailed
``pytest`` counts and lists *skip* and *xfail* tests separately. Detailed
information about skipped/xfailed tests is not shown by default to avoid
cluttering the output. You can use the ``-r`` option to see details
corresponding to the "short" letters shown in the test progress::
@ -29,7 +29,7 @@ corresponding to the "short" letters shown in the test progress::
Marking a test function to be skipped
-------------------------------------------
.. versionadded:: 2.4
.. versionadded:: 2.0, 2.4
Here is an example of marking a test function to be skipped
when run on a Python3.3 interpreter::
@ -70,7 +70,8 @@ For larger test suites it's usually a good idea to have one file
where you define the markers which you then consistently apply
throughout your test suite.
Alternatively, the pre pytest-2.4 way to specify `condition strings <condition strings>`_ instead of booleans will remain fully supported in future
Alternatively, the pre pytest-2.4 way to specify :ref:`condition strings
<string conditions>` instead of booleans will remain fully supported in future
versions of pytest. It couldn't be easily used for importing markers
between test modules so it's no longer advertised as the primary method.
@ -103,7 +104,7 @@ you can set the ``pytestmark`` attribute of a class::
"will not be setup or run under 'win32' platform"
As with the class-decorator, the ``pytestmark`` special name tells
py.test to apply it to each test function in the class.
``pytest`` to apply it to each test function in the class.
If you want to skip all test functions of a module, you must use
the ``pytestmark`` name on the global level::
@ -158,7 +159,7 @@ Running it with the report-on-xfail option gives this output::
example $ py.test -rx xfail_demo.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.5
platform linux2 -- Python 2.7.3 -- pytest-2.5.1
collected 6 items
xfail_demo.py xxxxxx
@ -175,7 +176,7 @@ Running it with the report-on-xfail option gives this output::
XFAIL xfail_demo.py::test_hello6
reason: reason
======================== 6 xfailed in 0.05 seconds =========================
======================== 6 xfailed in 0.04 seconds =========================
.. _`skip/xfail with parametrize`:
@ -183,7 +184,7 @@ Skip/xfail with parametrize
---------------------------
It is possible to apply markers like skip and xfail to individual
test instances when using parametrize:
test instances when using parametrize::
import pytest
@ -191,7 +192,7 @@ test instances when using parametrize:
(1, 2),
pytest.mark.xfail((1, 0)),
pytest.mark.xfail(reason="some bug")((1, 3)),
(2, 3),
(2, 3),
(3, 4),
(4, 5),
pytest.mark.skipif("sys.version_info >= (3,0)")((10, 11)),
@ -211,7 +212,7 @@ imperatively, in test or setup code::
if not valid_config():
pytest.xfail("failing configuration (but should work)")
# or
pytest.skipif("unsupported configuration")
pytest.skip("unsupported configuration")
Skipping on a missing import dependency
@ -232,7 +233,7 @@ The version will be read from the specified
module's ``__version__`` attribute.
.. _`string conditions`:
.. _string conditions:
specifying conditions as strings versus booleans
----------------------------------------------------------
@ -254,7 +255,7 @@ because markers can then be freely imported between test modules.
With strings you need to import not only the marker but all variables
everything used by the marker, which violates encapsulation.
The reason for specifying the condition as a string was that py.test can
The reason for specifying the condition as a string was that ``pytest`` can
report a summary of skip conditions based purely on the condition string.
With conditions as booleans you are required to specify a ``reason`` string.

5
doc/en/status.txt Normal file
View File

@ -0,0 +1,5 @@
pytest development status
================================
https://drone.io/bitbucket.org/hpk42/pytest

View File

@ -10,14 +10,20 @@ Tutorial examples and blog postings
.. _`tutorial1 repository`: http://bitbucket.org/hpk42/pytest-tutorial1/
.. _`pycon 2010 tutorial PDF`: http://bitbucket.org/hpk42/pytest-tutorial1/raw/tip/pytest-basic.pdf
Basic usage and funcargs:
Basic usage and fixtures:
- `pytest feature and release highlights (GERMAN, October 2013)
<http://pyvideo.org/video/2429/pytest-feature-and-new-release-highlights>`_
- `pytest introduction from Brian Okken (January 2013)
<http://pythontesting.net/framework/pytest-introduction/>`_
- `3-part blog series about pytest from Daniel Greenfeld (January
2014) <http://pydanny.com/pytest-no-boilerplate-testing.html>`_
- `pycon australia 2012 pytest talk from Brianna Laugher
<http://2012.pycon-au.org/schedule/52/view_talk?day=sunday>`_ (`video <http://www.youtube.com/watch?v=DTNejE9EraI>`_, `slides <http://www.slideshare.net/pfctdayelise/funcargs-other-fun-with-pytest>`_, `code <https://gist.github.com/3386951>`_)
- `pycon 2012 US talk video from Holger Krekel <http://www.youtube.com/watch?v=9LVqBQcFmyw>`_
- `pycon 2012 US talk video from Holger Krekel <http://www.youtube.com/watch?v=9LVqBQcFmyw>`_
- `pycon 2010 tutorial PDF`_ and `tutorial1 repository`_
@ -36,8 +42,8 @@ Test parametrization:
Assertion introspection:
- `(07/2011) Behind the scenes of py.test's new assertion rewriting
<http://pybites.blogspot.com/2011/07/behind-scenes-of-pytests-new-assertion.html>`_
- `(07/2011) Behind the scenes of pytest's new assertion rewriting
<http://pybites.blogspot.com/2011/07/behind-scenes-of-pytests-new-assertion.html>`_
Distributed testing:
@ -45,11 +51,11 @@ Distributed testing:
Plugin specific examples:
- `skipping slow tests by default in py.test`_ (blog entry)
- `skipping slow tests by default in pytest`_ (blog entry)
- `many examples in the docs for plugins`_
.. _`skipping slow tests by default in py.test`: http://bruynooghe.blogspot.com/2009/12/skipping-slow-test-by-default-in-pytest.html
.. _`skipping slow tests by default in pytest`: http://bruynooghe.blogspot.com/2009/12/skipping-slow-test-by-default-in-pytest.html
.. _`many examples in the docs for plugins`: plugin/index.html
.. _`monkeypatch plugin`: plugin/monkeypatch.html
.. _`application setup in test functions with funcargs`: funcargs.html#appsetup
@ -66,14 +72,14 @@ Older conference talks and tutorials
- `ep2009-rapidtesting.pdf`_ tutorial slides (July 2009):
- testing terminology
- basic py.test usage, file system layout
- basic pytest usage, file system layout
- test function arguments (funcargs_) and test fixtures
- existing plugins
- distributed testing
- `ep2009-pytest.pdf`_ 60 minute py.test talk, highlighting unique features and a roadmap (July 2009)
- `ep2009-pytest.pdf`_ 60 minute pytest talk, highlighting unique features and a roadmap (July 2009)
- `pycon2009-pytest-introduction.zip`_ slides and files, extended version of py.test basic introduction, discusses more options, also introduces old-style xUnit setup, looponfailing and other features.
- `pycon2009-pytest-introduction.zip`_ slides and files, extended version of pytest basic introduction, discusses more options, also introduces old-style xUnit setup, looponfailing and other features.
- `pycon2009-pytest-advanced.pdf`_ contain a slightly older version of funcargs and distributed testing, compared to the EuroPython 2009 slides.

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