Skip to content

Commit 1f46015

Browse files
committed
Merge remote-tracking branch 'upstream/features' into features
2 parents 4405dd0 + da10451 commit 1f46015

40 files changed

+433
-117
lines changed

.github/ISSUE_TEMPLATE.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Thanks for submitting an issue!
22

33
Here's a quick checklist in what to include:
44

5-
[ ] Include a detailed description of the bug or suggestion
6-
[ ] `pip list` of the virtual environment you are using
7-
[ ] py.test and operating system versions
8-
[ ] Minimal example if possible
5+
- [ ] Include a detailed description of the bug or suggestion
6+
- [ ] `pip list` of the virtual environment you are using
7+
- [ ] py.test and operating system versions
8+
- [ ] Minimal example if possible

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Thanks for submitting a PR, your contribution is really appreciated!
22

33
Here's a quick checklist that should be present in PRs:
44

5-
[ ] Target: for bug or doc fixes, target `master`; for new features, target `features`
6-
[ ] Make sure to include one or more tests for your change
7-
[ ] Add yourself to `AUTHORS`
8-
[ ] Add a new entry to the `CHANGELOG` (choose any open position to avoid merge conflicts with other PRs)
5+
- [ ] Target: for bug or doc fixes, target `master`; for new features, target `features`
6+
- [ ] Make sure to include one or more tests for your change
7+
- [ ] Add yourself to `AUTHORS`
8+
- [ ] Add a new entry to the `CHANGELOG` (choose any open position to avoid merge conflicts with other PRs)

AUTHORS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Andy Freeland
1010
Anthon van der Neut
1111
Armin Rigo
1212
Aron Curzon
13+
Aviv Palivoda
1314
Benjamin Peterson
1415
Bob Ippolito
1516
Brian Dorsey
@@ -23,6 +24,7 @@ Christian Theunert
2324
Christian Tismer
2425
Christopher Gilling
2526
Daniel Grana
27+
Daniel Hahler
2628
Daniel Nuri
2729
Dave Hunt
2830
David Mohr
@@ -60,6 +62,7 @@ Marc Schlaich
6062
Mark Abramowitz
6163
Markus Unterwaditzer
6264
Martijn Faassen
65+
Matt Bachmann
6366
Matt Williams
6467
Michael Aquilina
6568
Michael Birtwell

CHANGELOG.rst

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
explicitly passed parametrize ids do not get escaped to ascii.
2828
Thanks `@ceridwen`_ for the PR.
2929

30+
* parametrize ids can accept None as specific test id. The
31+
automatically generated id for that argument will be used.
32+
3033
*
3134

3235
*
@@ -42,17 +45,54 @@
4245
.. _#1454: https://github.com/pytest-dev/pytest/pull/1454
4346

4447

45-
2.9.1.dev1
48+
2.9.2.dev1
4649
==========
4750

4851
**Bug Fixes**
4952

53+
* When receiving identical test ids in parametrize we generate unique test ids.
54+
5055
*
5156

5257
*
5358

5459
*
5560

61+
62+
2.9.1
63+
=====
64+
65+
**Bug Fixes**
66+
67+
* Improve error message when a plugin fails to load.
68+
Thanks `@nicoddemus`_ for the PR.
69+
70+
* Fix (`#1178 <https://github.com/pytest-dev/pytest/issues/1178>`_):
71+
``pytest.fail`` with non-ascii characters raises an internal pytest error.
72+
Thanks `@nicoddemus`_ for the PR.
73+
74+
* Fix (`#469`_): junit parses report.nodeid incorrectly, when params IDs
75+
contain ``::``. Thanks `@tomviner`_ for the PR (`#1431`_).
76+
77+
* Fix (`#578 <https://github.com/pytest-dev/pytest/issues/578>`_): SyntaxErrors
78+
containing non-ascii lines at the point of failure generated an internal
79+
py.test error.
80+
Thanks `@asottile`_ for the report and `@nicoddemus`_ for the PR.
81+
82+
* Fix (`#1437`_): When passing in a bytestring regex pattern to parameterize
83+
attempt to decode it as utf-8 ignoring errors.
84+
85+
* Fix (`#649`_): parametrized test nodes cannot be specified to run on the command line.
86+
87+
88+
.. _#1437: https://github.com/pytest-dev/pytest/issues/1437
89+
.. _#469: https://github.com/pytest-dev/pytest/issues/469
90+
.. _#1431: https://github.com/pytest-dev/pytest/pull/1431
91+
.. _#649: https://github.com/pytest-dev/pytest/issues/649
92+
93+
.. _@asottile: https://github.com/asottile
94+
95+
5696
2.9.0
5797
=====
5898

