Skip to content

Commit 467c526

Browse files
committed
Merge remote-tracking branch 'upstream/master' into features
2 parents c67bf9d + b2d7c26 commit 467c526

File tree

13 files changed

+129
-22
lines changed

13 files changed

+129
-22
lines changed

CHANGELOG.rst

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,38 @@
88
99
.. towncrier release notes start
1010
11+
Pytest 3.1.2 (2017-06-08)
12+
=========================
13+
14+
Bug Fixes
15+
---------
16+
17+
- Required options added via ``pytest_addoption`` will no longer prevent using
18+
--help without passing them. (#1999)
19+
20+
- Respect ``python_files`` in assertion rewriting. (#2121)
21+
22+
- Fix recursion error detection when frames in the traceback contain objects
23+
that can't be compared (like ``numpy`` arrays). (#2459)
24+
25+
- ``UnicodeWarning`` is issued from the internal pytest warnings plugin only
26+
when the message contains non-ascii unicode (Python 2 only). (#2463)
27+
28+
- Added a workaround for Python 3.6 WindowsConsoleIO breaking due to Pytests's
29+
FDCapture. Other code using console handles might still be affected by the
30+
very same issue and might require further workarounds/fixes, i.e. colorama.
31+
(#2467)
32+
33+
34+
Improved Documentation
35+
----------------------
36+
37+
- Fix internal API links to ``pluggy`` objects. (#2331)
38+
39+
- Make it clear that ``pytest.xfail`` stops test execution at the calling point
40+
and improve overall flow of the ``skipping`` docs. (#810)
41+
42+
1143
Pytest 3.1.1 (2017-05-30)
1244
=========================
1345

_pytest/_code/code.py

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from inspect import CO_VARARGS, CO_VARKEYWORDS
44
import re
55
from weakref import ref
6-
from _pytest.compat import _PY2, _PY3, PY35
6+
from _pytest.compat import _PY2, _PY3, PY35, safe_str
77

88
import py
99
builtin_repr = repr
@@ -602,21 +602,48 @@ def repr_traceback(self, excinfo):
602602
traceback = excinfo.traceback
603603
if self.tbfilter:
604604
traceback = traceback.filter()
605-
recursionindex = None
605+
606606
if is_recursion_error(excinfo):
607-
recursionindex = traceback.recursionindex()
607+
traceback, extraline = self._truncate_recursive_traceback(traceback)
608+
else:
609+
extraline = None
610+
608611
last = traceback[-1]
609612
entries = []
610-
extraline = None
611613
for index, entry in enumerate(traceback):
612614
einfo = (last == entry) and excinfo or None
613615
reprentry = self.repr_traceback_entry(entry, einfo)
614616
entries.append(reprentry)
615-
if index == recursionindex:
616-
extraline = "!!! Recursion detected (same locals & position)"
617-
break
618617
return ReprTraceback(entries, extraline, style=self.style)
619618

619+
def _truncate_recursive_traceback(self, traceback):
620+
"""
621+
Truncate the given recursive traceback trying to find the starting point
622+
of the recursion.
623+
624+
The detection is done by going through each traceback entry and finding the
625+
point in which the locals of the frame are equal to the locals of a previous frame (see ``recursionindex()``.
626+
627+
Handle the situation where the recursion process might raise an exception (for example
628+
comparing numpy arrays using equality raises a TypeError), in which case we do our best to
629+
warn the user of the error and show a limited traceback.
630+
"""
631+
try:
632+
recursionindex = traceback.recursionindex()
633+
except Exception as e:
634+
max_frames = 10
635+
extraline = (
636+
'!!! Recursion error detected, but an error occurred locating the origin of recursion.\n'
637+
' The following exception happened when comparing locals in the stack frame:\n'
638+
' {exc_type}: {exc_msg}\n'
639+
' Displaying first and last {max_frames} stack frames out of {total}.'
640+
).format(exc_type=type(e).__name__, exc_msg=safe_str(e), max_frames=max_frames, total=len(traceback))
641+
traceback = traceback[:max_frames] + traceback[-max_frames:]
642+
else:
643+
extraline = "!!! Recursion detected (same locals & position)"
644+
traceback = traceback[:recursionindex + 1]
645+
646+
return traceback, extraline
620647

621648
def repr_excinfo(self, excinfo):
622649
if _PY2:

_pytest/warnings.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,9 @@ def catch_warnings_for_item(item):
6666
unicode_warning = False
6767

6868
if compat._PY2 and any(isinstance(m, compat.UNICODE_TYPES) for m in warn_msg.args):
69-
warn_msg.args = [compat.safe_str(m) for m in warn_msg.args]
70-
unicode_warning = True
69+
new_args = [compat.safe_str(m) for m in warn_msg.args]
70+
unicode_warning = warn_msg.args != new_args
71+
warn_msg.args = new_args
7172

7273
msg = warnings.formatwarning(
7374
warn_msg, warning.category,
@@ -76,8 +77,8 @@ def catch_warnings_for_item(item):
7677

7778
if unicode_warning:
7879
warnings.warn(
79-
"This warning %s is broken as it's message is not a str instance"
80-
"(after all this is a stdlib problem workaround)" % msg,
80+
"Warning is using unicode non convertible to ascii, "
81+
"converting to a safe representation:\n %s" % msg,
8182
UnicodeWarning)
8283

8384

changelog/1999.bugfix

Lines changed: 0 additions & 2 deletions
This file was deleted.

changelog/2121.bugfix

Lines changed: 0 additions & 1 deletion
This file was deleted.

changelog/2331.doc

Lines changed: 0 additions & 1 deletion
This file was deleted.

changelog/2467.bugfix

Lines changed: 0 additions & 3 deletions
This file was deleted.

changelog/810.doc

Lines changed: 0 additions & 1 deletion
This file was deleted.

doc/en/announce/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Release announcements
66
:maxdepth: 2
77

88

9+
release-3.1.2
910
release-3.1.1
1011
release-3.1.0
1112
release-3.0.7

doc/en/announce/release-3.1.2.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
pytest-3.1.2
2+
=======================================
3+
4+
pytest 3.1.2 has just been released to PyPI.
5+
6+
This is a bug-fix release, being a drop-in replacement. To upgrade::
7+
8+
pip install --upgrade pytest
9+
10+
The full changelog is available at http://doc.pytest.org/en/latest/changelog.html.
11+
12+
Thanks to all who contributed to this release, among them:
13+
14+
* Andreas Pelme
15+
* ApaDoctor
16+
* Bruno Oliveira
17+
* Florian Bruhin
18+
* Ronny Pfannschmidt
19+
* Segev Finer
20+
21+
22+
Happy testing,
23+
The pytest Development Team

doc/en/skipping.rst

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,8 +322,6 @@ Running it with the report-on-xfail option gives this output::
322322
323323
======= 7 xfailed in 0.12 seconds ========
324324

325-
326-
327325
.. _`skip/xfail with parametrize`:
328326

329327
Skip/xfail with parametrize

testing/code/test_excinfo.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,3 +1140,36 @@ def test(tmpdir):
11401140
result = testdir.runpytest()
11411141
result.stdout.fnmatch_lines(['* 1 failed in *'])
11421142
assert 'INTERNALERROR' not in result.stdout.str() + result.stderr.str()
1143+
1144+
1145+
def test_exception_repr_extraction_error_on_recursion():
1146+
"""
1147+
Ensure we can properly detect a recursion error even
1148+
if some locals raise error on comparision (#2459).
1149+
"""
1150+
class numpy_like(object):
1151+
1152+
def __eq__(self, other):
1153+
if type(other) is numpy_like:
1154+
raise ValueError('The truth value of an array '
1155+
'with more than one element is ambiguous.')
1156+
1157+
def a(x):
1158+
return b(numpy_like())
1159+
1160+
def b(x):
1161+
return a(numpy_like())
1162+
1163+
try:
1164+
a(numpy_like())
1165+
except:
1166+
from _pytest._code.code import ExceptionInfo
1167+
from _pytest.pytester import LineMatcher
1168+
exc_info = ExceptionInfo()
1169+
1170+
matcher = LineMatcher(str(exc_info.getrepr()).splitlines())
1171+
matcher.fnmatch_lines([
1172+
'!!! Recursion error detected, but an error occurred locating the origin of recursion.',
1173+
'*The following exception happened*',
1174+
'*ValueError: The truth value of an array*',
1175+
])

testing/test_warnings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ def test_func(fix):
161161

162162
'*test_py2_unicode.py:8: UserWarning: \u6d4b\u8bd5',
163163
'*warnings.warn(u"\u6d4b\u8bd5")',
164-
'*warnings.py:*: UnicodeWarning: This warning*\u6d4b\u8bd5',
164+
'*warnings.py:*: UnicodeWarning: Warning is using unicode non*',
165165
'* 1 passed, 2 warnings*',
166166
])
167167

0 commit comments

Comments
 (0)