Skip to content

Commit 6c17020

Browse files
Merge branch 'master' into features
2 parents bf4de4b + 80d6d94 commit 6c17020

File tree

12 files changed

+230
-59
lines changed

12 files changed

+230
-59
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ Endre Galaczi
3333
Elizaveta Shashkova
3434
Eric Hunsberger
3535
Eric Siegerman
36+
Erik M. Bray
3637
Florian Bruhin
3738
Floris Bruynooghe
3839
Gabriel Reis

CHANGELOG

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,20 @@ Bug Fixes
4040
2.8.6.dev1
4141
==========
4242

43+
- fix #1259: allow for double nodeids in junitxml,
44+
this was a regression failing plugins combinations
45+
like pytest-pep8 + pytest-flakes
46+
47+
- Workaround for exception that occurs in pyreadline when using
48+
``--pdb`` with standard I/O capture enabled.
49+
Thanks Erik M. Bray for the PR.
50+
51+
- fix #900: Better error message in case the target of a ``monkeypatch`` call
52+
raises an ``ImportError``.
53+
54+
- fix #1292: monkeypatch calls (setattr, setenv, etc.) are now O(1).
55+
Thanks David R. MacIver for the report and Bruno Oliveira for the PR.
56+
4357

4458
2.8.5
4559
=====

README.rst

Lines changed: 66 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,99 @@
1-
======
2-
pytest
3-
======
1+
.. image:: doc/en/img/pytest1.png
2+
:target: http://pytest.org
3+
:align: center
4+
:alt: pytest
45

5-
The ``pytest`` testing tool makes it easy to write small tests, yet
6-
scales to support complex functional testing.
6+
------
77

88
.. image:: https://img.shields.io/pypi/v/pytest.svg
99
:target: https://pypi.python.org/pypi/pytest
10+
.. image:: https://img.shields.io/pypi/pyversions/pytest.svg
11+
:target: https://pypi.python.org/pypi/pytest
1012
.. image:: https://img.shields.io/coveralls/pytest-dev/pytest/master.svg
1113
:target: https://coveralls.io/r/pytest-dev/pytest
1214
.. image:: https://travis-ci.org/pytest-dev/pytest.svg?branch=master
1315
:target: https://travis-ci.org/pytest-dev/pytest
1416
.. image:: https://ci.appveyor.com/api/projects/status/mrgbjaua7t33pg6b?svg=true
1517
:target: https://ci.appveyor.com/project/pytestbot/pytest
1618

17-
Documentation: http://pytest.org/latest/
19+
The ``pytest`` framework makes it easy to write small tests, yet
20+
scales to support complex functional testing for applications and libraries.
1821

19-
Changelog: http://pytest.org/latest/changelog.html
22+
An example of a simple test:
2023

21-
Issues: https://github.com/pytest-dev/pytest/issues
24+
.. code-block:: python
25+
26+
# content of test_sample.py
27+
def func(x):
28+
return x + 1
29+
30+
def test_answer():
31+
assert func(3) == 5
32+
33+
34+
To execute it::
35+
36+
$ py.test
37+
======= test session starts ========
38+
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
39+
collected 1 items
40+
41+
test_sample.py F
42+
43+
======= FAILURES ========
44+
_______ test_answer ________
45+
46+
def test_answer():
47+
> assert func(3) == 5
48+
E assert 4 == 5
49+
E + where 4 = func(3)
50+
51+
test_sample.py:5: AssertionError
52+
======= 1 failed in 0.12 seconds ========
53+
54+
Due to ``py.test``'s detailed assertion introspection, only plain ``assert`` statements are used. See `getting-started <http://pytest.org/latest/getting-started.html#our-first-test-run>`_ for more examples.
55+
2256

2357
Features
2458
--------
2559