_pytest/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
#
2+
23
__version__ = '2.10.0.dev1'

_pytest/_code/_py2traceback.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ def format_exception_only(etype, value):
4747
filename = filename or "<string>"
4848
lines.append(' File "%s", line %d\n' % (filename, lineno))
4949
if badline is not None:
50-
lines.append(' %s\n' % badline.strip())
50+
if isinstance(badline, bytes): # python 2 only
51+
badline = badline.decode('utf-8', 'replace')
52+
lines.append(u' %s\n' % badline.strip())
5153
if offset is not None:
5254
caretspace = badline.rstrip('\n')[:offset].lstrip()
5355
# non-space whitespace (likes tabs) must be kept for alignment

_pytest/cacheprovider.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,9 @@ def pytest_sessionfinish(self, session):
149149
config = self.config
150150
if config.getvalue("cacheshow") or hasattr(config, "slaveinput"):
151151
return
152-
config.cache.set("cache/lastfailed", self.lastfailed)
152+
prev_failed = config.cache.get("cache/lastfailed", None) is not None
153+
if (session.testscollected and prev_failed) or self.lastfailed:
154+
config.cache.set("cache/lastfailed", self.lastfailed)
153155

154156

155157
def pytest_addoption(parser):

_pytest/config.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -383,8 +383,13 @@ def import_plugin(self, modname):
383383
importspec = modname
384384
try:
385385
__import__(importspec)
386-
except ImportError:
387-
raise
386+
except ImportError as e:
387+
new_exc = ImportError('Error importing plugin "%s": %s' % (modname, e))
388+
# copy over name and path attributes
389+
for attr in ('name', 'path'):
390+
if hasattr(e, attr):
391+
setattr(new_exc, attr, getattr(e, attr))
392+
raise new_exc
388393
except Exception as e:
389394
import pytest
390395
if not hasattr(pytest, 'skip') or not isinstance(e, pytest.skip.Exception):

_pytest/hookspec.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,14 @@ def pytest_plugin_registered(plugin, manager):
2828

2929
@hookspec(historic=True)
3030
def pytest_addoption(parser):
31-
"""register argparse-style options and ini-style config values.
31+
"""register argparse-style options and ini-style config values,
32+
called once at the beginning of a test run.
3233
33-
.. warning::
34+
.. note::
3435
35-
This function must be implemented in a :ref:`plugin <pluginorder>`
36-
and is called once at the beginning of a test run.
37-
38-
Implementing this hook from ``conftest.py`` files is **strongly**
39-
discouraged because ``conftest.py`` files are lazily loaded and
40-
may give strange *unknown option* errors depending on the directory
41-
``py.test`` is invoked from.
36+
This function should be implemented only in plugins or ``conftest.py``
37+
files situated at the tests root directory due to how py.test
38+
:ref:`discovers plugins during startup <pluginorder>`.
4239
4340
:arg parser: To add command line options, call
4441
:py:func:`parser.addoption(...) <_pytest.config.Parser.addoption>`.
@@ -84,7 +81,7 @@ def pytest_cmdline_main(config):
8481
""" called for performing the main command line action. The default
8582
implementation will invoke the configure hooks and runtest_mainloop. """
8683

87-
def pytest_load_initial_conftests(args, early_config, parser):
84+
def pytest_load_initial_conftests(early_config, parser, args):
8885
""" implements the loading of initial conftest files ahead
8986
of command line option parsing. """
9087

_pytest/junitxml.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ class Junit(py.xml.Namespace):
4646
del _legal_ranges
4747
del _legal_xml_re
4848

49+
_py_ext_re = re.compile(r"\.py$")
50+
4951

