Merge remote-tracking branch 'upstream/master' into features

This commit is contained in:
Bruno Oliveira 2017-06-09 18:33:46 -03:00
commit 467c526307
13 changed files with 129 additions and 22 deletions

View File

@ -8,6 +8,38 @@
.. towncrier release notes start .. towncrier release notes start
Pytest 3.1.2 (2017-06-08)
=========================
Bug Fixes
---------
- Required options added via ``pytest_addoption`` will no longer prevent using
--help without passing them. (#1999)
- Respect ``python_files`` in assertion rewriting. (#2121)
- Fix recursion error detection when frames in the traceback contain objects
that can't be compared (like ``numpy`` arrays). (#2459)
- ``UnicodeWarning`` is issued from the internal pytest warnings plugin only
when the message contains non-ascii unicode (Python 2 only). (#2463)
- Added a workaround for Python 3.6 WindowsConsoleIO breaking due to Pytests's
FDCapture. Other code using console handles might still be affected by the
very same issue and might require further workarounds/fixes, i.e. colorama.
(#2467)
Improved Documentation
----------------------
- Fix internal API links to ``pluggy`` objects. (#2331)
- Make it clear that ``pytest.xfail`` stops test execution at the calling point
and improve overall flow of the ``skipping`` docs. (#810)
Pytest 3.1.1 (2017-05-30) Pytest 3.1.1 (2017-05-30)
========================= =========================

View File

@ -3,7 +3,7 @@ import sys
from inspect import CO_VARARGS, CO_VARKEYWORDS from inspect import CO_VARARGS, CO_VARKEYWORDS
import re import re
from weakref import ref from weakref import ref
from _pytest.compat import _PY2, _PY3, PY35 from _pytest.compat import _PY2, _PY3, PY35, safe_str
import py import py
builtin_repr = repr builtin_repr = repr
@ -602,21 +602,48 @@ class FormattedExcinfo(object):
traceback = excinfo.traceback traceback = excinfo.traceback
if self.tbfilter: if self.tbfilter:
traceback = traceback.filter() traceback = traceback.filter()
recursionindex = None
if is_recursion_error(excinfo): if is_recursion_error(excinfo):
recursionindex = traceback.recursionindex() traceback, extraline = self._truncate_recursive_traceback(traceback)
else:
extraline = None
last = traceback[-1] last = traceback[-1]
entries = [] entries = []
extraline = None
for index, entry in enumerate(traceback): for index, entry in enumerate(traceback):
einfo = (last == entry) and excinfo or None einfo = (last == entry) and excinfo or None
reprentry = self.repr_traceback_entry(entry, einfo) reprentry = self.repr_traceback_entry(entry, einfo)
entries.append(reprentry) entries.append(reprentry)
if index == recursionindex:
extraline = "!!! Recursion detected (same locals & position)"
break
return ReprTraceback(entries, extraline, style=self.style) return ReprTraceback(entries, extraline, style=self.style)
def _truncate_recursive_traceback(self, traceback):
"""
Truncate the given recursive traceback trying to find the starting point
of the recursion.
The detection is done by going through each traceback entry and finding the
point in which the locals of the frame are equal to the locals of a previous frame (see ``recursionindex()``.
Handle the situation where the recursion process might raise an exception (for example
comparing numpy arrays using equality raises a TypeError), in which case we do our best to
warn the user of the error and show a limited traceback.
"""
try:
recursionindex = traceback.recursionindex()
except Exception as e:
max_frames = 10
extraline = (
'!!! Recursion error detected, but an error occurred locating the origin of recursion.\n'
' The following exception happened when comparing locals in the stack frame:\n'
' {exc_type}: {exc_msg}\n'
' Displaying first and last {max_frames} stack frames out of {total}.'
).format(exc_type=type(e).__name__, exc_msg=safe_str(e), max_frames=max_frames, total=len(traceback))
traceback = traceback[:max_frames] + traceback[-max_frames:]
else:
extraline = "!!! Recursion detected (same locals & position)"
traceback = traceback[:recursionindex + 1]
return traceback, extraline
def repr_excinfo(self, excinfo): def repr_excinfo(self, excinfo):
if _PY2: if _PY2:

View File

@ -66,8 +66,9 @@ def catch_warnings_for_item(item):
unicode_warning = False unicode_warning = False
if compat._PY2 and any(isinstance(m, compat.UNICODE_TYPES) for m in warn_msg.args): if compat._PY2 and any(isinstance(m, compat.UNICODE_TYPES) for m in warn_msg.args):
warn_msg.args = [compat.safe_str(m) for m in warn_msg.args] new_args = [compat.safe_str(m) for m in warn_msg.args]
unicode_warning = True unicode_warning = warn_msg.args != new_args
warn_msg.args = new_args
msg = warnings.formatwarning( msg = warnings.formatwarning(
warn_msg, warning.category, warn_msg, warning.category,
@ -76,8 +77,8 @@ def catch_warnings_for_item(item):
if unicode_warning: if unicode_warning:
warnings.warn( warnings.warn(
"This warning %s is broken as it's message is not a str instance" "Warning is using unicode non convertible to ascii, "
"(after all this is a stdlib problem workaround)" % msg, "converting to a safe representation:\n %s" % msg,
UnicodeWarning) UnicodeWarning)

View File

@ -1,2 +0,0 @@
Required options added via ``pytest_addoption`` will no longer prevent
using --help without passing them.

View File

@ -1 +0,0 @@
Respect ``python_files`` in assertion rewriting.

View File

@ -1 +0,0 @@
Fix internal API links to ``pluggy`` objects.

View File

@ -1,3 +0,0 @@
Added a workaround for Python 3.6 WindowsConsoleIO breaking due to Pytests's
FDCapture. Other code using console handles might still be affected by the
very same issue and might require further workarounds/fixes, i.e. colorama.

View File

@ -1 +0,0 @@
Make it clear that ``pytest.xfail`` stops test execution at the calling point and improve overall flow of the ``skipping`` docs.

View File

@ -6,6 +6,7 @@ Release announcements
:maxdepth: 2 :maxdepth: 2
release-3.1.2
release-3.1.1 release-3.1.1
release-3.1.0 release-3.1.0
release-3.0.7 release-3.0.7

View File

@ -0,0 +1,23 @@
pytest-3.1.2
=======================================
pytest 3.1.2 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at http://doc.pytest.org/en/latest/changelog.html.
Thanks to all who contributed to this release, among them:
* Andreas Pelme
* ApaDoctor
* Bruno Oliveira
* Florian Bruhin
* Ronny Pfannschmidt
* Segev Finer
Happy testing,
The pytest Development Team

View File

@ -322,8 +322,6 @@ Running it with the report-on-xfail option gives this output::
======= 7 xfailed in 0.12 seconds ======== ======= 7 xfailed in 0.12 seconds ========
.. _`skip/xfail with parametrize`: .. _`skip/xfail with parametrize`:
Skip/xfail with parametrize Skip/xfail with parametrize

View File

@ -1140,3 +1140,36 @@ def test_cwd_deleted(testdir):
result = testdir.runpytest() result = testdir.runpytest()
result.stdout.fnmatch_lines(['* 1 failed in *']) result.stdout.fnmatch_lines(['* 1 failed in *'])
assert 'INTERNALERROR' not in result.stdout.str() + result.stderr.str() assert 'INTERNALERROR' not in result.stdout.str() + result.stderr.str()
def test_exception_repr_extraction_error_on_recursion():
"""
Ensure we can properly detect a recursion error even
if some locals raise error on comparision (#2459).
"""
class numpy_like(object):
def __eq__(self, other):
if type(other) is numpy_like:
raise ValueError('The truth value of an array '
'with more than one element is ambiguous.')
def a(x):
return b(numpy_like())
def b(x):
return a(numpy_like())
try:
a(numpy_like())
except:
from _pytest._code.code import ExceptionInfo
from _pytest.pytester import LineMatcher
exc_info = ExceptionInfo()
matcher = LineMatcher(str(exc_info.getrepr()).splitlines())
matcher.fnmatch_lines([
'!!! Recursion error detected, but an error occurred locating the origin of recursion.',
'*The following exception happened*',
'*ValueError: The truth value of an array*',
])

View File

@ -161,7 +161,7 @@ def test_py2_unicode(testdir, pyfile_with_warnings):
'*test_py2_unicode.py:8: UserWarning: \u6d4b\u8bd5', '*test_py2_unicode.py:8: UserWarning: \u6d4b\u8bd5',
'*warnings.warn(u"\u6d4b\u8bd5")', '*warnings.warn(u"\u6d4b\u8bd5")',
'*warnings.py:*: UnicodeWarning: This warning*\u6d4b\u8bd5', '*warnings.py:*: UnicodeWarning: Warning is using unicode non*',
'* 1 passed, 2 warnings*', '* 1 passed, 2 warnings*',
]) ])