26-
- `auto-discovery
60+
- Detailed info on failing `assert statements <http://pytest.org/latest/assert.html>`_ (no need to remember ``self.assert*`` names);
61+
62+
- `Auto-discovery
2763
<http://pytest.org/latest/goodpractises.html#python-test-discovery>`_
28-
of test modules and functions,
29-
- detailed info on failing `assert statements <http://pytest.org/latest/assert.html>`_ (no need to remember ``self.assert*`` names)
30-
- `modular fixtures <http://pytest.org/latest/fixture.html>`_ for
31-
managing small or parametrized long-lived test resources.
32-
- multi-paradigm support: you can use ``pytest`` to run test suites based
33-
on `unittest <http://pytest.org/latest/unittest.html>`_ (or trial),
34-
`nose <http://pytest.org/latest/nose.html>`_
35-
- single-source compatibility from Python2.6 all the way up to
36-
Python3.5, PyPy-2.3, (jython-2.5 untested)
64+
of test modules and functions;
3765

66+
- `Modular fixtures <http://pytest.org/latest/fixture.html>`_ for
67+
managing small or parametrized long-lived test resources;
3868

39-
- many `external plugins <http://pytest.org/latest/plugins.html#installing-external-plugins-searching>`_.
69+
- Can run `unittest <http://pytest.org/latest/unittest.html>`_ (or trial),
70+
`nose <http://pytest.org/latest/nose.html>`_ test suites out of the box;
4071

41-
A simple example for a test:
72+
- Python2.6+, Python3.2+, PyPy-2.3, Jython-2.5 (untested);
4273

43-
.. code-block:: python
74+
- Rich plugin architecture, with over 150+ `external plugins <http://pytest.org/latest/plugins.html#installing-external-plugins-searching>`_ and thriving comminity;
75+
76+
77+
Documentation
78+
-------------
4479

45-
# content of test_module.py
46-
def test_function():
47-
i = 4
48-
assert i == 3
80+
For full documentation, including installation, tutorials and PDF documents, please see http://pytest.org.
4981

50-
which can be run with ``py.test test_module.py``. See `getting-started <http://pytest.org/latest/getting-started.html#our-first-test-run>`_ for more examples.
5182

52-
For much more info, including PDF docs, see
83+
Bugs/Requests
84+
-------------
5385

54-
http://pytest.org
86+
Please use the `GitHub issue tracker <https://github.com/pytest-dev/pytest/issues>`_ to submit bugs or request features.
5587

56-
and report bugs at:
5788

58-
https://github.com/pytest-dev/pytest/issues
89+
Changelog
90+
---------
5991

60-
and checkout or fork repo at:
92+
Consult the `Changelog <http://pytest.org/latest/changelog.html>`_ page for fixes and enhancements of each version.
6193

62-
https://github.com/pytest-dev/pytest
6394

95+
License
96+
-------
6497

65-
Copyright Holger Krekel and others, 2004-2015
98+
Copyright Holger Krekel and others, 2004-2016.
6699
Licensed under the MIT license.

_pytest/capture.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ def pytest_addoption(parser):
3131

3232
@pytest.hookimpl(hookwrapper=True)
3333
def pytest_load_initial_conftests(early_config, parser, args):
34+
_readline_workaround()
3435
ns = early_config.known_args_namespace
3536
pluginmanager = early_config.pluginmanager
3637
capman = CaptureManager(ns.capture)
@@ -442,3 +443,30 @@ def isatty(self):
442443

443444
def close(self):
444445
pass
446+
447+
448+
def _readline_workaround():
449+
"""
450+
Ensure readline is imported so that it attaches to the correct stdio
451+
handles on Windows.
452+
453+
Pdb uses readline support where available--when not running from the Python
454+
prompt, the readline module is not imported until running the pdb REPL. If
455+
running py.test with the --pdb option this means the readline module is not
456+
imported until after I/O capture has been started.
457+
458+
This is a problem for pyreadline, which is often used to implement readline
459+
support on Windows, as it does not attach to the correct handles for stdout
460+
and/or stdin if they have been redirected by the FDCapture mechanism. This
461+
workaround ensures that readline is imported before I/O capture is setup so
462+
that it can attach to the actual stdin/out for the console.
463+
464+
See https://github.com/pytest-dev/pytest/pull/1281
465+
"""
466+
467+
if not sys.platform.startswith('win32'):
468+
return
469+
try:
470+
import readline # noqa
471+
except ImportError:
472+
pass