5052
def bin_xml_escape(arg):
5153
def repl(matchobj):
@@ -89,7 +91,7 @@ def make_properties_node(self):
8991

9092
def record_testreport(self, testreport):
9193
assert not self.testcase
92-
names = mangle_testnames(testreport.nodeid.split("::"))
94+
names = mangle_test_address(testreport.nodeid)
9395
classnames = names[:-1]
9496
if self.xml.prefix:
9597
classnames.insert(0, self.xml.prefix)
@@ -235,9 +237,18 @@ def pytest_unconfigure(config):
235237
config.pluginmanager.unregister(xml)
236238

237239

238-
def mangle_testnames(names):
239-
names = [x.replace(".py", "") for x in names if x != '()']
240+
def mangle_test_address(address):
241+
path, possible_open_bracket, params = address.partition('[')
242+
names = path.split("::")
243+
try:
244+
names.remove('()')
245+
except ValueError:
246+
pass
247+
# convert file path to dotted path
240248
names[0] = names[0].replace("/", '.')
249+
names[0] = _py_ext_re.sub("", names[0])
250+
# put any params back
251+
names[-1] += possible_open_bracket + params
241252
return names
242253

243254

_pytest/main.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -718,7 +718,8 @@ def _matchnodes(self, matching, names):
718718
if rep.passed:
719719
has_matched = False
720720
for x in rep.result:
721-
if x.name == name:
721+
# TODO: remove parametrized workaround once collection structure contains parametrization
722+
if x.name == name or x.name.split("[")[0] == name:
722723
resultnodes.extend(self.matchnodes([x], nextnames))
723724
has_matched = True
724725
# XXX accept IDs that don't have "()" for class instances

_pytest/python.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -751,7 +751,7 @@ def _prunetraceback(self, excinfo):
751751
def _repr_failure_py(self, excinfo, style="long"):
752752
if excinfo.errisinstance(pytest.fail.Exception):
753753
if not excinfo.value.pytrace:
754-
return str(excinfo.value)
754+
return py._builtin._totext(excinfo.value)
755755
return super(FunctionMixin, self)._repr_failure_py(excinfo,
756756
style=style)
757757

@@ -967,7 +967,8 @@ def parametrize(self, argnames, argvalues, indirect=False, ids=None,
967967
968968
:arg ids: list of string ids, or a callable.
969969
If strings, each is corresponding to the argvalues so that they are
970-
part of the test id.
970+
part of the test id. If None is given as id of specific test, the
971+
automatically generated id for that argument will be used.
971972
If callable, it should take one argument (a single argvalue) and return
972973
a string or return None. If None, the automatically generated id for that
973974
argument will be used.
@@ -1025,14 +1026,10 @@ def parametrize(self, argnames, argvalues, indirect=False, ids=None,
10251026
if callable(ids):
10261027
idfn = ids
10271028
ids = None
1028-
if ids:
1029-
if len(ids) != len(argvalues):
1030-
raise ValueError('%d tests specified with %d ids' %(
1031-
len(argvalues), len(ids)))
1032-
else:
1033-
ids = [_escape_strings(i) for i in ids]
1034-
if not ids:
1035-
ids = idmaker(argnames, argvalues, idfn)
1029+
if ids and len(ids) != len(argvalues):
1030+
raise ValueError('%d tests specified with %d ids' %(
1031+
len(argvalues), len(ids)))
1032+
ids = idmaker(argnames, argvalues, idfn, ids)
10361033
newcalls = []
10371034
for callspec in self._calls or [CallSpec2(self)]:
10381035
for param_index, valset in enumerate(argvalues):
@@ -1153,13 +1150,16 @@ def _idval(val, argname, idx, idfn):
11531150
return val.__name__
11541151
return str(argname)+str(idx)
11551152

1156-
def _idvalset(idx, valset, argnames, idfn):
1157-
this_id = [_idval(val, argname, idx, idfn)
1158-
for val, argname in zip(valset, argnames)]
1159-
return "-".join(this_id)
1153+
def _idvalset(idx, valset, argnames, idfn, ids):
1154+
if ids is None or ids[idx] is None:
1155+
this_id = [_idval(val, argname, idx, idfn)
1156+
for val, argname in zip(valset, argnames)]
1157+
return "-".join(this_id)
1158+
else:
1159+
return _escape_strings(ids[idx])
11601160

1161-
def idmaker(argnames, argvalues, idfn=None):
1162-
ids = [_idvalset(valindex, valset, argnames, idfn)
1161+
def idmaker(argnames, argvalues, idfn=None, ids=None):
1162+
ids = [_idvalset(valindex, valset, argnames, idfn, ids)
11631163
for valindex, valset in enumerate(argvalues)]
11641164
if len(set(ids)) < len(ids):
11651165
# user may have provided a bad idfn which means the ids are not unique

_pytest/runner.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,10 @@ def __init__(self, msg=None, pytrace=True):
435435

436436
def __repr__(self):
437437
if self.msg:
438-
return str(self.msg)
438+
val = self.msg
439+
if isinstance(val, bytes):
440+
val = py._builtin._totext(val, errors='replace')
441+
return val
439442
return "<%s instance>" %(self.__class__.__name__,)
440443
__str__ = __repr__
441444

doc/en/announce/release-2.9.1.rst

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
pytest-2.9.1
2+
============
3+
4+
pytest is a mature Python testing tool with more than a 1100 tests
5+
against itself, passing on many different interpreters and platforms.
6+
7+
See below for the changes and see docs at:
8+
9+
http://pytest.org
10+
11+
As usual, you can upgrade from pypi via::
12+
13+
pip install -U pytest
14+
15+
Thanks to all who contributed to this release, among them:
16+
17+
Bruno Oliveira
18+
Daniel Hahler
19+
Dmitry Malinovsky
20+
Florian Bruhin
21+
Floris Bruynooghe
22+
Matt Bachmann
23+
Ronny Pfannschmidt
24+
TomV
25+
Vladimir Bolshakov
26+
Zearin
27+
palaviv
28+
29+
30+
Happy testing,
31+
The py.test Development Team
32+
33+
34+
2.9.1 (compared to 2.9.0)
35+
-------------------------
36+
37+
**Bug Fixes**
38+
39+
* Improve error message when a plugin fails to load.
40+
Thanks `@nicoddemus`_ for the PR.
41+
42+
* Fix (`#1178 <https://github.com/pytest-dev/pytest/issues/1178>`_):
43+
``pytest.fail`` with non-ascii characters raises an internal pytest error.
44+
Thanks `@nicoddemus`_ for the PR.
45+
46+
* Fix (`#469`_): junit parses report.nodeid incorrectly, when params IDs
47+
contain ``::``. Thanks `@tomviner`_ for the PR (`#1431`_).
48+
49+
* Fix (`#578 <https://github.com/pytest-dev/pytest/issues/578>`_): SyntaxErrors
50+
containing non-ascii lines at the point of failure generated an internal
51+
py.test error.
52+
Thanks `@asottile`_ for the report and `@nicoddemus`_ for the PR.
53+
54+
* Fix (`#1437`_): When passing in a bytestring regex pattern to parameterize
55+
attempt to decode it as utf-8 ignoring errors.
56+
57+
* Fix (`#649`_): parametrized test nodes cannot be specified to run on the command line.
58+
59+
60+
.. _#1437: https://github.com/pytest-dev/pytest/issues/1437
61+
.. _#469: https://github.com/pytest-dev/pytest/issues/469
62+
.. _#1431: https://github.com/pytest-dev/pytest/pull/1431
63+
.. _#649: https://github.com/pytest-dev/pytest/issues/649
64+
65+
.. _@asottile: https://github.com/asottile

doc/en/assert.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ you will see the return value of the function call::
2626

2727
$ py.test test_assert1.py
2828
======= test session starts ========
29-
platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1
29+
platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
3030
rootdir: $REGENDOC_TMPDIR, inifile:
3131
collected 1 items
3232
@@ -143,7 +143,7 @@ if you run this module::
143143

144144
$ py.test test_assert2.py
145145
======= test session starts ========
146-
platform linux -- Python 3.4.0, pytest-2.9.0, py-1.4.31, pluggy-0.3.1
146+
platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
147147
rootdir: $REGENDOC_TMPDIR, inifile:
148148
collected 1 items
149149

0 commit comments

Comments
 (0)