_pytest/hookspec.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -282,12 +282,11 @@ def pytest_keyboard_interrupt(excinfo):
282282
""" called for keyboard interrupt. """
283283

284284
def pytest_exception_interact(node, call, report):
285-
""" (experimental, new in 2.4) called when
286-
an exception was raised which can potentially be
285+
"""called when an exception was raised which can potentially be
287286
interactively handled.
288287
289288
This hook is only called if an exception was raised
290-
that is not an internal exception like "skip.Exception".
289+
that is not an internal exception like ``skip.Exception``.
291290
"""
292291

293292
def pytest_enter_pdb(config):

_pytest/junitxml.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ def __init__(self, nodeid, xml):
7171
self.testcase = None
7272
self.attrs = {}
7373

74-
7574
def append(self, node):
7675
self.xml.add_stats(type(node).__name__)
7776
self.nodes.append(node)
@@ -82,7 +81,6 @@ def add_property(self, name, value):
8281
self.property_insert_order.append(name)
8382
self.properties[name] = bin_xml_escape(value)
8483

85-
8684
def make_properties_node(self):
8785
"""Return a Junit node containing custom properties, if any.
8886
"""
@@ -93,7 +91,6 @@ def make_properties_node(self):
9391
])
9492
return ''
9593

96-
9794
def record_testreport(self, testreport):
9895
assert not self.testcase
9996
names = mangle_testnames(testreport.nodeid.split("::"))
@@ -182,7 +179,6 @@ def append_skipped(self, report):
182179
message=skipreason))
183180
self._write_captured_output(report)
184181

185-
186182
def finalize(self):
187183
data = self.to_xml().unicode(indent=0)
188184
self.__dict__.clear()
@@ -262,6 +258,14 @@ def __init__(self, logfile, prefix):
262258
self.node_reporters = {} # nodeid -> _NodeReporter
263259
self.node_reporters_ordered = []
264260

261+
def finalize(self, report):
262+
nodeid = getattr(report, 'nodeid', report)
263+
# local hack to handle xdist report order
264+
slavenode = getattr(report, 'node', None)
265+
reporter = self.node_reporters.pop((nodeid, slavenode))
266+
if reporter is not None:
267+
reporter.finalize()
268+
265269
def node_reporter(self, report):
266270
nodeid = getattr(report, 'nodeid', report)
267271
# local hack to handle xdist report order
@@ -270,7 +274,7 @@ def node_reporter(self, report):
270274
key = nodeid, slavenode
271275

272276
if key in self.node_reporters:
273-
#TODO: breasks for --dist=each
277+
# TODO: breasks for --dist=each
274278
return self.node_reporters[key]
275279
reporter = _NodeReporter(nodeid, self)
276280
self.node_reporters[key] = reporter
@@ -324,7 +328,7 @@ def pytest_runtest_logreport(self, report):
324328
reporter.append_skipped(report)
325329
self.update_testcase_duration(report)
326330
if report.when == "teardown":
327-
self.node_reporter(report).finalize()
331+
self.finalize(report)
328332

329333
def update_testcase_duration(self, report):
330334
"""accumulates total duration for nodeid from given report and updates

_pytest/monkeypatch.py

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
""" monkeypatching and mocking functionality. """
22

33
import os, sys
4+
import re
5+
46
from py.builtin import _basestring
57

8+
9+
RE_IMPORT_ERROR_NAME = re.compile("^No module named (.*)$")
10+
11+
612
def pytest_funcarg__monkeypatch(request):
713
"""The returned ``monkeypatch`` funcarg provides these
814
helper methods to modify objects, dictionaries or os.environ::
@@ -34,14 +40,28 @@ def derive_importpath(import_path, raising):
3440
(import_path,))
3541
rest = []
3642
target = import_path
43+
target_parts = set(target.split("."))
3744
while target:
3845
try:
3946
obj = __import__(target, None, None, "__doc__")
40-
except ImportError:
47+
except ImportError as ex:
48+
if hasattr(ex, 'name'):
49+
# Python >= 3.3
50+
failed_name = ex.name
51+
else:
52+
match = RE_IMPORT_ERROR_NAME.match(ex.args[0])
53+
assert match
54+
failed_name = match.group(1)
55+
4156
if "." not in target:
4257
__tracebackhide__ = True
4358
pytest.fail("could not import any sub part: %s" %
4459
import_path)
60+
elif failed_name != target \
61+
and not any(p == failed_name for p in target_parts):
62+
# target is importable but causes ImportError itself
63+
__tracebackhide__ = True
64+
pytest.fail("import error in %s: %s" % (target, ex.args[0]))
4565
target, name = target.rsplit(".", 1)
4666
rest.append(name)
4767
else:
@@ -106,7 +126,7 @@ def setattr(self, target, name, value=notset, raising=True):
106126
# avoid class descriptors like staticmethod/classmethod
107127
if inspect.isclass(target):
108128
oldval = target.__dict__.get(name, notset)
109-
self._setattr.insert(0, (target, name, oldval))
129+
self._setattr.append((target, name, oldval))
110130
setattr(target, name, value)
111131

112132
def delattr(self, target, name=notset, raising=True):
@@ -132,13 +152,12 @@ def delattr(self, target, name=notset, raising=True):
132152
if raising:
133153
raise AttributeError(name)
134154
else:
135-
self._setattr.insert(0, (target, name,
136-
getattr(target, name, notset)))
155+
self._setattr.append((target, name, getattr(target, name, notset)))
137156
delattr(target, name)
138157

139158
def setitem(self, dic, name, value):
140159
""" Set dictionary entry ``name`` to value. """
141-
self._setitem.insert(0, (dic, name, dic.get(name, notset)))
160+
self._setitem.append((dic, name, dic.get(name, notset)))
142161
dic[name] = value
143162

144163
def delitem(self, dic, name, raising=True):
@@ -151,7 +170,7 @@ def delitem(self, dic, name, raising=True):
151170
if raising:
152171
raise KeyError(name)
153172
else:
154-
self._setitem.insert(0, (dic, name, dic.get(name, notset)))
173+
self._setitem.append((dic, name, dic.get(name, notset)))
155174
del dic[name]
156175

157176
def setenv(self, name, value, prepend=None):
@@ -203,13 +222,13 @@ def undo(self):
203222
calling `undo()` will undo all of the changes made in
204223
both functions.
205224
"""
206-
for obj, name, value in self._setattr:
225+
for obj, name, value in reversed(self._setattr):
207226
if value is not notset:
208227
setattr(obj, name, value)
209228
else:
210229
delattr(obj, name)
211230
self._setattr[:] = []
212-
for dictionary, name, value in self._setitem:
231+
for dictionary, name, value in reversed(self._setitem):
213232
if value is notset:
214233
try:
215234
del dictionary[name]

doc/en/example/parametrize.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ The result of this test will be successful::
333333
Parametrizing test methods through per-class configuration
334334
--------------------------------------------------------------
335335

336-
.. _`unittest parametrizer`: http://code.google.com/p/unittest-ext/source/browse/trunk/params.py
336+
.. _`unittest parametrizer`: https://github.com/testing-cabal/unittest-ext/blob/master/params.py
337337

338338

339339
Here is an example ``pytest_generate_function`` function implementing a

doc/en/faq.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ in a managed class/module/function scope.
120120

121121
.. _`Convention over Configuration`: http://en.wikipedia.org/wiki/Convention_over_Configuration
122122

123-
Can I yield multiple values from a fixture function function?
123+
Can I yield multiple values from a fixture function?
124124
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
125125

126126
There are two conceptual reasons why yielding from a factory function

0 commit comments

Comments
 (0)