diff options
author | shadchin <shadchin@yandex-team.ru> | 2022-02-10 16:44:30 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:44:30 +0300 |
commit | 2598ef1d0aee359b4b6d5fdd1758916d5907d04f (patch) | |
tree | 012bb94d777798f1f56ac1cec429509766d05181 /contrib/python/pytest/py2/_pytest | |
parent | 6751af0b0c1b952fede40b19b71da8025b5d8bcf (diff) | |
download | ydb-2598ef1d0aee359b4b6d5fdd1758916d5907d04f.tar.gz |
Restoring authorship annotation for <shadchin@yandex-team.ru>. Commit 1 of 2.
Diffstat (limited to 'contrib/python/pytest/py2/_pytest')
55 files changed, 3531 insertions, 3531 deletions
diff --git a/contrib/python/pytest/py2/_pytest/__init__.py b/contrib/python/pytest/py2/_pytest/__init__.py index 17cc20b615..96cc45f128 100644 --- a/contrib/python/pytest/py2/_pytest/__init__.py +++ b/contrib/python/pytest/py2/_pytest/__init__.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- __all__ = ["__version__"] try: diff --git a/contrib/python/pytest/py2/_pytest/_argcomplete.py b/contrib/python/pytest/py2/_pytest/_argcomplete.py index c6cf1d8fdd..0f1ff1b78b 100644 --- a/contrib/python/pytest/py2/_pytest/_argcomplete.py +++ b/contrib/python/pytest/py2/_pytest/_argcomplete.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """allow bash-completion for argparse with argcomplete if installed needs argcomplete>=0.5.6 for python 3.2/3.3 (older versions fail to find the magic string, so _ARGCOMPLETE env. var is never set, and diff --git a/contrib/python/pytest/py2/_pytest/_code/__init__.py b/contrib/python/pytest/py2/_pytest/_code/__init__.py index 1394b2b10e..989743ea51 100644 --- a/contrib/python/pytest/py2/_pytest/_code/__init__.py +++ b/contrib/python/pytest/py2/_pytest/_code/__init__.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ python inspection/code generation API """ from __future__ import absolute_import from __future__ import division diff --git a/contrib/python/pytest/py2/_pytest/_code/_py2traceback.py b/contrib/python/pytest/py2/_pytest/_code/_py2traceback.py index faacc02166..cd614642b7 100644 --- a/contrib/python/pytest/py2/_pytest/_code/_py2traceback.py +++ b/contrib/python/pytest/py2/_pytest/_code/_py2traceback.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- # copied from python-2.7.3's traceback.py # CHANGES: # - some_str is replaced, trying to create unicode strings diff --git a/contrib/python/pytest/py2/_pytest/_code/code.py b/contrib/python/pytest/py2/_pytest/_code/code.py index 175d6fda01..38ca0a5b19 100644 --- a/contrib/python/pytest/py2/_pytest/_code/code.py +++ b/contrib/python/pytest/py2/_pytest/_code/code.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- from __future__ import absolute_import from __future__ import division from __future__ import print_function @@ -17,8 +17,8 @@ import py from six import text_type import _pytest -from _pytest._io.saferepr import safeformat -from _pytest._io.saferepr import saferepr +from _pytest._io.saferepr import safeformat +from _pytest._io.saferepr import saferepr from _pytest.compat import _PY2 from _pytest.compat import _PY3 from _pytest.compat import PY35 @@ -138,12 +138,12 @@ class Frame(object): """ f_locals = self.f_locals.copy() f_locals.update(vars) - exec(code, self.f_globals, f_locals) + exec(code, self.f_globals, f_locals) def repr(self, object): """ return a 'safe' (non-recursive, one-line) string repr for 'object' """ - return saferepr(object) + return saferepr(object) def is_true(self, object): return object @@ -241,20 +241,20 @@ class TracebackEntry(object): def ishidden(self): """ return True if the current frame has a var __tracebackhide__ - resolving to True. + resolving to True. If __tracebackhide__ is a callable, it gets called with the ExceptionInfo instance and can decide whether to hide the traceback. mostly for internal use """ - f = self.frame - tbh = f.f_locals.get( - "__tracebackhide__", f.f_globals.get("__tracebackhide__", False) - ) - if tbh and callable(tbh): + f = self.frame + tbh = f.f_locals.get( + "__tracebackhide__", f.f_globals.get("__tracebackhide__", False) + ) + if tbh and callable(tbh): return tbh(None if self._excinfo is None else self._excinfo()) - return tbh + return tbh def __str__(self): try: @@ -385,7 +385,7 @@ co_equal = compile( ) -@attr.s(repr=False) +@attr.s(repr=False) class ExceptionInfo(object): """ wraps sys.exc_info() objects and offers help for navigating the traceback. @@ -395,76 +395,76 @@ class ExceptionInfo(object): "AssertionError(u'assert " if _PY2 else "AssertionError('assert " ) - _excinfo = attr.ib() - _striptext = attr.ib(default="") - _traceback = attr.ib(default=None) - - @classmethod - def from_current(cls, exprinfo=None): - """returns an ExceptionInfo matching the current traceback - - .. warning:: - - Experimental API - - - :param exprinfo: a text string helping to determine if we should - strip ``AssertionError`` from the output, defaults - to the exception message/``__str__()`` - """ - tup = sys.exc_info() - assert tup[0] is not None, "no current exception" - _striptext = "" - if exprinfo is None and isinstance(tup[1], AssertionError): - exprinfo = getattr(tup[1], "msg", None) - if exprinfo is None: - exprinfo = saferepr(tup[1]) - if exprinfo and exprinfo.startswith(cls._assert_start_repr): - _striptext = "AssertionError: " - - return cls(tup, _striptext) - - @classmethod - def for_later(cls): - """return an unfilled ExceptionInfo - """ - return cls(None) - - @property - def type(self): - """the exception class""" - return self._excinfo[0] - - @property - def value(self): - """the exception value""" - return self._excinfo[1] - - @property - def tb(self): - """the exception raw traceback""" - return self._excinfo[2] - - @property - def typename(self): - """the type name of the exception""" - return self.type.__name__ - - @property - def traceback(self): - """the traceback""" - if self._traceback is None: - self._traceback = Traceback(self.tb, excinfo=ref(self)) - return self._traceback - - @traceback.setter - def traceback(self, value): - self._traceback = value - + _excinfo = attr.ib() + _striptext = attr.ib(default="") + _traceback = attr.ib(default=None) + + @classmethod + def from_current(cls, exprinfo=None): + """returns an ExceptionInfo matching the current traceback + + .. warning:: + + Experimental API + + + :param exprinfo: a text string helping to determine if we should + strip ``AssertionError`` from the output, defaults + to the exception message/``__str__()`` + """ + tup = sys.exc_info() + assert tup[0] is not None, "no current exception" + _striptext = "" + if exprinfo is None and isinstance(tup[1], AssertionError): + exprinfo = getattr(tup[1], "msg", None) + if exprinfo is None: + exprinfo = saferepr(tup[1]) + if exprinfo and exprinfo.startswith(cls._assert_start_repr): + _striptext = "AssertionError: " + + return cls(tup, _striptext) + + @classmethod + def for_later(cls): + """return an unfilled ExceptionInfo + """ + return cls(None) + + @property + def type(self): + """the exception class""" + return self._excinfo[0] + + @property + def value(self): + """the exception value""" + return self._excinfo[1] + + @property + def tb(self): + """the exception raw traceback""" + return self._excinfo[2] + + @property + def typename(self): + """the type name of the exception""" + return self.type.__name__ + + @property + def traceback(self): + """the traceback""" + if self._traceback is None: + self._traceback = Traceback(self.tb, excinfo=ref(self)) + return self._traceback + + @traceback.setter + def traceback(self, value): + self._traceback = value + def __repr__(self): - if self._excinfo is None: - return "<ExceptionInfo for raises contextmanager>" - return "<ExceptionInfo %s tblen=%d>" % (self.typename, len(self.traceback)) + if self._excinfo is None: + return "<ExceptionInfo for raises contextmanager>" + return "<ExceptionInfo %s tblen=%d>" % (self.typename, len(self.traceback)) def exconly(self, tryshort=False): """ return the exception as a string @@ -552,11 +552,11 @@ class ExceptionInfo(object): return fmt.repr_excinfo(self) def __str__(self): - if self._excinfo is None: + if self._excinfo is None: return repr(self) - entry = self.traceback[-1] - loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly()) - return str(loc) + entry = self.traceback[-1] + loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly()) + return str(loc) def __unicode__(self): entry = self.traceback[-1] @@ -565,20 +565,20 @@ class ExceptionInfo(object): def match(self, regexp): """ - Check whether the regular expression 'regexp' is found in the string - representation of the exception using ``re.search``. If it matches - then True is returned (so that it is possible to write - ``assert excinfo.match()``). If it doesn't match an AssertionError is - raised. + Check whether the regular expression 'regexp' is found in the string + representation of the exception using ``re.search``. If it matches + then True is returned (so that it is possible to write + ``assert excinfo.match()``). If it doesn't match an AssertionError is + raised. """ __tracebackhide__ = True - value = ( - text_type(self.value) if isinstance(regexp, text_type) else str(self.value) - ) - if not re.search(regexp, value): - raise AssertionError( - u"Pattern {!r} not found in {!r}".format(regexp, value) - ) + value = ( + text_type(self.value) if isinstance(regexp, text_type) else str(self.value) + ) + if not re.search(regexp, value): + raise AssertionError( + u"Pattern {!r} not found in {!r}".format(regexp, value) + ) return True @@ -624,7 +624,7 @@ class FormattedExcinfo(object): if self.funcargs: args = [] for argname, argvalue in entry.frame.getargs(var=True): - args.append((argname, saferepr(argvalue))) + args.append((argname, saferepr(argvalue))) return ReprFuncArgs(args) def get_source(self, source, line_index=-1, excinfo=None, short=False): @@ -677,9 +677,9 @@ class FormattedExcinfo(object): # _repr() function, which is only reprlib.Repr in # disguise, so is very configurable. if self.truncate_locals: - str_repr = saferepr(value) + str_repr = saferepr(value) else: - str_repr = safeformat(value) + str_repr = safeformat(value) # if len(str_repr) < 70 or not isinstance(value, # (list, tuple, dict)): lines.append("%-10s = %s" % (name, str_repr)) diff --git a/contrib/python/pytest/py2/_pytest/_code/source.py b/contrib/python/pytest/py2/_pytest/_code/source.py index b35e97b9ce..74e9c77600 100644 --- a/contrib/python/pytest/py2/_pytest/_code/source.py +++ b/contrib/python/pytest/py2/_pytest/_code/source.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- from __future__ import absolute_import from __future__ import division from __future__ import print_function @@ -128,8 +128,8 @@ class Source(object): else: source = str(self) try: - ast.parse(source) - except (SyntaxError, ValueError, TypeError): + ast.parse(source) + except (SyntaxError, ValueError, TypeError): return False else: return True @@ -199,9 +199,9 @@ def compile_(source, filename=None, mode="exec", flags=0, dont_inherit=0): def getfslineno(obj): """ Return source location (path, lineno) for the given object. - If the source cannot be determined return ("", -1). - - The line number is 0-based. + If the source cannot be determined return ("", -1). + + The line number is 0-based. """ from .code import Code @@ -235,7 +235,7 @@ def getfslineno(obj): def findsource(obj): try: sourcelines, lineno = inspect.findsource(obj) - except Exception: + except Exception: return None, -1 source = Source() source.lines = [line.rstrip() for line in sourcelines] diff --git a/contrib/python/pytest/py2/_pytest/_io/saferepr.py b/contrib/python/pytest/py2/_pytest/_io/saferepr.py index 9b412dccad..219171f9f5 100644 --- a/contrib/python/pytest/py2/_pytest/_io/saferepr.py +++ b/contrib/python/pytest/py2/_pytest/_io/saferepr.py @@ -1,83 +1,83 @@ -# -*- coding: utf-8 -*- -import pprint - -from six.moves import reprlib - - -def _call_and_format_exception(call, x, *args): - try: - # Try the vanilla repr and make sure that the result is a string - return call(x, *args) - except Exception as exc: - exc_name = type(exc).__name__ - try: - exc_info = str(exc) - except Exception: - exc_info = "unknown" - return '<[%s("%s") raised in repr()] %s object at 0x%x>' % ( - exc_name, - exc_info, - x.__class__.__name__, - id(x), - ) - - -class SafeRepr(reprlib.Repr): - """subclass of repr.Repr that limits the resulting size of repr() - and includes information on exceptions raised during the call. - """ - - def repr(self, x): - return self._callhelper(reprlib.Repr.repr, self, x) - - def repr_unicode(self, x, level): - # Strictly speaking wrong on narrow builds - def repr(u): - if "'" not in u: - return u"'%s'" % u - elif '"' not in u: - return u'"%s"' % u - else: - return u"'%s'" % u.replace("'", r"\'") - - s = repr(x[: self.maxstring]) - if len(s) > self.maxstring: - i = max(0, (self.maxstring - 3) // 2) - j = max(0, self.maxstring - 3 - i) - s = repr(x[:i] + x[len(x) - j :]) - s = s[:i] + "..." + s[len(s) - j :] - return s - - def repr_instance(self, x, level): - return self._callhelper(repr, x) - - def _callhelper(self, call, x, *args): - s = _call_and_format_exception(call, x, *args) - if len(s) > self.maxsize: - i = max(0, (self.maxsize - 3) // 2) - j = max(0, self.maxsize - 3 - i) - s = s[:i] + "..." + s[len(s) - j :] - return s - - -def safeformat(obj): - """return a pretty printed string for the given object. - Failing __repr__ functions of user instances will be represented - with a short exception info. - """ - return _call_and_format_exception(pprint.pformat, obj) - - -def saferepr(obj, maxsize=240): - """return a size-limited safe repr-string for the given object. - Failing __repr__ functions of user instances will be represented - with a short exception info and 'saferepr' generally takes - care to never raise exceptions itself. This function is a wrapper - around the Repr/reprlib functionality of the standard 2.6 lib. - """ - # review exception handling - srepr = SafeRepr() - srepr.maxstring = maxsize - srepr.maxsize = maxsize - srepr.maxother = 160 - return srepr.repr(obj) +# -*- coding: utf-8 -*- +import pprint + +from six.moves import reprlib + + +def _call_and_format_exception(call, x, *args): + try: + # Try the vanilla repr and make sure that the result is a string + return call(x, *args) + except Exception as exc: + exc_name = type(exc).__name__ + try: + exc_info = str(exc) + except Exception: + exc_info = "unknown" + return '<[%s("%s") raised in repr()] %s object at 0x%x>' % ( + exc_name, + exc_info, + x.__class__.__name__, + id(x), + ) + + +class SafeRepr(reprlib.Repr): + """subclass of repr.Repr that limits the resulting size of repr() + and includes information on exceptions raised during the call. + """ + + def repr(self, x): + return self._callhelper(reprlib.Repr.repr, self, x) + + def repr_unicode(self, x, level): + # Strictly speaking wrong on narrow builds + def repr(u): + if "'" not in u: + return u"'%s'" % u + elif '"' not in u: + return u'"%s"' % u + else: + return u"'%s'" % u.replace("'", r"\'") + + s = repr(x[: self.maxstring]) + if len(s) > self.maxstring: + i = max(0, (self.maxstring - 3) // 2) + j = max(0, self.maxstring - 3 - i) + s = repr(x[:i] + x[len(x) - j :]) + s = s[:i] + "..." + s[len(s) - j :] + return s + + def repr_instance(self, x, level): + return self._callhelper(repr, x) + + def _callhelper(self, call, x, *args): + s = _call_and_format_exception(call, x, *args) + if len(s) > self.maxsize: + i = max(0, (self.maxsize - 3) // 2) + j = max(0, self.maxsize - 3 - i) + s = s[:i] + "..." + s[len(s) - j :] + return s + + +def safeformat(obj): + """return a pretty printed string for the given object. + Failing __repr__ functions of user instances will be represented + with a short exception info. + """ + return _call_and_format_exception(pprint.pformat, obj) + + +def saferepr(obj, maxsize=240): + """return a size-limited safe repr-string for the given object. + Failing __repr__ functions of user instances will be represented + with a short exception info and 'saferepr' generally takes + care to never raise exceptions itself. This function is a wrapper + around the Repr/reprlib functionality of the standard 2.6 lib. + """ + # review exception handling + srepr = SafeRepr() + srepr.maxstring = maxsize + srepr.maxsize = maxsize + srepr.maxother = 160 + return srepr.repr(obj) diff --git a/contrib/python/pytest/py2/_pytest/_version.py b/contrib/python/pytest/py2/_pytest/_version.py index 3d587586fc..f2cd9ffdf6 100644 --- a/contrib/python/pytest/py2/_pytest/_version.py +++ b/contrib/python/pytest/py2/_pytest/_version.py @@ -1,4 +1,4 @@ # coding: utf-8 # file generated by setuptools_scm # don't change, don't track in version control -version = '4.6.11' +version = '4.6.11' diff --git a/contrib/python/pytest/py2/_pytest/assertion/__init__.py b/contrib/python/pytest/py2/_pytest/assertion/__init__.py index 6b6abb863a..f83f862cda 100644 --- a/contrib/python/pytest/py2/_pytest/assertion/__init__.py +++ b/contrib/python/pytest/py2/_pytest/assertion/__init__.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ support for presenting detailed information in failing assertions. """ diff --git a/contrib/python/pytest/py2/_pytest/assertion/rewrite.py b/contrib/python/pytest/py2/_pytest/assertion/rewrite.py index 6cfd81a32f..0c1822b529 100644 --- a/contrib/python/pytest/py2/_pytest/assertion/rewrite.py +++ b/contrib/python/pytest/py2/_pytest/assertion/rewrite.py @@ -1,12 +1,12 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """Rewrite assertion AST to produce nice error messages""" from __future__ import absolute_import from __future__ import division from __future__ import print_function -import warnings -warnings.filterwarnings("ignore", category=DeprecationWarning, module="_pytest.assertion.rewrite") - +import warnings +warnings.filterwarnings("ignore", category=DeprecationWarning, module="_pytest.assertion.rewrite") + import ast import errno import imp @@ -23,11 +23,11 @@ import atomicwrites import py import six -from _pytest._io.saferepr import saferepr +from _pytest._io.saferepr import saferepr from _pytest.assertion import util -from _pytest.assertion.util import ( # noqa: F401 - format_explanation as _format_explanation, -) +from _pytest.assertion.util import ( # noqa: F401 + format_explanation as _format_explanation, +) from _pytest.compat import spec_from_file_location from _pytest.pathlib import fnmatch_ex from _pytest.pathlib import PurePath @@ -64,10 +64,10 @@ class AssertionRewritingHook(object): def __init__(self, config): self.config = config - try: - self.fnpats = config.getini("python_files") - except ValueError: - self.fnpats = ["test_*.py", "*_test.py"] + try: + self.fnpats = config.getini("python_files") + except ValueError: + self.fnpats = ["test_*.py", "*_test.py"] self.session = None self.modules = {} self._rewritten_names = set() @@ -274,14 +274,14 @@ class AssertionRewritingHook(object): self._marked_for_rewrite_cache.clear() def _warn_already_imported(self, name): - from _pytest.warning_types import PytestAssertRewriteWarning - from _pytest.warnings import _issue_warning_captured - - _issue_warning_captured( - PytestAssertRewriteWarning( - "Module already imported so cannot be rewritten: %s" % name - ), - self.config.hook, + from _pytest.warning_types import PytestAssertRewriteWarning + from _pytest.warnings import _issue_warning_captured + + _issue_warning_captured( + PytestAssertRewriteWarning( + "Module already imported so cannot be rewritten: %s" % name + ), + self.config.hook, stacklevel=5, ) @@ -304,7 +304,7 @@ class AssertionRewritingHook(object): mod.__loader__ = self # Normally, this attribute is 3.4+ mod.__spec__ = spec_from_file_location(name, co.co_filename, loader=self) - exec(co, mod.__dict__) + exec(co, mod.__dict__) except: # noqa if name in sys.modules: del sys.modules[name] @@ -337,11 +337,11 @@ def _write_pyc(state, co, source_stat, pyc): try: with atomicwrites.atomic_write(pyc, mode="wb", overwrite=True) as fp: fp.write(imp.get_magic()) - # as of now, bytecode header expects 32-bit numbers for size and mtime (#4903) - mtime = int(source_stat.mtime) & 0xFFFFFFFF + # as of now, bytecode header expects 32-bit numbers for size and mtime (#4903) + mtime = int(source_stat.mtime) & 0xFFFFFFFF size = source_stat.size & 0xFFFFFFFF - # "<LL" stands for 2 unsigned longs, little-ending - fp.write(struct.pack("<LL", mtime, size)) + # "<LL" stands for 2 unsigned longs, little-ending + fp.write(struct.pack("<LL", mtime, size)) fp.write(marshal.dumps(co)) except EnvironmentError as e: state.trace("error writing pyc file at %s: errno=%s" % (pyc, e.errno)) @@ -436,7 +436,7 @@ def _read_pyc(source, pyc, trace=lambda x: None): if ( len(data) != 12 or data[:4] != imp.get_magic() - or struct.unpack("<LL", data[4:]) != (mtime & 0xFFFFFFFF, size & 0xFFFFFFFF) + or struct.unpack("<LL", data[4:]) != (mtime & 0xFFFFFFFF, size & 0xFFFFFFFF) ): trace("_read_pyc(%s): invalid or out of date pyc" % source) return None @@ -467,7 +467,7 @@ def _saferepr(obj): JSON reprs. """ - r = saferepr(obj) + r = saferepr(obj) # only occurs in python2.x, repr must return text in python3+ if isinstance(r, bytes): # Represent unprintable bytes as `\x##` @@ -483,7 +483,7 @@ def _format_assertmsg(obj): For strings this simply replaces newlines with '\n~' so that util.format_explanation() will preserve them instead of escaping - newlines. For other objects saferepr() is used first. + newlines. For other objects saferepr() is used first. """ # reprlib appears to have a bug which means that if a string @@ -492,7 +492,7 @@ def _format_assertmsg(obj): # However in either case we want to preserve the newline. replaces = [(u"\n", u"\n~"), (u"%", u"%%")] if not isinstance(obj, six.string_types): - obj = saferepr(obj) + obj = saferepr(obj) replaces.append((u"\\n", u"\n~")) if isinstance(obj, bytes): @@ -505,15 +505,15 @@ def _format_assertmsg(obj): def _should_repr_global_name(obj): - if callable(obj): - return False - - try: - return not hasattr(obj, "__name__") - except Exception: - return True + if callable(obj): + return False + try: + return not hasattr(obj, "__name__") + except Exception: + return True + def _format_boolop(explanations, is_or): explanation = "(" + (is_or and " or " or " and ").join(explanations) + ")" if isinstance(explanation, six.text_type): @@ -658,7 +658,7 @@ class AssertionRewriter(ast.NodeVisitor): # Insert some special imports at the top of the module but after any # docstrings and __future__ imports. aliases = [ - ast.alias(six.moves.builtins.__name__, "@py_builtins"), + ast.alias(six.moves.builtins.__name__, "@py_builtins"), ast.alias("_pytest.assertion.rewrite", "@pytest_ar"), ] doc = getattr(mod, "docstring", None) @@ -733,13 +733,13 @@ class AssertionRewriter(ast.NodeVisitor): return ast.Name(name, ast.Load()) def display(self, expr): - """Call saferepr on the expression.""" - return self.helper("_saferepr", expr) + """Call saferepr on the expression.""" + return self.helper("_saferepr", expr) def helper(self, name, *args): """Call a helper in this module.""" py_name = ast.Name("@pytest_ar", ast.Load()) - attr = ast.Attribute(py_name, name, ast.Load()) + attr = ast.Attribute(py_name, name, ast.Load()) return ast_Call(attr, list(args), []) def builtin(self, name): @@ -809,13 +809,13 @@ class AssertionRewriter(ast.NodeVisitor): """ if isinstance(assert_.test, ast.Tuple) and len(assert_.test.elts) >= 1: - from _pytest.warning_types import PytestAssertRewriteWarning + from _pytest.warning_types import PytestAssertRewriteWarning import warnings warnings.warn_explicit( - PytestAssertRewriteWarning( - "assertion is always true, perhaps remove parentheses?" - ), + PytestAssertRewriteWarning( + "assertion is always true, perhaps remove parentheses?" + ), category=None, filename=str(self.module_path), lineno=assert_.lineno, @@ -829,26 +829,26 @@ class AssertionRewriter(ast.NodeVisitor): self.push_format_context() # Rewrite assert into a bunch of statements. top_condition, explanation = self.visit(assert_.test) - # If in a test module, check if directly asserting None, in order to warn [Issue #3191] - if self.module_path is not None: - self.statements.append( - self.warn_about_none_ast( - top_condition, module_path=self.module_path, lineno=assert_.lineno - ) - ) + # If in a test module, check if directly asserting None, in order to warn [Issue #3191] + if self.module_path is not None: + self.statements.append( + self.warn_about_none_ast( + top_condition, module_path=self.module_path, lineno=assert_.lineno + ) + ) # Create failure message. body = self.on_failure negation = ast.UnaryOp(ast.Not(), top_condition) self.statements.append(ast.If(negation, body, [])) if assert_.msg: - assertmsg = self.helper("_format_assertmsg", assert_.msg) + assertmsg = self.helper("_format_assertmsg", assert_.msg) explanation = "\n>assert " + explanation else: assertmsg = ast.Str("") explanation = "assert " + explanation template = ast.BinOp(assertmsg, ast.Add(), ast.Str(explanation)) msg = self.pop_format_context(template) - fmt = self.helper("_format_explanation", msg) + fmt = self.helper("_format_explanation", msg) err_name = ast.Name("AssertionError", ast.Load()) exc = ast_Call(err_name, [fmt], []) if sys.version_info[0] >= 3: @@ -866,39 +866,39 @@ class AssertionRewriter(ast.NodeVisitor): set_location(stmt, assert_.lineno, assert_.col_offset) return self.statements - def warn_about_none_ast(self, node, module_path, lineno): - """ - Returns an AST issuing a warning if the value of node is `None`. - This is used to warn the user when asserting a function that asserts - internally already. - See issue #3191 for more details. - """ - - # Using parse because it is different between py2 and py3. - AST_NONE = ast.parse("None").body[0].value - val_is_none = ast.Compare(node, [ast.Is()], [AST_NONE]) - send_warning = ast.parse( - """ -from _pytest.warning_types import PytestAssertRewriteWarning -from warnings import warn_explicit -warn_explicit( - PytestAssertRewriteWarning('asserting the value None, please use "assert is None"'), - category=None, - filename={filename!r}, - lineno={lineno}, -) - """.format( - filename=module_path.strpath, lineno=lineno - ) - ).body - return ast.If(val_is_none, send_warning, []) - + def warn_about_none_ast(self, node, module_path, lineno): + """ + Returns an AST issuing a warning if the value of node is `None`. + This is used to warn the user when asserting a function that asserts + internally already. + See issue #3191 for more details. + """ + + # Using parse because it is different between py2 and py3. + AST_NONE = ast.parse("None").body[0].value + val_is_none = ast.Compare(node, [ast.Is()], [AST_NONE]) + send_warning = ast.parse( + """ +from _pytest.warning_types import PytestAssertRewriteWarning +from warnings import warn_explicit +warn_explicit( + PytestAssertRewriteWarning('asserting the value None, please use "assert is None"'), + category=None, + filename={filename!r}, + lineno={lineno}, +) + """.format( + filename=module_path.strpath, lineno=lineno + ) + ).body + return ast.If(val_is_none, send_warning, []) + def visit_Name(self, name): # Display the repr of the name if it's a local variable or # _should_repr_global_name() thinks it's acceptable. locs = ast_Call(self.builtin("locals"), [], []) inlocs = ast.Compare(ast.Str(name.id), [ast.In()], [locs]) - dorepr = self.helper("_should_repr_global_name", name) + dorepr = self.helper("_should_repr_global_name", name) test = ast.BoolOp(ast.Or(), [inlocs, dorepr]) expr = ast.IfExp(test, self.display(name), ast.Str(name.id)) return name, self.explanation_param(expr) @@ -934,7 +934,7 @@ warn_explicit( self.statements = body = inner self.statements = save self.on_failure = fail_save - expl_template = self.helper("_format_boolop", expl_list, ast.Num(is_or)) + expl_template = self.helper("_format_boolop", expl_list, ast.Num(is_or)) expl = self.pop_format_context(expl_template) return ast.Name(res_var, ast.Load()), self.explanation_param(expl) @@ -1059,7 +1059,7 @@ warn_explicit( left_res, left_expl = next_res, next_expl # Use pytest.assertion.util._reprcompare if that's available. expl_call = self.helper( - "_call_reprcompare", + "_call_reprcompare", ast.Tuple(syms, ast.Load()), ast.Tuple(load_names, ast.Load()), ast.Tuple(expls, ast.Load()), diff --git a/contrib/python/pytest/py2/_pytest/assertion/truncate.py b/contrib/python/pytest/py2/_pytest/assertion/truncate.py index 525896ea9a..0451daa091 100644 --- a/contrib/python/pytest/py2/_pytest/assertion/truncate.py +++ b/contrib/python/pytest/py2/_pytest/assertion/truncate.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ Utilities for truncating assertion output. diff --git a/contrib/python/pytest/py2/_pytest/assertion/util.py b/contrib/python/pytest/py2/_pytest/assertion/util.py index c382f1c609..12d59c0e02 100644 --- a/contrib/python/pytest/py2/_pytest/assertion/util.py +++ b/contrib/python/pytest/py2/_pytest/assertion/util.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """Utilities for assertion debugging""" from __future__ import absolute_import from __future__ import division @@ -10,9 +10,9 @@ import six import _pytest._code from ..compat import Sequence -from _pytest import outcomes -from _pytest._io.saferepr import saferepr -from _pytest.compat import ATTRS_EQ_FIELD +from _pytest import outcomes +from _pytest._io.saferepr import saferepr +from _pytest.compat import ATTRS_EQ_FIELD # The _reprcompare attribute on the util module is used by the new assertion # interpretation code and assertion rewriter to detect this plugin was @@ -105,46 +105,46 @@ except NameError: basestring = str -def issequence(x): - return isinstance(x, Sequence) and not isinstance(x, basestring) - - -def istext(x): - return isinstance(x, basestring) - - -def isdict(x): - return isinstance(x, dict) - - -def isset(x): - return isinstance(x, (set, frozenset)) - - -def isdatacls(obj): - return getattr(obj, "__dataclass_fields__", None) is not None - - -def isattrs(obj): - return getattr(obj, "__attrs_attrs__", None) is not None - - -def isiterable(obj): - try: - iter(obj) - return not istext(obj) - except TypeError: - return False - - -def assertrepr_compare(config, op, left, right): - """Return specialised explanations for some operators/operands""" - width = 80 - 15 - len(op) - 2 # 15 chars indentation, 1 space around op - left_repr = saferepr(left, maxsize=int(width // 2)) - right_repr = saferepr(right, maxsize=width - len(left_repr)) - - summary = u"%s %s %s" % (ecu(left_repr), op, ecu(right_repr)) - +def issequence(x): + return isinstance(x, Sequence) and not isinstance(x, basestring) + + +def istext(x): + return isinstance(x, basestring) + + +def isdict(x): + return isinstance(x, dict) + + +def isset(x): + return isinstance(x, (set, frozenset)) + + +def isdatacls(obj): + return getattr(obj, "__dataclass_fields__", None) is not None + + +def isattrs(obj): + return getattr(obj, "__attrs_attrs__", None) is not None + + +def isiterable(obj): + try: + iter(obj) + return not istext(obj) + except TypeError: + return False + + +def assertrepr_compare(config, op, left, right): + """Return specialised explanations for some operators/operands""" + width = 80 - 15 - len(op) - 2 # 15 chars indentation, 1 space around op + left_repr = saferepr(left, maxsize=int(width // 2)) + right_repr = saferepr(right, maxsize=width - len(left_repr)) + + summary = u"%s %s %s" % (ecu(left_repr), op, ecu(right_repr)) + verbose = config.getoption("verbose") explanation = None try: @@ -158,11 +158,11 @@ def assertrepr_compare(config, op, left, right): explanation = _compare_eq_set(left, right, verbose) elif isdict(left) and isdict(right): explanation = _compare_eq_dict(left, right, verbose) - elif type(left) == type(right) and (isdatacls(left) or isattrs(left)): - type_fn = (isdatacls, isattrs) - explanation = _compare_eq_cls(left, right, verbose, type_fn) - elif verbose > 0: - explanation = _compare_eq_verbose(left, right) + elif type(left) == type(right) and (isdatacls(left) or isattrs(left)): + type_fn = (isdatacls, isattrs) + explanation = _compare_eq_cls(left, right, verbose, type_fn) + elif verbose > 0: + explanation = _compare_eq_verbose(left, right) if isiterable(left) and isiterable(right): expl = _compare_eq_iterable(left, right, verbose) if explanation is not None: @@ -172,13 +172,13 @@ def assertrepr_compare(config, op, left, right): elif op == "not in": if istext(left) and istext(right): explanation = _notin_text(left, right, verbose) - except outcomes.Exit: - raise + except outcomes.Exit: + raise except Exception: explanation = [ u"(pytest_assertion plugin: representation of details failed. " u"Probably an object has a faulty __repr__.)", - six.text_type(_pytest._code.ExceptionInfo.from_current()), + six.text_type(_pytest._code.ExceptionInfo.from_current()), ] if not explanation: @@ -187,8 +187,8 @@ def assertrepr_compare(config, op, left, right): return [summary] + explanation -def _diff_text(left, right, verbose=0): - """Return the explanation for the diff between text or bytes. +def _diff_text(left, right, verbose=0): + """Return the explanation for the diff between text or bytes. Unless --verbose is used this will skip leading and trailing characters which are identical to keep the diff minimal. @@ -214,7 +214,7 @@ def _diff_text(left, right, verbose=0): left = escape_for_readable_diff(left) if isinstance(right, bytes): right = escape_for_readable_diff(right) - if verbose < 1: + if verbose < 1: i = 0 # just in case left or right has zero length for i in range(min(len(left), len(right))): if left[i] != right[i]: @@ -250,19 +250,19 @@ def _diff_text(left, right, verbose=0): return explanation -def _compare_eq_verbose(left, right): - keepends = True - left_lines = repr(left).splitlines(keepends) - right_lines = repr(right).splitlines(keepends) - - explanation = [] - explanation += [u"-" + line for line in left_lines] - explanation += [u"+" + line for line in right_lines] - - return explanation - - -def _compare_eq_iterable(left, right, verbose=0): +def _compare_eq_verbose(left, right): + keepends = True + left_lines = repr(left).splitlines(keepends) + right_lines = repr(right).splitlines(keepends) + + explanation = [] + explanation += [u"-" + line for line in left_lines] + explanation += [u"+" + line for line in right_lines] + + return explanation + + +def _compare_eq_iterable(left, right, verbose=0): if not verbose: return [u"Use -v to get the full diff"] # dynamic import to speedup pytest @@ -285,55 +285,55 @@ def _compare_eq_iterable(left, right, verbose=0): return explanation -def _compare_eq_sequence(left, right, verbose=0): +def _compare_eq_sequence(left, right, verbose=0): explanation = [] - len_left = len(left) - len_right = len(right) - for i in range(min(len_left, len_right)): + len_left = len(left) + len_right = len(right) + for i in range(min(len_left, len_right)): if left[i] != right[i]: explanation += [u"At index %s diff: %r != %r" % (i, left[i], right[i])] break - len_diff = len_left - len_right - - if len_diff: - if len_diff > 0: - dir_with_more = "Left" - extra = saferepr(left[len_right]) - else: - len_diff = 0 - len_diff - dir_with_more = "Right" - extra = saferepr(right[len_left]) - - if len_diff == 1: - explanation += [u"%s contains one more item: %s" % (dir_with_more, extra)] - else: - explanation += [ - u"%s contains %d more items, first extra item: %s" - % (dir_with_more, len_diff, extra) - ] + len_diff = len_left - len_right + + if len_diff: + if len_diff > 0: + dir_with_more = "Left" + extra = saferepr(left[len_right]) + else: + len_diff = 0 - len_diff + dir_with_more = "Right" + extra = saferepr(right[len_left]) + + if len_diff == 1: + explanation += [u"%s contains one more item: %s" % (dir_with_more, extra)] + else: + explanation += [ + u"%s contains %d more items, first extra item: %s" + % (dir_with_more, len_diff, extra) + ] return explanation -def _compare_eq_set(left, right, verbose=0): +def _compare_eq_set(left, right, verbose=0): explanation = [] diff_left = left - right diff_right = right - left if diff_left: explanation.append(u"Extra items in the left set:") for item in diff_left: - explanation.append(saferepr(item)) + explanation.append(saferepr(item)) if diff_right: explanation.append(u"Extra items in the right set:") for item in diff_right: - explanation.append(saferepr(item)) + explanation.append(saferepr(item)) return explanation -def _compare_eq_dict(left, right, verbose=0): +def _compare_eq_dict(left, right, verbose=0): explanation = [] - set_left = set(left) - set_right = set(right) - common = set_left.intersection(set_right) + set_left = set(left) + set_right = set(right) + common = set_left.intersection(set_right) same = {k: left[k] for k in common if left[k] == right[k]} if same and verbose < 2: explanation += [u"Omitting %s identical items, use -vv to show" % len(same)] @@ -344,71 +344,71 @@ def _compare_eq_dict(left, right, verbose=0): if diff: explanation += [u"Differing items:"] for k in diff: - explanation += [saferepr({k: left[k]}) + " != " + saferepr({k: right[k]})] - extra_left = set_left - set_right - len_extra_left = len(extra_left) - if len_extra_left: - explanation.append( - u"Left contains %d more item%s:" - % (len_extra_left, "" if len_extra_left == 1 else "s") - ) + explanation += [saferepr({k: left[k]}) + " != " + saferepr({k: right[k]})] + extra_left = set_left - set_right + len_extra_left = len(extra_left) + if len_extra_left: + explanation.append( + u"Left contains %d more item%s:" + % (len_extra_left, "" if len_extra_left == 1 else "s") + ) explanation.extend( pprint.pformat({k: left[k] for k in extra_left}).splitlines() ) - extra_right = set_right - set_left - len_extra_right = len(extra_right) - if len_extra_right: - explanation.append( - u"Right contains %d more item%s:" - % (len_extra_right, "" if len_extra_right == 1 else "s") - ) + extra_right = set_right - set_left + len_extra_right = len(extra_right) + if len_extra_right: + explanation.append( + u"Right contains %d more item%s:" + % (len_extra_right, "" if len_extra_right == 1 else "s") + ) explanation.extend( pprint.pformat({k: right[k] for k in extra_right}).splitlines() ) return explanation -def _compare_eq_cls(left, right, verbose, type_fns): - isdatacls, isattrs = type_fns - if isdatacls(left): - all_fields = left.__dataclass_fields__ - fields_to_check = [field for field, info in all_fields.items() if info.compare] - elif isattrs(left): - all_fields = left.__attrs_attrs__ - fields_to_check = [ - field.name for field in all_fields if getattr(field, ATTRS_EQ_FIELD) - ] - - same = [] - diff = [] - for field in fields_to_check: - if getattr(left, field) == getattr(right, field): - same.append(field) - else: - diff.append(field) - - explanation = [] - if same and verbose < 2: - explanation.append(u"Omitting %s identical items, use -vv to show" % len(same)) - elif same: - explanation += [u"Matching attributes:"] - explanation += pprint.pformat(same).splitlines() - if diff: - explanation += [u"Differing attributes:"] - for field in diff: - explanation += [ - (u"%s: %r != %r") % (field, getattr(left, field), getattr(right, field)) - ] - return explanation - - -def _notin_text(term, text, verbose=0): +def _compare_eq_cls(left, right, verbose, type_fns): + isdatacls, isattrs = type_fns + if isdatacls(left): + all_fields = left.__dataclass_fields__ + fields_to_check = [field for field, info in all_fields.items() if info.compare] + elif isattrs(left): + all_fields = left.__attrs_attrs__ + fields_to_check = [ + field.name for field in all_fields if getattr(field, ATTRS_EQ_FIELD) + ] + + same = [] + diff = [] + for field in fields_to_check: + if getattr(left, field) == getattr(right, field): + same.append(field) + else: + diff.append(field) + + explanation = [] + if same and verbose < 2: + explanation.append(u"Omitting %s identical items, use -vv to show" % len(same)) + elif same: + explanation += [u"Matching attributes:"] + explanation += pprint.pformat(same).splitlines() + if diff: + explanation += [u"Differing attributes:"] + for field in diff: + explanation += [ + (u"%s: %r != %r") % (field, getattr(left, field), getattr(right, field)) + ] + return explanation + + +def _notin_text(term, text, verbose=0): index = text.find(term) head = text[:index] tail = text[index + len(term) :] correct_text = head + tail diff = _diff_text(correct_text, text, verbose) - newdiff = [u"%s is contained here:" % saferepr(term, maxsize=42)] + newdiff = [u"%s is contained here:" % saferepr(term, maxsize=42)] for line in diff: if line.startswith(u"Skipping"): continue diff --git a/contrib/python/pytest/py2/_pytest/cacheprovider.py b/contrib/python/pytest/py2/_pytest/cacheprovider.py index f5c5545484..f1e05676d2 100644 --- a/contrib/python/pytest/py2/_pytest/cacheprovider.py +++ b/contrib/python/pytest/py2/_pytest/cacheprovider.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ merged implementation of the cache provider @@ -21,7 +21,7 @@ import pytest from .compat import _PY2 as PY2 from .pathlib import Path from .pathlib import resolve_from_str -from .pathlib import rm_rf +from .pathlib import rm_rf README_CONTENT = u"""\ # pytest cache directory # @@ -34,14 +34,14 @@ which provides the `--lf` and `--ff` options, as well as the `cache` fixture. See [the docs](https://docs.pytest.org/en/latest/cache.html) for more information. """ -CACHEDIR_TAG_CONTENT = b"""\ -Signature: 8a477f597d28d172789f06886806bc55 -# This file is a cache directory tag created by pytest. -# For information about cache directory tags, see: -# http://www.bford.info/cachedir/spec.html -""" - +CACHEDIR_TAG_CONTENT = b"""\ +Signature: 8a477f597d28d172789f06886806bc55 +# This file is a cache directory tag created by pytest. +# For information about cache directory tags, see: +# http://www.bford.info/cachedir/spec.html +""" + @attr.s class Cache(object): _cachedir = attr.ib(repr=False) @@ -51,7 +51,7 @@ class Cache(object): def for_config(cls, config): cachedir = cls.cache_dir_from_config(config) if config.getoption("cacheclear") and cachedir.exists(): - rm_rf(cachedir) + rm_rf(cachedir) cachedir.mkdir() return cls(cachedir, config) @@ -60,12 +60,12 @@ class Cache(object): return resolve_from_str(config.getini("cache_dir"), config.rootdir) def warn(self, fmt, **args): - from _pytest.warnings import _issue_warning_captured - from _pytest.warning_types import PytestCacheWarning + from _pytest.warnings import _issue_warning_captured + from _pytest.warning_types import PytestCacheWarning - _issue_warning_captured( - PytestCacheWarning(fmt.format(**args) if args else fmt), - self._config.hook, + _issue_warning_captured( + PytestCacheWarning(fmt.format(**args) if args else fmt), + self._config.hook, stacklevel=3, ) @@ -122,12 +122,12 @@ class Cache(object): cache_dir_exists_already = True else: cache_dir_exists_already = self._cachedir.exists() - path.parent.mkdir(exist_ok=True, parents=True) + path.parent.mkdir(exist_ok=True, parents=True) except (IOError, OSError): self.warn("could not create cache path {path}", path=path) return - if not cache_dir_exists_already: - self._ensure_supporting_files() + if not cache_dir_exists_already: + self._ensure_supporting_files() try: f = path.open("wb" if PY2 else "w") except (IOError, OSError): @@ -138,17 +138,17 @@ class Cache(object): def _ensure_supporting_files(self): """Create supporting files in the cache dir that are not really part of the cache.""" - readme_path = self._cachedir / "README.md" - readme_path.write_text(README_CONTENT) - - gitignore_path = self._cachedir.joinpath(".gitignore") - msg = u"# Created by pytest automatically.\n*" - gitignore_path.write_text(msg, encoding="UTF-8") + readme_path = self._cachedir / "README.md" + readme_path.write_text(README_CONTENT) - cachedir_tag_path = self._cachedir.joinpath("CACHEDIR.TAG") - cachedir_tag_path.write_bytes(CACHEDIR_TAG_CONTENT) + gitignore_path = self._cachedir.joinpath(".gitignore") + msg = u"# Created by pytest automatically.\n*" + gitignore_path.write_text(msg, encoding="UTF-8") + cachedir_tag_path = self._cachedir.joinpath("CACHEDIR.TAG") + cachedir_tag_path.write_bytes(CACHEDIR_TAG_CONTENT) + class LFPlugin(object): """ Plugin which implements the --lf (run last-failing) option """ @@ -158,37 +158,37 @@ class LFPlugin(object): self.active = any(config.getoption(key) for key in active_keys) self.lastfailed = config.cache.get("cache/lastfailed", {}) self._previously_failed_count = None - self._report_status = None - self._skipped_files = 0 # count skipped files during collection due to --lf - - def last_failed_paths(self): - """Returns a set with all Paths()s of the previously failed nodeids (cached). - """ - try: - return self._last_failed_paths - except AttributeError: - rootpath = Path(self.config.rootdir) - result = {rootpath / nodeid.split("::")[0] for nodeid in self.lastfailed} - result = {x for x in result if x.exists()} - self._last_failed_paths = result - return result - - def pytest_ignore_collect(self, path): - """ - Ignore this file path if we are in --lf mode and it is not in the list of - previously failed files. - """ - if self.active and self.config.getoption("lf") and path.isfile(): - last_failed_paths = self.last_failed_paths() - if last_failed_paths: - skip_it = Path(path) not in self.last_failed_paths() - if skip_it: - self._skipped_files += 1 - return skip_it - + self._report_status = None + self._skipped_files = 0 # count skipped files during collection due to --lf + + def last_failed_paths(self): + """Returns a set with all Paths()s of the previously failed nodeids (cached). + """ + try: + return self._last_failed_paths + except AttributeError: + rootpath = Path(self.config.rootdir) + result = {rootpath / nodeid.split("::")[0] for nodeid in self.lastfailed} + result = {x for x in result if x.exists()} + self._last_failed_paths = result + return result + + def pytest_ignore_collect(self, path): + """ + Ignore this file path if we are in --lf mode and it is not in the list of + previously failed files. + """ + if self.active and self.config.getoption("lf") and path.isfile(): + last_failed_paths = self.last_failed_paths() + if last_failed_paths: + skip_it = Path(path) not in self.last_failed_paths() + if skip_it: + self._skipped_files += 1 + return skip_it + def pytest_report_collectionfinish(self): if self.active and self.config.getoption("verbose") >= 0: - return "run-last-failure: %s" % self._report_status + return "run-last-failure: %s" % self._report_status def pytest_runtest_logreport(self, report): if (report.when == "call" and report.passed) or report.skipped: @@ -206,51 +206,51 @@ class LFPlugin(object): self.lastfailed[report.nodeid] = True def pytest_collection_modifyitems(self, session, config, items): - if not self.active: - return - - if self.lastfailed: - previously_failed = [] - previously_passed = [] - for item in items: - if item.nodeid in self.lastfailed: - previously_failed.append(item) - else: - previously_passed.append(item) - self._previously_failed_count = len(previously_failed) - - if not previously_failed: - # Running a subset of all tests with recorded failures - # only outside of it. - self._report_status = "%d known failures not in selected tests" % ( - len(self.lastfailed), - ) - else: + if not self.active: + return + + if self.lastfailed: + previously_failed = [] + previously_passed = [] + for item in items: + if item.nodeid in self.lastfailed: + previously_failed.append(item) + else: + previously_passed.append(item) + self._previously_failed_count = len(previously_failed) + + if not previously_failed: + # Running a subset of all tests with recorded failures + # only outside of it. + self._report_status = "%d known failures not in selected tests" % ( + len(self.lastfailed), + ) + else: if self.config.getoption("lf"): items[:] = previously_failed config.hook.pytest_deselected(items=previously_passed) - else: # --failedfirst + else: # --failedfirst items[:] = previously_failed + previously_passed - - noun = "failure" if self._previously_failed_count == 1 else "failures" - suffix = " first" if self.config.getoption("failedfirst") else "" - self._report_status = "rerun previous {count} {noun}{suffix}".format( - count=self._previously_failed_count, suffix=suffix, noun=noun - ) - - if self._skipped_files > 0: - files_noun = "file" if self._skipped_files == 1 else "files" - self._report_status += " (skipped {files} {files_noun})".format( - files=self._skipped_files, files_noun=files_noun - ) - else: - self._report_status = "no previously failed tests, " - if self.config.getoption("last_failed_no_failures") == "none": - self._report_status += "deselecting all items." + + noun = "failure" if self._previously_failed_count == 1 else "failures" + suffix = " first" if self.config.getoption("failedfirst") else "" + self._report_status = "rerun previous {count} {noun}{suffix}".format( + count=self._previously_failed_count, suffix=suffix, noun=noun + ) + + if self._skipped_files > 0: + files_noun = "file" if self._skipped_files == 1 else "files" + self._report_status += " (skipped {files} {files_noun})".format( + files=self._skipped_files, files_noun=files_noun + ) + else: + self._report_status = "no previously failed tests, " + if self.config.getoption("last_failed_no_failures") == "none": + self._report_status += "deselecting all items." config.hook.pytest_deselected(items=items) items[:] = [] - else: - self._report_status += "not deselecting items." + else: + self._report_status += "not deselecting items." def pytest_sessionfinish(self, session): config = self.config @@ -325,13 +325,13 @@ def pytest_addoption(parser): ) group.addoption( "--cache-show", - action="append", - nargs="?", + action="append", + nargs="?", dest="cacheshow", - help=( - "show cache contents, don't perform collection or tests. " - "Optional argument: glob (default: '*')." - ), + help=( + "show cache contents, don't perform collection or tests. " + "Optional argument: glob (default: '*')." + ), ) group.addoption( "--cache-clear", @@ -350,7 +350,7 @@ def pytest_addoption(parser): dest="last_failed_no_failures", choices=("all", "none"), default="all", - help="which tests to run with no previously (known) failures.", + help="which tests to run with no previously (known) failures.", ) @@ -386,7 +386,7 @@ def cache(request): def pytest_report_header(config): """Display cachedir with --cache-show and if non-default.""" - if config.option.verbose > 0 or config.getini("cache_dir") != ".pytest_cache": + if config.option.verbose > 0 or config.getini("cache_dir") != ".pytest_cache": cachedir = config.cache._cachedir # TODO: evaluate generating upward relative paths # starting with .., ../.. if sensible @@ -406,16 +406,16 @@ def cacheshow(config, session): if not config.cache._cachedir.is_dir(): tw.line("cache is empty") return 0 - - glob = config.option.cacheshow[0] - if glob is None: - glob = "*" - + + glob = config.option.cacheshow[0] + if glob is None: + glob = "*" + dummy = object() basedir = config.cache._cachedir vdir = basedir / "v" - tw.sep("-", "cache values for %r" % glob) - for valpath in sorted(x for x in vdir.rglob(glob) if x.is_file()): + tw.sep("-", "cache values for %r" % glob) + for valpath in sorted(x for x in vdir.rglob(glob) if x.is_file()): key = valpath.relative_to(vdir) val = config.cache.get(key, dummy) if val is dummy: @@ -427,8 +427,8 @@ def cacheshow(config, session): ddir = basedir / "d" if ddir.is_dir(): - contents = sorted(ddir.rglob(glob)) - tw.sep("-", "cache directories for %r" % glob) + contents = sorted(ddir.rglob(glob)) + tw.sep("-", "cache directories for %r" % glob) for p in contents: # if p.check(dir=1): # print("%s/" % p.relto(basedir)) diff --git a/contrib/python/pytest/py2/_pytest/capture.py b/contrib/python/pytest/py2/_pytest/capture.py index 68c17772f3..98e2cfa8f8 100644 --- a/contrib/python/pytest/py2/_pytest/capture.py +++ b/contrib/python/pytest/py2/_pytest/capture.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ per-test stdout/stderr capturing mechanism. @@ -18,7 +18,7 @@ from tempfile import TemporaryFile import six import pytest -from _pytest.compat import _PY3 +from _pytest.compat import _PY3 from _pytest.compat import CaptureIO patchsysdict = {0: "stdin", 1: "stdout", 2: "stderr"} @@ -85,13 +85,13 @@ class CaptureManager(object): self._global_capturing = None self._current_item = None - def __repr__(self): - return "<CaptureManager _method=%r _global_capturing=%r _current_item=%r>" % ( - self._method, - self._global_capturing, - self._current_item, - ) - + def __repr__(self): + return "<CaptureManager _method=%r _global_capturing=%r _current_item=%r>" % ( + self._method, + self._global_capturing, + self._current_item, + ) + def _getcapture(self, method): if method == "fd": return MultiCapture(out=True, err=True, Capture=FDCapture) @@ -99,18 +99,18 @@ class CaptureManager(object): return MultiCapture(out=True, err=True, Capture=SysCapture) elif method == "no": return MultiCapture(out=False, err=False, in_=False) - raise ValueError("unknown capturing method: %r" % method) # pragma: no cover - - def is_capturing(self): - if self.is_globally_capturing(): - return "global" - capture_fixture = getattr(self._current_item, "_capture_fixture", None) - if capture_fixture is not None: - return ( - "fixture %s" % self._current_item._capture_fixture.request.fixturename - ) - return False - + raise ValueError("unknown capturing method: %r" % method) # pragma: no cover + + def is_capturing(self): + if self.is_globally_capturing(): + return "global" + capture_fixture = getattr(self._current_item, "_capture_fixture", None) + if capture_fixture is not None: + return ( + "fixture %s" % self._current_item._capture_fixture.request.fixturename + ) + return False + # Global capturing control def is_globally_capturing(self): @@ -128,25 +128,25 @@ class CaptureManager(object): self._global_capturing = None def resume_global_capture(self): - # During teardown of the python process, and on rare occasions, capture - # attributes can be `None` while trying to resume global capture. - if self._global_capturing is not None: - self._global_capturing.resume_capturing() + # During teardown of the python process, and on rare occasions, capture + # attributes can be `None` while trying to resume global capture. + if self._global_capturing is not None: + self._global_capturing.resume_capturing() def suspend_global_capture(self, in_=False): cap = getattr(self, "_global_capturing", None) if cap is not None: cap.suspend_capturing(in_=in_) - def suspend(self, in_=False): - # Need to undo local capsys-et-al if it exists before disabling global capture. - self.suspend_fixture(self._current_item) - self.suspend_global_capture(in_) - - def resume(self): - self.resume_global_capture() - self.resume_fixture(self._current_item) - + def suspend(self, in_=False): + # Need to undo local capsys-et-al if it exists before disabling global capture. + self.suspend_fixture(self._current_item) + self.suspend_global_capture(in_) + + def resume(self): + self.resume_global_capture() + self.resume_fixture(self._current_item) + def read_global_capture(self): return self._global_capturing.readouterr() @@ -180,12 +180,12 @@ class CaptureManager(object): @contextlib.contextmanager def global_and_fixture_disabled(self): - """Context manager to temporarily disable global and current fixture capturing.""" - self.suspend() + """Context manager to temporarily disable global and current fixture capturing.""" + self.suspend() try: yield finally: - self.resume() + self.resume() @contextlib.contextmanager def item_capture(self, when, item): @@ -263,11 +263,11 @@ def _ensure_only_one_capture_fixture(request, name): @pytest.fixture def capsys(request): - """Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``. - - The captured output is made available via ``capsys.readouterr()`` method - calls, which return a ``(out, err)`` namedtuple. - ``out`` and ``err`` will be ``text`` objects. + """Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``. + + The captured output is made available via ``capsys.readouterr()`` method + calls, which return a ``(out, err)`` namedtuple. + ``out`` and ``err`` will be ``text`` objects. """ _ensure_only_one_capture_fixture(request, "capsys") with _install_capture_fixture_on_item(request, SysCapture) as fixture: @@ -276,28 +276,28 @@ def capsys(request): @pytest.fixture def capsysbinary(request): - """Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``. - - The captured output is made available via ``capsysbinary.readouterr()`` - method calls, which return a ``(out, err)`` namedtuple. - ``out`` and ``err`` will be ``bytes`` objects. + """Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``. + + The captured output is made available via ``capsysbinary.readouterr()`` + method calls, which return a ``(out, err)`` namedtuple. + ``out`` and ``err`` will be ``bytes`` objects. """ _ensure_only_one_capture_fixture(request, "capsysbinary") # Currently, the implementation uses the python3 specific `.buffer` # property of CaptureIO. if sys.version_info < (3,): - raise request.raiseerror("capsysbinary is only supported on Python 3") + raise request.raiseerror("capsysbinary is only supported on Python 3") with _install_capture_fixture_on_item(request, SysCaptureBinary) as fixture: yield fixture @pytest.fixture def capfd(request): - """Enable text capturing of writes to file descriptors ``1`` and ``2``. - - The captured output is made available via ``capfd.readouterr()`` method - calls, which return a ``(out, err)`` namedtuple. - ``out`` and ``err`` will be ``text`` objects. + """Enable text capturing of writes to file descriptors ``1`` and ``2``. + + The captured output is made available via ``capfd.readouterr()`` method + calls, which return a ``(out, err)`` namedtuple. + ``out`` and ``err`` will be ``text`` objects. """ _ensure_only_one_capture_fixture(request, "capfd") if not hasattr(os, "dup"): @@ -310,11 +310,11 @@ def capfd(request): @pytest.fixture def capfdbinary(request): - """Enable bytes capturing of writes to file descriptors ``1`` and ``2``. - - The captured output is made available via ``capfd.readouterr()`` method - calls, which return a ``(out, err)`` namedtuple. - ``out`` and ``err`` will be ``byte`` objects. + """Enable bytes capturing of writes to file descriptors ``1`` and ``2``. + + The captured output is made available via ``capfd.readouterr()`` method + calls, which return a ``(out, err)`` namedtuple. + ``out`` and ``err`` will be ``byte`` objects. """ _ensure_only_one_capture_fixture(request, "capfdbinary") if not hasattr(os, "dup"): @@ -336,9 +336,9 @@ def _install_capture_fixture_on_item(request, capture_class): """ request.node._capture_fixture = fixture = CaptureFixture(capture_class, request) capmanager = request.config.pluginmanager.getplugin("capturemanager") - # Need to active this fixture right away in case it is being used by another fixture (setup phase). - # If this fixture is being used only by a test function (call phase), then we wouldn't need this - # activation, but it doesn't hurt. + # Need to active this fixture right away in case it is being used by another fixture (setup phase). + # If this fixture is being used only by a test function (call phase), then we wouldn't need this + # activation, but it doesn't hurt. capmanager.activate_fixture(request.node) yield fixture fixture.close() @@ -359,7 +359,7 @@ class CaptureFixture(object): self._captured_err = self.captureclass.EMPTY_BUFFER def _start(self): - if self._capture is None: + if self._capture is None: self._capture = MultiCapture( out=True, err=True, in_=False, Capture=self.captureclass ) @@ -376,7 +376,7 @@ class CaptureFixture(object): def readouterr(self): """Read and return the captured output so far, resetting the internal buffer. - :return: captured content as a namedtuple with ``out`` and ``err`` string attributes + :return: captured content as a namedtuple with ``out`` and ``err`` string attributes """ captured_out, captured_err = self._captured_out, self._captured_err if self._capture is not None: @@ -389,13 +389,13 @@ class CaptureFixture(object): def _suspend(self): """Suspends this fixture's own capturing temporarily.""" - if self._capture is not None: - self._capture.suspend_capturing() + if self._capture is not None: + self._capture.suspend_capturing() def _resume(self): """Resumes this fixture's own capturing temporarily.""" - if self._capture is not None: - self._capture.resume_capturing() + if self._capture is not None: + self._capture.resume_capturing() @contextlib.contextmanager def disabled(self): @@ -434,10 +434,10 @@ class EncodedFile(object): def write(self, obj): if isinstance(obj, six.text_type): obj = obj.encode(self.encoding, "replace") - elif _PY3: - raise TypeError( - "write() argument must be str, not {}".format(type(obj).__name__) - ) + elif _PY3: + raise TypeError( + "write() argument must be str, not {}".format(type(obj).__name__) + ) self.buffer.write(obj) def writelines(self, linelist): @@ -449,10 +449,10 @@ class EncodedFile(object): """Ensure that file.name is a string.""" return repr(self.buffer) - @property - def mode(self): - return self.buffer.mode.replace("b", "") - + @property + def mode(self): + return self.buffer.mode.replace("b", "") + def __getattr__(self, name): return getattr(object.__getattribute__(self, "buffer"), name) @@ -462,7 +462,7 @@ CaptureResult = collections.namedtuple("CaptureResult", ["out", "err"]) class MultiCapture(object): out = err = in_ = None - _state = None + _state = None def __init__(self, out=True, err=True, in_=True, Capture=None): if in_: @@ -472,17 +472,17 @@ class MultiCapture(object): if err: self.err = Capture(2) - def __repr__(self): - return "<MultiCapture out=%r err=%r in_=%r _state=%r _in_suspended=%r>" % ( - self.out, - self.err, - self.in_, - self._state, - getattr(self, "_in_suspended", "<UNSET>"), - ) - + def __repr__(self): + return "<MultiCapture out=%r err=%r in_=%r _state=%r _in_suspended=%r>" % ( + self.out, + self.err, + self.in_, + self._state, + getattr(self, "_in_suspended", "<UNSET>"), + ) + def start_capturing(self): - self._state = "started" + self._state = "started" if self.in_: self.in_.start() if self.out: @@ -500,7 +500,7 @@ class MultiCapture(object): return out, err def suspend_capturing(self, in_=False): - self._state = "suspended" + self._state = "suspended" if self.out: self.out.suspend() if self.err: @@ -510,7 +510,7 @@ class MultiCapture(object): self._in_suspended = True def resume_capturing(self): - self._state = "resumed" + self._state = "resumed" if self.out: self.out.resume() if self.err: @@ -521,9 +521,9 @@ class MultiCapture(object): def stop_capturing(self): """ stop capturing and reset capturing streams """ - if self._state == "stopped": + if self._state == "stopped": raise ValueError("was already stopped") - self._state = "stopped" + self._state = "stopped" if self.out: self.out.done() if self.err: @@ -551,7 +551,7 @@ class FDCaptureBinary(object): """ EMPTY_BUFFER = b"" - _state = None + _state = None def __init__(self, targetfd, tmpfile=None): self.targetfd = targetfd @@ -578,11 +578,11 @@ class FDCaptureBinary(object): self.tmpfile_fd = tmpfile.fileno() def __repr__(self): - return "<FDCapture %s oldfd=%s _state=%r>" % ( - self.targetfd, - getattr(self, "targetfd_save", None), - self._state, - ) + return "<FDCapture %s oldfd=%s _state=%r>" % ( + self.targetfd, + getattr(self, "targetfd_save", None), + self._state, + ) def start(self): """ Start capturing on targetfd using memorized tmpfile. """ @@ -592,7 +592,7 @@ class FDCaptureBinary(object): raise ValueError("saved filedescriptor not valid anymore") os.dup2(self.tmpfile_fd, self.targetfd) self.syscapture.start() - self._state = "started" + self._state = "started" def snap(self): self.tmpfile.seek(0) @@ -609,17 +609,17 @@ class FDCaptureBinary(object): os.close(targetfd_save) self.syscapture.done() _attempt_to_close_capture_file(self.tmpfile) - self._state = "done" + self._state = "done" def suspend(self): self.syscapture.suspend() os.dup2(self.targetfd_save, self.targetfd) - self._state = "suspended" + self._state = "suspended" def resume(self): self.syscapture.resume() os.dup2(self.tmpfile_fd, self.targetfd) - self._state = "resumed" + self._state = "resumed" def writeorg(self, data): """ write to original file descriptor. """ @@ -637,7 +637,7 @@ class FDCapture(FDCaptureBinary): EMPTY_BUFFER = str() def snap(self): - res = super(FDCapture, self).snap() + res = super(FDCapture, self).snap() enc = getattr(self.tmpfile, "encoding", None) if enc and isinstance(res, bytes): res = six.text_type(res, enc, "replace") @@ -647,7 +647,7 @@ class FDCapture(FDCaptureBinary): class SysCapture(object): EMPTY_BUFFER = str() - _state = None + _state = None def __init__(self, fd, tmpfile=None): name = patchsysdict[fd] @@ -660,17 +660,17 @@ class SysCapture(object): tmpfile = CaptureIO() self.tmpfile = tmpfile - def __repr__(self): - return "<SysCapture %s _old=%r, tmpfile=%r _state=%r>" % ( - self.name, - self._old, - self.tmpfile, - self._state, - ) - + def __repr__(self): + return "<SysCapture %s _old=%r, tmpfile=%r _state=%r>" % ( + self.name, + self._old, + self.tmpfile, + self._state, + ) + def start(self): setattr(sys, self.name, self.tmpfile) - self._state = "started" + self._state = "started" def snap(self): res = self.tmpfile.getvalue() @@ -682,15 +682,15 @@ class SysCapture(object): setattr(sys, self.name, self._old) del self._old _attempt_to_close_capture_file(self.tmpfile) - self._state = "done" + self._state = "done" def suspend(self): setattr(sys, self.name, self._old) - self._state = "suspended" + self._state = "suspended" def resume(self): setattr(sys, self.name, self.tmpfile) - self._state = "resumed" + self._state = "resumed" def writeorg(self, data): self._old.write(data) @@ -753,11 +753,11 @@ def _colorama_workaround(): first import of colorama while I/O capture is active, colorama will fail in various ways. """ - if sys.platform.startswith("win32"): - try: - import colorama # noqa: F401 - except ImportError: - pass + if sys.platform.startswith("win32"): + try: + import colorama # noqa: F401 + except ImportError: + pass def _readline_workaround(): @@ -778,11 +778,11 @@ def _readline_workaround(): See https://github.com/pytest-dev/pytest/pull/1281 """ - if sys.platform.startswith("win32"): - try: - import readline # noqa: F401 - except ImportError: - pass + if sys.platform.startswith("win32"): + try: + import readline # noqa: F401 + except ImportError: + pass def _py36_windowsconsoleio_workaround(stream): @@ -834,9 +834,9 @@ def _py36_windowsconsoleio_workaround(stream): f.line_buffering, ) - sys.stdin = _reopen_stdio(sys.stdin, "rb") - sys.stdout = _reopen_stdio(sys.stdout, "wb") - sys.stderr = _reopen_stdio(sys.stderr, "wb") + sys.stdin = _reopen_stdio(sys.stdin, "rb") + sys.stdout = _reopen_stdio(sys.stdout, "wb") + sys.stderr = _reopen_stdio(sys.stderr, "wb") def _attempt_to_close_capture_file(f): diff --git a/contrib/python/pytest/py2/_pytest/compat.py b/contrib/python/pytest/py2/_pytest/compat.py index e5c3b84667..6b8b597cf6 100644 --- a/contrib/python/pytest/py2/_pytest/compat.py +++ b/contrib/python/pytest/py2/_pytest/compat.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ python version compatibility code """ @@ -13,13 +13,13 @@ import re import sys from contextlib import contextmanager -import attr +import attr import py import six from six import text_type import _pytest -from _pytest._io.saferepr import saferepr +from _pytest._io.saferepr import saferepr from _pytest.outcomes import fail from _pytest.outcomes import TEST_OUTCOME @@ -47,11 +47,11 @@ MODULE_NOT_FOUND_ERROR = "ModuleNotFoundError" if PY36 else "ImportError" if _PY3: from collections.abc import MutableMapping as MappingMixin - from collections.abc import Iterable, Mapping, Sequence, Sized + from collections.abc import Iterable, Mapping, Sequence, Sized else: # those raise DeprecationWarnings in Python >=3.7 from collections import MutableMapping as MappingMixin # noqa - from collections import Iterable, Mapping, Sequence, Sized # noqa + from collections import Iterable, Mapping, Sequence, Sized # noqa if sys.version_info >= (3, 4): @@ -62,12 +62,12 @@ else: return None -if sys.version_info >= (3, 8): - from importlib import metadata as importlib_metadata # noqa -else: - import importlib_metadata # noqa - - +if sys.version_info >= (3, 8): + from importlib import metadata as importlib_metadata # noqa +else: + import importlib_metadata # noqa + + def _format_args(func): return str(signature(func)) @@ -190,18 +190,18 @@ def get_default_arg_names(function): ) -_non_printable_ascii_translate_table = { - i: u"\\x{:02x}".format(i) for i in range(128) if i not in range(32, 127) -} -_non_printable_ascii_translate_table.update( - {ord("\t"): u"\\t", ord("\r"): u"\\r", ord("\n"): u"\\n"} -) - - -def _translate_non_printable(s): - return s.translate(_non_printable_ascii_translate_table) - - +_non_printable_ascii_translate_table = { + i: u"\\x{:02x}".format(i) for i in range(128) if i not in range(32, 127) +} +_non_printable_ascii_translate_table.update( + {ord("\t"): u"\\t", ord("\r"): u"\\r", ord("\n"): u"\\n"} +) + + +def _translate_non_printable(s): + return s.translate(_non_printable_ascii_translate_table) + + if _PY3: STRING_TYPES = bytes, str UNICODE_TYPES = six.text_type @@ -241,10 +241,10 @@ if _PY3: """ if isinstance(val, bytes): - ret = _bytes_to_ascii(val) + ret = _bytes_to_ascii(val) else: - ret = val - return ret + ret = val + return ret else: @@ -262,12 +262,12 @@ else: """ if isinstance(val, bytes): try: - ret = val.decode("utf-8") + ret = val.decode("utf-8") except UnicodeDecodeError: - ret = val.decode("utf-8", "ignore") + ret = val.decode("utf-8", "ignore") else: - ret = val.encode("utf-8", "replace").decode("utf-8") - return ret + ret = val.encode("utf-8", "replace").decode("utf-8") + return ret class _PytestWrapper(object): @@ -302,7 +302,7 @@ def get_real_func(obj): else: raise ValueError( ("could not find real function of {start}\nstopped at {current}").format( - start=saferepr(start_obj), current=saferepr(obj) + start=saferepr(start_obj), current=saferepr(obj) ) ) if isinstance(obj, functools.partial): @@ -378,16 +378,16 @@ if _PY3: def safe_str(v): """returns v as string""" - try: - return str(v) - except UnicodeEncodeError: - return str(v, encoding="utf-8") + try: + return str(v) + except UnicodeEncodeError: + return str(v, encoding="utf-8") else: def safe_str(v): - """returns v as string, converting to utf-8 if necessary""" + """returns v as string, converting to utf-8 if necessary""" try: return str(v) except UnicodeError: @@ -416,8 +416,8 @@ def _setup_collect_fakemodule(): pytest.collect = ModuleType("pytest.collect") pytest.collect.__all__ = [] # used for setns - for attribute in COLLECT_FAKEMODULE_ATTRIBUTES: - setattr(pytest.collect, attribute, getattr(pytest, attribute)) + for attribute in COLLECT_FAKEMODULE_ATTRIBUTES: + setattr(pytest.collect, attribute, getattr(pytest, attribute)) if _PY2: @@ -465,9 +465,9 @@ if six.PY2: else: from functools import lru_cache # noqa: F401 - - -if getattr(attr, "__version_info__", ()) >= (19, 2): - ATTRS_EQ_FIELD = "eq" -else: - ATTRS_EQ_FIELD = "cmp" + + +if getattr(attr, "__version_info__", ()) >= (19, 2): + ATTRS_EQ_FIELD = "eq" +else: + ATTRS_EQ_FIELD = "cmp" diff --git a/contrib/python/pytest/py2/_pytest/config/__init__.py b/contrib/python/pytest/py2/_pytest/config/__init__.py index 0737ff9d51..eb976516db 100644 --- a/contrib/python/pytest/py2/_pytest/config/__init__.py +++ b/contrib/python/pytest/py2/_pytest/config/__init__.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ command line options, ini-file and conftest.py processing. """ from __future__ import absolute_import from __future__ import division @@ -13,10 +13,10 @@ import sys import types import warnings -import attr +import attr import py import six -from packaging.version import Version +from packaging.version import Version from pluggy import HookimplMarker from pluggy import HookspecMarker from pluggy import PluginManager @@ -28,16 +28,16 @@ from .exceptions import PrintHelp from .exceptions import UsageError from .findpaths import determine_setup from .findpaths import exists -from _pytest import deprecated +from _pytest import deprecated from _pytest._code import ExceptionInfo from _pytest._code import filter_traceback -from _pytest.compat import importlib_metadata +from _pytest.compat import importlib_metadata from _pytest.compat import lru_cache from _pytest.compat import safe_str -from _pytest.outcomes import fail +from _pytest.outcomes import fail from _pytest.outcomes import Skipped -from _pytest.pathlib import Path -from _pytest.warning_types import PytestConfigWarning +from _pytest.pathlib import Path +from _pytest.warning_types import PytestConfigWarning hookimpl = HookimplMarker("pytest") hookspec = HookspecMarker("pytest") @@ -117,18 +117,18 @@ def directory_arg(path, optname): return path -# Plugins that cannot be disabled via "-p no:X" currently. -essential_plugins = ( +# Plugins that cannot be disabled via "-p no:X" currently. +essential_plugins = ( "mark", "main", "runner", - "fixtures", - "helpconfig", # Provides -p. -) - -default_plugins = essential_plugins + ( + "fixtures", + "helpconfig", # Provides -p. +) + +default_plugins = essential_plugins + ( "python", - "terminal", + "terminal", "debugging", "unittest", "capture", @@ -149,27 +149,27 @@ default_plugins = essential_plugins + ( "stepwise", "warnings", "logging", - "reports", + "reports", ) builtin_plugins = set(default_plugins) builtin_plugins.add("pytester") -def get_config(args=None, plugins=None): +def get_config(args=None, plugins=None): # subsequent calls to main will create a fresh instance pluginmanager = PytestPluginManager() - config = Config( - pluginmanager, - invocation_params=Config.InvocationParams( - args=args, plugins=plugins, dir=Path().resolve() - ), - ) - - if args is not None: - # Handle any "-p no:plugin" args. - pluginmanager.consider_preparse(args) - + config = Config( + pluginmanager, + invocation_params=Config.InvocationParams( + args=args, plugins=plugins, dir=Path().resolve() + ), + ) + + if args is not None: + # Handle any "-p no:plugin" args. + pluginmanager.consider_preparse(args) + for spec in default_plugins: pluginmanager.import_plugin(spec) return config @@ -194,10 +194,10 @@ def _prepareconfig(args=None, plugins=None): elif isinstance(args, py.path.local): args = [str(args)] elif not isinstance(args, (tuple, list)): - msg = "`args` parameter expected to be a list or tuple of strings, got: {!r} (type: {})" - raise TypeError(msg.format(args, type(args))) + msg = "`args` parameter expected to be a list or tuple of strings, got: {!r} (type: {})" + raise TypeError(msg.format(args, type(args))) - config = get_config(args, plugins) + config = get_config(args, plugins) pluginmanager = config.pluginmanager try: if plugins: @@ -207,9 +207,9 @@ def _prepareconfig(args=None, plugins=None): else: pluginmanager.register(plugin) if warning: - from _pytest.warnings import _issue_warning_captured + from _pytest.warnings import _issue_warning_captured - _issue_warning_captured(warning, hook=config.hook, stacklevel=4) + _issue_warning_captured(warning, hook=config.hook, stacklevel=4) return pluginmanager.hook.pytest_cmdline_parse( pluginmanager=pluginmanager, args=args ) @@ -263,7 +263,7 @@ class PytestPluginManager(PluginManager): Use :py:meth:`pluggy.PluginManager.add_hookspecs <PluginManager.add_hookspecs>` instead. """ - warnings.warn(deprecated.PLUGIN_MANAGER_ADDHOOKS, stacklevel=2) + warnings.warn(deprecated.PLUGIN_MANAGER_ADDHOOKS, stacklevel=2) return self.add_hookspecs(module_or_class) def parse_hookimpl_opts(self, plugin, name): @@ -272,8 +272,8 @@ class PytestPluginManager(PluginManager): # (see issue #1073) if not name.startswith("pytest_"): return - # ignore names which can not be hooks - if name == "pytest_plugins": + # ignore names which can not be hooks + if name == "pytest_plugins": return method = getattr(plugin, name) @@ -286,13 +286,13 @@ class PytestPluginManager(PluginManager): # collect unmarked hooks as long as they have the `pytest_' prefix if opts is None and name.startswith("pytest_"): opts = {} - if opts is not None: - # TODO: DeprecationWarning, people should use hookimpl - # https://github.com/pytest-dev/pytest/issues/4562 - known_marks = {m.name for m in getattr(method, "pytestmark", [])} + if opts is not None: + # TODO: DeprecationWarning, people should use hookimpl + # https://github.com/pytest-dev/pytest/issues/4562 + known_marks = {m.name for m in getattr(method, "pytestmark", [])} for name in ("tryfirst", "trylast", "optionalhook", "hookwrapper"): - opts.setdefault(name, hasattr(method, name) or name in known_marks) + opts.setdefault(name, hasattr(method, name) or name in known_marks) return opts def parse_hookspec_opts(self, module_or_class, name): @@ -301,27 +301,27 @@ class PytestPluginManager(PluginManager): ) if opts is None: method = getattr(module_or_class, name) - + if name.startswith("pytest_"): - # todo: deprecate hookspec hacks - # https://github.com/pytest-dev/pytest/issues/4562 - known_marks = {m.name for m in getattr(method, "pytestmark", [])} + # todo: deprecate hookspec hacks + # https://github.com/pytest-dev/pytest/issues/4562 + known_marks = {m.name for m in getattr(method, "pytestmark", [])} opts = { - "firstresult": hasattr(method, "firstresult") - or "firstresult" in known_marks, - "historic": hasattr(method, "historic") - or "historic" in known_marks, + "firstresult": hasattr(method, "firstresult") + or "firstresult" in known_marks, + "historic": hasattr(method, "historic") + or "historic" in known_marks, } return opts def register(self, plugin, name=None): if name in ["pytest_catchlog", "pytest_capturelog"]: - warnings.warn( - PytestConfigWarning( - "{} plugin has been merged into the core, " - "please remove it from your requirements.".format( - name.replace("_", "-") - ) + warnings.warn( + PytestConfigWarning( + "{} plugin has been merged into the core, " + "please remove it from your requirements.".format( + name.replace("_", "-") + ) ) ) return @@ -425,10 +425,10 @@ class PytestPluginManager(PluginManager): continue conftestpath = parent.join("conftest.py") if conftestpath.isfile(): - # Use realpath to avoid loading the same conftest twice - # with build systems that create build directories containing - # symlinks to actual files. - mod = self._importconftest(conftestpath.realpath()) + # Use realpath to avoid loading the same conftest twice + # with build systems that create build directories containing + # symlinks to actual files. + mod = self._importconftest(conftestpath.realpath()) clist.append(mod) self._dirpath2confmods[directory] = clist return clist @@ -457,14 +457,14 @@ class PytestPluginManager(PluginManager): and not self._using_pyargs ): from _pytest.deprecated import ( - PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST, + PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST, ) - fail( - PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST.format( - conftestpath, self._confcutdir - ), - pytrace=False, + fail( + PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST.format( + conftestpath, self._confcutdir + ), + pytrace=False, ) except Exception: raise ConftestImportFailure(conftestpath, sys.exc_info()) @@ -487,30 +487,30 @@ class PytestPluginManager(PluginManager): # def consider_preparse(self, args): - i = 0 - n = len(args) - while i < n: - opt = args[i] - i += 1 - if isinstance(opt, six.string_types): - if opt == "-p": - try: - parg = args[i] - except IndexError: - return - i += 1 - elif opt.startswith("-p"): - parg = opt[2:] - else: - continue - self.consider_pluginarg(parg) + i = 0 + n = len(args) + while i < n: + opt = args[i] + i += 1 + if isinstance(opt, six.string_types): + if opt == "-p": + try: + parg = args[i] + except IndexError: + return + i += 1 + elif opt.startswith("-p"): + parg = opt[2:] + else: + continue + self.consider_pluginarg(parg) def consider_pluginarg(self, arg): if arg.startswith("no:"): name = arg[3:] - if name in essential_plugins: - raise UsageError("plugin %s cannot be disabled" % name) - + if name in essential_plugins: + raise UsageError("plugin %s cannot be disabled" % name) + # PR #4304 : remove stepwise if cacheprovider is blocked if name == "cacheprovider": self.set_blocked("stepwise") @@ -520,15 +520,15 @@ class PytestPluginManager(PluginManager): if not name.startswith("pytest_"): self.set_blocked("pytest_" + name) else: - name = arg - # Unblock the plugin. None indicates that it has been blocked. - # There is no interface with pluggy for this. - if self._name2plugin.get(name, -1) is None: - del self._name2plugin[name] - if not name.startswith("pytest_"): - if self._name2plugin.get("pytest_" + name, -1) is None: - del self._name2plugin["pytest_" + name] - self.import_plugin(arg, consider_entry_points=True) + name = arg + # Unblock the plugin. None indicates that it has been blocked. + # There is no interface with pluggy for this. + if self._name2plugin.get(name, -1) is None: + del self._name2plugin[name] + if not name.startswith("pytest_"): + if self._name2plugin.get("pytest_" + name, -1) is None: + del self._name2plugin["pytest_" + name] + self.import_plugin(arg, consider_entry_points=True) def consider_conftest(self, conftestmodule): self.register(conftestmodule, name=conftestmodule.__file__) @@ -544,30 +544,30 @@ class PytestPluginManager(PluginManager): for import_spec in plugins: self.import_plugin(import_spec) - def import_plugin(self, modname, consider_entry_points=False): - """ - Imports a plugin with ``modname``. If ``consider_entry_points`` is True, entry point - names are also considered to find a plugin. - """ + def import_plugin(self, modname, consider_entry_points=False): + """ + Imports a plugin with ``modname``. If ``consider_entry_points`` is True, entry point + names are also considered to find a plugin. + """ # most often modname refers to builtin modules, e.g. "pytester", # "terminal" or "capture". Those plugins are registered under their # basename for historic purposes but must be imported with the # _pytest prefix. - assert isinstance(modname, six.string_types), ( + assert isinstance(modname, six.string_types), ( "module name as text required, got %r" % modname ) modname = str(modname) if self.is_blocked(modname) or self.get_plugin(modname) is not None: return - - importspec = "_pytest." + modname if modname in builtin_plugins else modname + + importspec = "_pytest." + modname if modname in builtin_plugins else modname self.rewrite_hook.mark_rewrite(importspec) - - if consider_entry_points: - loaded = self.load_setuptools_entrypoints("pytest11", name=modname) - if loaded: - return - + + if consider_entry_points: + loaded = self.load_setuptools_entrypoints("pytest11", name=modname) + if loaded: + return + try: __import__(importspec) except ImportError as e: @@ -575,19 +575,19 @@ class PytestPluginManager(PluginManager): modname, safe_str(e.args[0]), ) - new_exc = ImportError(new_exc_message) - tb = sys.exc_info()[2] + new_exc = ImportError(new_exc_message) + tb = sys.exc_info()[2] - six.reraise(ImportError, new_exc, tb) + six.reraise(ImportError, new_exc, tb) except Skipped as e: - from _pytest.warnings import _issue_warning_captured - - _issue_warning_captured( - PytestConfigWarning("skipped plugin %r: %s" % (modname, e.msg)), - self.hook, - stacklevel=1, - ) + from _pytest.warnings import _issue_warning_captured + + _issue_warning_captured( + PytestConfigWarning("skipped plugin %r: %s" % (modname, e.msg)), + self.hook, + stacklevel=1, + ) else: mod = sys.modules[importspec] self.register(mod, modname) @@ -601,8 +601,8 @@ def _get_plugin_specs_as_list(specs): which case it is returned as a list. Specs can also be `None` in which case an empty list is returned. """ - if specs is not None and not isinstance(specs, types.ModuleType): - if isinstance(specs, six.string_types): + if specs is not None and not isinstance(specs, types.ModuleType): + if isinstance(specs, six.string_types): specs = specs.split(",") if specs else [] if not isinstance(specs, (list, tuple)): raise UsageError( @@ -629,117 +629,117 @@ notset = Notset() def _iter_rewritable_modules(package_files): - """ - Given an iterable of file names in a source distribution, return the "names" that should - be marked for assertion rewrite (for example the package "pytest_mock/__init__.py" should - be added as "pytest_mock" in the assertion rewrite mechanism. - - This function has to deal with dist-info based distributions and egg based distributions - (which are still very much in use for "editable" installs). - - Here are the file names as seen in a dist-info based distribution: - - pytest_mock/__init__.py - pytest_mock/_version.py - pytest_mock/plugin.py - pytest_mock.egg-info/PKG-INFO - - Here are the file names as seen in an egg based distribution: - - src/pytest_mock/__init__.py - src/pytest_mock/_version.py - src/pytest_mock/plugin.py - src/pytest_mock.egg-info/PKG-INFO - LICENSE - setup.py - - We have to take in account those two distribution flavors in order to determine which - names should be considered for assertion rewriting. - - More information: - https://github.com/pytest-dev/pytest-mock/issues/167 - """ - package_files = list(package_files) - seen_some = False + """ + Given an iterable of file names in a source distribution, return the "names" that should + be marked for assertion rewrite (for example the package "pytest_mock/__init__.py" should + be added as "pytest_mock" in the assertion rewrite mechanism. + + This function has to deal with dist-info based distributions and egg based distributions + (which are still very much in use for "editable" installs). + + Here are the file names as seen in a dist-info based distribution: + + pytest_mock/__init__.py + pytest_mock/_version.py + pytest_mock/plugin.py + pytest_mock.egg-info/PKG-INFO + + Here are the file names as seen in an egg based distribution: + + src/pytest_mock/__init__.py + src/pytest_mock/_version.py + src/pytest_mock/plugin.py + src/pytest_mock.egg-info/PKG-INFO + LICENSE + setup.py + + We have to take in account those two distribution flavors in order to determine which + names should be considered for assertion rewriting. + + More information: + https://github.com/pytest-dev/pytest-mock/issues/167 + """ + package_files = list(package_files) + seen_some = False for fn in package_files: is_simple_module = "/" not in fn and fn.endswith(".py") is_package = fn.count("/") == 1 and fn.endswith("__init__.py") if is_simple_module: module_name, _ = os.path.splitext(fn) - # we ignore "setup.py" at the root of the distribution - if module_name != "setup": - seen_some = True - yield module_name + # we ignore "setup.py" at the root of the distribution + if module_name != "setup": + seen_some = True + yield module_name elif is_package: package_name = os.path.dirname(fn) - seen_some = True + seen_some = True yield package_name - if not seen_some: - # at this point we did not find any packages or modules suitable for assertion - # rewriting, so we try again by stripping the first path component (to account for - # "src" based source trees for example) - # this approach lets us have the common case continue to be fast, as egg-distributions - # are rarer - new_package_files = [] - for fn in package_files: - parts = fn.split("/") - new_fn = "/".join(parts[1:]) - if new_fn: - new_package_files.append(new_fn) - if new_package_files: - for _module in _iter_rewritable_modules(new_package_files): - yield _module - - + if not seen_some: + # at this point we did not find any packages or modules suitable for assertion + # rewriting, so we try again by stripping the first path component (to account for + # "src" based source trees for example) + # this approach lets us have the common case continue to be fast, as egg-distributions + # are rarer + new_package_files = [] + for fn in package_files: + parts = fn.split("/") + new_fn = "/".join(parts[1:]) + if new_fn: + new_package_files.append(new_fn) + if new_package_files: + for _module in _iter_rewritable_modules(new_package_files): + yield _module + + class Config(object): - """ - Access to configuration values, pluginmanager and plugin hooks. - - :ivar PytestPluginManager pluginmanager: the plugin manager handles plugin registration and hook invocation. - - :ivar argparse.Namespace option: access to command line option as attributes. - - :ivar InvocationParams invocation_params: - - Object containing the parameters regarding the ``pytest.main`` - invocation. - Contains the followinig read-only attributes: - * ``args``: list of command-line arguments as passed to ``pytest.main()``. - * ``plugins``: list of extra plugins, might be None - * ``dir``: directory where ``pytest.main()`` was invoked from. - """ - - @attr.s(frozen=True) - class InvocationParams(object): - """Holds parameters passed during ``pytest.main()`` - - .. note:: - - Currently the environment variable PYTEST_ADDOPTS is also handled by - pytest implicitly, not being part of the invocation. - - Plugins accessing ``InvocationParams`` must be aware of that. - """ - - args = attr.ib() - plugins = attr.ib() - dir = attr.ib() - - def __init__(self, pluginmanager, invocation_params=None, *args): - from .argparsing import Parser, FILE_OR_DIR - - if invocation_params is None: - invocation_params = self.InvocationParams( - args=(), plugins=None, dir=Path().resolve() - ) - + """ + Access to configuration values, pluginmanager and plugin hooks. + + :ivar PytestPluginManager pluginmanager: the plugin manager handles plugin registration and hook invocation. + + :ivar argparse.Namespace option: access to command line option as attributes. + + :ivar InvocationParams invocation_params: + + Object containing the parameters regarding the ``pytest.main`` + invocation. + Contains the followinig read-only attributes: + * ``args``: list of command-line arguments as passed to ``pytest.main()``. + * ``plugins``: list of extra plugins, might be None + * ``dir``: directory where ``pytest.main()`` was invoked from. + """ + + @attr.s(frozen=True) + class InvocationParams(object): + """Holds parameters passed during ``pytest.main()`` + + .. note:: + + Currently the environment variable PYTEST_ADDOPTS is also handled by + pytest implicitly, not being part of the invocation. + + Plugins accessing ``InvocationParams`` must be aware of that. + """ + + args = attr.ib() + plugins = attr.ib() + dir = attr.ib() + + def __init__(self, pluginmanager, invocation_params=None, *args): + from .argparsing import Parser, FILE_OR_DIR + + if invocation_params is None: + invocation_params = self.InvocationParams( + args=(), plugins=None, dir=Path().resolve() + ) + #: access to command line option as attributes. #: (deprecated), use :py:func:`getoption() <_pytest.config.Config.getoption>` instead self.option = argparse.Namespace() - self.invocation_params = invocation_params - + self.invocation_params = invocation_params + _a = FILE_OR_DIR self._parser = Parser( usage="%%(prog)s [options] [%s] [%s] [...]" % (_a, _a), @@ -755,12 +755,12 @@ class Config(object): self._cleanup = [] self.pluginmanager.register(self, "pytestconfig") self._configured = False - self.hook.pytest_addoption.call_historic(kwargs=dict(parser=self._parser)) + self.hook.pytest_addoption.call_historic(kwargs=dict(parser=self._parser)) - @property - def invocation_dir(self): - """Backward compatibility""" - return py.path.local(str(self.invocation_params.dir)) + @property + def invocation_dir(self): + """Backward compatibility""" + return py.path.local(str(self.invocation_params.dir)) def add_cleanup(self, func): """ Add a function to be called when the config object gets out of @@ -781,35 +781,35 @@ class Config(object): fin = self._cleanup.pop() fin() - def get_terminal_writer(self): - return self.pluginmanager.get_plugin("terminalreporter")._tw + def get_terminal_writer(self): + return self.pluginmanager.get_plugin("terminalreporter")._tw - def pytest_cmdline_parse(self, pluginmanager, args): - try: - self.parse(args) - except UsageError: - - # Handle --version and --help here in a minimal fashion. - # This gets done via helpconfig normally, but its - # pytest_cmdline_main is not called in case of errors. - if getattr(self.option, "version", False) or "--version" in args: - from _pytest.helpconfig import showversion - - showversion(self) - elif ( - getattr(self.option, "help", False) or "--help" in args or "-h" in args - ): - self._parser._getparser().print_help() - sys.stdout.write( - "\nNOTE: displaying only minimal help due to UsageError.\n\n" - ) + def pytest_cmdline_parse(self, pluginmanager, args): + try: + self.parse(args) + except UsageError: - raise + # Handle --version and --help here in a minimal fashion. + # This gets done via helpconfig normally, but its + # pytest_cmdline_main is not called in case of errors. + if getattr(self.option, "version", False) or "--version" in args: + from _pytest.helpconfig import showversion + + showversion(self) + elif ( + getattr(self.option, "help", False) or "--help" in args or "-h" in args + ): + self._parser._getparser().print_help() + sys.stdout.write( + "\nNOTE: displaying only minimal help due to UsageError.\n\n" + ) + + raise return self def notify_exception(self, excinfo, option=None): - if option and getattr(option, "fulltrace", False): + if option and getattr(option, "fulltrace", False): style = "long" else: style = "native" @@ -832,7 +832,7 @@ class Config(object): @classmethod def fromdictargs(cls, option_dict, args): """ constructor useable for subprocesses. """ - config = get_config(args) + config = get_config(args) config.option.__dict__.update(option_dict) config.parse(args, addopts=False) for x in config.option.plugins: @@ -876,7 +876,7 @@ class Config(object): by the importhook. """ ns, unknown_args = self._parser.parse_known_and_unknown_args(args) - mode = getattr(ns, "assertmode", "plain") + mode = getattr(ns, "assertmode", "plain") if mode == "rewrite": try: hook = _pytest.assertion.install_importhook(self) @@ -899,41 +899,41 @@ class Config(object): return package_files = ( - str(file) - for dist in importlib_metadata.distributions() - if any(ep.group == "pytest11" for ep in dist.entry_points) - for file in dist.files or [] + str(file) + for dist in importlib_metadata.distributions() + if any(ep.group == "pytest11" for ep in dist.entry_points) + for file in dist.files or [] ) for name in _iter_rewritable_modules(package_files): hook.mark_rewrite(name) - def _validate_args(self, args, via): - """Validate known args.""" - self._parser._config_source_hint = via - try: - self._parser.parse_known_and_unknown_args( - args, namespace=copy.copy(self.option) - ) - finally: - del self._parser._config_source_hint - - return args - + def _validate_args(self, args, via): + """Validate known args.""" + self._parser._config_source_hint = via + try: + self._parser.parse_known_and_unknown_args( + args, namespace=copy.copy(self.option) + ) + finally: + del self._parser._config_source_hint + + return args + def _preparse(self, args, addopts=True): if addopts: - env_addopts = os.environ.get("PYTEST_ADDOPTS", "") - if len(env_addopts): - args[:] = ( - self._validate_args(shlex.split(env_addopts), "via PYTEST_ADDOPTS") - + args - ) + env_addopts = os.environ.get("PYTEST_ADDOPTS", "") + if len(env_addopts): + args[:] = ( + self._validate_args(shlex.split(env_addopts), "via PYTEST_ADDOPTS") + + args + ) self._initini(args) if addopts: - args[:] = ( - self._validate_args(self.getini("addopts"), "via addopts config") + args - ) - + args[:] = ( + self._validate_args(self.getini("addopts"), "via addopts config") + args + ) + self._checkversion() self._consider_importhook(args) self.pluginmanager.consider_preparse(args) @@ -957,15 +957,15 @@ class Config(object): if ns.help or ns.version: # we don't want to prevent --help/--version to work # so just let is pass and print a warning at the end - from _pytest.warnings import _issue_warning_captured - - _issue_warning_captured( - PytestConfigWarning( - "could not load initial conftests: {}".format(e.path) - ), - self.hook, - stacklevel=2, - ) + from _pytest.warnings import _issue_warning_captured + + _issue_warning_captured( + PytestConfigWarning( + "could not load initial conftests: {}".format(e.path) + ), + self.hook, + stacklevel=2, + ) else: raise @@ -974,7 +974,7 @@ class Config(object): minver = self.inicfg.get("minversion", None) if minver: - if Version(minver) > Version(pytest.__version__): + if Version(minver) > Version(pytest.__version__): raise pytest.UsageError( "%s:%d: requires pytest-%s, actual pytest-%s'" % ( diff --git a/contrib/python/pytest/py2/_pytest/config/argparsing.py b/contrib/python/pytest/py2/_pytest/config/argparsing.py index 37fb772db9..26b5bd88dd 100644 --- a/contrib/python/pytest/py2/_pytest/config/argparsing.py +++ b/contrib/python/pytest/py2/_pytest/config/argparsing.py @@ -1,11 +1,11 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- import argparse import warnings import py import six -from _pytest.config.exceptions import UsageError +from _pytest.config.exceptions import UsageError FILE_OR_DIR = "file_or_dir" @@ -17,8 +17,8 @@ class Parser(object): there's an error processing the command line arguments. """ - prog = None - + prog = None + def __init__(self, usage=None, processopt=None): self._anonymous = OptionGroup("custom options", parser=self) self._groups = [] @@ -83,7 +83,7 @@ class Parser(object): def _getparser(self): from _pytest._argcomplete import filescompleter - optparser = MyOptionParser(self, self.extra_info, prog=self.prog) + optparser = MyOptionParser(self, self.extra_info, prog=self.prog) groups = self._groups + [self._anonymous] for group in groups: if group.options: @@ -320,13 +320,13 @@ class OptionGroup(object): class MyOptionParser(argparse.ArgumentParser): - def __init__(self, parser, extra_info=None, prog=None): + def __init__(self, parser, extra_info=None, prog=None): if not extra_info: extra_info = {} self._parser = parser argparse.ArgumentParser.__init__( self, - prog=prog, + prog=prog, usage=parser._usage, add_help=False, formatter_class=DropShorterLongHelpFormatter, @@ -336,14 +336,14 @@ class MyOptionParser(argparse.ArgumentParser): self.extra_info = extra_info def error(self, message): - """Transform argparse error message into UsageError.""" - msg = "%s: error: %s" % (self.prog, message) - - if hasattr(self._parser, "_config_source_hint"): - msg = "%s (%s)" % (msg, self._parser._config_source_hint) + """Transform argparse error message into UsageError.""" + msg = "%s: error: %s" % (self.prog, message) - raise UsageError(self.format_usage() + msg) + if hasattr(self._parser, "_config_source_hint"): + msg = "%s (%s)" % (msg, self._parser._config_source_hint) + raise UsageError(self.format_usage() + msg) + def parse_args(self, args=None, namespace=None): """allow splitting of positional arguments""" args, argv = self.parse_known_args(args, namespace) diff --git a/contrib/python/pytest/py2/_pytest/config/exceptions.py b/contrib/python/pytest/py2/_pytest/config/exceptions.py index bf58fde5db..eb902229be 100644 --- a/contrib/python/pytest/py2/_pytest/config/exceptions.py +++ b/contrib/python/pytest/py2/_pytest/config/exceptions.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- class UsageError(Exception): """ error in pytest usage or invocation""" diff --git a/contrib/python/pytest/py2/_pytest/config/findpaths.py b/contrib/python/pytest/py2/_pytest/config/findpaths.py index e6779b289b..0af3e525a6 100644 --- a/contrib/python/pytest/py2/_pytest/config/findpaths.py +++ b/contrib/python/pytest/py2/_pytest/config/findpaths.py @@ -1,10 +1,10 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- import os import py from .exceptions import UsageError -from _pytest.outcomes import fail +from _pytest.outcomes import fail def exists(path, ignore=EnvironmentError): @@ -33,24 +33,24 @@ def getcfg(args, config=None): for inibasename in inibasenames: p = base.join(inibasename) if exists(p): - try: - iniconfig = py.iniconfig.IniConfig(p) - except py.iniconfig.ParseError as exc: - raise UsageError(str(exc)) + try: + iniconfig = py.iniconfig.IniConfig(p) + except py.iniconfig.ParseError as exc: + raise UsageError(str(exc)) if ( inibasename == "setup.cfg" and "tool:pytest" in iniconfig.sections ): return base, p, iniconfig["tool:pytest"] - elif "pytest" in iniconfig.sections: - if inibasename == "setup.cfg" and config is not None: - - fail( - CFG_PYTEST_SECTION.format(filename=inibasename), - pytrace=False, - ) - return base, p, iniconfig["pytest"] + elif "pytest" in iniconfig.sections: + if inibasename == "setup.cfg" and config is not None: + + fail( + CFG_PYTEST_SECTION.format(filename=inibasename), + pytrace=False, + ) + return base, p, iniconfig["pytest"] elif inibasename == "pytest.ini": # allowed to be empty return base, p, {} @@ -114,40 +114,40 @@ def determine_setup(inifile, args, rootdir_cmd_arg=None, config=None): if is_cfg_file and section == "pytest" and config is not None: from _pytest.deprecated import CFG_PYTEST_SECTION - fail( - CFG_PYTEST_SECTION.format(filename=str(inifile)), pytrace=False + fail( + CFG_PYTEST_SECTION.format(filename=str(inifile)), pytrace=False ) break except KeyError: inicfg = None - if rootdir_cmd_arg is None: - rootdir = get_common_ancestor(dirs) + if rootdir_cmd_arg is None: + rootdir = get_common_ancestor(dirs) else: ancestor = get_common_ancestor(dirs) rootdir, inifile, inicfg = getcfg([ancestor], config=config) - if rootdir is None and rootdir_cmd_arg is None: - for possible_rootdir in ancestor.parts(reverse=True): - if possible_rootdir.join("setup.py").exists(): - rootdir = possible_rootdir + if rootdir is None and rootdir_cmd_arg is None: + for possible_rootdir in ancestor.parts(reverse=True): + if possible_rootdir.join("setup.py").exists(): + rootdir = possible_rootdir break else: - if dirs != [ancestor]: - rootdir, inifile, inicfg = getcfg(dirs, config=config) + if dirs != [ancestor]: + rootdir, inifile, inicfg = getcfg(dirs, config=config) if rootdir is None: - if config is not None: - cwd = config.invocation_dir - else: - cwd = py.path.local() - rootdir = get_common_ancestor([cwd, ancestor]) + if config is not None: + cwd = config.invocation_dir + else: + cwd = py.path.local() + rootdir = get_common_ancestor([cwd, ancestor]) is_fs_root = os.path.splitdrive(str(rootdir))[1] == "/" if is_fs_root: rootdir = ancestor if rootdir_cmd_arg: - rootdir = py.path.local(os.path.expandvars(rootdir_cmd_arg)) - if not rootdir.isdir(): + rootdir = py.path.local(os.path.expandvars(rootdir_cmd_arg)) + if not rootdir.isdir(): raise UsageError( "Directory '{}' not found. Check your '--rootdir' option.".format( - rootdir + rootdir ) ) return rootdir, inifile, inicfg or {} diff --git a/contrib/python/pytest/py2/_pytest/debugging.py b/contrib/python/pytest/py2/_pytest/debugging.py index bc114c8683..1bbca0f7bb 100644 --- a/contrib/python/pytest/py2/_pytest/debugging.py +++ b/contrib/python/pytest/py2/_pytest/debugging.py @@ -1,18 +1,18 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ interactive debugging with PDB, the Python Debugger. """ from __future__ import absolute_import from __future__ import division from __future__ import print_function import os -import argparse +import argparse import pdb import sys from doctest import UnexpectedException from _pytest import outcomes from _pytest.config import hookimpl -from _pytest.config.exceptions import UsageError +from _pytest.config.exceptions import UsageError def import_readline(): @@ -51,17 +51,17 @@ def tty(): sys.path = old_sys_path -def _validate_usepdb_cls(value): - """Validate syntax of --pdbcls option.""" - try: - modname, classname = value.split(":") - except ValueError: - raise argparse.ArgumentTypeError( - "{!r} is not in the format 'modname:classname'".format(value) - ) - return (modname, classname) - - +def _validate_usepdb_cls(value): + """Validate syntax of --pdbcls option.""" + try: + modname, classname = value.split(":") + except ValueError: + raise argparse.ArgumentTypeError( + "{!r} is not in the format 'modname:classname'".format(value) + ) + return (modname, classname) + + def pytest_addoption(parser): group = parser.getgroup("general") group._addoption( @@ -74,7 +74,7 @@ def pytest_addoption(parser): "--pdbcls", dest="usepdb_cls", metavar="modulename:classname", - type=_validate_usepdb_cls, + type=_validate_usepdb_cls, help="start a custom interactive Python debugger on errors. " "For example: --pdbcls=IPython.terminal.debugger:TerminalPdb", ) @@ -93,7 +93,7 @@ def pytest_configure(config): config.pluginmanager.register(PdbInvoke(), "pdbinvoke") pytestPDB._saved.append( - (pdb.set_trace, pytestPDB._pluginmanager, pytestPDB._config) + (pdb.set_trace, pytestPDB._pluginmanager, pytestPDB._config) ) pdb.set_trace = pytestPDB.set_trace pytestPDB._pluginmanager = config.pluginmanager @@ -117,181 +117,181 @@ class pytestPDB(object): _pluginmanager = None _config = None _saved = [] - _recursive_debug = 0 - _wrapped_pdb_cls = None - - @classmethod - def _is_capturing(cls, capman): - if capman: - return capman.is_capturing() - return False - - @classmethod - def _import_pdb_cls(cls, capman): - if not cls._config: - # Happens when using pytest.set_trace outside of a test. - return pdb.Pdb - - usepdb_cls = cls._config.getvalue("usepdb_cls") - - if cls._wrapped_pdb_cls and cls._wrapped_pdb_cls[0] == usepdb_cls: - return cls._wrapped_pdb_cls[1] - - if usepdb_cls: - modname, classname = usepdb_cls - - try: - __import__(modname) - mod = sys.modules[modname] - - # Handle --pdbcls=pdb:pdb.Pdb (useful e.g. with pdbpp). - parts = classname.split(".") - pdb_cls = getattr(mod, parts[0]) - for part in parts[1:]: - pdb_cls = getattr(pdb_cls, part) - except Exception as exc: - value = ":".join((modname, classname)) - raise UsageError( - "--pdbcls: could not import {!r}: {}".format(value, exc) - ) - else: - pdb_cls = pdb.Pdb - - wrapped_cls = cls._get_pdb_wrapper_class(pdb_cls, capman) - cls._wrapped_pdb_cls = (usepdb_cls, wrapped_cls) - return wrapped_cls + _recursive_debug = 0 + _wrapped_pdb_cls = None @classmethod - def _get_pdb_wrapper_class(cls, pdb_cls, capman): + def _is_capturing(cls, capman): + if capman: + return capman.is_capturing() + return False + + @classmethod + def _import_pdb_cls(cls, capman): + if not cls._config: + # Happens when using pytest.set_trace outside of a test. + return pdb.Pdb + + usepdb_cls = cls._config.getvalue("usepdb_cls") + + if cls._wrapped_pdb_cls and cls._wrapped_pdb_cls[0] == usepdb_cls: + return cls._wrapped_pdb_cls[1] + + if usepdb_cls: + modname, classname = usepdb_cls + + try: + __import__(modname) + mod = sys.modules[modname] + + # Handle --pdbcls=pdb:pdb.Pdb (useful e.g. with pdbpp). + parts = classname.split(".") + pdb_cls = getattr(mod, parts[0]) + for part in parts[1:]: + pdb_cls = getattr(pdb_cls, part) + except Exception as exc: + value = ":".join((modname, classname)) + raise UsageError( + "--pdbcls: could not import {!r}: {}".format(value, exc) + ) + else: + pdb_cls = pdb.Pdb + + wrapped_cls = cls._get_pdb_wrapper_class(pdb_cls, capman) + cls._wrapped_pdb_cls = (usepdb_cls, wrapped_cls) + return wrapped_cls + + @classmethod + def _get_pdb_wrapper_class(cls, pdb_cls, capman): import _pytest.config - class PytestPdbWrapper(pdb_cls, object): - _pytest_capman = capman - _continued = False - - def do_debug(self, arg): - cls._recursive_debug += 1 - ret = super(PytestPdbWrapper, self).do_debug(arg) - cls._recursive_debug -= 1 - return ret - - def do_continue(self, arg): - ret = super(PytestPdbWrapper, self).do_continue(arg) - if cls._recursive_debug == 0: - tw = _pytest.config.create_terminal_writer(cls._config) - tw.line() - - capman = self._pytest_capman - capturing = pytestPDB._is_capturing(capman) - if capturing: - if capturing == "global": + class PytestPdbWrapper(pdb_cls, object): + _pytest_capman = capman + _continued = False + + def do_debug(self, arg): + cls._recursive_debug += 1 + ret = super(PytestPdbWrapper, self).do_debug(arg) + cls._recursive_debug -= 1 + return ret + + def do_continue(self, arg): + ret = super(PytestPdbWrapper, self).do_continue(arg) + if cls._recursive_debug == 0: + tw = _pytest.config.create_terminal_writer(cls._config) + tw.line() + + capman = self._pytest_capman + capturing = pytestPDB._is_capturing(capman) + if capturing: + if capturing == "global": tw.sep(">", "PDB continue (IO-capturing resumed)") else: - tw.sep( - ">", - "PDB continue (IO-capturing resumed for %s)" - % capturing, - ) - capman.resume() - else: - tw.sep(">", "PDB continue") - cls._pluginmanager.hook.pytest_leave_pdb(config=cls._config, pdb=self) - self._continued = True - return ret - - do_c = do_cont = do_continue - - def do_quit(self, arg): - """Raise Exit outcome when quit command is used in pdb. - - This is a bit of a hack - it would be better if BdbQuit - could be handled, but this would require to wrap the - whole pytest run, and adjust the report etc. - """ - ret = super(PytestPdbWrapper, self).do_quit(arg) - - if cls._recursive_debug == 0: - outcomes.exit("Quitting debugger") - - return ret - - do_q = do_quit - do_exit = do_quit - - def setup(self, f, tb): - """Suspend on setup(). - - Needed after do_continue resumed, and entering another - breakpoint again. - """ - ret = super(PytestPdbWrapper, self).setup(f, tb) - if not ret and self._continued: - # pdb.setup() returns True if the command wants to exit - # from the interaction: do not suspend capturing then. - if self._pytest_capman: - self._pytest_capman.suspend_global_capture(in_=True) - return ret - - def get_stack(self, f, t): - stack, i = super(PytestPdbWrapper, self).get_stack(f, t) - if f is None: - # Find last non-hidden frame. - i = max(0, len(stack) - 1) - while i and stack[i][0].f_locals.get("__tracebackhide__", False): - i -= 1 - return stack, i - - return PytestPdbWrapper - - @classmethod - def _init_pdb(cls, method, *args, **kwargs): - """ Initialize PDB debugging, dropping any IO capturing. """ - import _pytest.config - - if cls._pluginmanager is not None: - capman = cls._pluginmanager.getplugin("capturemanager") + tw.sep( + ">", + "PDB continue (IO-capturing resumed for %s)" + % capturing, + ) + capman.resume() + else: + tw.sep(">", "PDB continue") + cls._pluginmanager.hook.pytest_leave_pdb(config=cls._config, pdb=self) + self._continued = True + return ret + + do_c = do_cont = do_continue + + def do_quit(self, arg): + """Raise Exit outcome when quit command is used in pdb. + + This is a bit of a hack - it would be better if BdbQuit + could be handled, but this would require to wrap the + whole pytest run, and adjust the report etc. + """ + ret = super(PytestPdbWrapper, self).do_quit(arg) + + if cls._recursive_debug == 0: + outcomes.exit("Quitting debugger") + + return ret + + do_q = do_quit + do_exit = do_quit + + def setup(self, f, tb): + """Suspend on setup(). + + Needed after do_continue resumed, and entering another + breakpoint again. + """ + ret = super(PytestPdbWrapper, self).setup(f, tb) + if not ret and self._continued: + # pdb.setup() returns True if the command wants to exit + # from the interaction: do not suspend capturing then. + if self._pytest_capman: + self._pytest_capman.suspend_global_capture(in_=True) + return ret + + def get_stack(self, f, t): + stack, i = super(PytestPdbWrapper, self).get_stack(f, t) + if f is None: + # Find last non-hidden frame. + i = max(0, len(stack) - 1) + while i and stack[i][0].f_locals.get("__tracebackhide__", False): + i -= 1 + return stack, i + + return PytestPdbWrapper + + @classmethod + def _init_pdb(cls, method, *args, **kwargs): + """ Initialize PDB debugging, dropping any IO capturing. """ + import _pytest.config + + if cls._pluginmanager is not None: + capman = cls._pluginmanager.getplugin("capturemanager") else: - capman = None - if capman: - capman.suspend(in_=True) - - if cls._config: - tw = _pytest.config.create_terminal_writer(cls._config) - tw.line() - - if cls._recursive_debug == 0: - # Handle header similar to pdb.set_trace in py37+. - header = kwargs.pop("header", None) - if header is not None: - tw.sep(">", header) - else: - capturing = cls._is_capturing(capman) - if capturing == "global": - tw.sep(">", "PDB %s (IO-capturing turned off)" % (method,)) - elif capturing: - tw.sep( - ">", - "PDB %s (IO-capturing turned off for %s)" - % (method, capturing), - ) - else: - tw.sep(">", "PDB %s" % (method,)) - - _pdb = cls._import_pdb_cls(capman)(**kwargs) - - if cls._pluginmanager: - cls._pluginmanager.hook.pytest_enter_pdb(config=cls._config, pdb=_pdb) - return _pdb - - @classmethod - def set_trace(cls, *args, **kwargs): - """Invoke debugging via ``Pdb.set_trace``, dropping any IO capturing.""" - tty() - frame = sys._getframe().f_back - _pdb = cls._init_pdb("set_trace", *args, **kwargs) - _pdb.set_trace(frame) - - + capman = None + if capman: + capman.suspend(in_=True) + + if cls._config: + tw = _pytest.config.create_terminal_writer(cls._config) + tw.line() + + if cls._recursive_debug == 0: + # Handle header similar to pdb.set_trace in py37+. + header = kwargs.pop("header", None) + if header is not None: + tw.sep(">", header) + else: + capturing = cls._is_capturing(capman) + if capturing == "global": + tw.sep(">", "PDB %s (IO-capturing turned off)" % (method,)) + elif capturing: + tw.sep( + ">", + "PDB %s (IO-capturing turned off for %s)" + % (method, capturing), + ) + else: + tw.sep(">", "PDB %s" % (method,)) + + _pdb = cls._import_pdb_cls(capman)(**kwargs) + + if cls._pluginmanager: + cls._pluginmanager.hook.pytest_enter_pdb(config=cls._config, pdb=_pdb) + return _pdb + + @classmethod + def set_trace(cls, *args, **kwargs): + """Invoke debugging via ``Pdb.set_trace``, dropping any IO capturing.""" + tty() + frame = sys._getframe().f_back + _pdb = cls._init_pdb("set_trace", *args, **kwargs) + _pdb.set_trace(frame) + + class PdbInvoke(object): def pytest_exception_interact(self, node, call, report): capman = node.config.pluginmanager.getplugin("capturemanager") @@ -316,15 +316,15 @@ class PdbTrace(object): def _test_pytest_function(pyfuncitem): - _pdb = pytestPDB._init_pdb("runcall") + _pdb = pytestPDB._init_pdb("runcall") testfunction = pyfuncitem.obj - pyfuncitem.obj = _pdb.runcall - if "func" in pyfuncitem._fixtureinfo.argnames: # pragma: no branch - raise ValueError("--trace can't be used with a fixture named func!") - pyfuncitem.funcargs["func"] = testfunction - new_list = list(pyfuncitem._fixtureinfo.argnames) - new_list.append("func") - pyfuncitem._fixtureinfo.argnames = tuple(new_list) + pyfuncitem.obj = _pdb.runcall + if "func" in pyfuncitem._fixtureinfo.argnames: # pragma: no branch + raise ValueError("--trace can't be used with a fixture named func!") + pyfuncitem.funcargs["func"] = testfunction + new_list = list(pyfuncitem._fixtureinfo.argnames) + new_list.append("func") + pyfuncitem._fixtureinfo.argnames = tuple(new_list) def _enter_pdb(node, excinfo, rep): @@ -352,7 +352,7 @@ def _enter_pdb(node, excinfo, rep): tw.sep(">", "entering PDB") tb = _postmortem_traceback(excinfo) rep._pdbshown = True - post_mortem(tb) + post_mortem(tb) return rep @@ -366,8 +366,8 @@ def _postmortem_traceback(excinfo): def post_mortem(t): - p = pytestPDB._init_pdb("post_mortem") + p = pytestPDB._init_pdb("post_mortem") p.reset() p.interaction(None, t) - if p.quitting: - outcomes.exit("Quitting debugger") + if p.quitting: + outcomes.exit("Quitting debugger") diff --git a/contrib/python/pytest/py2/_pytest/deprecated.py b/contrib/python/pytest/py2/_pytest/deprecated.py index 12394aca3f..13573dc86e 100644 --- a/contrib/python/pytest/py2/_pytest/deprecated.py +++ b/contrib/python/pytest/py2/_pytest/deprecated.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ This module contains deprecation messages and bits of code used elsewhere in the codebase that is planned to be removed in the next pytest release. @@ -17,80 +17,80 @@ from _pytest.warning_types import PytestDeprecationWarning from _pytest.warning_types import RemovedInPytest4Warning from _pytest.warning_types import UnformattedWarning -YIELD_TESTS = "yield tests were removed in pytest 4.0 - {name} will be ignored" +YIELD_TESTS = "yield tests were removed in pytest 4.0 - {name} will be ignored" -FIXTURE_FUNCTION_CALL = ( - 'Fixture "{name}" called directly. Fixtures are not meant to be called directly,\n' - "but are created automatically when test functions request them as parameters.\n" - "See https://docs.pytest.org/en/latest/fixture.html for more information about fixtures, and\n" - "https://docs.pytest.org/en/latest/deprecations.html#calling-fixtures-directly about how to update your code." +FIXTURE_FUNCTION_CALL = ( + 'Fixture "{name}" called directly. Fixtures are not meant to be called directly,\n' + "but are created automatically when test functions request them as parameters.\n" + "See https://docs.pytest.org/en/latest/fixture.html for more information about fixtures, and\n" + "https://docs.pytest.org/en/latest/deprecations.html#calling-fixtures-directly about how to update your code." ) FIXTURE_NAMED_REQUEST = PytestDeprecationWarning( "'request' is a reserved name for fixtures and will raise an error in future versions" ) -CFG_PYTEST_SECTION = "[pytest] section in {filename} files is no longer supported, change to [tool:pytest] instead." +CFG_PYTEST_SECTION = "[pytest] section in {filename} files is no longer supported, change to [tool:pytest] instead." GETFUNCARGVALUE = RemovedInPytest4Warning( "getfuncargvalue is deprecated, use getfixturevalue" ) -RAISES_MESSAGE_PARAMETER = PytestDeprecationWarning( - "The 'message' parameter is deprecated.\n" - "(did you mean to use `match='some regex'` to check the exception message?)\n" - "Please see:\n" - " https://docs.pytest.org/en/4.6-maintenance/deprecations.html#message-parameter-of-pytest-raises" +RAISES_MESSAGE_PARAMETER = PytestDeprecationWarning( + "The 'message' parameter is deprecated.\n" + "(did you mean to use `match='some regex'` to check the exception message?)\n" + "Please see:\n" + " https://docs.pytest.org/en/4.6-maintenance/deprecations.html#message-parameter-of-pytest-raises" ) -RESULT_LOG = PytestDeprecationWarning( - "--result-log is deprecated and scheduled for removal in pytest 5.0.\n" - "See https://docs.pytest.org/en/latest/deprecations.html#result-log-result-log for more information." +RESULT_LOG = PytestDeprecationWarning( + "--result-log is deprecated and scheduled for removal in pytest 5.0.\n" + "See https://docs.pytest.org/en/latest/deprecations.html#result-log-result-log for more information." ) -RAISES_EXEC = PytestDeprecationWarning( - "raises(..., 'code(as_a_string)') is deprecated, use the context manager form or use `exec()` directly\n\n" - "See https://docs.pytest.org/en/latest/deprecations.html#raises-warns-exec" +RAISES_EXEC = PytestDeprecationWarning( + "raises(..., 'code(as_a_string)') is deprecated, use the context manager form or use `exec()` directly\n\n" + "See https://docs.pytest.org/en/latest/deprecations.html#raises-warns-exec" ) -WARNS_EXEC = PytestDeprecationWarning( - "warns(..., 'code(as_a_string)') is deprecated, use the context manager form or use `exec()` directly.\n\n" - "See https://docs.pytest.org/en/latest/deprecations.html#raises-warns-exec" +WARNS_EXEC = PytestDeprecationWarning( + "warns(..., 'code(as_a_string)') is deprecated, use the context manager form or use `exec()` directly.\n\n" + "See https://docs.pytest.org/en/latest/deprecations.html#raises-warns-exec" ) -PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST = ( - "Defining 'pytest_plugins' in a non-top-level conftest is no longer supported " - "because it affects the entire directory tree in a non-explicit way.\n" - " {}\n" - "Please move it to a top level conftest file at the rootdir:\n" - " {}\n" - "For more information, visit:\n" - " https://docs.pytest.org/en/latest/deprecations.html#pytest-plugins-in-non-top-level-conftest-files" +PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST = ( + "Defining 'pytest_plugins' in a non-top-level conftest is no longer supported " + "because it affects the entire directory tree in a non-explicit way.\n" + " {}\n" + "Please move it to a top level conftest file at the rootdir:\n" + " {}\n" + "For more information, visit:\n" + " https://docs.pytest.org/en/latest/deprecations.html#pytest-plugins-in-non-top-level-conftest-files" ) -PYTEST_CONFIG_GLOBAL = PytestDeprecationWarning( - "the `pytest.config` global is deprecated. Please use `request.config` " - "or `pytest_configure` (if you're a pytest plugin) instead." +PYTEST_CONFIG_GLOBAL = PytestDeprecationWarning( + "the `pytest.config` global is deprecated. Please use `request.config` " + "or `pytest_configure` (if you're a pytest plugin) instead." ) -PYTEST_ENSURETEMP = RemovedInPytest4Warning( - "pytest/tmpdir_factory.ensuretemp is deprecated, \n" - "please use the tmp_path fixture or tmp_path_factory.mktemp" +PYTEST_ENSURETEMP = RemovedInPytest4Warning( + "pytest/tmpdir_factory.ensuretemp is deprecated, \n" + "please use the tmp_path fixture or tmp_path_factory.mktemp" ) -PYTEST_LOGWARNING = PytestDeprecationWarning( - "pytest_logwarning is deprecated, no longer being called, and will be removed soon\n" - "please use pytest_warning_captured instead" +PYTEST_LOGWARNING = PytestDeprecationWarning( + "pytest_logwarning is deprecated, no longer being called, and will be removed soon\n" + "please use pytest_warning_captured instead" ) -PYTEST_WARNS_UNKNOWN_KWARGS = UnformattedWarning( - PytestDeprecationWarning, - "pytest.warns() got unexpected keyword arguments: {args!r}.\n" - "This will be an error in future versions.", +PYTEST_WARNS_UNKNOWN_KWARGS = UnformattedWarning( + PytestDeprecationWarning, + "pytest.warns() got unexpected keyword arguments: {args!r}.\n" + "This will be an error in future versions.", ) -PYTEST_PARAM_UNKNOWN_KWARGS = UnformattedWarning( - PytestDeprecationWarning, - "pytest.param() got unexpected keyword arguments: {args!r}.\n" - "This will be an error in future versions.", +PYTEST_PARAM_UNKNOWN_KWARGS = UnformattedWarning( + PytestDeprecationWarning, + "pytest.param() got unexpected keyword arguments: {args!r}.\n" + "This will be an error in future versions.", ) diff --git a/contrib/python/pytest/py2/_pytest/doctest.py b/contrib/python/pytest/py2/_pytest/doctest.py index 659d24aeeb..ade99d120f 100644 --- a/contrib/python/pytest/py2/_pytest/doctest.py +++ b/contrib/python/pytest/py2/_pytest/doctest.py @@ -1,24 +1,24 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ discover and run doctests in modules and test files.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function -import inspect +import inspect import platform import sys import traceback -import warnings -from contextlib import contextmanager +import warnings +from contextlib import contextmanager import pytest from _pytest._code.code import ExceptionInfo from _pytest._code.code import ReprFileLocation from _pytest._code.code import TerminalRepr -from _pytest.compat import safe_getattr +from _pytest.compat import safe_getattr from _pytest.fixtures import FixtureRequest -from _pytest.outcomes import Skipped -from _pytest.warning_types import PytestWarning +from _pytest.outcomes import Skipped +from _pytest.warning_types import PytestWarning DOCTEST_REPORT_CHOICE_NONE = "none" DOCTEST_REPORT_CHOICE_CDIFF = "cdiff" @@ -157,8 +157,8 @@ def _init_runner_class(): raise failure def report_unexpected_exception(self, out, test, example, exc_info): - if isinstance(exc_info[1], Skipped): - raise exc_info[1] + if isinstance(exc_info[1], Skipped): + raise exc_info[1] failure = doctest.UnexpectedException(test, example, exc_info) if self.continue_on_failure: out.append(failure) @@ -354,69 +354,69 @@ def _check_all_skipped(test): pytest.skip("all tests skipped by +SKIP option") -def _is_mocked(obj): - """ - returns if a object is possibly a mock object by checking the existence of a highly improbable attribute - """ - return ( - safe_getattr(obj, "pytest_mock_example_attribute_that_shouldnt_exist", None) - is not None - ) - - -@contextmanager -def _patch_unwrap_mock_aware(): - """ - contextmanager which replaces ``inspect.unwrap`` with a version - that's aware of mock objects and doesn't recurse on them - """ - real_unwrap = getattr(inspect, "unwrap", None) - if real_unwrap is None: - yield - else: - - def _mock_aware_unwrap(obj, stop=None): - try: - if stop is None or stop is _is_mocked: - return real_unwrap(obj, stop=_is_mocked) - return real_unwrap(obj, stop=lambda obj: _is_mocked(obj) or stop(obj)) - except Exception as e: - warnings.warn( - "Got %r when unwrapping %r. This is usually caused " - "by a violation of Python's object protocol; see e.g. " - "https://github.com/pytest-dev/pytest/issues/5080" % (e, obj), - PytestWarning, - ) - raise - - inspect.unwrap = _mock_aware_unwrap - try: - yield - finally: - inspect.unwrap = real_unwrap - - +def _is_mocked(obj): + """ + returns if a object is possibly a mock object by checking the existence of a highly improbable attribute + """ + return ( + safe_getattr(obj, "pytest_mock_example_attribute_that_shouldnt_exist", None) + is not None + ) + + +@contextmanager +def _patch_unwrap_mock_aware(): + """ + contextmanager which replaces ``inspect.unwrap`` with a version + that's aware of mock objects and doesn't recurse on them + """ + real_unwrap = getattr(inspect, "unwrap", None) + if real_unwrap is None: + yield + else: + + def _mock_aware_unwrap(obj, stop=None): + try: + if stop is None or stop is _is_mocked: + return real_unwrap(obj, stop=_is_mocked) + return real_unwrap(obj, stop=lambda obj: _is_mocked(obj) or stop(obj)) + except Exception as e: + warnings.warn( + "Got %r when unwrapping %r. This is usually caused " + "by a violation of Python's object protocol; see e.g. " + "https://github.com/pytest-dev/pytest/issues/5080" % (e, obj), + PytestWarning, + ) + raise + + inspect.unwrap = _mock_aware_unwrap + try: + yield + finally: + inspect.unwrap = real_unwrap + + class DoctestModule(pytest.Module): def collect(self): import doctest - class MockAwareDocTestFinder(doctest.DocTestFinder): - """ - a hackish doctest finder that overrides stdlib internals to fix a stdlib bug - - https://github.com/pytest-dev/pytest/issues/3456 - https://bugs.python.org/issue25532 - """ - - def _find(self, tests, obj, name, module, source_lines, globs, seen): - if _is_mocked(obj): - return - with _patch_unwrap_mock_aware(): - - doctest.DocTestFinder._find( - self, tests, obj, name, module, source_lines, globs, seen - ) - + class MockAwareDocTestFinder(doctest.DocTestFinder): + """ + a hackish doctest finder that overrides stdlib internals to fix a stdlib bug + + https://github.com/pytest-dev/pytest/issues/3456 + https://bugs.python.org/issue25532 + """ + + def _find(self, tests, obj, name, module, source_lines, globs, seen): + if _is_mocked(obj): + return + with _patch_unwrap_mock_aware(): + + doctest.DocTestFinder._find( + self, tests, obj, name, module, source_lines, globs, seen + ) + if self.fspath.basename == "conftest.py": module = self.config.pluginmanager._importconftest(self.fspath) else: @@ -428,7 +428,7 @@ class DoctestModule(pytest.Module): else: raise # uses internal doctest module parsing mechanism - finder = MockAwareDocTestFinder() + finder = MockAwareDocTestFinder() optionflags = get_optionflags(self) runner = _get_runner( verbose=0, diff --git a/contrib/python/pytest/py2/_pytest/fixtures.py b/contrib/python/pytest/py2/_pytest/fixtures.py index 280a48608b..244b0ef4f8 100644 --- a/contrib/python/pytest/py2/_pytest/fixtures.py +++ b/contrib/python/pytest/py2/_pytest/fixtures.py @@ -1,11 +1,11 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- from __future__ import absolute_import from __future__ import division from __future__ import print_function import functools import inspect -import itertools +import itertools import sys import warnings from collections import defaultdict @@ -18,7 +18,7 @@ import six import _pytest from _pytest import nodes -from _pytest._code.code import FormattedExcinfo +from _pytest._code.code import FormattedExcinfo from _pytest._code.code import TerminalRepr from _pytest.compat import _format_args from _pytest.compat import _PytestWrapper @@ -308,8 +308,8 @@ class FuncFixtureInfo(object): # fixture names specified via usefixtures and via autouse=True in fixture # definitions. initialnames = attr.ib(type=tuple) - names_closure = attr.ib() # List[str] - name2fixturedefs = attr.ib() # List[str, List[FixtureDef]] + names_closure = attr.ib() # List[str] + name2fixturedefs = attr.ib() # List[str, List[FixtureDef]] def prune_dependency_tree(self): """Recompute names_closure from initialnames and name2fixturedefs @@ -567,7 +567,7 @@ class FixtureRequest(FuncargnamesCompatAttr): ) fail(msg, pytrace=False) else: - param_index = funcitem.callspec.indices[argname] + param_index = funcitem.callspec.indices[argname] # if a parametrize invocation set a scope it will override # the static scope defined with the fixture function paramscopenum = funcitem.callspec._arg2scopenum.get(argname) @@ -586,14 +586,14 @@ class FixtureRequest(FuncargnamesCompatAttr): # call the fixture function fixturedef.execute(request=subrequest) finally: - self._schedule_finalizers(fixturedef, subrequest) - - def _schedule_finalizers(self, fixturedef, subrequest): - # if fixture function failed it might have registered finalizers - self.session._setupstate.addfinalizer( - functools.partial(fixturedef.finish, request=subrequest), subrequest.node - ) - + self._schedule_finalizers(fixturedef, subrequest) + + def _schedule_finalizers(self, fixturedef, subrequest): + # if fixture function failed it might have registered finalizers + self.session._setupstate.addfinalizer( + functools.partial(fixturedef.finish, request=subrequest), subrequest.node + ) + def _check_scope(self, argname, invoking_scope, requested_scope): if argname == "request": return @@ -615,7 +615,7 @@ class FixtureRequest(FuncargnamesCompatAttr): fs, lineno = getfslineno(factory) p = self._pyfuncitem.session.fspath.bestrelpath(fs) args = _format_args(factory) - lines.append("%s:%d: def %s%s" % (p, lineno + 1, factory.__name__, args)) + lines.append("%s:%d: def %s%s" % (p, lineno + 1, factory.__name__, args)) return lines def _getscopeitem(self, scope): @@ -662,15 +662,15 @@ class SubRequest(FixtureRequest): def addfinalizer(self, finalizer): self._fixturedef.addfinalizer(finalizer) - def _schedule_finalizers(self, fixturedef, subrequest): - # if the executing fixturedef was not explicitly requested in the argument list (via - # getfixturevalue inside the fixture call) then ensure this fixture def will be finished - # first - if fixturedef.argname not in self.funcargnames: - fixturedef.addfinalizer( - functools.partial(self._fixturedef.finish, request=self) - ) - super(SubRequest, self)._schedule_finalizers(fixturedef, subrequest) + def _schedule_finalizers(self, fixturedef, subrequest): + # if the executing fixturedef was not explicitly requested in the argument list (via + # getfixturevalue inside the fixture call) then ensure this fixture def will be finished + # first + if fixturedef.argname not in self.funcargnames: + fixturedef.addfinalizer( + functools.partial(self._fixturedef.finish, request=self) + ) + super(SubRequest, self)._schedule_finalizers(fixturedef, subrequest) scopes = "session package module class function".split() @@ -854,8 +854,8 @@ class FixtureDef(object): exceptions.append(sys.exc_info()) if exceptions: e = exceptions[0] - # Ensure to not keep frame references through traceback. - del exceptions + # Ensure to not keep frame references through traceback. + del exceptions six.reraise(*e) finally: hook = self._fixturemanager.session.gethookproxy(request.node.fspath) @@ -949,17 +949,17 @@ def _ensure_immutable_ids(ids): return tuple(ids) -def wrap_function_to_error_out_if_called_directly(function, fixture_marker): - """Wrap the given fixture function so we can raise an error about it being called directly, - instead of used as an argument in a test function. +def wrap_function_to_error_out_if_called_directly(function, fixture_marker): + """Wrap the given fixture function so we can raise an error about it being called directly, + instead of used as an argument in a test function. """ - message = FIXTURE_FUNCTION_CALL.format( + message = FIXTURE_FUNCTION_CALL.format( name=fixture_marker.name or function.__name__ ) - @six.wraps(function) - def result(*args, **kwargs): - fail(message, pytrace=False) + @six.wraps(function) + def result(*args, **kwargs): + fail(message, pytrace=False) # keep reference to the original function in our own custom attribute so we don't unwrap # further than this point and lose useful wrappings like @mock.patch (#3774) @@ -985,7 +985,7 @@ class FixtureFunctionMarker(object): "fixture is being applied more than once to the same function" ) - function = wrap_function_to_error_out_if_called_directly(function, self) + function = wrap_function_to_error_out_if_called_directly(function, self) name = self.name or function.__name__ if name == "request": @@ -1022,7 +1022,7 @@ def fixture(scope="function", params=None, autouse=False, ids=None, name=None): :arg params: an optional list of parameters which will cause multiple invocations of the fixture function and all of the tests using it. - The current parameter is available in ``request.param``. + The current parameter is available in ``request.param``. :arg autouse: if True, the fixture func is activated for all tests that can see it. If False (the default) then an explicit @@ -1067,22 +1067,22 @@ def pytestconfig(request): Example:: def test_foo(pytestconfig): - if pytestconfig.getoption("verbose") > 0: + if pytestconfig.getoption("verbose") > 0: ... """ return request.config -def pytest_addoption(parser): - parser.addini( - "usefixtures", - type="args", - default=[], - help="list of default fixtures to be used with this project", - ) - - +def pytest_addoption(parser): + parser.addini( + "usefixtures", + type="args", + default=[], + help="list of default fixtures to be used with this project", + ) + + class FixtureManager(object): """ pytest fixtures definitions and information is stored and managed @@ -1127,40 +1127,40 @@ class FixtureManager(object): self._nodeid_and_autousenames = [("", self.config.getini("usefixtures"))] session.config.pluginmanager.register(self, "funcmanage") - def _get_direct_parametrize_args(self, node): - """This function returns all the direct parametrization - arguments of a node, so we don't mistake them for fixtures - - Check https://github.com/pytest-dev/pytest/issues/5036 - - This things are done later as well when dealing with parametrization - so this could be improved - """ - from _pytest.mark import ParameterSet - - parametrize_argnames = [] - for marker in node.iter_markers(name="parametrize"): - if not marker.kwargs.get("indirect", False): - p_argnames, _ = ParameterSet._parse_parametrize_args( - *marker.args, **marker.kwargs - ) - parametrize_argnames.extend(p_argnames) - - return parametrize_argnames - + def _get_direct_parametrize_args(self, node): + """This function returns all the direct parametrization + arguments of a node, so we don't mistake them for fixtures + + Check https://github.com/pytest-dev/pytest/issues/5036 + + This things are done later as well when dealing with parametrization + so this could be improved + """ + from _pytest.mark import ParameterSet + + parametrize_argnames = [] + for marker in node.iter_markers(name="parametrize"): + if not marker.kwargs.get("indirect", False): + p_argnames, _ = ParameterSet._parse_parametrize_args( + *marker.args, **marker.kwargs + ) + parametrize_argnames.extend(p_argnames) + + return parametrize_argnames + def getfixtureinfo(self, node, func, cls, funcargs=True): if funcargs and not getattr(node, "nofuncargs", False): argnames = getfuncargnames(func, cls=cls) else: argnames = () - - usefixtures = itertools.chain.from_iterable( + + usefixtures = itertools.chain.from_iterable( mark.args for mark in node.iter_markers(name="usefixtures") ) initialnames = tuple(usefixtures) + argnames fm = node.session._fixturemanager initialnames, names_closure, arg2fixturedefs = fm.getfixtureclosure( - initialnames, node, ignore_args=self._get_direct_parametrize_args(node) + initialnames, node, ignore_args=self._get_direct_parametrize_args(node) ) return FuncFixtureInfo(argnames, initialnames, names_closure, arg2fixturedefs) @@ -1194,7 +1194,7 @@ class FixtureManager(object): autousenames.extend(basenames) return autousenames - def getfixtureclosure(self, fixturenames, parentnode, ignore_args=()): + def getfixtureclosure(self, fixturenames, parentnode, ignore_args=()): # collect the closure of all fixtures , starting with the given # fixturenames as the initial set. As we have to visit all # factory definitions anyway, we also return an arg2fixturedefs @@ -1222,8 +1222,8 @@ class FixtureManager(object): while lastlen != len(fixturenames_closure): lastlen = len(fixturenames_closure) for argname in fixturenames_closure: - if argname in ignore_args: - continue + if argname in ignore_args: + continue if argname in arg2fixturedefs: continue fixturedefs = self.getfixturedefs(argname, parentid) @@ -1248,19 +1248,19 @@ class FixtureManager(object): if faclist: fixturedef = faclist[-1] if fixturedef.params is not None: - markers = list(metafunc.definition.iter_markers("parametrize")) - for parametrize_mark in markers: - if "argnames" in parametrize_mark.kwargs: - argnames = parametrize_mark.kwargs["argnames"] - else: - argnames = parametrize_mark.args[0] - - if not isinstance(argnames, (tuple, list)): - argnames = [ - x.strip() for x in argnames.split(",") if x.strip() - ] - if argname in argnames: - break + markers = list(metafunc.definition.iter_markers("parametrize")) + for parametrize_mark in markers: + if "argnames" in parametrize_mark.kwargs: + argnames = parametrize_mark.kwargs["argnames"] + else: + argnames = parametrize_mark.args[0] + + if not isinstance(argnames, (tuple, list)): + argnames = [ + x.strip() for x in argnames.split(",") if x.strip() + ] + if argname in argnames: + break else: metafunc.parametrize( argname, @@ -1292,14 +1292,14 @@ class FixtureManager(object): # access below can raise. safe_getatt() ignores such exceptions. obj = safe_getattr(holderobj, name, None) marker = getfixturemarker(obj) - if not isinstance(marker, FixtureFunctionMarker): + if not isinstance(marker, FixtureFunctionMarker): # magic globals with __getattr__ might have got us a wrong # fixture attribute continue - if marker.name: - name = marker.name - + if marker.name: + name = marker.name + # during fixture definition we wrap the original fixture function # to issue a warning if called directly, so here we unwrap it in order to not emit the warning # when pytest itself calls the fixture function diff --git a/contrib/python/pytest/py2/_pytest/freeze_support.py b/contrib/python/pytest/py2/_pytest/freeze_support.py index aeeec2a56b..acbf4db251 100644 --- a/contrib/python/pytest/py2/_pytest/freeze_support.py +++ b/contrib/python/pytest/py2/_pytest/freeze_support.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ Provides a function to report all internal modules for using freezing tools pytest diff --git a/contrib/python/pytest/py2/_pytest/helpconfig.py b/contrib/python/pytest/py2/_pytest/helpconfig.py index 5681160160..be4bc2c0bc 100644 --- a/contrib/python/pytest/py2/_pytest/helpconfig.py +++ b/contrib/python/pytest/py2/_pytest/helpconfig.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ version info, help messages, tracing configuration. """ from __future__ import absolute_import from __future__ import division @@ -61,7 +61,7 @@ def pytest_addoption(parser): dest="plugins", default=[], metavar="name", - help="early-load given plugin module name or entry point (multi-allowed). " + help="early-load given plugin module name or entry point (multi-allowed). " "To avoid loading of plugins, use the `no:` prefix, e.g. " "`no:doctest`.", ) @@ -119,20 +119,20 @@ def pytest_cmdline_parse(): config.add_cleanup(unset_tracing) -def showversion(config): - p = py.path.local(pytest.__file__) - sys.stderr.write( - "This is pytest version %s, imported from %s\n" % (pytest.__version__, p) - ) - plugininfo = getpluginversioninfo(config) - if plugininfo: - for line in plugininfo: - sys.stderr.write(line + "\n") - - +def showversion(config): + p = py.path.local(pytest.__file__) + sys.stderr.write( + "This is pytest version %s, imported from %s\n" % (pytest.__version__, p) + ) + plugininfo = getpluginversioninfo(config) + if plugininfo: + for line in plugininfo: + sys.stderr.write(line + "\n") + + def pytest_cmdline_main(config): if config.option.version: - showversion(config) + showversion(config) return 0 elif config.option.help: config._do_configure() @@ -142,8 +142,8 @@ def pytest_cmdline_main(config): def showhelp(config): - import textwrap - + import textwrap + reporter = config.pluginmanager.get_plugin("terminalreporter") tw = reporter._tw tw.write(config._parser.optparser.format_help()) @@ -153,38 +153,38 @@ def showhelp(config): ) tw.line() - columns = tw.fullwidth # costly call - indent_len = 24 # based on argparse's max_help_position=24 - indent = " " * indent_len + columns = tw.fullwidth # costly call + indent_len = 24 # based on argparse's max_help_position=24 + indent = " " * indent_len for name in config._parser._ininames: help, type, default = config._parser._inidict[name] if type is None: type = "string" - spec = "%s (%s):" % (name, type) - tw.write(" %s" % spec) - spec_len = len(spec) - if spec_len > (indent_len - 3): - # Display help starting at a new line. - tw.line() - helplines = textwrap.wrap( - help, - columns, - initial_indent=indent, - subsequent_indent=indent, - break_on_hyphens=False, - ) - - for line in helplines: - tw.line(line) - else: - # Display help starting after the spec, following lines indented. - tw.write(" " * (indent_len - spec_len - 2)) - wrapped = textwrap.wrap(help, columns - indent_len, break_on_hyphens=False) - - tw.line(wrapped[0]) - for line in wrapped[1:]: - tw.line(indent + line) - + spec = "%s (%s):" % (name, type) + tw.write(" %s" % spec) + spec_len = len(spec) + if spec_len > (indent_len - 3): + # Display help starting at a new line. + tw.line() + helplines = textwrap.wrap( + help, + columns, + initial_indent=indent, + subsequent_indent=indent, + break_on_hyphens=False, + ) + + for line in helplines: + tw.line(line) + else: + # Display help starting after the spec, following lines indented. + tw.write(" " * (indent_len - spec_len - 2)) + wrapped = textwrap.wrap(help, columns - indent_len, break_on_hyphens=False) + + tw.line(wrapped[0]) + for line in wrapped[1:]: + tw.line(indent + line) + tw.line() tw.line("environment variables:") vars = [ diff --git a/contrib/python/pytest/py2/_pytest/hookspec.py b/contrib/python/pytest/py2/_pytest/hookspec.py index 7ab6154b17..d98652a199 100644 --- a/contrib/python/pytest/py2/_pytest/hookspec.py +++ b/contrib/python/pytest/py2/_pytest/hookspec.py @@ -1,8 +1,8 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ hook specifications for pytest plugins, invoked from main.py and builtin plugins. """ from pluggy import HookspecMarker -from _pytest.deprecated import PYTEST_LOGWARNING +from _pytest.deprecated import PYTEST_LOGWARNING hookspec = HookspecMarker("pytest") @@ -100,8 +100,8 @@ def pytest_cmdline_parse(pluginmanager, args): Stops at first non-None result, see :ref:`firstresult` .. note:: - This hook will only be called for plugin classes passed to the ``plugins`` arg when using `pytest.main`_ to - perform an in-process test run. + This hook will only be called for plugin classes passed to the ``plugins`` arg when using `pytest.main`_ to + perform an in-process test run. :param _pytest.config.PytestPluginManager pluginmanager: pytest plugin manager :param list[str] args: list of arguments passed on the command line @@ -189,7 +189,7 @@ def pytest_ignore_collect(path, config): Stops at first non-None result, see :ref:`firstresult` - :param path: a :py:class:`py.path.local` - the path to analyze + :param path: a :py:class:`py.path.local` - the path to analyze :param _pytest.config.Config config: pytest config object """ @@ -200,7 +200,7 @@ def pytest_collect_directory(path, parent): Stops at first non-None result, see :ref:`firstresult` - :param path: a :py:class:`py.path.local` - the path to analyze + :param path: a :py:class:`py.path.local` - the path to analyze """ @@ -208,7 +208,7 @@ def pytest_collect_file(path, parent): """ return collection Node or None for the given path. Any new node needs to have the specified ``parent`` as a parent. - :param path: a :py:class:`py.path.local` - the path to collect + :param path: a :py:class:`py.path.local` - the path to collect """ @@ -228,7 +228,7 @@ def pytest_collectreport(report): def pytest_deselected(items): - """ called for test items deselected, e.g. by keyword. """ + """ called for test items deselected, e.g. by keyword. """ @hookspec(firstresult=True) @@ -250,12 +250,12 @@ def pytest_pycollect_makemodule(path, parent): The pytest_collect_file hook needs to be used if you want to create test modules for files that do not match as a test module. - Stops at first non-None result, see :ref:`firstresult` - - :param path: a :py:class:`py.path.local` - the path of module to collect - """ + Stops at first non-None result, see :ref:`firstresult` + :param path: a :py:class:`py.path.local` - the path of module to collect + """ + @hookspec(firstresult=True) def pytest_pycollect_makeitem(collector, name, obj): """ return custom item/collector for a python object in a module, or None. @@ -380,41 +380,41 @@ def pytest_runtest_logreport(report): the respective phase of executing a test. """ -@hookspec(firstresult=True) -def pytest_report_to_serializable(config, report): - """ - .. warning:: - This hook is experimental and subject to change between pytest releases, even - bug fixes. - - The intent is for this to be used by plugins maintained by the core-devs, such - as ``pytest-xdist``, ``pytest-subtests``, and as a replacement for the internal - 'resultlog' plugin. - - In the future it might become part of the public hook API. - - Serializes the given report object into a data structure suitable for sending - over the wire, e.g. converted to JSON. - """ - - -@hookspec(firstresult=True) -def pytest_report_from_serializable(config, data): - """ - .. warning:: - This hook is experimental and subject to change between pytest releases, even - bug fixes. - - The intent is for this to be used by plugins maintained by the core-devs, such - as ``pytest-xdist``, ``pytest-subtests``, and as a replacement for the internal - 'resultlog' plugin. - - In the future it might become part of the public hook API. - - Restores a report object previously serialized with pytest_report_to_serializable(). - """ - - +@hookspec(firstresult=True) +def pytest_report_to_serializable(config, report): + """ + .. warning:: + This hook is experimental and subject to change between pytest releases, even + bug fixes. + + The intent is for this to be used by plugins maintained by the core-devs, such + as ``pytest-xdist``, ``pytest-subtests``, and as a replacement for the internal + 'resultlog' plugin. + + In the future it might become part of the public hook API. + + Serializes the given report object into a data structure suitable for sending + over the wire, e.g. converted to JSON. + """ + + +@hookspec(firstresult=True) +def pytest_report_from_serializable(config, data): + """ + .. warning:: + This hook is experimental and subject to change between pytest releases, even + bug fixes. + + The intent is for this to be used by plugins maintained by the core-devs, such + as ``pytest-xdist``, ``pytest-subtests``, and as a replacement for the internal + 'resultlog' plugin. + + In the future it might become part of the public hook API. + + Restores a report object previously serialized with pytest_report_to_serializable(). + """ + + # ------------------------------------------------------------------------- # Fixture related hooks # ------------------------------------------------------------------------- @@ -520,27 +520,27 @@ def pytest_report_collectionfinish(config, startdir, items): @hookspec(firstresult=True) -def pytest_report_teststatus(report, config): +def pytest_report_teststatus(report, config): """ return result-category, shortletter and verbose word for reporting. - :param _pytest.config.Config config: pytest config object - + :param _pytest.config.Config config: pytest config object + Stops at first non-None result, see :ref:`firstresult` """ -def pytest_terminal_summary(terminalreporter, exitstatus, config): +def pytest_terminal_summary(terminalreporter, exitstatus, config): """Add a section to terminal summary reporting. :param _pytest.terminal.TerminalReporter terminalreporter: the internal terminal reporter object :param int exitstatus: the exit status that will be reported back to the OS - :param _pytest.config.Config config: pytest config object + :param _pytest.config.Config config: pytest config object - .. versionadded:: 4.2 + .. versionadded:: 4.2 The ``config`` parameter. """ -@hookspec(historic=True, warn_on_impl=PYTEST_LOGWARNING) +@hookspec(historic=True, warn_on_impl=PYTEST_LOGWARNING) def pytest_logwarning(message, code, nodeid, fslocation): """ .. deprecated:: 3.8 diff --git a/contrib/python/pytest/py2/_pytest/junitxml.py b/contrib/python/pytest/py2/_pytest/junitxml.py index 853dcb7744..2cd6f58ee2 100644 --- a/contrib/python/pytest/py2/_pytest/junitxml.py +++ b/contrib/python/pytest/py2/_pytest/junitxml.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ report test results in JUnit-XML format, for use with Jenkins and build integration servers. @@ -15,14 +15,14 @@ from __future__ import print_function import functools import os -import platform +import platform import re import sys import time -from datetime import datetime +from datetime import datetime import py -import six +import six import pytest from _pytest import nodes @@ -45,12 +45,12 @@ class Junit(py.xml.Namespace): _legal_chars = (0x09, 0x0A, 0x0D) _legal_ranges = ((0x20, 0x7E), (0x80, 0xD7FF), (0xE000, 0xFFFD), (0x10000, 0x10FFFF)) _legal_xml_re = [ - u"%s-%s" % (six.unichr(low), six.unichr(high)) + u"%s-%s" % (six.unichr(low), six.unichr(high)) for (low, high) in _legal_ranges if low < sys.maxunicode ] -_legal_xml_re = [six.unichr(x) for x in _legal_chars] + _legal_xml_re -illegal_xml_re = re.compile(u"[^%s]" % u"".join(_legal_xml_re)) +_legal_xml_re = [six.unichr(x) for x in _legal_chars] + _legal_xml_re +illegal_xml_re = re.compile(u"[^%s]" % u"".join(_legal_xml_re)) del _legal_chars del _legal_ranges del _legal_xml_re @@ -62,41 +62,41 @@ def bin_xml_escape(arg): def repl(matchobj): i = ord(matchobj.group()) if i <= 0xFF: - return u"#x%02X" % i + return u"#x%02X" % i else: - return u"#x%04X" % i + return u"#x%04X" % i return py.xml.raw(illegal_xml_re.sub(repl, py.xml.escape(arg))) -def merge_family(left, right): - result = {} - for kl, vl in left.items(): - for kr, vr in right.items(): - if not isinstance(vl, list): - raise TypeError(type(vl)) - result[kl] = vl + vr - left.update(result) - - -families = {} -families["_base"] = {"testcase": ["classname", "name"]} -families["_base_legacy"] = {"testcase": ["file", "line", "url"]} - -# xUnit 1.x inherits legacy attributes -families["xunit1"] = families["_base"].copy() -merge_family(families["xunit1"], families["_base_legacy"]) - -# xUnit 2.x uses strict base attributes -families["xunit2"] = families["_base"] - - +def merge_family(left, right): + result = {} + for kl, vl in left.items(): + for kr, vr in right.items(): + if not isinstance(vl, list): + raise TypeError(type(vl)) + result[kl] = vl + vr + left.update(result) + + +families = {} +families["_base"] = {"testcase": ["classname", "name"]} +families["_base_legacy"] = {"testcase": ["file", "line", "url"]} + +# xUnit 1.x inherits legacy attributes +families["xunit1"] = families["_base"].copy() +merge_family(families["xunit1"], families["_base_legacy"]) + +# xUnit 2.x uses strict base attributes +families["xunit2"] = families["_base"] + + class _NodeReporter(object): def __init__(self, nodeid, xml): self.id = nodeid self.xml = xml self.add_stats = self.xml.add_stats - self.family = self.xml.family + self.family = self.xml.family self.duration = 0 self.properties = [] self.nodes = [] @@ -144,20 +144,20 @@ class _NodeReporter(object): self.attrs = attrs self.attrs.update(existing_attrs) # restore any user-defined attributes - # Preserve legacy testcase behavior - if self.family == "xunit1": - return - - # Filter out attributes not permitted by this test family. - # Including custom attributes because they are not valid here. - temp_attrs = {} - for key in self.attrs.keys(): - if key in families[self.family]["testcase"]: - temp_attrs[key] = self.attrs[key] - self.attrs = temp_attrs - + # Preserve legacy testcase behavior + if self.family == "xunit1": + return + + # Filter out attributes not permitted by this test family. + # Including custom attributes because they are not valid here. + temp_attrs = {} + for key in self.attrs.keys(): + if key in families[self.family]["testcase"]: + temp_attrs[key] = self.attrs[key] + self.attrs = temp_attrs + def to_xml(self): - testcase = Junit.testcase(time="%.3f" % self.duration, **self.attrs) + testcase = Junit.testcase(time="%.3f" % self.duration, **self.attrs) testcase.append(self.make_properties_node()) for node in self.nodes: testcase.append(node) @@ -169,9 +169,9 @@ class _NodeReporter(object): self.append(node) def write_captured_output(self, report): - if not self.xml.log_passing_tests and report.passed: - return - + if not self.xml.log_passing_tests and report.passed: + return + content_out = report.capstdout content_log = report.caplog content_err = report.capstderr @@ -231,7 +231,7 @@ class _NodeReporter(object): else: if hasattr(report.longrepr, "reprcrash"): message = report.longrepr.reprcrash.message - elif isinstance(report.longrepr, six.string_types): + elif isinstance(report.longrepr, six.string_types): message = report.longrepr else: message = str(report.longrepr) @@ -250,7 +250,7 @@ class _NodeReporter(object): self._add_simple(Junit.skipped, "collection skipped", report.longrepr) def append_error(self, report): - if report.when == "teardown": + if report.when == "teardown": msg = "test teardown failure" else: msg = "test setup failure" @@ -258,14 +258,14 @@ class _NodeReporter(object): def append_skipped(self, report): if hasattr(report, "wasxfail"): - xfailreason = report.wasxfail - if xfailreason.startswith("reason: "): - xfailreason = xfailreason[8:] - self.append( - Junit.skipped( - "", type="pytest.xfail", message=bin_xml_escape(xfailreason) - ) - ) + xfailreason = report.wasxfail + if xfailreason.startswith("reason: "): + xfailreason = xfailreason[8:] + self.append( + Junit.skipped( + "", type="pytest.xfail", message=bin_xml_escape(xfailreason) + ) + ) else: filename, lineno, skipreason = report.longrepr if skipreason.startswith("Skipped: "): @@ -287,21 +287,21 @@ class _NodeReporter(object): self.to_xml = lambda: py.xml.raw(data) -def _warn_incompatibility_with_xunit2(request, fixture_name): - """Emits a PytestWarning about the given fixture being incompatible with newer xunit revisions""" - from _pytest.warning_types import PytestWarning - - xml = getattr(request.config, "_xml", None) - if xml is not None and xml.family not in ("xunit1", "legacy"): - request.node.warn( - PytestWarning( - "{fixture_name} is incompatible with junit_family '{family}' (use 'legacy' or 'xunit1')".format( - fixture_name=fixture_name, family=xml.family - ) - ) - ) - - +def _warn_incompatibility_with_xunit2(request, fixture_name): + """Emits a PytestWarning about the given fixture being incompatible with newer xunit revisions""" + from _pytest.warning_types import PytestWarning + + xml = getattr(request.config, "_xml", None) + if xml is not None and xml.family not in ("xunit1", "legacy"): + request.node.warn( + PytestWarning( + "{fixture_name} is incompatible with junit_family '{family}' (use 'legacy' or 'xunit1')".format( + fixture_name=fixture_name, family=xml.family + ) + ) + ) + + @pytest.fixture def record_property(request): """Add an extra properties the calling test. @@ -315,7 +315,7 @@ def record_property(request): def test_function(record_property): record_property("example_key", 1) """ - _warn_incompatibility_with_xunit2(request, "record_property") + _warn_incompatibility_with_xunit2(request, "record_property") def append_property(name, value): request.node.user_properties.append((name, value)) @@ -329,67 +329,67 @@ def record_xml_attribute(request): The fixture is callable with ``(name, value)``, with value being automatically xml-encoded """ - from _pytest.warning_types import PytestExperimentalApiWarning - - request.node.warn( - PytestExperimentalApiWarning("record_xml_attribute is an experimental feature") - ) - - _warn_incompatibility_with_xunit2(request, "record_xml_attribute") - - # Declare noop - def add_attr_noop(name, value): - pass - - attr_func = add_attr_noop - + from _pytest.warning_types import PytestExperimentalApiWarning + + request.node.warn( + PytestExperimentalApiWarning("record_xml_attribute is an experimental feature") + ) + + _warn_incompatibility_with_xunit2(request, "record_xml_attribute") + + # Declare noop + def add_attr_noop(name, value): + pass + + attr_func = add_attr_noop + xml = getattr(request.config, "_xml", None) if xml is not None: node_reporter = xml.node_reporter(request.node.nodeid) - attr_func = node_reporter.add_attribute - - return attr_func - - -def _check_record_param_type(param, v): - """Used by record_testsuite_property to check that the given parameter name is of the proper - type""" - __tracebackhide__ = True - if not isinstance(v, six.string_types): - msg = "{param} parameter needs to be a string, but {g} given" - raise TypeError(msg.format(param=param, g=type(v).__name__)) - - -@pytest.fixture(scope="session") -def record_testsuite_property(request): - """ - Records a new ``<property>`` tag as child of the root ``<testsuite>``. This is suitable to - writing global information regarding the entire test suite, and is compatible with ``xunit2`` JUnit family. - - This is a ``session``-scoped fixture which is called with ``(name, value)``. Example: - - .. code-block:: python - - def test_foo(record_testsuite_property): - record_testsuite_property("ARCH", "PPC") - record_testsuite_property("STORAGE_TYPE", "CEPH") - - ``name`` must be a string, ``value`` will be converted to a string and properly xml-escaped. - """ - - __tracebackhide__ = True - - def record_func(name, value): - """noop function in case --junitxml was not passed in the command-line""" - __tracebackhide__ = True - _check_record_param_type("name", name) - - xml = getattr(request.config, "_xml", None) - if xml is not None: - record_func = xml.add_global_property # noqa - return record_func - - + attr_func = node_reporter.add_attribute + + return attr_func + + +def _check_record_param_type(param, v): + """Used by record_testsuite_property to check that the given parameter name is of the proper + type""" + __tracebackhide__ = True + if not isinstance(v, six.string_types): + msg = "{param} parameter needs to be a string, but {g} given" + raise TypeError(msg.format(param=param, g=type(v).__name__)) + + +@pytest.fixture(scope="session") +def record_testsuite_property(request): + """ + Records a new ``<property>`` tag as child of the root ``<testsuite>``. This is suitable to + writing global information regarding the entire test suite, and is compatible with ``xunit2`` JUnit family. + + This is a ``session``-scoped fixture which is called with ``(name, value)``. Example: + + .. code-block:: python + + def test_foo(record_testsuite_property): + record_testsuite_property("ARCH", "PPC") + record_testsuite_property("STORAGE_TYPE", "CEPH") + + ``name`` must be a string, ``value`` will be converted to a string and properly xml-escaped. + """ + + __tracebackhide__ = True + + def record_func(name, value): + """noop function in case --junitxml was not passed in the command-line""" + __tracebackhide__ = True + _check_record_param_type("name", name) + + xml = getattr(request.config, "_xml", None) + if xml is not None: + record_func = xml.add_global_property # noqa + return record_func + + def pytest_addoption(parser): group = parser.getgroup("terminal reporting") group.addoption( @@ -419,22 +419,22 @@ def pytest_addoption(parser): "one of no|system-out|system-err", default="no", ) # choices=['no', 'stdout', 'stderr']) - parser.addini( - "junit_log_passing_tests", - "Capture log information for passing tests to JUnit report: ", - type="bool", - default=True, - ) - parser.addini( - "junit_duration_report", - "Duration time to report: one of total|call", - default="total", - ) # choices=['total', 'call']) - parser.addini( - "junit_family", - "Emit XML for schema: one of legacy|xunit1|xunit2", - default="xunit1", - ) + parser.addini( + "junit_log_passing_tests", + "Capture log information for passing tests to JUnit report: ", + type="bool", + default=True, + ) + parser.addini( + "junit_duration_report", + "Duration time to report: one of total|call", + default="total", + ) # choices=['total', 'call']) + parser.addini( + "junit_family", + "Emit XML for schema: one of legacy|xunit1|xunit2", + default="xunit1", + ) def pytest_configure(config): @@ -446,9 +446,9 @@ def pytest_configure(config): config.option.junitprefix, config.getini("junit_suite_name"), config.getini("junit_logging"), - config.getini("junit_duration_report"), - config.getini("junit_family"), - config.getini("junit_log_passing_tests"), + config.getini("junit_duration_report"), + config.getini("junit_family"), + config.getini("junit_log_passing_tests"), ) config.pluginmanager.register(config._xml) @@ -476,37 +476,37 @@ def mangle_test_address(address): class LogXML(object): - def __init__( - self, - logfile, - prefix, - suite_name="pytest", - logging="no", - report_duration="total", - family="xunit1", - log_passing_tests=True, - ): + def __init__( + self, + logfile, + prefix, + suite_name="pytest", + logging="no", + report_duration="total", + family="xunit1", + log_passing_tests=True, + ): logfile = os.path.expanduser(os.path.expandvars(logfile)) self.logfile = os.path.normpath(os.path.abspath(logfile)) self.prefix = prefix self.suite_name = suite_name self.logging = logging - self.log_passing_tests = log_passing_tests - self.report_duration = report_duration - self.family = family + self.log_passing_tests = log_passing_tests + self.report_duration = report_duration + self.family = family self.stats = dict.fromkeys(["error", "passed", "failure", "skipped"], 0) self.node_reporters = {} # nodeid -> _NodeReporter self.node_reporters_ordered = [] self.global_properties = [] - + # List of reports that failed on call but teardown is pending. self.open_reports = [] self.cnt_double_fail_tests = 0 - # Replaces convenience family with real family - if self.family == "legacy": - self.family = "xunit1" - + # Replaces convenience family with real family + if self.family == "legacy": + self.family = "xunit1" + def finalize(self, report): nodeid = getattr(report, "nodeid", report) # local hack to handle xdist report order @@ -597,8 +597,8 @@ class LogXML(object): if report.when == "call": reporter.append_failure(report) self.open_reports.append(report) - if not self.log_passing_tests: - reporter.write_captured_output(report) + if not self.log_passing_tests: + reporter.write_captured_output(report) else: reporter.append_error(report) elif report.skipped: @@ -634,9 +634,9 @@ class LogXML(object): """accumulates total duration for nodeid from given report and updates the Junit.testcase with the new total if already created. """ - if self.report_duration == "total" or report.when == self.report_duration: - reporter = self.node_reporter(report) - reporter.duration += getattr(report, "duration", 0.0) + if self.report_duration == "total" or report.when == self.report_duration: + reporter = self.node_reporter(report) + reporter.duration += getattr(report, "duration", 0.0) def pytest_collectreport(self, report): if not report.passed: @@ -671,28 +671,28 @@ class LogXML(object): ) logfile.write('<?xml version="1.0" encoding="utf-8"?>') - suite_node = Junit.testsuite( - self._get_global_properties_node(), - [x.to_xml() for x in self.node_reporters_ordered], - name=self.suite_name, - errors=self.stats["error"], - failures=self.stats["failure"], - skipped=self.stats["skipped"], - tests=numtests, - time="%.3f" % suite_time_delta, - timestamp=datetime.fromtimestamp(self.suite_start_time).isoformat(), - hostname=platform.node(), + suite_node = Junit.testsuite( + self._get_global_properties_node(), + [x.to_xml() for x in self.node_reporters_ordered], + name=self.suite_name, + errors=self.stats["error"], + failures=self.stats["failure"], + skipped=self.stats["skipped"], + tests=numtests, + time="%.3f" % suite_time_delta, + timestamp=datetime.fromtimestamp(self.suite_start_time).isoformat(), + hostname=platform.node(), ) - logfile.write(Junit.testsuites([suite_node]).unicode(indent=0)) + logfile.write(Junit.testsuites([suite_node]).unicode(indent=0)) logfile.close() def pytest_terminal_summary(self, terminalreporter): terminalreporter.write_sep("-", "generated xml file: %s" % (self.logfile)) def add_global_property(self, name, value): - __tracebackhide__ = True - _check_record_param_type("name", name) - self.global_properties.append((name, bin_xml_escape(value))) + __tracebackhide__ = True + _check_record_param_type("name", name) + self.global_properties.append((name, bin_xml_escape(value))) def _get_global_properties_node(self): """Return a Junit node containing custom properties, if any. diff --git a/contrib/python/pytest/py2/_pytest/logging.py b/contrib/python/pytest/py2/_pytest/logging.py index 2400737ee4..0f94f361db 100644 --- a/contrib/python/pytest/py2/_pytest/logging.py +++ b/contrib/python/pytest/py2/_pytest/logging.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ Access and control log capturing. """ from __future__ import absolute_import from __future__ import division @@ -14,17 +14,17 @@ import six import pytest from _pytest.compat import dummy_context_manager from _pytest.config import create_terminal_writer -from _pytest.pathlib import Path +from _pytest.pathlib import Path -DEFAULT_LOG_FORMAT = "%(levelname)-8s %(name)s:%(filename)s:%(lineno)d %(message)s" +DEFAULT_LOG_FORMAT = "%(levelname)-8s %(name)s:%(filename)s:%(lineno)d %(message)s" DEFAULT_LOG_DATE_FORMAT = "%H:%M:%S" -_ANSI_ESCAPE_SEQ = re.compile(r"\x1b\[[\d;]+m") - - -def _remove_ansi_escape_sequences(text): - return _ANSI_ESCAPE_SEQ.sub("", text) +_ANSI_ESCAPE_SEQ = re.compile(r"\x1b\[[\d;]+m") +def _remove_ansi_escape_sequences(text): + return _ANSI_ESCAPE_SEQ.sub("", text) + + class ColoredLevelFormatter(logging.Formatter): """ Colorize the %(levelname)..s part of the log format passed to __init__. @@ -77,36 +77,36 @@ class ColoredLevelFormatter(logging.Formatter): return super(ColoredLevelFormatter, self).format(record) -if not six.PY2: - # Formatter classes don't support format styles in PY2 - - class PercentStyleMultiline(logging.PercentStyle): - """A logging style with special support for multiline messages. - - If the message of a record consists of multiple lines, this style - formats the message as if each line were logged separately. - """ - - @staticmethod - def _update_message(record_dict, message): - tmp = record_dict.copy() - tmp["message"] = message - return tmp - - def format(self, record): - if "\n" in record.message: - lines = record.message.splitlines() - formatted = self._fmt % self._update_message(record.__dict__, lines[0]) - # TODO optimize this by introducing an option that tells the - # logging framework that the indentation doesn't - # change. This allows to compute the indentation only once. - indentation = _remove_ansi_escape_sequences(formatted).find(lines[0]) - lines[0] = formatted - return ("\n" + " " * indentation).join(lines) - else: - return self._fmt % record.__dict__ - - +if not six.PY2: + # Formatter classes don't support format styles in PY2 + + class PercentStyleMultiline(logging.PercentStyle): + """A logging style with special support for multiline messages. + + If the message of a record consists of multiple lines, this style + formats the message as if each line were logged separately. + """ + + @staticmethod + def _update_message(record_dict, message): + tmp = record_dict.copy() + tmp["message"] = message + return tmp + + def format(self, record): + if "\n" in record.message: + lines = record.message.splitlines() + formatted = self._fmt % self._update_message(record.__dict__, lines[0]) + # TODO optimize this by introducing an option that tells the + # logging framework that the indentation doesn't + # change. This allows to compute the indentation only once. + indentation = _remove_ansi_escape_sequences(formatted).find(lines[0]) + lines[0] = formatted + return ("\n" + " " * indentation).join(lines) + else: + return self._fmt % record.__dict__ + + def get_option_ini(config, *names): for name in names: ret = config.getoption(name) # 'default' arg won't work as expected @@ -253,7 +253,7 @@ class LogCaptureFixture(object): """Creates a new funcarg.""" self._item = item # dict of log name -> log level - self._initial_log_levels = {} # Dict[str, int] + self._initial_log_levels = {} # Dict[str, int] def _finalize(self): """Finalizes the fixture. @@ -292,8 +292,8 @@ class LogCaptureFixture(object): @property def text(self): - """Returns the formatted log text.""" - return _remove_ansi_escape_sequences(self.handler.stream.getvalue()) + """Returns the formatted log text.""" + return _remove_ansi_escape_sequences(self.handler.stream.getvalue()) @property def records(self): @@ -406,8 +406,8 @@ def get_actual_log_level(config, *setting_names): ) -# run after terminalreporter/capturemanager are configured -@pytest.hookimpl(trylast=True) +# run after terminalreporter/capturemanager are configured +@pytest.hookimpl(trylast=True) def pytest_configure(config): config.pluginmanager.register(LoggingPlugin(config), "logging-plugin") @@ -425,99 +425,99 @@ class LoggingPlugin(object): self._config = config self.print_logs = get_option_ini(config, "log_print") - self.formatter = self._create_formatter( + self.formatter = self._create_formatter( get_option_ini(config, "log_format"), get_option_ini(config, "log_date_format"), ) self.log_level = get_actual_log_level(config, "log_level") - self.log_file_level = get_actual_log_level(config, "log_file_level") - self.log_file_format = get_option_ini(config, "log_file_format", "log_format") - self.log_file_date_format = get_option_ini( - config, "log_file_date_format", "log_date_format" - ) - self.log_file_formatter = logging.Formatter( - self.log_file_format, datefmt=self.log_file_date_format - ) - + self.log_file_level = get_actual_log_level(config, "log_file_level") + self.log_file_format = get_option_ini(config, "log_file_format", "log_format") + self.log_file_date_format = get_option_ini( + config, "log_file_date_format", "log_date_format" + ) + self.log_file_formatter = logging.Formatter( + self.log_file_format, datefmt=self.log_file_date_format + ) + log_file = get_option_ini(config, "log_file") if log_file: self.log_file_handler = logging.FileHandler( log_file, mode="w", encoding="UTF-8" ) - self.log_file_handler.setFormatter(self.log_file_formatter) + self.log_file_handler.setFormatter(self.log_file_formatter) else: self.log_file_handler = None self.log_cli_handler = None - self.live_logs_context = lambda: dummy_context_manager() - # Note that the lambda for the live_logs_context is needed because - # live_logs_context can otherwise not be entered multiple times due - # to limitations of contextlib.contextmanager. - - if self._log_cli_enabled(): - self._setup_cli_logging() - - def _create_formatter(self, log_format, log_date_format): - # color option doesn't exist if terminal plugin is disabled - color = getattr(self._config.option, "color", "no") - if color != "no" and ColoredLevelFormatter.LEVELNAME_FMT_REGEX.search( - log_format - ): - formatter = ColoredLevelFormatter( - create_terminal_writer(self._config), log_format, log_date_format - ) - else: - formatter = logging.Formatter(log_format, log_date_format) - - if not six.PY2: - formatter._style = PercentStyleMultiline(formatter._style._fmt) - return formatter - - def _setup_cli_logging(self): - config = self._config - terminal_reporter = config.pluginmanager.get_plugin("terminalreporter") - if terminal_reporter is None: - # terminal reporter is disabled e.g. by pytest-xdist. - return - - capture_manager = config.pluginmanager.get_plugin("capturemanager") - # if capturemanager plugin is disabled, live logging still works. - log_cli_handler = _LiveLoggingStreamHandler(terminal_reporter, capture_manager) - - log_cli_formatter = self._create_formatter( - get_option_ini(config, "log_cli_format", "log_format"), - get_option_ini(config, "log_cli_date_format", "log_date_format"), - ) - - log_cli_level = get_actual_log_level(config, "log_cli_level", "log_level") - self.log_cli_handler = log_cli_handler - self.live_logs_context = lambda: catching_logs( - log_cli_handler, formatter=log_cli_formatter, level=log_cli_level - ) - - def set_log_path(self, fname): - """Public method, which can set filename parameter for - Logging.FileHandler(). Also creates parent directory if - it does not exist. - - .. warning:: - Please considered as an experimental API. - """ - fname = Path(fname) - - if not fname.is_absolute(): - fname = Path(self._config.rootdir, fname) - - if not fname.parent.exists(): - fname.parent.mkdir(exist_ok=True, parents=True) - - self.log_file_handler = logging.FileHandler( - str(fname), mode="w", encoding="UTF-8" - ) - self.log_file_handler.setFormatter(self.log_file_formatter) - + self.live_logs_context = lambda: dummy_context_manager() + # Note that the lambda for the live_logs_context is needed because + # live_logs_context can otherwise not be entered multiple times due + # to limitations of contextlib.contextmanager. + + if self._log_cli_enabled(): + self._setup_cli_logging() + + def _create_formatter(self, log_format, log_date_format): + # color option doesn't exist if terminal plugin is disabled + color = getattr(self._config.option, "color", "no") + if color != "no" and ColoredLevelFormatter.LEVELNAME_FMT_REGEX.search( + log_format + ): + formatter = ColoredLevelFormatter( + create_terminal_writer(self._config), log_format, log_date_format + ) + else: + formatter = logging.Formatter(log_format, log_date_format) + + if not six.PY2: + formatter._style = PercentStyleMultiline(formatter._style._fmt) + return formatter + + def _setup_cli_logging(self): + config = self._config + terminal_reporter = config.pluginmanager.get_plugin("terminalreporter") + if terminal_reporter is None: + # terminal reporter is disabled e.g. by pytest-xdist. + return + + capture_manager = config.pluginmanager.get_plugin("capturemanager") + # if capturemanager plugin is disabled, live logging still works. + log_cli_handler = _LiveLoggingStreamHandler(terminal_reporter, capture_manager) + + log_cli_formatter = self._create_formatter( + get_option_ini(config, "log_cli_format", "log_format"), + get_option_ini(config, "log_cli_date_format", "log_date_format"), + ) + + log_cli_level = get_actual_log_level(config, "log_cli_level", "log_level") + self.log_cli_handler = log_cli_handler + self.live_logs_context = lambda: catching_logs( + log_cli_handler, formatter=log_cli_formatter, level=log_cli_level + ) + + def set_log_path(self, fname): + """Public method, which can set filename parameter for + Logging.FileHandler(). Also creates parent directory if + it does not exist. + + .. warning:: + Please considered as an experimental API. + """ + fname = Path(fname) + + if not fname.is_absolute(): + fname = Path(self._config.rootdir, fname) + + if not fname.parent.exists(): + fname.parent.mkdir(exist_ok=True, parents=True) + + self.log_file_handler = logging.FileHandler( + str(fname), mode="w", encoding="UTF-8" + ) + self.log_file_handler.setFormatter(self.log_file_formatter) + def _log_cli_enabled(self): """Return True if log_cli should be considered enabled, either explicitly or because --log-cli-level was given in the command-line. @@ -540,15 +540,15 @@ class LoggingPlugin(object): @contextmanager def _runtest_for(self, item, when): - with self._runtest_for_main(item, when): - if self.log_file_handler is not None: - with catching_logs(self.log_file_handler, level=self.log_file_level): - yield - else: - yield - - @contextmanager - def _runtest_for_main(self, item, when): + with self._runtest_for_main(item, when): + if self.log_file_handler is not None: + with catching_logs(self.log_file_handler, level=self.log_file_level): + yield + else: + yield + + @contextmanager + def _runtest_for_main(self, item, when): """Implements the internals of pytest_runtest_xxx() hook.""" with catching_logs( LogCaptureHandler(), formatter=self.formatter, level=self.log_level @@ -603,26 +603,26 @@ class LoggingPlugin(object): with self._runtest_for(None, "finish"): yield - @pytest.hookimpl(hookwrapper=True) - def pytest_runtest_logreport(self): - with self._runtest_for(None, "logreport"): - yield - + @pytest.hookimpl(hookwrapper=True) + def pytest_runtest_logreport(self): + with self._runtest_for(None, "logreport"): + yield + @pytest.hookimpl(hookwrapper=True, tryfirst=True) def pytest_sessionfinish(self): with self.live_logs_context(): if self.log_cli_handler: self.log_cli_handler.set_when("sessionfinish") if self.log_file_handler is not None: - try: - with catching_logs( - self.log_file_handler, level=self.log_file_level - ): - yield - finally: - # Close the FileHandler explicitly. - # (logging.shutdown might have lost the weakref?!) - self.log_file_handler.close() + try: + with catching_logs( + self.log_file_handler, level=self.log_file_level + ): + yield + finally: + # Close the FileHandler explicitly. + # (logging.shutdown might have lost the weakref?!) + self.log_file_handler.close() else: yield @@ -640,15 +640,15 @@ class LoggingPlugin(object): @pytest.hookimpl(hookwrapper=True) def pytest_runtestloop(self, session): """Runs all collected test items.""" - - if session.config.option.collectonly: - yield - return - - if self._log_cli_enabled() and self._config.getoption("verbose") < 1: - # setting verbose flag is needed to avoid messy test progress output - self._config.option.verbose = 1 - + + if session.config.option.collectonly: + yield + return + + if self._log_cli_enabled() and self._config.getoption("verbose") < 1: + # setting verbose flag is needed to avoid messy test progress output + self._config.option.verbose = 1 + with self.live_logs_context(): if self.log_file_handler is not None: with catching_logs(self.log_file_handler, level=self.log_file_level): diff --git a/contrib/python/pytest/py2/_pytest/main.py b/contrib/python/pytest/py2/_pytest/main.py index a9d310cb62..95af7acf96 100644 --- a/contrib/python/pytest/py2/_pytest/main.py +++ b/contrib/python/pytest/py2/_pytest/main.py @@ -1,16 +1,16 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ core implementation of testing process: init, session, runtest loop. """ from __future__ import absolute_import from __future__ import division from __future__ import print_function import contextlib -import fnmatch +import fnmatch import functools import os import pkgutil import sys -import warnings +import warnings import attr import py @@ -21,7 +21,7 @@ from _pytest import nodes from _pytest.config import directory_arg from _pytest.config import hookimpl from _pytest.config import UsageError -from _pytest.deprecated import PYTEST_CONFIG_GLOBAL +from _pytest.deprecated import PYTEST_CONFIG_GLOBAL from _pytest.outcomes import exit from _pytest.runner import collect_one_node @@ -67,10 +67,10 @@ def pytest_addoption(parser): help="exit after first num failures or errors.", ) group._addoption( - "--strict-markers", + "--strict-markers", "--strict", action="store_true", - help="markers not registered in the `markers` section of the configuration file raise errors.", + help="markers not registered in the `markers` section of the configuration file raise errors.", ) group._addoption( "-c", @@ -115,12 +115,12 @@ def pytest_addoption(parser): help="ignore path during collection (multi-allowed).", ) group.addoption( - "--ignore-glob", - action="append", - metavar="path", - help="ignore path pattern during collection (multi-allowed).", - ) - group.addoption( + "--ignore-glob", + action="append", + metavar="path", + help="ignore path pattern during collection (multi-allowed).", + ) + group.addoption( "--deselect", action="append", metavar="nodeid_prefix", @@ -172,24 +172,24 @@ def pytest_addoption(parser): ) -class _ConfigDeprecated(object): - def __init__(self, config): - self.__dict__["_config"] = config - - def __getattr__(self, attr): - warnings.warn(PYTEST_CONFIG_GLOBAL, stacklevel=2) - return getattr(self._config, attr) - - def __setattr__(self, attr, val): - warnings.warn(PYTEST_CONFIG_GLOBAL, stacklevel=2) - return setattr(self._config, attr, val) - - def __repr__(self): - return "{}({!r})".format(type(self).__name__, self._config) - - +class _ConfigDeprecated(object): + def __init__(self, config): + self.__dict__["_config"] = config + + def __getattr__(self, attr): + warnings.warn(PYTEST_CONFIG_GLOBAL, stacklevel=2) + return getattr(self._config, attr) + + def __setattr__(self, attr, val): + warnings.warn(PYTEST_CONFIG_GLOBAL, stacklevel=2) + return setattr(self._config, attr, val) + + def __repr__(self): + return "{}({!r})".format(type(self).__name__, self._config) + + def pytest_configure(config): - __import__("pytest").config = _ConfigDeprecated(config) # compatibility + __import__("pytest").config = _ConfigDeprecated(config) # compatibility def wrap_session(config, doit): @@ -205,28 +205,28 @@ def wrap_session(config, doit): initstate = 2 session.exitstatus = doit(config, session) or 0 except UsageError: - session.exitstatus = EXIT_USAGEERROR + session.exitstatus = EXIT_USAGEERROR raise except Failed: session.exitstatus = EXIT_TESTSFAILED - except (KeyboardInterrupt, exit.Exception): - excinfo = _pytest._code.ExceptionInfo.from_current() + except (KeyboardInterrupt, exit.Exception): + excinfo = _pytest._code.ExceptionInfo.from_current() exitstatus = EXIT_INTERRUPTED - if isinstance(excinfo.value, exit.Exception): + if isinstance(excinfo.value, exit.Exception): if excinfo.value.returncode is not None: exitstatus = excinfo.value.returncode - if initstate < 2: - sys.stderr.write( - "{}: {}\n".format(excinfo.typename, excinfo.value.msg) - ) + if initstate < 2: + sys.stderr.write( + "{}: {}\n".format(excinfo.typename, excinfo.value.msg) + ) config.hook.pytest_keyboard_interrupt(excinfo=excinfo) session.exitstatus = exitstatus except: # noqa - excinfo = _pytest._code.ExceptionInfo.from_current() + excinfo = _pytest._code.ExceptionInfo.from_current() config.notify_exception(excinfo, config.option) session.exitstatus = EXIT_INTERNALERROR if excinfo.errisinstance(SystemExit): - sys.stderr.write("mainloop: caught unexpected SystemExit!\n") + sys.stderr.write("mainloop: caught unexpected SystemExit!\n") finally: excinfo = None # Explicitly break reference cycle. @@ -303,20 +303,20 @@ def pytest_ignore_collect(path, config): if py.path.local(path) in ignore_paths: return True - ignore_globs = config._getconftest_pathlist( - "collect_ignore_glob", path=path.dirpath() - ) - ignore_globs = ignore_globs or [] - excludeglobopt = config.getoption("ignore_glob") - if excludeglobopt: - ignore_globs.extend([py.path.local(x) for x in excludeglobopt]) - - if any( - fnmatch.fnmatch(six.text_type(path), six.text_type(glob)) - for glob in ignore_globs - ): - return True - + ignore_globs = config._getconftest_pathlist( + "collect_ignore_glob", path=path.dirpath() + ) + ignore_globs = ignore_globs or [] + excludeglobopt = config.getoption("ignore_glob") + if excludeglobopt: + ignore_globs.extend([py.path.local(x) for x in excludeglobopt]) + + if any( + fnmatch.fnmatch(six.text_type(path), six.text_type(glob)) + for glob in ignore_globs + ): + return True + allow_in_venv = config.getoption("collect_in_virtualenv") if not allow_in_venv and _in_venv(path): return True @@ -432,7 +432,7 @@ class Session(nodes.FSCollector): self.shouldfail = False self.trace = config.trace.root.get("collection") self._norecursepatterns = config.getini("norecursedirs") - self.startdir = config.invocation_dir + self.startdir = config.invocation_dir self._initialpaths = frozenset() # Keep track of any collected nodes in here, so we don't duplicate fixtures self._node_cache = {} @@ -442,15 +442,15 @@ class Session(nodes.FSCollector): self.config.pluginmanager.register(self, name="session") - def __repr__(self): - return "<%s %s exitstatus=%r testsfailed=%d testscollected=%d>" % ( - self.__class__.__name__, - self.name, - getattr(self, "exitstatus", "<UNSET>"), - self.testsfailed, - self.testscollected, - ) - + def __repr__(self): + return "<%s %s exitstatus=%r testsfailed=%d testscollected=%d>" % ( + self.__class__.__name__, + self.name, + getattr(self, "exitstatus", "<UNSET>"), + self.testsfailed, + self.testscollected, + ) + def _node_location_to_relpath(self, node_path): # bestrelpath is a quite slow function return self._bestrelpathcache[node_path] @@ -558,7 +558,7 @@ class Session(nodes.FSCollector): # Start with a Session root, and delve to argpath item (dir or file) # and stack all Packages found on the way. # No point in finding packages when collecting doctests - if not self.config.getoption("doctestmodules", False): + if not self.config.getoption("doctestmodules", False): pm = self.config.pluginmanager for parent in reversed(argpath.parts()): if pm._confcutdir and pm._confcutdir.relto(parent): @@ -582,7 +582,7 @@ class Session(nodes.FSCollector): seen_dirs = set() for path in argpath.visit( - fil=self._visit_filter, rec=self._recurse, bf=True, sort=True + fil=self._visit_filter, rec=self._recurse, bf=True, sort=True ): dirpath = path.dirpath() if dirpath not in seen_dirs: @@ -612,7 +612,7 @@ class Session(nodes.FSCollector): col = self._node_cache[argpath] else: collect_root = self._pkg_roots.get(argpath.dirname, self) - col = collect_root._collectfile(argpath, handle_dupes=False) + col = collect_root._collectfile(argpath, handle_dupes=False) if col: self._node_cache[argpath] = col m = self.matchnodes(col, names) @@ -621,24 +621,24 @@ class Session(nodes.FSCollector): # Module itself, so just use that. If this special case isn't taken, then all # the files in the package will be yielded. if argpath.basename == "__init__.py": - try: - yield next(m[0].collect()) - except StopIteration: - # The package collects nothing with only an __init__.py - # file in it, which gets ignored by the default - # "python_files" option. - pass + try: + yield next(m[0].collect()) + except StopIteration: + # The package collects nothing with only an __init__.py + # file in it, which gets ignored by the default + # "python_files" option. + pass return for y in m: yield y def _collectfile(self, path, handle_dupes=True): - assert path.isfile(), "%r is not a file (isdir=%r, exists=%r, islink=%r)" % ( - path, - path.isdir(), - path.exists(), - path.islink(), - ) + assert path.isfile(), "%r is not a file (isdir=%r, exists=%r, islink=%r)" % ( + path, + path.isdir(), + path.exists(), + path.islink(), + ) ihook = self.gethookproxy(path) if not self.isinitpath(path): if ihook.pytest_ignore_collect(path=path, config=self.config): @@ -668,18 +668,18 @@ class Session(nodes.FSCollector): ihook.pytest_collect_directory(path=dirpath, parent=self) return True - if six.PY2: - - @staticmethod - def _visit_filter(f): - return f.check(file=1) and not f.strpath.endswith("*.pyc") - - else: - - @staticmethod - def _visit_filter(f): - return f.check(file=1) - + if six.PY2: + + @staticmethod + def _visit_filter(f): + return f.check(file=1) and not f.strpath.endswith("*.pyc") + + else: + + @staticmethod + def _visit_filter(f): + return f.check(file=1) + def _tryconvertpyarg(self, x): """Convert a dotted module name to path.""" try: diff --git a/contrib/python/pytest/py2/_pytest/mark/__init__.py b/contrib/python/pytest/py2/_pytest/mark/__init__.py index 6bc22fe27d..3d0f5912c8 100644 --- a/contrib/python/pytest/py2/_pytest/mark/__init__.py +++ b/contrib/python/pytest/py2/_pytest/mark/__init__.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ generic mechanism for marking and selecting python functions. """ from __future__ import absolute_import from __future__ import division @@ -15,7 +15,7 @@ from .structures import MarkGenerator from .structures import ParameterSet from _pytest.config import UsageError -__all__ = ["Mark", "MarkDecorator", "MarkGenerator", "get_empty_parameterset_mark"] +__all__ = ["Mark", "MarkDecorator", "MarkGenerator", "get_empty_parameterset_mark"] def param(*values, **kw): @@ -53,7 +53,7 @@ def pytest_addoption(parser): "other' matches all test functions and classes whose name " "contains 'test_method' or 'test_other', while -k 'not test_method' " "matches those that don't contain 'test_method' in their names. " - "-k 'not test_method and not test_other' will eliminate the matches. " + "-k 'not test_method and not test_other' will eliminate the matches. " "Additionally keywords are matched to classes and functions " "containing extra names in their 'extra_keyword_matches' set, " "as well as functions which have names assigned directly to them.", @@ -101,9 +101,9 @@ pytest_cmdline_main.tryfirst = True def deselect_by_keyword(items, config): keywordexpr = config.option.keyword.lstrip() - if not keywordexpr: - return - + if not keywordexpr: + return + if keywordexpr.startswith("-"): keywordexpr = "not " + keywordexpr[1:] selectuntil = False @@ -151,7 +151,7 @@ def pytest_collection_modifyitems(items, config): def pytest_configure(config): config._old_mark_config = MARK_GEN._config - MARK_GEN._config = config + MARK_GEN._config = config empty_parameterset = config.getini(EMPTY_PARAMETERSET_OPTION) diff --git a/contrib/python/pytest/py2/_pytest/mark/evaluate.py b/contrib/python/pytest/py2/_pytest/mark/evaluate.py index 506546e253..a20ff94cd5 100644 --- a/contrib/python/pytest/py2/_pytest/mark/evaluate.py +++ b/contrib/python/pytest/py2/_pytest/mark/evaluate.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- import os import platform import sys diff --git a/contrib/python/pytest/py2/_pytest/mark/legacy.py b/contrib/python/pytest/py2/_pytest/mark/legacy.py index c56482f14d..7b865f5ff8 100644 --- a/contrib/python/pytest/py2/_pytest/mark/legacy.py +++ b/contrib/python/pytest/py2/_pytest/mark/legacy.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ this is a place where we put datastructures used by legacy apis we hope ot remove @@ -46,15 +46,15 @@ class KeywordMapping(object): mapped_names.add(item.name) # Add the names added as extra keywords to current or parent items - mapped_names.update(item.listextrakeywords()) + mapped_names.update(item.listextrakeywords()) # Add the names attached to the current function through direct assignment if hasattr(item, "function"): - mapped_names.update(item.function.__dict__) - - # add the markers to the keywords as we no longer handle them correctly - mapped_names.update(mark.name for mark in item.iter_markers()) + mapped_names.update(item.function.__dict__) + # add the markers to the keywords as we no longer handle them correctly + mapped_names.update(mark.name for mark in item.iter_markers()) + return cls(mapped_names) def __getitem__(self, subname): diff --git a/contrib/python/pytest/py2/_pytest/mark/structures.py b/contrib/python/pytest/py2/_pytest/mark/structures.py index aaebe927be..71adac02bc 100644 --- a/contrib/python/pytest/py2/_pytest/mark/structures.py +++ b/contrib/python/pytest/py2/_pytest/mark/structures.py @@ -1,20 +1,20 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- import inspect import warnings from collections import namedtuple from operator import attrgetter import attr -import six +import six -from ..compat import ascii_escaped -from ..compat import ATTRS_EQ_FIELD +from ..compat import ascii_escaped +from ..compat import ATTRS_EQ_FIELD from ..compat import getfslineno from ..compat import MappingMixin from ..compat import NOTSET -from _pytest.deprecated import PYTEST_PARAM_UNKNOWN_KWARGS +from _pytest.deprecated import PYTEST_PARAM_UNKNOWN_KWARGS from _pytest.outcomes import fail -from _pytest.warning_types import PytestUnknownMarkWarning +from _pytest.warning_types import PytestUnknownMarkWarning EMPTY_PARAMETERSET_OPTION = "empty_parameter_set_mark" @@ -48,7 +48,7 @@ def get_empty_parameterset_mark(config, argnames, func): f_name = func.__name__ _, lineno = getfslineno(func) raise Collector.CollectError( - "Empty parameter set in '%s' at line %d" % (f_name, lineno + 1) + "Empty parameter set in '%s' at line %d" % (f_name, lineno + 1) ) else: raise LookupError(requested_mark) @@ -64,90 +64,90 @@ def get_empty_parameterset_mark(config, argnames, func): class ParameterSet(namedtuple("ParameterSet", "values, marks, id")): @classmethod - def param(cls, *values, **kwargs): - marks = kwargs.pop("marks", ()) + def param(cls, *values, **kwargs): + marks = kwargs.pop("marks", ()) if isinstance(marks, MarkDecorator): marks = (marks,) else: assert isinstance(marks, (tuple, list, set)) - id_ = kwargs.pop("id", None) - if id_ is not None: - if not isinstance(id_, six.string_types): - raise TypeError( - "Expected id to be a string, got {}: {!r}".format(type(id_), id_) - ) - id_ = ascii_escaped(id_) - - if kwargs: - warnings.warn( - PYTEST_PARAM_UNKNOWN_KWARGS.format(args=sorted(kwargs)), stacklevel=3 - ) + id_ = kwargs.pop("id", None) + if id_ is not None: + if not isinstance(id_, six.string_types): + raise TypeError( + "Expected id to be a string, got {}: {!r}".format(type(id_), id_) + ) + id_ = ascii_escaped(id_) + + if kwargs: + warnings.warn( + PYTEST_PARAM_UNKNOWN_KWARGS.format(args=sorted(kwargs)), stacklevel=3 + ) return cls(values, marks, id_) @classmethod - def extract_from(cls, parameterset, force_tuple=False): + def extract_from(cls, parameterset, force_tuple=False): """ :param parameterset: a legacy style parameterset that may or may not be a tuple, and may or may not be wrapped into a mess of mark objects - :param force_tuple: + :param force_tuple: enforce tuple wrapping so single argument tuple values don't get decomposed and break tests """ if isinstance(parameterset, cls): return parameterset - if force_tuple: + if force_tuple: return cls.param(parameterset) - else: - return cls(parameterset, marks=[], id=None) + else: + return cls(parameterset, marks=[], id=None) - @staticmethod - def _parse_parametrize_args(argnames, argvalues, *args, **kwargs): + @staticmethod + def _parse_parametrize_args(argnames, argvalues, *args, **kwargs): if not isinstance(argnames, (tuple, list)): argnames = [x.strip() for x in argnames.split(",") if x.strip()] force_tuple = len(argnames) == 1 else: force_tuple = False - return argnames, force_tuple - - @staticmethod - def _parse_parametrize_parameters(argvalues, force_tuple): - return [ - ParameterSet.extract_from(x, force_tuple=force_tuple) for x in argvalues + return argnames, force_tuple + + @staticmethod + def _parse_parametrize_parameters(argvalues, force_tuple): + return [ + ParameterSet.extract_from(x, force_tuple=force_tuple) for x in argvalues ] - - @classmethod - def _for_parametrize(cls, argnames, argvalues, func, config, function_definition): - argnames, force_tuple = cls._parse_parametrize_args(argnames, argvalues) - parameters = cls._parse_parametrize_parameters(argvalues, force_tuple) + + @classmethod + def _for_parametrize(cls, argnames, argvalues, func, config, function_definition): + argnames, force_tuple = cls._parse_parametrize_args(argnames, argvalues) + parameters = cls._parse_parametrize_parameters(argvalues, force_tuple) del argvalues if parameters: # check all parameter sets have the correct number of values for param in parameters: if len(param.values) != len(argnames): - msg = ( - '{nodeid}: in "parametrize" the number of names ({names_len}):\n' - " {names}\n" - "must be equal to the number of values ({values_len}):\n" - " {values}" - ) - fail( - msg.format( - nodeid=function_definition.nodeid, - values=param.values, - names=argnames, - names_len=len(argnames), - values_len=len(param.values), - ), - pytrace=False, + msg = ( + '{nodeid}: in "parametrize" the number of names ({names_len}):\n' + " {names}\n" + "must be equal to the number of values ({values_len}):\n" + " {values}" ) + fail( + msg.format( + nodeid=function_definition.nodeid, + values=param.values, + names=argnames, + names_len=len(argnames), + values_len=len(param.values), + ), + pytrace=False, + ) else: # empty parameter set (likely computed at runtime): create a single - # parameter set with NOTSET values, with the "empty parameter set" mark applied to it + # parameter set with NOTSET values, with the "empty parameter set" mark applied to it mark = get_empty_parameterset_mark(config, argnames, func) parameters.append( ParameterSet(values=(NOTSET,) * len(argnames), marks=[mark], id=None) @@ -160,9 +160,9 @@ class Mark(object): #: name of the mark name = attr.ib(type=str) #: positional arguments of the mark decorator - args = attr.ib() # List[object] + args = attr.ib() # List[object] #: keyword arguments of the mark decorator - kwargs = attr.ib() # Dict[str, object] + kwargs = attr.ib() # Dict[str, object] def combined_with(self, other): """ @@ -170,7 +170,7 @@ class Mark(object): :type other: Mark :rtype: Mark - combines by appending args and merging the mappings + combines by appending args and merging the mappings """ assert self.name == other.name return Mark( @@ -247,7 +247,7 @@ class MarkDecorator(object): func = args[0] is_class = inspect.isclass(func) if len(args) == 1 and (istestfunc(func) or is_class): - store_mark(func, self.mark) + store_mark(func, self.mark) return func return self.with_args(*args, **kwargs) @@ -269,13 +269,13 @@ def normalize_mark_list(mark_list): :type mark_list: List[Union[Mark, Markdecorator]] :rtype: List[Mark] """ - extracted = [ - getattr(mark, "mark", mark) for mark in mark_list - ] # unpack MarkDecorator - for mark in extracted: - if not isinstance(mark, Mark): - raise TypeError("got {!r} instead of Mark".format(mark)) - return [x for x in extracted if isinstance(x, Mark)] + extracted = [ + getattr(mark, "mark", mark) for mark in mark_list + ] # unpack MarkDecorator + for mark in extracted: + if not isinstance(mark, Mark): + raise TypeError("got {!r} instead of Mark".format(mark)) + return [x for x in extracted if isinstance(x, Mark)] def store_mark(obj, mark): @@ -301,43 +301,43 @@ class MarkGenerator(object): on the ``test_function`` object. """ _config = None - _markers = set() + _markers = set() def __getattr__(self, name): if name[0] == "_": raise AttributeError("Marker name must NOT start with underscore") - + if self._config is not None: - # We store a set of markers as a performance optimisation - if a mark - # name is in the set we definitely know it, but a mark may be known and - # not in the set. We therefore start by updating the set! - if name not in self._markers: - for line in self._config.getini("markers"): - # example lines: "skipif(condition): skip the given test if..." - # or "hypothesis: tests which use Hypothesis", so to get the - # marker name we split on both `:` and `(`. - if line == "ya:external": - marker = line - else: - marker = line.split(":")[0].split("(")[0].strip() - self._markers.add(marker) - - # If the name is not in the set of known marks after updating, - # then it really is time to issue a warning or an error. - if name not in self._markers: - if self._config.option.strict_markers: - fail( - "{!r} not found in `markers` configuration option".format(name), - pytrace=False, - ) - else: - warnings.warn( - "Unknown pytest.mark.%s - is this a typo? You can register " - "custom marks to avoid this warning - for details, see " - "https://docs.pytest.org/en/latest/mark.html" % name, - PytestUnknownMarkWarning, - ) - + # We store a set of markers as a performance optimisation - if a mark + # name is in the set we definitely know it, but a mark may be known and + # not in the set. We therefore start by updating the set! + if name not in self._markers: + for line in self._config.getini("markers"): + # example lines: "skipif(condition): skip the given test if..." + # or "hypothesis: tests which use Hypothesis", so to get the + # marker name we split on both `:` and `(`. + if line == "ya:external": + marker = line + else: + marker = line.split(":")[0].split("(")[0].strip() + self._markers.add(marker) + + # If the name is not in the set of known marks after updating, + # then it really is time to issue a warning or an error. + if name not in self._markers: + if self._config.option.strict_markers: + fail( + "{!r} not found in `markers` configuration option".format(name), + pytrace=False, + ) + else: + warnings.warn( + "Unknown pytest.mark.%s - is this a typo? You can register " + "custom marks to avoid this warning - for details, see " + "https://docs.pytest.org/en/latest/mark.html" % name, + PytestUnknownMarkWarning, + ) + return MarkDecorator(Mark(name, (), {})) @@ -381,8 +381,8 @@ class NodeKeywords(MappingMixin): return "<NodeKeywords for node %s>" % (self.node,) -# mypy cannot find this overload, remove when on attrs>=19.2 -@attr.s(hash=False, **{ATTRS_EQ_FIELD: False}) # type: ignore +# mypy cannot find this overload, remove when on attrs>=19.2 +@attr.s(hash=False, **{ATTRS_EQ_FIELD: False}) # type: ignore class NodeMarkers(object): """ internal structure for storing marks belonging to a node diff --git a/contrib/python/pytest/py2/_pytest/monkeypatch.py b/contrib/python/pytest/py2/_pytest/monkeypatch.py index e8671b0c70..f3bbb62e31 100644 --- a/contrib/python/pytest/py2/_pytest/monkeypatch.py +++ b/contrib/python/pytest/py2/_pytest/monkeypatch.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ monkeypatching and mocking functionality. """ from __future__ import absolute_import from __future__ import division @@ -182,8 +182,8 @@ class MonkeyPatch(object): attribute is missing. """ __tracebackhide__ = True - import inspect - + import inspect + if name is notset: if not isinstance(target, six.string_types): raise TypeError( @@ -197,11 +197,11 @@ class MonkeyPatch(object): if raising: raise AttributeError(name) else: - oldval = getattr(target, name, notset) - # Avoid class descriptors like staticmethod/classmethod. - if inspect.isclass(target): - oldval = target.__dict__.get(name, notset) - self._setattr.append((target, name, oldval)) + oldval = getattr(target, name, notset) + # Avoid class descriptors like staticmethod/classmethod. + if inspect.isclass(target): + oldval = target.__dict__.get(name, notset) + self._setattr.append((target, name, oldval)) delattr(target, name) def setitem(self, dic, name, value): @@ -263,27 +263,27 @@ class MonkeyPatch(object): def syspath_prepend(self, path): """ Prepend ``path`` to ``sys.path`` list of import locations. """ - from pkg_resources import fixup_namespace_packages - + from pkg_resources import fixup_namespace_packages + if self._savesyspath is None: self._savesyspath = sys.path[:] sys.path.insert(0, str(path)) - # https://github.com/pypa/setuptools/blob/d8b901bc/docs/pkg_resources.txt#L162-L171 - fixup_namespace_packages(str(path)) - - # A call to syspathinsert() usually means that the caller wants to - # import some dynamically created files, thus with python3 we - # invalidate its import caches. - # This is especially important when any namespace package is in used, - # since then the mtime based FileFinder cache (that gets created in - # this case already) gets not invalidated when writing the new files - # quickly afterwards. - if sys.version_info >= (3, 3): - from importlib import invalidate_caches - - invalidate_caches() - + # https://github.com/pypa/setuptools/blob/d8b901bc/docs/pkg_resources.txt#L162-L171 + fixup_namespace_packages(str(path)) + + # A call to syspathinsert() usually means that the caller wants to + # import some dynamically created files, thus with python3 we + # invalidate its import caches. + # This is especially important when any namespace package is in used, + # since then the mtime based FileFinder cache (that gets created in + # this case already) gets not invalidated when writing the new files + # quickly afterwards. + if sys.version_info >= (3, 3): + from importlib import invalidate_caches + + invalidate_caches() + def chdir(self, path): """ Change the current working directory to the specified path. Path can be a string or a py.path.local object. diff --git a/contrib/python/pytest/py2/_pytest/nodes.py b/contrib/python/pytest/py2/_pytest/nodes.py index 206e9ae163..a68a2cc65d 100644 --- a/contrib/python/pytest/py2/_pytest/nodes.py +++ b/contrib/python/pytest/py2/_pytest/nodes.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- from __future__ import absolute_import from __future__ import division from __future__ import print_function @@ -103,18 +103,18 @@ class Node(object): return self.session.gethookproxy(self.fspath) def __repr__(self): - return "<%s %s>" % (self.__class__.__name__, getattr(self, "name", None)) + return "<%s %s>" % (self.__class__.__name__, getattr(self, "name", None)) - def warn(self, warning): + def warn(self, warning): """Issue a warning for this item. - Warnings will be displayed after the test session, unless explicitly suppressed + Warnings will be displayed after the test session, unless explicitly suppressed - :param Warning warning: the warning instance to issue. Must be a subclass of PytestWarning. + :param Warning warning: the warning instance to issue. Must be a subclass of PytestWarning. - :raise ValueError: if ``warning`` instance is not a subclass of PytestWarning. + :raise ValueError: if ``warning`` instance is not a subclass of PytestWarning. - Example usage: + Example usage: .. code-block:: python @@ -249,7 +249,7 @@ class Node(object): if excinfo.errisinstance(fm.FixtureLookupError): return excinfo.value.formatrepr() tbfilter = True - if self.config.getoption("fulltrace", False): + if self.config.getoption("fulltrace", False): style = "long" else: tb = _pytest._code.Traceback([excinfo.traceback[-1]]) @@ -261,12 +261,12 @@ class Node(object): style = "long" # XXX should excinfo.getrepr record all data and toterminal() process it? if style is None: - if self.config.getoption("tbstyle", "auto") == "short": + if self.config.getoption("tbstyle", "auto") == "short": style = "short" else: style = "long" - if self.config.getoption("verbose", 0) > 1: + if self.config.getoption("verbose", 0) > 1: truncate_locals = False else: truncate_locals = True @@ -280,7 +280,7 @@ class Node(object): return excinfo.getrepr( funcargs=True, abspath=abspath, - showlocals=self.config.getoption("showlocals", False), + showlocals=self.config.getoption("showlocals", False), style=style, tbfilter=tbfilter, truncate_locals=truncate_locals, @@ -327,14 +327,14 @@ class Collector(Node): exc = excinfo.value return str(exc.args[0]) - # Respect explicit tbstyle option, but default to "short" - # (None._repr_failure_py defaults to "long" without "fulltrace" option). - tbstyle = self.config.getoption("tbstyle", "auto") - if tbstyle == "auto": - tbstyle = "short" - - return self._repr_failure_py(excinfo, style=tbstyle) - + # Respect explicit tbstyle option, but default to "short" + # (None._repr_failure_py defaults to "long" without "fulltrace" option). + tbstyle = self.config.getoption("tbstyle", "auto") + if tbstyle == "auto": + tbstyle = "short" + + return self._repr_failure_py(excinfo, style=tbstyle) + def _prunetraceback(self, excinfo): if hasattr(self, "fspath"): traceback = excinfo.traceback diff --git a/contrib/python/pytest/py2/_pytest/nose.py b/contrib/python/pytest/py2/_pytest/nose.py index fbab91da24..3dd41e44eb 100644 --- a/contrib/python/pytest/py2/_pytest/nose.py +++ b/contrib/python/pytest/py2/_pytest/nose.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ run test suites written for nose. """ from __future__ import absolute_import from __future__ import division @@ -6,9 +6,9 @@ from __future__ import print_function import sys -import six - -import pytest +import six + +import pytest from _pytest import python from _pytest import runner from _pytest import unittest @@ -27,9 +27,9 @@ def get_skip_exceptions(): def pytest_runtest_makereport(item, call): if call.excinfo and call.excinfo.errisinstance(get_skip_exceptions()): # let's substitute the excinfo with a pytest.skip one - call2 = runner.CallInfo.from_call( - lambda: pytest.skip(six.text_type(call.excinfo.value)), call.when - ) + call2 = runner.CallInfo.from_call( + lambda: pytest.skip(six.text_type(call.excinfo.value)), call.when + ) call.excinfo = call2.excinfo diff --git a/contrib/python/pytest/py2/_pytest/outcomes.py b/contrib/python/pytest/py2/_pytest/outcomes.py index 4620f957c7..2246ba1413 100644 --- a/contrib/python/pytest/py2/_pytest/outcomes.py +++ b/contrib/python/pytest/py2/_pytest/outcomes.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ exception classes and constants handling test outcomes as well as functions creating them @@ -9,9 +9,9 @@ from __future__ import print_function import sys -from packaging.version import Version - +from packaging.version import Version + class OutcomeException(BaseException): """ OutcomeException and its subclass instances indicate and contain info about test and collection outcomes. @@ -52,13 +52,13 @@ class Failed(OutcomeException): __module__ = "builtins" -class Exit(Exception): +class Exit(Exception): """ raised for immediate program exits (no tracebacks/summaries)""" def __init__(self, msg="unknown reason", returncode=None): self.msg = msg self.returncode = returncode - super(Exit, self).__init__(msg) + super(Exit, self).__init__(msg) # exposed helper methods @@ -66,7 +66,7 @@ class Exit(Exception): def exit(msg, returncode=None): """ - Exit testing process. + Exit testing process. :param str msg: message to display upon exit. :param int returncode: return code to be used when exiting pytest. @@ -83,8 +83,8 @@ def skip(msg="", **kwargs): Skip an executing test with the given message. This function should be called only during testing (setup, call or teardown) or - during collection by using the ``allow_module_level`` flag. This function can - be called in doctests as well. + during collection by using the ``allow_module_level`` flag. This function can + be called in doctests as well. :kwarg bool allow_module_level: allows this function to be called at module level, skipping the rest of the module. Default to False. @@ -93,14 +93,14 @@ def skip(msg="", **kwargs): It is better to use the :ref:`pytest.mark.skipif ref` marker when possible to declare a test to be skipped under certain conditions like mismatching platforms or dependencies. - Similarly, use the ``# doctest: +SKIP`` directive (see `doctest.SKIP - <https://docs.python.org/3/library/doctest.html#doctest.SKIP>`_) - to skip a doctest statically. + Similarly, use the ``# doctest: +SKIP`` directive (see `doctest.SKIP + <https://docs.python.org/3/library/doctest.html#doctest.SKIP>`_) + to skip a doctest statically. """ __tracebackhide__ = True allow_module_level = kwargs.pop("allow_module_level", False) if kwargs: - raise TypeError("unexpected keyword arguments: {}".format(sorted(kwargs))) + raise TypeError("unexpected keyword arguments: {}".format(sorted(kwargs))) raise Skipped(msg=msg, allow_module_level=allow_module_level) @@ -143,21 +143,21 @@ def xfail(reason=""): xfail.Exception = XFailed -def importorskip(modname, minversion=None, reason=None): - """Imports and returns the requested module ``modname``, or skip the current test - if the module cannot be imported. - - :param str modname: the name of the module to import - :param str minversion: if given, the imported module ``__version__`` attribute must be - at least this minimal version, otherwise the test is still skipped. - :param str reason: if given, this reason is shown as the message when the module - cannot be imported. +def importorskip(modname, minversion=None, reason=None): + """Imports and returns the requested module ``modname``, or skip the current test + if the module cannot be imported. + + :param str modname: the name of the module to import + :param str minversion: if given, the imported module ``__version__`` attribute must be + at least this minimal version, otherwise the test is still skipped. + :param str reason: if given, this reason is shown as the message when the module + cannot be imported. """ import warnings __tracebackhide__ = True compile(modname, "", "eval") # to catch syntaxerrors - import_exc = None + import_exc = None with warnings.catch_warnings(): # make sure to ignore ImportWarnings that might happen because @@ -166,19 +166,19 @@ def importorskip(modname, minversion=None, reason=None): warnings.simplefilter("ignore") try: __import__(modname) - except ImportError as exc: + except ImportError as exc: # Do not raise chained exception here(#1485) - import_exc = exc - if import_exc: - if reason is None: - reason = "could not import %r: %s" % (modname, import_exc) - raise Skipped(reason, allow_module_level=True) + import_exc = exc + if import_exc: + if reason is None: + reason = "could not import %r: %s" % (modname, import_exc) + raise Skipped(reason, allow_module_level=True) mod = sys.modules[modname] if minversion is None: return mod verattr = getattr(mod, "__version__", None) if minversion is not None: - if verattr is None or Version(verattr) < Version(minversion): + if verattr is None or Version(verattr) < Version(minversion): raise Skipped( "module %r has __version__ %r, required is: %r" % (modname, verattr, minversion), diff --git a/contrib/python/pytest/py2/_pytest/pastebin.py b/contrib/python/pytest/py2/_pytest/pastebin.py index 7a3e80231c..85ff3f8d29 100644 --- a/contrib/python/pytest/py2/_pytest/pastebin.py +++ b/contrib/python/pytest/py2/_pytest/pastebin.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ submit failure or test session information to a pastebin service. """ from __future__ import absolute_import from __future__ import division @@ -77,7 +77,7 @@ def create_new_paste(contents): from urllib.request import urlopen from urllib.parse import urlencode - params = {"code": contents, "lexer": "text", "expiry": "1week"} + params = {"code": contents, "lexer": "text", "expiry": "1week"} url = "https://bpaste.net" response = urlopen(url, data=urlencode(params).encode("ascii")).read() m = re.search(r'href="/raw/(\w+)"', response.decode("utf-8")) diff --git a/contrib/python/pytest/py2/_pytest/pathlib.py b/contrib/python/pytest/py2/_pytest/pathlib.py index 42071f4310..2e5bc362b9 100644 --- a/contrib/python/pytest/py2/_pytest/pathlib.py +++ b/contrib/python/pytest/py2/_pytest/pathlib.py @@ -1,6 +1,6 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import - +# -*- coding: utf-8 -*- +from __future__ import absolute_import + import atexit import errno import fnmatch @@ -10,8 +10,8 @@ import os import shutil import sys import uuid -import warnings -from functools import partial +import warnings +from functools import partial from functools import reduce from os.path import expanduser from os.path import expandvars @@ -23,7 +23,7 @@ import six from six.moves import map from .compat import PY36 -from _pytest.warning_types import PytestWarning +from _pytest.warning_types import PytestWarning if PY36: from pathlib import Path, PurePath @@ -43,74 +43,74 @@ def ensure_reset_dir(path): ensures the given path is an empty directory """ if path.exists(): - rm_rf(path) + rm_rf(path) path.mkdir() -def on_rm_rf_error(func, path, exc, **kwargs): - """Handles known read-only errors during rmtree. - - The returned value is used only by our own tests. - """ - start_path = kwargs["start_path"] - exctype, excvalue = exc[:2] - - # another process removed the file in the middle of the "rm_rf" (xdist for example) - # more context: https://github.com/pytest-dev/pytest/issues/5974#issuecomment-543799018 - if isinstance(excvalue, OSError) and excvalue.errno == errno.ENOENT: - return False - - if not isinstance(excvalue, OSError) or excvalue.errno not in ( - errno.EACCES, - errno.EPERM, - ): - warnings.warn( - PytestWarning( - "(rm_rf) error removing {}\n{}: {}".format(path, exctype, excvalue) - ) - ) - return False - - if func not in (os.rmdir, os.remove, os.unlink): - warnings.warn( - PytestWarning( - "(rm_rf) unknown function {} when removing {}:\n{}: {}".format( - path, func, exctype, excvalue - ) - ) - ) - return False - - # Chmod + retry. - import stat - - def chmod_rw(p): - mode = os.stat(p).st_mode - os.chmod(p, mode | stat.S_IRUSR | stat.S_IWUSR) - - # For files, we need to recursively go upwards in the directories to - # ensure they all are also writable. - p = Path(path) - if p.is_file(): - for parent in p.parents: - chmod_rw(str(parent)) - # stop when we reach the original path passed to rm_rf - if parent == start_path: - break - chmod_rw(str(path)) - - func(path) - return True - - -def rm_rf(path): - """Remove the path contents recursively, even if some elements - are read-only. - """ - onerror = partial(on_rm_rf_error, start_path=path) - shutil.rmtree(str(path), onerror=onerror) - - +def on_rm_rf_error(func, path, exc, **kwargs): + """Handles known read-only errors during rmtree. + + The returned value is used only by our own tests. + """ + start_path = kwargs["start_path"] + exctype, excvalue = exc[:2] + + # another process removed the file in the middle of the "rm_rf" (xdist for example) + # more context: https://github.com/pytest-dev/pytest/issues/5974#issuecomment-543799018 + if isinstance(excvalue, OSError) and excvalue.errno == errno.ENOENT: + return False + + if not isinstance(excvalue, OSError) or excvalue.errno not in ( + errno.EACCES, + errno.EPERM, + ): + warnings.warn( + PytestWarning( + "(rm_rf) error removing {}\n{}: {}".format(path, exctype, excvalue) + ) + ) + return False + + if func not in (os.rmdir, os.remove, os.unlink): + warnings.warn( + PytestWarning( + "(rm_rf) unknown function {} when removing {}:\n{}: {}".format( + path, func, exctype, excvalue + ) + ) + ) + return False + + # Chmod + retry. + import stat + + def chmod_rw(p): + mode = os.stat(p).st_mode + os.chmod(p, mode | stat.S_IRUSR | stat.S_IWUSR) + + # For files, we need to recursively go upwards in the directories to + # ensure they all are also writable. + p = Path(path) + if p.is_file(): + for parent in p.parents: + chmod_rw(str(parent)) + # stop when we reach the original path passed to rm_rf + if parent == start_path: + break + chmod_rw(str(path)) + + func(path) + return True + + +def rm_rf(path): + """Remove the path contents recursively, even if some elements + are read-only. + """ + onerror = partial(on_rm_rf_error, start_path=path) + shutil.rmtree(str(path), onerror=onerror) + + def find_prefixed(root, prefix): """finds all elements in root that begin with the prefix, case insensitive""" l_prefix = prefix.lower() @@ -246,7 +246,7 @@ def maybe_delete_a_numbered_dir(path): garbage = parent.joinpath("garbage-{}".format(uuid.uuid4())) path.rename(garbage) - rm_rf(garbage) + rm_rf(garbage) except (OSError, EnvironmentError): # known races: # * other process did a cleanup at the same time diff --git a/contrib/python/pytest/py2/_pytest/pytester.py b/contrib/python/pytest/py2/_pytest/pytester.py index f1d739c991..c2101e68d3 100644 --- a/contrib/python/pytest/py2/_pytest/pytester.py +++ b/contrib/python/pytest/py2/_pytest/pytester.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """(disabled by default) support for testing pytest and pytest plugins.""" from __future__ import absolute_import from __future__ import division @@ -21,16 +21,16 @@ import six import pytest from _pytest._code import Source -from _pytest._io.saferepr import saferepr +from _pytest._io.saferepr import saferepr from _pytest.assertion.rewrite import AssertionRewritingHook from _pytest.capture import MultiCapture from _pytest.capture import SysCapture from _pytest.compat import safe_str -from _pytest.compat import Sequence +from _pytest.compat import Sequence from _pytest.main import EXIT_INTERRUPTED from _pytest.main import EXIT_OK from _pytest.main import Session -from _pytest.monkeypatch import MonkeyPatch +from _pytest.monkeypatch import MonkeyPatch from _pytest.pathlib import Path IGNORE_PAM = [ # filenames added when obtaining details about the current user @@ -69,19 +69,19 @@ def pytest_configure(config): if checker.matching_platform(): config.pluginmanager.register(checker) - config.addinivalue_line( - "markers", - "pytester_example_path(*path_segments): join the given path " - "segments to `pytester_example_dir` for this test.", - ) - + config.addinivalue_line( + "markers", + "pytester_example_path(*path_segments): join the given path " + "segments to `pytester_example_dir` for this test.", + ) + def raise_on_kwargs(kwargs): - __tracebackhide__ = True - if kwargs: # pragma: no branch - raise TypeError( - "Unexpected keyword arguments: {}".format(", ".join(sorted(kwargs))) - ) + __tracebackhide__ = True + if kwargs: # pragma: no branch + raise TypeError( + "Unexpected keyword arguments: {}".format(", ".join(sorted(kwargs))) + ) class LsofFdLeakChecker(object): @@ -92,11 +92,11 @@ class LsofFdLeakChecker(object): def _exec_lsof(self): pid = os.getpid() - # py3: use subprocess.DEVNULL directly. - with open(os.devnull, "wb") as devnull: - return subprocess.check_output( - ("lsof", "-Ffn0", "-p", str(pid)), stderr=devnull - ).decode() + # py3: use subprocess.DEVNULL directly. + with open(os.devnull, "wb") as devnull: + return subprocess.check_output( + ("lsof", "-Ffn0", "-p", str(pid)), stderr=devnull + ).decode() def _parse_lsof_output(self, out): def isopen(line): @@ -123,8 +123,8 @@ class LsofFdLeakChecker(object): def matching_platform(self): try: - subprocess.check_output(("lsof", "-v")) - except (OSError, subprocess.CalledProcessError): + subprocess.check_output(("lsof", "-v")) + except (OSError, subprocess.CalledProcessError): return False else: return True @@ -270,11 +270,11 @@ class HookRecorder(object): """return a testreport whose dotted import path matches""" values = [] for rep in self.getreports(names=names): - if not when and rep.when != "call" and rep.passed: - # setup/teardown passing reports - let's ignore those - continue - if when and rep.when != when: + if not when and rep.when != "call" and rep.passed: + # setup/teardown passing reports - let's ignore those continue + if when and rep.when != when: + continue if not inamepart or inamepart in rep.nodeid.split("::"): values.append(rep) if not values: @@ -300,12 +300,12 @@ class HookRecorder(object): failed = [] for rep in self.getreports("pytest_collectreport pytest_runtest_logreport"): if rep.passed: - if rep.when == "call": + if rep.when == "call": passed.append(rep) elif rep.skipped: skipped.append(rep) - else: - assert rep.failed, "Unexpected outcome: {!r}".format(rep) + else: + assert rep.failed, "Unexpected outcome: {!r}".format(rep) failed.append(rep) return passed, skipped, failed @@ -337,24 +337,24 @@ def testdir(request, tmpdir_factory): return Testdir(request, tmpdir_factory) -@pytest.fixture -def _sys_snapshot(): - snappaths = SysPathsSnapshot() - snapmods = SysModulesSnapshot() - yield - snapmods.restore() - snappaths.restore() - - -@pytest.fixture -def _config_for_test(): - from _pytest.config import get_config - - config = get_config() - yield config - config._ensure_unconfigure() # cleanup, e.g. capman closing tmpfiles. - - +@pytest.fixture +def _sys_snapshot(): + snappaths = SysPathsSnapshot() + snapmods = SysModulesSnapshot() + yield + snapmods.restore() + snappaths.restore() + + +@pytest.fixture +def _config_for_test(): + from _pytest.config import get_config + + config = get_config() + yield config + config._ensure_unconfigure() # cleanup, e.g. capman closing tmpfiles. + + rex_outcome = re.compile(r"(\d+) ([\w-]+)") @@ -382,12 +382,12 @@ class RunResult(object): self.stderr = LineMatcher(errlines) self.duration = duration - def __repr__(self): - return ( - "<RunResult ret=%r len(stdout.lines)=%d len(stderr.lines)=%d duration=%.2fs>" - % (self.ret, len(self.stdout.lines), len(self.stderr.lines), self.duration) - ) - + def __repr__(self): + return ( + "<RunResult ret=%r len(stdout.lines)=%d len(stderr.lines)=%d duration=%.2fs>" + % (self.ret, len(self.stdout.lines), len(self.stderr.lines), self.duration) + ) + def parseoutcomes(self): """Return a dictionary of outcomestring->num from parsing the terminal output that the test process produced. @@ -478,8 +478,8 @@ class Testdir(object): """ - CLOSE_STDIN = object - + CLOSE_STDIN = object + class TimeoutExpired(Exception): pass @@ -501,17 +501,17 @@ class Testdir(object): elif method == "subprocess": self._runpytest_method = self.runpytest_subprocess - mp = self.monkeypatch = MonkeyPatch() - mp.setenv("PYTEST_DEBUG_TEMPROOT", str(self.test_tmproot)) - # Ensure no unexpected caching via tox. - mp.delenv("TOX_ENV_DIR", raising=False) - # Discard outer pytest options. - mp.delenv("PYTEST_ADDOPTS", raising=False) - - # Environment (updates) for inner runs. - tmphome = str(self.tmpdir) - self._env_run_update = {"HOME": tmphome, "USERPROFILE": tmphome} - + mp = self.monkeypatch = MonkeyPatch() + mp.setenv("PYTEST_DEBUG_TEMPROOT", str(self.test_tmproot)) + # Ensure no unexpected caching via tox. + mp.delenv("TOX_ENV_DIR", raising=False) + # Discard outer pytest options. + mp.delenv("PYTEST_ADDOPTS", raising=False) + + # Environment (updates) for inner runs. + tmphome = str(self.tmpdir) + self._env_run_update = {"HOME": tmphome, "USERPROFILE": tmphome} + def __repr__(self): return "<Testdir %r>" % (self.tmpdir,) @@ -529,7 +529,7 @@ class Testdir(object): self._sys_modules_snapshot.restore() self._sys_path_snapshot.restore() self._cwd_snapshot.restore() - self.monkeypatch.undo() + self.monkeypatch.undo() def __take_sys_modules_snapshot(self): # some zope modules used by twisted-related tests keep internal state @@ -627,7 +627,7 @@ class Testdir(object): if path is None: path = self.tmpdir - self.monkeypatch.syspath_prepend(str(path)) + self.monkeypatch.syspath_prepend(str(path)) def mkdir(self, name): """Create a new (sub)directory.""" @@ -670,7 +670,7 @@ class Testdir(object): else: raise LookupError( "{} cant be found as module or package in {}".format( - func_name, example_dir.bestrelpath(self.request.config.rootdir) + func_name, example_dir.bestrelpath(self.request.config.rootdir) ) ) else: @@ -795,23 +795,23 @@ class Testdir(object): :param args: command line arguments to pass to :py:func:`pytest.main` - :param plugins: (keyword-only) extra plugin instances the + :param plugins: (keyword-only) extra plugin instances the ``pytest.main()`` instance should use :return: a :py:class:`HookRecorder` instance - """ - plugins = kwargs.pop("plugins", []) - no_reraise_ctrlc = kwargs.pop("no_reraise_ctrlc", None) - raise_on_kwargs(kwargs) + """ + plugins = kwargs.pop("plugins", []) + no_reraise_ctrlc = kwargs.pop("no_reraise_ctrlc", None) + raise_on_kwargs(kwargs) finalizers = [] try: - # Do not load user config (during runs only). - mp_run = MonkeyPatch() - for k, v in self._env_run_update.items(): - mp_run.setenv(k, v) - finalizers.append(mp_run.undo) - + # Do not load user config (during runs only). + mp_run = MonkeyPatch() + for k, v in self._env_run_update.items(): + mp_run.setenv(k, v) + finalizers.append(mp_run.undo) + # When running pytest inline any plugins active in the main test # process are already imported. So this disables the warning which # will trigger to say they can no longer be rewritten, which is @@ -856,7 +856,7 @@ class Testdir(object): # typically we reraise keyboard interrupts from the child run # because it's our user requesting interruption of the testing - if ret == EXIT_INTERRUPTED and not no_reraise_ctrlc: + if ret == EXIT_INTERRUPTED and not no_reraise_ctrlc: calls = reprec.getcalls("pytest_keyboard_interrupt") if calls and calls[-1].excinfo.type == KeyboardInterrupt: raise KeyboardInterrupt() @@ -868,10 +868,10 @@ class Testdir(object): def runpytest_inprocess(self, *args, **kwargs): """Return result of running pytest in-process, providing a similar interface to what self.runpytest() provides. - """ - syspathinsert = kwargs.pop("syspathinsert", False) + """ + syspathinsert = kwargs.pop("syspathinsert", False) - if syspathinsert: + if syspathinsert: self.syspathinsert() now = time.time() capture = MultiCapture(Capture=SysCapture) @@ -1029,14 +1029,14 @@ class Testdir(object): if colitem.name == name: return colitem - def popen( - self, - cmdargs, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - stdin=CLOSE_STDIN, - **kw - ): + def popen( + self, + cmdargs, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=CLOSE_STDIN, + **kw + ): """Invoke subprocess.Popen. This calls subprocess.Popen making sure the current working directory @@ -1049,22 +1049,22 @@ class Testdir(object): env["PYTHONPATH"] = os.pathsep.join( filter(None, [os.getcwd(), env.get("PYTHONPATH", "")]) ) - env.update(self._env_run_update) + env.update(self._env_run_update) kw["env"] = env - if stdin is Testdir.CLOSE_STDIN: - kw["stdin"] = subprocess.PIPE - elif isinstance(stdin, bytes): - kw["stdin"] = subprocess.PIPE - else: - kw["stdin"] = stdin - - popen = subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr, **kw) - if stdin is Testdir.CLOSE_STDIN: - popen.stdin.close() - elif isinstance(stdin, bytes): - popen.stdin.write(stdin) - + if stdin is Testdir.CLOSE_STDIN: + kw["stdin"] = subprocess.PIPE + elif isinstance(stdin, bytes): + kw["stdin"] = subprocess.PIPE + else: + kw["stdin"] = stdin + + popen = subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr, **kw) + if stdin is Testdir.CLOSE_STDIN: + popen.stdin.close() + elif isinstance(stdin, bytes): + popen.stdin.write(stdin) + return popen def run(self, *cmdargs, **kwargs): @@ -1075,10 +1075,10 @@ class Testdir(object): :param args: the sequence of arguments to pass to `subprocess.Popen()` :param timeout: the period in seconds after which to timeout and raise :py:class:`Testdir.TimeoutExpired` - :param stdin: optional standard input. Bytes are being send, closing - the pipe, otherwise it is passed through to ``popen``. - Defaults to ``CLOSE_STDIN``, which translates to using a pipe - (``subprocess.PIPE``) that gets closed. + :param stdin: optional standard input. Bytes are being send, closing + the pipe, otherwise it is passed through to ``popen``. + Defaults to ``CLOSE_STDIN``, which translates to using a pipe + (``subprocess.PIPE``) that gets closed. Returns a :py:class:`RunResult`. @@ -1086,7 +1086,7 @@ class Testdir(object): __tracebackhide__ = True timeout = kwargs.pop("timeout", None) - stdin = kwargs.pop("stdin", Testdir.CLOSE_STDIN) + stdin = kwargs.pop("stdin", Testdir.CLOSE_STDIN) raise_on_kwargs(kwargs) cmdargs = [ @@ -1101,14 +1101,14 @@ class Testdir(object): try: now = time.time() popen = self.popen( - cmdargs, - stdin=stdin, - stdout=f1, - stderr=f2, - close_fds=(sys.platform != "win32"), + cmdargs, + stdin=stdin, + stdout=f1, + stderr=f2, + close_fds=(sys.platform != "win32"), ) - if isinstance(stdin, bytes): - popen.stdin.close() + if isinstance(stdin, bytes): + popen.stdin.close() def handle_timeout(): __tracebackhide__ = True @@ -1124,7 +1124,7 @@ class Testdir(object): if timeout is None: ret = popen.wait() - elif not six.PY2: + elif not six.PY2: try: ret = popen.wait(timeout) except subprocess.TimeoutExpired: @@ -1196,8 +1196,8 @@ class Testdir(object): Returns a :py:class:`RunResult`. """ __tracebackhide__ = True - timeout = kwargs.pop("timeout", None) - raise_on_kwargs(kwargs) + timeout = kwargs.pop("timeout", None) + raise_on_kwargs(kwargs) p = py.path.local.make_numbered_dir( prefix="runpytest-", keep=None, rootdir=self.tmpdir @@ -1207,7 +1207,7 @@ class Testdir(object): if plugins: args = ("-p", plugins[0]) + args args = self._getpytestargs() + args - return self.run(*args, timeout=timeout) + return self.run(*args, timeout=timeout) def spawn_pytest(self, string, expect_timeout=10.0): """Run pytest using pexpect. @@ -1235,12 +1235,12 @@ class Testdir(object): if sys.platform.startswith("freebsd"): pytest.xfail("pexpect does not work reliably on freebsd") logfile = self.tmpdir.join("spawn.out").open("wb") - - # Do not load user config. - env = os.environ.copy() - env.update(self._env_run_update) - - child = pexpect.spawn(cmd, logfile=logfile, env=env) + + # Do not load user config. + env = os.environ.copy() + env.update(self._env_run_update) + + child = pexpect.spawn(cmd, logfile=logfile, env=env) self.request.addfinalizer(logfile.close) child.timeout = expect_timeout return child @@ -1250,7 +1250,7 @@ def getdecoded(out): try: return out.decode("utf-8") except UnicodeDecodeError: - return "INTERNAL not-utf8-decodeable, truncated string:\n%s" % (saferepr(out),) + return "INTERNAL not-utf8-decodeable, truncated string:\n%s" % (saferepr(out),) class LineComp(object): @@ -1344,7 +1344,7 @@ class LineMatcher(object): raise ValueError("line %r not found in output" % fnline) def _log(self, *args): - self._log_output.append(" ".join(str(x) for x in args)) + self._log_output.append(" ".join(str(x) for x in args)) @property def _log_text(self): @@ -1385,7 +1385,7 @@ class LineMatcher(object): will be logged to stdout when a match occurs """ - assert isinstance(lines2, Sequence) + assert isinstance(lines2, Sequence) lines2 = self._getlines(lines2) lines1 = self.lines[:] nextline = None diff --git a/contrib/python/pytest/py2/_pytest/python.py b/contrib/python/pytest/py2/_pytest/python.py index f7c368b0c4..244c22e191 100644 --- a/contrib/python/pytest/py2/_pytest/python.py +++ b/contrib/python/pytest/py2/_pytest/python.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ Python test discovery, setup and run of test functions. """ from __future__ import absolute_import from __future__ import division @@ -10,7 +10,7 @@ import inspect import os import sys import warnings -from functools import partial +from functools import partial from textwrap import dedent import py @@ -39,14 +39,14 @@ from _pytest.compat import safe_str from _pytest.compat import STRING_TYPES from _pytest.config import hookimpl from _pytest.main import FSHookProxy -from _pytest.mark import MARK_GEN +from _pytest.mark import MARK_GEN from _pytest.mark.structures import get_unpacked_marks from _pytest.mark.structures import normalize_mark_list from _pytest.outcomes import fail -from _pytest.outcomes import skip +from _pytest.outcomes import skip from _pytest.pathlib import parts -from _pytest.warning_types import PytestCollectionWarning -from _pytest.warning_types import PytestUnhandledCoroutineWarning +from _pytest.warning_types import PytestCollectionWarning +from _pytest.warning_types import PytestUnhandledCoroutineWarning def pyobj_property(name): @@ -82,7 +82,7 @@ def pytest_addoption(parser): parser.addini( "python_files", type="args", - # NOTE: default is also used in AssertionRewritingHook. + # NOTE: default is also used in AssertionRewritingHook. default=["test_*.py", "*_test.py"], help="glob-style file patterns for Python test module discovery", ) @@ -98,13 +98,13 @@ def pytest_addoption(parser): default=["test"], help="prefixes or glob names for Python test function and method discovery", ) - parser.addini( - "disable_test_id_escaping_and_forfeit_all_rights_to_community_support", - type="bool", - default=False, - help="disable string escape non-ascii characters, might cause unwanted " - "side effects(use at your own risk)", - ) + parser.addini( + "disable_test_id_escaping_and_forfeit_all_rights_to_community_support", + type="bool", + default=False, + help="disable string escape non-ascii characters, might cause unwanted " + "side effects(use at your own risk)", + ) group.addoption( "--import-mode", @@ -129,10 +129,10 @@ def pytest_generate_tests(metafunc): # those alternative spellings are common - raise a specific error to alert # the user alt_spellings = ["parameterize", "parametrise", "parameterise"] - for mark_name in alt_spellings: - if metafunc.definition.get_closest_marker(mark_name): + for mark_name in alt_spellings: + if metafunc.definition.get_closest_marker(mark_name): msg = "{0} has '{1}' mark, spelling should be 'parametrize'" - fail(msg.format(metafunc.function.__name__, mark_name), pytrace=False) + fail(msg.format(metafunc.function.__name__, mark_name), pytrace=False) for marker in metafunc.definition.iter_markers(name="parametrize"): metafunc.parametrize(*marker.args, **marker.kwargs) @@ -160,18 +160,18 @@ def pytest_configure(config): @hookimpl(trylast=True) def pytest_pyfunc_call(pyfuncitem): testfunction = pyfuncitem.obj - iscoroutinefunction = getattr(inspect, "iscoroutinefunction", None) - if iscoroutinefunction is not None and iscoroutinefunction(testfunction): - msg = "Coroutine functions are not natively supported and have been skipped.\n" - msg += "You need to install a suitable plugin for your async framework, for example:\n" - msg += " - pytest-asyncio\n" - msg += " - pytest-trio\n" - msg += " - pytest-tornasync" - warnings.warn(PytestUnhandledCoroutineWarning(msg.format(pyfuncitem.nodeid))) - skip(msg="coroutine function and no async plugin installed (see warnings)") - funcargs = pyfuncitem.funcargs - testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames} - testfunction(**testargs) + iscoroutinefunction = getattr(inspect, "iscoroutinefunction", None) + if iscoroutinefunction is not None and iscoroutinefunction(testfunction): + msg = "Coroutine functions are not natively supported and have been skipped.\n" + msg += "You need to install a suitable plugin for your async framework, for example:\n" + msg += " - pytest-asyncio\n" + msg += " - pytest-trio\n" + msg += " - pytest-tornasync" + warnings.warn(PytestUnhandledCoroutineWarning(msg.format(pyfuncitem.nodeid))) + skip(msg="coroutine function and no async plugin installed (see warnings)") + funcargs = pyfuncitem.funcargs + testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames} + testfunction(**testargs) return True @@ -217,7 +217,7 @@ def pytest_pycollect_makeitem(collector, name, obj): if not (isfunction(obj) or isfunction(get_real_func(obj))): filename, lineno = getfslineno(obj) warnings.warn_explicit( - message=PytestCollectionWarning( + message=PytestCollectionWarning( "cannot collect %r because it is not a function." % name ), category=None, @@ -226,10 +226,10 @@ def pytest_pycollect_makeitem(collector, name, obj): ) elif getattr(obj, "__test__", True): if is_generator(obj): - res = Function(name, parent=collector) - reason = deprecated.YIELD_TESTS.format(name=name) - res.add_marker(MARK_GEN.xfail(run=False, reason=reason)) - res.warn(PytestCollectionWarning(reason)) + res = Function(name, parent=collector) + reason = deprecated.YIELD_TESTS.format(name=name) + res.add_marker(MARK_GEN.xfail(run=False, reason=reason)) + res.warn(PytestCollectionWarning(reason)) else: res = list(collector._genfunctions(name, obj)) outcome.force_result(res) @@ -251,24 +251,24 @@ class PyobjMixin(PyobjContext): def __init__(self, *k, **kw): super(PyobjMixin, self).__init__(*k, **kw) - @property - def obj(self): - """Underlying Python object.""" - obj = getattr(self, "_obj", None) - if obj is None: - self._obj = obj = self._getobj() - # XXX evil hack - # used to avoid Instance collector marker duplication - if self._ALLOW_MARKERS: - self.own_markers.extend(get_unpacked_marks(self.obj)) - return obj - - @obj.setter - def obj(self, value): - self._obj = value + @property + def obj(self): + """Underlying Python object.""" + obj = getattr(self, "_obj", None) + if obj is None: + self._obj = obj = self._getobj() + # XXX evil hack + # used to avoid Instance collector marker duplication + if self._ALLOW_MARKERS: + self.own_markers.extend(get_unpacked_marks(self.obj)) + return obj + + @obj.setter + def obj(self, value): + self._obj = value def _getobj(self): - """Gets the underlying Python object. May be overwritten by subclasses.""" + """Gets the underlying Python object. May be overwritten by subclasses.""" return getattr(self.parent.obj, self.name) def getmodpath(self, stopatmodule=True, includemodule=False): @@ -440,66 +440,66 @@ class Module(nodes.File, PyCollector): return self._importtestmodule() def collect(self): - self._inject_setup_module_fixture() - self._inject_setup_function_fixture() + self._inject_setup_module_fixture() + self._inject_setup_function_fixture() self.session._fixturemanager.parsefactories(self) return super(Module, self).collect() - def _inject_setup_module_fixture(self): - """Injects a hidden autouse, module scoped fixture into the collected module object - that invokes setUpModule/tearDownModule if either or both are available. - - Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with - other fixtures (#517). - """ - setup_module = _get_non_fixture_func(self.obj, "setUpModule") - if setup_module is None: - setup_module = _get_non_fixture_func(self.obj, "setup_module") - - teardown_module = _get_non_fixture_func(self.obj, "tearDownModule") - if teardown_module is None: - teardown_module = _get_non_fixture_func(self.obj, "teardown_module") - - if setup_module is None and teardown_module is None: - return - - @fixtures.fixture(autouse=True, scope="module") - def xunit_setup_module_fixture(request): - if setup_module is not None: - _call_with_optional_argument(setup_module, request.module) - yield - if teardown_module is not None: - _call_with_optional_argument(teardown_module, request.module) - - self.obj.__pytest_setup_module = xunit_setup_module_fixture - - def _inject_setup_function_fixture(self): - """Injects a hidden autouse, function scoped fixture into the collected module object - that invokes setup_function/teardown_function if either or both are available. - - Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with - other fixtures (#517). - """ - setup_function = _get_non_fixture_func(self.obj, "setup_function") - teardown_function = _get_non_fixture_func(self.obj, "teardown_function") - if setup_function is None and teardown_function is None: - return - - @fixtures.fixture(autouse=True, scope="function") - def xunit_setup_function_fixture(request): - if request.instance is not None: - # in this case we are bound to an instance, so we need to let - # setup_method handle this - yield - return - if setup_function is not None: - _call_with_optional_argument(setup_function, request.function) - yield - if teardown_function is not None: - _call_with_optional_argument(teardown_function, request.function) - - self.obj.__pytest_setup_function = xunit_setup_function_fixture - + def _inject_setup_module_fixture(self): + """Injects a hidden autouse, module scoped fixture into the collected module object + that invokes setUpModule/tearDownModule if either or both are available. + + Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with + other fixtures (#517). + """ + setup_module = _get_non_fixture_func(self.obj, "setUpModule") + if setup_module is None: + setup_module = _get_non_fixture_func(self.obj, "setup_module") + + teardown_module = _get_non_fixture_func(self.obj, "tearDownModule") + if teardown_module is None: + teardown_module = _get_non_fixture_func(self.obj, "teardown_module") + + if setup_module is None and teardown_module is None: + return + + @fixtures.fixture(autouse=True, scope="module") + def xunit_setup_module_fixture(request): + if setup_module is not None: + _call_with_optional_argument(setup_module, request.module) + yield + if teardown_module is not None: + _call_with_optional_argument(teardown_module, request.module) + + self.obj.__pytest_setup_module = xunit_setup_module_fixture + + def _inject_setup_function_fixture(self): + """Injects a hidden autouse, function scoped fixture into the collected module object + that invokes setup_function/teardown_function if either or both are available. + + Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with + other fixtures (#517). + """ + setup_function = _get_non_fixture_func(self.obj, "setup_function") + teardown_function = _get_non_fixture_func(self.obj, "teardown_function") + if setup_function is None and teardown_function is None: + return + + @fixtures.fixture(autouse=True, scope="function") + def xunit_setup_function_fixture(request): + if request.instance is not None: + # in this case we are bound to an instance, so we need to let + # setup_method handle this + yield + return + if setup_function is not None: + _call_with_optional_argument(setup_function, request.function) + yield + if teardown_function is not None: + _call_with_optional_argument(teardown_function, request.function) + + self.obj.__pytest_setup_function = xunit_setup_function_fixture + def _importtestmodule(self): # we assume we are only called once per module importmode = self.config.getoption("--import-mode") @@ -507,7 +507,7 @@ class Module(nodes.File, PyCollector): mod = self.fspath.pyimport(ensuresyspath=importmode) except SyntaxError: raise self.CollectError( - _pytest._code.ExceptionInfo.from_current().getrepr(style="short") + _pytest._code.ExceptionInfo.from_current().getrepr(style="short") ) except self.fspath.ImportMismatchError: e = sys.exc_info()[1] @@ -523,7 +523,7 @@ class Module(nodes.File, PyCollector): except ImportError: from _pytest._code.code import ExceptionInfo - exc_info = ExceptionInfo.from_current() + exc_info = ExceptionInfo.from_current() if self.config.getoption("verbose") < 2: exc_info.traceback = exc_info.traceback.filter(filter_traceback) exc_repr = ( @@ -562,22 +562,22 @@ class Package(Module): self._norecursepatterns = session._norecursepatterns self.fspath = fspath - def setup(self): - # not using fixtures to call setup_module here because autouse fixtures - # from packages are not called automatically (#4085) - setup_module = _get_non_fixture_func(self.obj, "setUpModule") - if setup_module is None: - setup_module = _get_non_fixture_func(self.obj, "setup_module") - if setup_module is not None: - _call_with_optional_argument(setup_module, self.obj) - - teardown_module = _get_non_fixture_func(self.obj, "tearDownModule") - if teardown_module is None: - teardown_module = _get_non_fixture_func(self.obj, "teardown_module") - if teardown_module is not None: - func = partial(_call_with_optional_argument, teardown_module, self.obj) - self.addfinalizer(func) - + def setup(self): + # not using fixtures to call setup_module here because autouse fixtures + # from packages are not called automatically (#4085) + setup_module = _get_non_fixture_func(self.obj, "setUpModule") + if setup_module is None: + setup_module = _get_non_fixture_func(self.obj, "setup_module") + if setup_module is not None: + _call_with_optional_argument(setup_module, self.obj) + + teardown_module = _get_non_fixture_func(self.obj, "tearDownModule") + if teardown_module is None: + teardown_module = _get_non_fixture_func(self.obj, "teardown_module") + if teardown_module is not None: + func = partial(_call_with_optional_argument, teardown_module, self.obj) + self.addfinalizer(func) + def _recurse(self, dirpath): if dirpath.basename == "__pycache__": return False @@ -606,12 +606,12 @@ class Package(Module): return proxy def _collectfile(self, path, handle_dupes=True): - assert path.isfile(), "%r is not a file (isdir=%r, exists=%r, islink=%r)" % ( - path, - path.isdir(), - path.exists(), - path.islink(), - ) + assert path.isfile(), "%r is not a file (isdir=%r, exists=%r, islink=%r)" % ( + path, + path.isdir(), + path.exists(), + path.islink(), + ) ihook = self.gethookproxy(path) if not self.isinitpath(path): if ihook.pytest_ignore_collect(path=path, config=self.config): @@ -644,8 +644,8 @@ class Package(Module): pkg_prefixes = set() for path in this_path.visit(rec=self._recurse, bf=True, sort=True): # We will visit our own __init__.py file, in which case we skip it. - is_file = path.isfile() - if is_file: + is_file = path.isfile() + if is_file: if path.basename == "__init__.py" and path.dirpath() == this_path: continue @@ -656,13 +656,13 @@ class Package(Module): ): continue - if is_file: - for x in self._collectfile(path): - yield x - elif not path.isdir(): - # Broken symlink or invalid/missing file. - continue - elif path.join("__init__.py").check(file=1): + if is_file: + for x in self._collectfile(path): + yield x + elif not path.isdir(): + # Broken symlink or invalid/missing file. + continue + elif path.join("__init__.py").check(file=1): pkg_prefixes.add(path) @@ -674,9 +674,9 @@ def _get_xunit_setup_teardown(holder, attr_name, param_obj=None): when the callable is called without arguments, defaults to the ``holder`` object. Return ``None`` if a suitable callable is not found. """ - # TODO: only needed because of Package! + # TODO: only needed because of Package! param_obj = param_obj if param_obj is not None else holder - result = _get_non_fixture_func(holder, attr_name) + result = _get_non_fixture_func(holder, attr_name) if result is not None: arg_count = result.__code__.co_argcount if inspect.ismethod(result): @@ -687,19 +687,19 @@ def _get_xunit_setup_teardown(holder, attr_name, param_obj=None): return result -def _call_with_optional_argument(func, arg): - """Call the given function with the given argument if func accepts one argument, otherwise - calls func without arguments""" - arg_count = func.__code__.co_argcount - if inspect.ismethod(func): - arg_count -= 1 - if arg_count: - func(arg) - else: - func() - - -def _get_non_fixture_func(obj, name): +def _call_with_optional_argument(func, arg): + """Call the given function with the given argument if func accepts one argument, otherwise + calls func without arguments""" + arg_count = func.__code__.co_argcount + if inspect.ismethod(func): + arg_count -= 1 + if arg_count: + func(arg) + else: + func() + + +def _get_non_fixture_func(obj, name): """Return the attribute from the given object to be used as a setup/teardown xunit-style function, but only if not marked as a fixture to avoid calling it twice. @@ -717,78 +717,78 @@ class Class(PyCollector): return [] if hasinit(self.obj): self.warn( - PytestCollectionWarning( + PytestCollectionWarning( "cannot collect test class %r because it has a " - "__init__ constructor (from: %s)" - % (self.obj.__name__, self.parent.nodeid) + "__init__ constructor (from: %s)" + % (self.obj.__name__, self.parent.nodeid) ) ) return [] elif hasnew(self.obj): self.warn( - PytestCollectionWarning( + PytestCollectionWarning( "cannot collect test class %r because it has a " - "__new__ constructor (from: %s)" - % (self.obj.__name__, self.parent.nodeid) + "__new__ constructor (from: %s)" + % (self.obj.__name__, self.parent.nodeid) ) ) return [] - self._inject_setup_class_fixture() - self._inject_setup_method_fixture() - - return [Instance(name="()", parent=self)] - - def _inject_setup_class_fixture(self): - """Injects a hidden autouse, class scoped fixture into the collected class object - that invokes setup_class/teardown_class if either or both are available. - - Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with - other fixtures (#517). - """ - setup_class = _get_non_fixture_func(self.obj, "setup_class") - teardown_class = getattr(self.obj, "teardown_class", None) - if setup_class is None and teardown_class is None: - return - - @fixtures.fixture(autouse=True, scope="class") - def xunit_setup_class_fixture(cls): - if setup_class is not None: - func = getimfunc(setup_class) - _call_with_optional_argument(func, self.obj) - yield - if teardown_class is not None: - func = getimfunc(teardown_class) - _call_with_optional_argument(func, self.obj) - - self.obj.__pytest_setup_class = xunit_setup_class_fixture - - def _inject_setup_method_fixture(self): - """Injects a hidden autouse, function scoped fixture into the collected class object - that invokes setup_method/teardown_method if either or both are available. - - Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with - other fixtures (#517). - """ - setup_method = _get_non_fixture_func(self.obj, "setup_method") - teardown_method = getattr(self.obj, "teardown_method", None) - if setup_method is None and teardown_method is None: - return - - @fixtures.fixture(autouse=True, scope="function") - def xunit_setup_method_fixture(self, request): - method = request.function - if setup_method is not None: - func = getattr(self, "setup_method") - _call_with_optional_argument(func, method) - yield - if teardown_method is not None: - func = getattr(self, "teardown_method") - _call_with_optional_argument(func, method) - - self.obj.__pytest_setup_method = xunit_setup_method_fixture - - + self._inject_setup_class_fixture() + self._inject_setup_method_fixture() + + return [Instance(name="()", parent=self)] + + def _inject_setup_class_fixture(self): + """Injects a hidden autouse, class scoped fixture into the collected class object + that invokes setup_class/teardown_class if either or both are available. + + Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with + other fixtures (#517). + """ + setup_class = _get_non_fixture_func(self.obj, "setup_class") + teardown_class = getattr(self.obj, "teardown_class", None) + if setup_class is None and teardown_class is None: + return + + @fixtures.fixture(autouse=True, scope="class") + def xunit_setup_class_fixture(cls): + if setup_class is not None: + func = getimfunc(setup_class) + _call_with_optional_argument(func, self.obj) + yield + if teardown_class is not None: + func = getimfunc(teardown_class) + _call_with_optional_argument(func, self.obj) + + self.obj.__pytest_setup_class = xunit_setup_class_fixture + + def _inject_setup_method_fixture(self): + """Injects a hidden autouse, function scoped fixture into the collected class object + that invokes setup_method/teardown_method if either or both are available. + + Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with + other fixtures (#517). + """ + setup_method = _get_non_fixture_func(self.obj, "setup_method") + teardown_method = getattr(self.obj, "teardown_method", None) + if setup_method is None and teardown_method is None: + return + + @fixtures.fixture(autouse=True, scope="function") + def xunit_setup_method_fixture(self, request): + method = request.function + if setup_method is not None: + func = getattr(self, "setup_method") + _call_with_optional_argument(func, method) + yield + if teardown_method is not None: + func = getattr(self, "teardown_method") + _call_with_optional_argument(func, method) + + self.obj.__pytest_setup_method = xunit_setup_method_fixture + + class Instance(PyCollector): _ALLOW_MARKERS = False # hack, destroy later # instances share the object with their parents in a way @@ -813,12 +813,12 @@ class FunctionMixin(PyobjMixin): def setup(self): """ perform setup for this test function. """ - if isinstance(self.parent, Instance): - self.parent.newinstance() + if isinstance(self.parent, Instance): + self.parent.newinstance() self.obj = self._getobj() def _prunetraceback(self, excinfo): - if hasattr(self, "_obj") and not self.config.getoption("fulltrace", False): + if hasattr(self, "_obj") and not self.config.getoption("fulltrace", False): code = _pytest._code.Code(get_real_func(self.obj)) path, firstlineno = code.path, code.firstlineno traceback = excinfo.traceback @@ -833,14 +833,14 @@ class FunctionMixin(PyobjMixin): excinfo.traceback = ntraceback.filter() # issue364: mark all but first and last frames to # only show a single-line message for each frame - if self.config.getoption("tbstyle", "auto") == "auto": + if self.config.getoption("tbstyle", "auto") == "auto": if len(excinfo.traceback) > 2: for entry in excinfo.traceback[1:-1]: entry.set_repr_style("short") def repr_failure(self, excinfo, outerr=None): assert outerr is None, "XXX outerr usage is deprecated" - style = self.config.getoption("tbstyle", "auto") + style = self.config.getoption("tbstyle", "auto") if style == "auto": style = "long" return self._repr_failure_py(excinfo, style=style) @@ -1048,7 +1048,7 @@ class Metafunc(fixtures.FuncargnamesCompatAttr): :rtype: List[str] :return: the list of ids for each argname given """ - from _pytest._io.saferepr import saferepr + from _pytest._io.saferepr import saferepr idfn = None if callable(ids): @@ -1161,30 +1161,30 @@ def _find_parametrized_scope(argnames, arg2fixturedefs, indirect): return "function" -def _ascii_escaped_by_config(val, config): - if config is None: - escape_option = False - else: - escape_option = config.getini( - "disable_test_id_escaping_and_forfeit_all_rights_to_community_support" - ) - return val if escape_option else ascii_escaped(val) - - +def _ascii_escaped_by_config(val, config): + if config is None: + escape_option = False + else: + escape_option = config.getini( + "disable_test_id_escaping_and_forfeit_all_rights_to_community_support" + ) + return val if escape_option else ascii_escaped(val) + + def _idval(val, argname, idx, idfn, item, config): if idfn: try: - generated_id = idfn(val) - if generated_id is not None: - val = generated_id + generated_id = idfn(val) + if generated_id is not None: + val = generated_id except Exception as e: # See issue https://github.com/pytest-dev/pytest/issues/2169 - msg = "{}: error raised while trying to determine id of parameter '{}' at position {}\n" - msg = msg.format(item.nodeid, argname, idx) - # we only append the exception type and message because on Python 2 reraise does nothing + msg = "{}: error raised while trying to determine id of parameter '{}' at position {}\n" + msg = msg.format(item.nodeid, argname, idx) + # we only append the exception type and message because on Python 2 reraise does nothing msg += " {}: {}\n".format(type(e).__name__, e) - six.raise_from(ValueError(msg), e) - elif config: + six.raise_from(ValueError(msg), e) + elif config: hook_id = config.hook.pytest_make_parametrize_id( config=config, val=val, argname=argname ) @@ -1192,8 +1192,8 @@ def _idval(val, argname, idx, idfn, item, config): return hook_id if isinstance(val, STRING_TYPES): - return _ascii_escaped_by_config(val, config) - elif val is None or isinstance(val, (float, int, bool)): + return _ascii_escaped_by_config(val, config) + elif val is None or isinstance(val, (float, int, bool)): return str(val) elif isinstance(val, REGEX_TYPE): return ascii_escaped(val.pattern) @@ -1218,10 +1218,10 @@ def limit_idval(limit): if len(idval) > limit: prefix = idval[:limit] # There might be same prefix for the different test cases - take item into account - name = "{}-{}".format(kw.get('item', ''), safe_str(prefix)) + name = "{}-{}".format(kw.get('item', ''), safe_str(prefix)) idx = names.setdefault(name, -1) + 1 names[name] = idx - idval = "{}-{}".format(safe_str(prefix), idx) + idval = "{}-{}".format(safe_str(prefix), idx) return idval return wrapper @@ -1241,7 +1241,7 @@ def _idvalset(idx, parameterset, argnames, idfn, ids, item, config): ] return "-".join(this_id) else: - return _ascii_escaped_by_config(ids[idx], config) + return _ascii_escaped_by_config(ids[idx], config) def idmaker(argnames, parametersets, idfn=None, ids=None, config=None, item=None): @@ -1366,22 +1366,22 @@ def _showfixtures_main(config, session): currentmodule = module if verbose <= 0 and argname[0] == "_": continue - tw.write(argname, green=True) - if fixturedef.scope != "function": - tw.write(" [%s scope]" % fixturedef.scope, cyan=True) + tw.write(argname, green=True) + if fixturedef.scope != "function": + tw.write(" [%s scope]" % fixturedef.scope, cyan=True) if verbose > 0: - tw.write(" -- %s" % bestrel, yellow=True) - tw.write("\n") + tw.write(" -- %s" % bestrel, yellow=True) + tw.write("\n") loc = getlocation(fixturedef.func, curdir) doc = fixturedef.func.__doc__ or "" if doc: write_docstring(tw, doc) else: tw.line(" %s: no docstring available" % (loc,), red=True) - tw.line() + tw.line() -def write_docstring(tw, doc, indent=" "): +def write_docstring(tw, doc, indent=" "): doc = doc.rstrip() if "\n" in doc: firstline, rest = doc.split("\n", 1) @@ -1389,11 +1389,11 @@ def write_docstring(tw, doc, indent=" "): firstline, rest = doc, "" if firstline.strip(): - tw.line(indent + firstline.strip()) + tw.line(indent + firstline.strip()) if rest: for line in dedent(rest).split("\n"): - tw.write(indent + line + "\n") + tw.write(indent + line + "\n") class Function(FunctionMixin, nodes.Item, fixtures.FuncargnamesCompatAttr): @@ -1437,23 +1437,23 @@ class Function(FunctionMixin, nodes.Item, fixtures.FuncargnamesCompatAttr): if keywords: self.keywords.update(keywords) - # todo: this is a hell of a hack - # https://github.com/pytest-dev/pytest/issues/4569 - - self.keywords.update( - dict.fromkeys( - [ - mark.name - for mark in self.iter_markers() - if mark.name not in self.keywords - ], - True, - ) - ) - + # todo: this is a hell of a hack + # https://github.com/pytest-dev/pytest/issues/4569 + + self.keywords.update( + dict.fromkeys( + [ + mark.name + for mark in self.iter_markers() + if mark.name not in self.keywords + ], + True, + ) + ) + if fixtureinfo is None: fixtureinfo = self.session._fixturemanager.getfixtureinfo( - self, self.obj, self.cls, funcargs=True + self, self.obj, self.cls, funcargs=True ) self._fixtureinfo = fixtureinfo self.fixturenames = fixtureinfo.names_closure diff --git a/contrib/python/pytest/py2/_pytest/python_api.py b/contrib/python/pytest/py2/_pytest/python_api.py index f6e475c3a2..11e6bc98ae 100644 --- a/contrib/python/pytest/py2/_pytest/python_api.py +++ b/contrib/python/pytest/py2/_pytest/python_api.py @@ -1,10 +1,10 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import - +# -*- coding: utf-8 -*- +from __future__ import absolute_import + import math import pprint import sys -import warnings +import warnings from decimal import Decimal from numbers import Number @@ -13,11 +13,11 @@ from six.moves import filterfalse from six.moves import zip import _pytest._code -from _pytest import deprecated +from _pytest import deprecated from _pytest.compat import isclass -from _pytest.compat import Iterable +from _pytest.compat import Iterable from _pytest.compat import Mapping -from _pytest.compat import Sized +from _pytest.compat import Sized from _pytest.compat import STRING_TYPES from _pytest.outcomes import fail @@ -150,10 +150,10 @@ class ApproxNumpy(ApproxBase): if np.isscalar(actual): for i in np.ndindex(self.expected.shape): - yield actual, self.expected[i].item() + yield actual, self.expected[i].item() else: for i in np.ndindex(self.expected.shape): - yield actual[i].item(), self.expected[i].item() + yield actual[i].item(), self.expected[i].item() class ApproxMapping(ApproxBase): @@ -187,7 +187,7 @@ class ApproxMapping(ApproxBase): raise _non_numeric_type_error(self.expected, at="key={!r}".format(key)) -class ApproxSequencelike(ApproxBase): +class ApproxSequencelike(ApproxBase): """ Perform approximate comparisons where the expected value is a sequence of numbers. @@ -525,12 +525,12 @@ def approx(expected, rel=None, abs=None, nan_ok=False): cls = ApproxMapping elif _is_numpy_array(expected): cls = ApproxNumpy - elif ( - isinstance(expected, Iterable) - and isinstance(expected, Sized) - and not isinstance(expected, STRING_TYPES) - ): - cls = ApproxSequencelike + elif ( + isinstance(expected, Iterable) + and isinstance(expected, Sized) + and not isinstance(expected, STRING_TYPES) + ): + cls = ApproxSequencelike else: raise _non_numeric_type_error(expected, at=None) @@ -556,55 +556,55 @@ def _is_numpy_array(obj): def raises(expected_exception, *args, **kwargs): r""" Assert that a code block/function call raises ``expected_exception`` - or raise a failure exception otherwise. - - :kwparam match: if specified, a string containing a regular expression, - or a regular expression object, that is tested against the string - representation of the exception using ``re.search``. To match a literal - string that may contain `special characters`__, the pattern can - first be escaped with ``re.escape``. - - __ https://docs.python.org/3/library/re.html#regular-expression-syntax + or raise a failure exception otherwise. - :kwparam message: **(deprecated since 4.1)** if specified, provides a custom failure message - if the exception is not raised. See :ref:`the deprecation docs <raises message deprecated>` for a workaround. + :kwparam match: if specified, a string containing a regular expression, + or a regular expression object, that is tested against the string + representation of the exception using ``re.search``. To match a literal + string that may contain `special characters`__, the pattern can + first be escaped with ``re.escape``. - .. currentmodule:: _pytest._code + __ https://docs.python.org/3/library/re.html#regular-expression-syntax - Use ``pytest.raises`` as a context manager, which will capture the exception of the given - type:: + :kwparam message: **(deprecated since 4.1)** if specified, provides a custom failure message + if the exception is not raised. See :ref:`the deprecation docs <raises message deprecated>` for a workaround. + .. currentmodule:: _pytest._code + + Use ``pytest.raises`` as a context manager, which will capture the exception of the given + type:: + >>> with raises(ZeroDivisionError): ... 1/0 - If the code block does not raise the expected exception (``ZeroDivisionError`` in the example - above), or no exception at all, the check will fail instead. - - You can also use the keyword argument ``match`` to assert that the - exception matches a text or regex:: - - >>> with raises(ValueError, match='must be 0 or None'): - ... raise ValueError("value must be 0 or None") - - >>> with raises(ValueError, match=r'must be \d+$'): - ... raise ValueError("value must be 42") - - The context manager produces an :class:`ExceptionInfo` object which can be used to inspect the - details of the captured exception:: - - >>> with raises(ValueError) as exc_info: - ... raise ValueError("value must be 42") - >>> assert exc_info.type is ValueError - >>> assert exc_info.value.args[0] == "value must be 42" - - .. deprecated:: 4.1 - - In the context manager form you may use the keyword argument - ``message`` to specify a custom failure message that will be displayed - in case the ``pytest.raises`` check fails. This has been deprecated as it - is considered error prone as users often mean to use ``match`` instead. - See :ref:`the deprecation docs <raises message deprecated>` for a workaround. - + If the code block does not raise the expected exception (``ZeroDivisionError`` in the example + above), or no exception at all, the check will fail instead. + + You can also use the keyword argument ``match`` to assert that the + exception matches a text or regex:: + + >>> with raises(ValueError, match='must be 0 or None'): + ... raise ValueError("value must be 0 or None") + + >>> with raises(ValueError, match=r'must be \d+$'): + ... raise ValueError("value must be 42") + + The context manager produces an :class:`ExceptionInfo` object which can be used to inspect the + details of the captured exception:: + + >>> with raises(ValueError) as exc_info: + ... raise ValueError("value must be 42") + >>> assert exc_info.type is ValueError + >>> assert exc_info.value.args[0] == "value must be 42" + + .. deprecated:: 4.1 + + In the context manager form you may use the keyword argument + ``message`` to specify a custom failure message that will be displayed + in case the ``pytest.raises`` check fails. This has been deprecated as it + is considered error prone as users often mean to use ``match`` instead. + See :ref:`the deprecation docs <raises message deprecated>` for a workaround. + .. note:: When using ``pytest.raises`` as a context manager, it's worthwhile to @@ -617,7 +617,7 @@ def raises(expected_exception, *args, **kwargs): >>> with raises(ValueError) as exc_info: ... if value > 10: ... raise ValueError("value must be <= 10") - ... assert exc_info.type is ValueError # this will not execute + ... assert exc_info.type is ValueError # this will not execute Instead, the following approach must be taken (note the difference in scope):: @@ -626,17 +626,17 @@ def raises(expected_exception, *args, **kwargs): ... if value > 10: ... raise ValueError("value must be <= 10") ... - >>> assert exc_info.type is ValueError + >>> assert exc_info.type is ValueError - **Using with** ``pytest.mark.parametrize`` + **Using with** ``pytest.mark.parametrize`` - When using :ref:`pytest.mark.parametrize ref` - it is possible to parametrize tests such that - some runs raise an exception and others do not. + When using :ref:`pytest.mark.parametrize ref` + it is possible to parametrize tests such that + some runs raise an exception and others do not. - See :ref:`parametrizing_conditional_raising` for an example. + See :ref:`parametrizing_conditional_raising` for an example. - **Legacy form** + **Legacy form** It is possible to specify a callable by passing a to-be-called lambda:: @@ -652,8 +652,8 @@ def raises(expected_exception, *args, **kwargs): >>> raises(ZeroDivisionError, f, x=0) <ExceptionInfo ...> - The form above is fully supported but discouraged for new code because the - context manager form is regarded as more readable and less error-prone. + The form above is fully supported but discouraged for new code because the + context manager form is regarded as more readable and less error-prone. .. note:: Similar to caught exception objects in Python, explicitly clearing @@ -684,35 +684,35 @@ def raises(expected_exception, *args, **kwargs): if not args: if "message" in kwargs: message = kwargs.pop("message") - warnings.warn(deprecated.RAISES_MESSAGE_PARAMETER, stacklevel=2) + warnings.warn(deprecated.RAISES_MESSAGE_PARAMETER, stacklevel=2) if "match" in kwargs: match_expr = kwargs.pop("match") if kwargs: msg = "Unexpected keyword arguments passed to pytest.raises: " - msg += ", ".join(sorted(kwargs)) + msg += ", ".join(sorted(kwargs)) raise TypeError(msg) return RaisesContext(expected_exception, message, match_expr) elif isinstance(args[0], str): - warnings.warn(deprecated.RAISES_EXEC, stacklevel=2) - (code,) = args + warnings.warn(deprecated.RAISES_EXEC, stacklevel=2) + (code,) = args assert isinstance(code, str) frame = sys._getframe(1) loc = frame.f_locals.copy() loc.update(kwargs) # print "raises frame scope: %r" % frame.f_locals try: - code = _pytest._code.Source(code).compile(_genframe=frame) - exec(code, frame.f_globals, loc) + code = _pytest._code.Source(code).compile(_genframe=frame) + exec(code, frame.f_globals, loc) # XXX didn't mean f_globals == f_locals something special? # this is destroyed here ... except expected_exception: - return _pytest._code.ExceptionInfo.from_current() + return _pytest._code.ExceptionInfo.from_current() else: func = args[0] try: func(*args[1:], **kwargs) except expected_exception: - return _pytest._code.ExceptionInfo.from_current() + return _pytest._code.ExceptionInfo.from_current() fail(message) @@ -727,7 +727,7 @@ class RaisesContext(object): self.excinfo = None def __enter__(self): - self.excinfo = _pytest._code.ExceptionInfo.for_later() + self.excinfo = _pytest._code.ExceptionInfo.for_later() return self.excinfo def __exit__(self, *tp): @@ -738,6 +738,6 @@ class RaisesContext(object): suppress_exception = issubclass(self.excinfo.type, self.expected_exception) if sys.version_info[0] == 2 and suppress_exception: sys.exc_clear() - if self.match_expr is not None and suppress_exception: + if self.match_expr is not None and suppress_exception: self.excinfo.match(self.match_expr) return suppress_exception diff --git a/contrib/python/pytest/py2/_pytest/recwarn.py b/contrib/python/pytest/py2/_pytest/recwarn.py index 7abf2e9355..e5fa9c6a50 100644 --- a/contrib/python/pytest/py2/_pytest/recwarn.py +++ b/contrib/python/pytest/py2/_pytest/recwarn.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ recording warnings during test function execution. """ from __future__ import absolute_import from __future__ import division @@ -12,8 +12,8 @@ import warnings import six import _pytest._code -from _pytest.deprecated import PYTEST_WARNS_UNKNOWN_KWARGS -from _pytest.deprecated import WARNS_EXEC +from _pytest.deprecated import PYTEST_WARNS_UNKNOWN_KWARGS +from _pytest.deprecated import WARNS_EXEC from _pytest.fixtures import yield_fixture from _pytest.outcomes import fail @@ -87,26 +87,26 @@ def warns(expected_warning, *args, **kwargs): """ __tracebackhide__ = True if not args: - match_expr = kwargs.pop("match", None) - if kwargs: - warnings.warn( - PYTEST_WARNS_UNKNOWN_KWARGS.format(args=sorted(kwargs)), stacklevel=2 - ) + match_expr = kwargs.pop("match", None) + if kwargs: + warnings.warn( + PYTEST_WARNS_UNKNOWN_KWARGS.format(args=sorted(kwargs)), stacklevel=2 + ) return WarningsChecker(expected_warning, match_expr=match_expr) elif isinstance(args[0], str): - warnings.warn(WARNS_EXEC, stacklevel=2) - (code,) = args + warnings.warn(WARNS_EXEC, stacklevel=2) + (code,) = args assert isinstance(code, str) frame = sys._getframe(1) loc = frame.f_locals.copy() loc.update(kwargs) - with WarningsChecker(expected_warning): + with WarningsChecker(expected_warning): code = _pytest._code.Source(code).compile() - exec(code, frame.f_globals, loc) + exec(code, frame.f_globals, loc) else: func = args[0] - with WarningsChecker(expected_warning): + with WarningsChecker(expected_warning): return func(*args[1:], **kwargs) @@ -196,11 +196,11 @@ class WarningsRecorder(warnings.catch_warnings): warnings.warn = self._saved_warn super(WarningsRecorder, self).__exit__(*exc_info) - # Built-in catch_warnings does not reset entered state so we do it - # manually here for this context manager to become reusable. - self._entered = False - + # Built-in catch_warnings does not reset entered state so we do it + # manually here for this context manager to become reusable. + self._entered = False + class WarningsChecker(WarningsRecorder): def __init__(self, expected_warning=None, match_expr=None): super(WarningsChecker, self).__init__() diff --git a/contrib/python/pytest/py2/_pytest/reports.py b/contrib/python/pytest/py2/_pytest/reports.py index 0bba6762c3..0310966b6d 100644 --- a/contrib/python/pytest/py2/_pytest/reports.py +++ b/contrib/python/pytest/py2/_pytest/reports.py @@ -1,20 +1,20 @@ -# -*- coding: utf-8 -*- -from pprint import pprint - +# -*- coding: utf-8 -*- +from pprint import pprint + import py -import six - -from _pytest._code.code import ExceptionInfo -from _pytest._code.code import ReprEntry -from _pytest._code.code import ReprEntryNative -from _pytest._code.code import ReprExceptionInfo -from _pytest._code.code import ReprFileLocation -from _pytest._code.code import ReprFuncArgs -from _pytest._code.code import ReprLocals -from _pytest._code.code import ReprTraceback +import six + +from _pytest._code.code import ExceptionInfo +from _pytest._code.code import ReprEntry +from _pytest._code.code import ReprEntryNative +from _pytest._code.code import ReprExceptionInfo +from _pytest._code.code import ReprFileLocation +from _pytest._code.code import ReprFuncArgs +from _pytest._code.code import ReprLocals +from _pytest._code.code import ReprTraceback from _pytest._code.code import TerminalRepr -from _pytest.outcomes import skip -from _pytest.pathlib import Path +from _pytest.outcomes import skip +from _pytest.pathlib import Path def getslaveinfoline(node): @@ -33,9 +33,9 @@ def getslaveinfoline(node): class BaseReport(object): - when = None - location = None - + when = None + location = None + def __init__(self, **kw): self.__dict__.update(kw) @@ -112,179 +112,179 @@ class BaseReport(object): def fspath(self): return self.nodeid.split("::")[0] - @property - def count_towards_summary(self): - """ - **Experimental** - - Returns True if this report should be counted towards the totals shown at the end of the - test session: "1 passed, 1 failure, etc". - - .. note:: - - This function is considered **experimental**, so beware that it is subject to changes - even in patch releases. - """ - return True - - @property - def head_line(self): - """ - **Experimental** - - Returns the head line shown with longrepr output for this report, more commonly during - traceback representation during failures:: - - ________ Test.foo ________ - - - In the example above, the head_line is "Test.foo". - - .. note:: - - This function is considered **experimental**, so beware that it is subject to changes - even in patch releases. - """ - if self.location is not None: - fspath, lineno, domain = self.location - return domain - - def _get_verbose_word(self, config): - _category, _short, verbose = config.hook.pytest_report_teststatus( - report=self, config=config - ) - return verbose - - def _to_json(self): - """ - This was originally the serialize_report() function from xdist (ca03269). - - Returns the contents of this report as a dict of builtin entries, suitable for - serialization. - - Experimental method. - """ - - def disassembled_report(rep): - reprtraceback = rep.longrepr.reprtraceback.__dict__.copy() - reprcrash = rep.longrepr.reprcrash.__dict__.copy() - - new_entries = [] - for entry in reprtraceback["reprentries"]: - entry_data = { - "type": type(entry).__name__, - "data": entry.__dict__.copy(), - } - for key, value in entry_data["data"].items(): - if hasattr(value, "__dict__"): - entry_data["data"][key] = value.__dict__.copy() - new_entries.append(entry_data) - - reprtraceback["reprentries"] = new_entries - - return { - "reprcrash": reprcrash, - "reprtraceback": reprtraceback, - "sections": rep.longrepr.sections, - } - - d = self.__dict__.copy() - if hasattr(self.longrepr, "toterminal"): - if hasattr(self.longrepr, "reprtraceback") and hasattr( - self.longrepr, "reprcrash" - ): - d["longrepr"] = disassembled_report(self) - else: - d["longrepr"] = six.text_type(self.longrepr) - else: - d["longrepr"] = self.longrepr - for name in d: - if isinstance(d[name], (py.path.local, Path)): - d[name] = str(d[name]) - elif name == "result": - d[name] = None # for now - return d - - @classmethod - def _from_json(cls, reportdict): - """ - This was originally the serialize_report() function from xdist (ca03269). - - Factory method that returns either a TestReport or CollectReport, depending on the calling - class. It's the callers responsibility to know which class to pass here. - - Experimental method. - """ - if reportdict["longrepr"]: - if ( - "reprcrash" in reportdict["longrepr"] - and "reprtraceback" in reportdict["longrepr"] - ): - - reprtraceback = reportdict["longrepr"]["reprtraceback"] - reprcrash = reportdict["longrepr"]["reprcrash"] - - unserialized_entries = [] - reprentry = None - for entry_data in reprtraceback["reprentries"]: - data = entry_data["data"] - entry_type = entry_data["type"] - if entry_type == "ReprEntry": - reprfuncargs = None - reprfileloc = None - reprlocals = None - if data["reprfuncargs"]: - reprfuncargs = ReprFuncArgs(**data["reprfuncargs"]) - if data["reprfileloc"]: - reprfileloc = ReprFileLocation(**data["reprfileloc"]) - if data["reprlocals"]: - reprlocals = ReprLocals(data["reprlocals"]["lines"]) - - reprentry = ReprEntry( - lines=data["lines"], - reprfuncargs=reprfuncargs, - reprlocals=reprlocals, - filelocrepr=reprfileloc, - style=data["style"], - ) - elif entry_type == "ReprEntryNative": - reprentry = ReprEntryNative(data["lines"]) - else: - _report_unserialization_failure(entry_type, cls, reportdict) - unserialized_entries.append(reprentry) - reprtraceback["reprentries"] = unserialized_entries - - exception_info = ReprExceptionInfo( - reprtraceback=ReprTraceback(**reprtraceback), - reprcrash=ReprFileLocation(**reprcrash), - ) - - for section in reportdict["longrepr"]["sections"]: - exception_info.addsection(*section) - reportdict["longrepr"] = exception_info - - return cls(**reportdict) - - -def _report_unserialization_failure(type_name, report_class, reportdict): - url = "https://github.com/pytest-dev/pytest/issues" - stream = py.io.TextIO() - pprint("-" * 100, stream=stream) - pprint("INTERNALERROR: Unknown entry type returned: %s" % type_name, stream=stream) - pprint("report_name: %s" % report_class, stream=stream) - pprint(reportdict, stream=stream) - pprint("Please report this bug at %s" % url, stream=stream) - pprint("-" * 100, stream=stream) - raise RuntimeError(stream.getvalue()) - - + @property + def count_towards_summary(self): + """ + **Experimental** + + Returns True if this report should be counted towards the totals shown at the end of the + test session: "1 passed, 1 failure, etc". + + .. note:: + + This function is considered **experimental**, so beware that it is subject to changes + even in patch releases. + """ + return True + + @property + def head_line(self): + """ + **Experimental** + + Returns the head line shown with longrepr output for this report, more commonly during + traceback representation during failures:: + + ________ Test.foo ________ + + + In the example above, the head_line is "Test.foo". + + .. note:: + + This function is considered **experimental**, so beware that it is subject to changes + even in patch releases. + """ + if self.location is not None: + fspath, lineno, domain = self.location + return domain + + def _get_verbose_word(self, config): + _category, _short, verbose = config.hook.pytest_report_teststatus( + report=self, config=config + ) + return verbose + + def _to_json(self): + """ + This was originally the serialize_report() function from xdist (ca03269). + + Returns the contents of this report as a dict of builtin entries, suitable for + serialization. + + Experimental method. + """ + + def disassembled_report(rep): + reprtraceback = rep.longrepr.reprtraceback.__dict__.copy() + reprcrash = rep.longrepr.reprcrash.__dict__.copy() + + new_entries = [] + for entry in reprtraceback["reprentries"]: + entry_data = { + "type": type(entry).__name__, + "data": entry.__dict__.copy(), + } + for key, value in entry_data["data"].items(): + if hasattr(value, "__dict__"): + entry_data["data"][key] = value.__dict__.copy() + new_entries.append(entry_data) + + reprtraceback["reprentries"] = new_entries + + return { + "reprcrash": reprcrash, + "reprtraceback": reprtraceback, + "sections": rep.longrepr.sections, + } + + d = self.__dict__.copy() + if hasattr(self.longrepr, "toterminal"): + if hasattr(self.longrepr, "reprtraceback") and hasattr( + self.longrepr, "reprcrash" + ): + d["longrepr"] = disassembled_report(self) + else: + d["longrepr"] = six.text_type(self.longrepr) + else: + d["longrepr"] = self.longrepr + for name in d: + if isinstance(d[name], (py.path.local, Path)): + d[name] = str(d[name]) + elif name == "result": + d[name] = None # for now + return d + + @classmethod + def _from_json(cls, reportdict): + """ + This was originally the serialize_report() function from xdist (ca03269). + + Factory method that returns either a TestReport or CollectReport, depending on the calling + class. It's the callers responsibility to know which class to pass here. + + Experimental method. + """ + if reportdict["longrepr"]: + if ( + "reprcrash" in reportdict["longrepr"] + and "reprtraceback" in reportdict["longrepr"] + ): + + reprtraceback = reportdict["longrepr"]["reprtraceback"] + reprcrash = reportdict["longrepr"]["reprcrash"] + + unserialized_entries = [] + reprentry = None + for entry_data in reprtraceback["reprentries"]: + data = entry_data["data"] + entry_type = entry_data["type"] + if entry_type == "ReprEntry": + reprfuncargs = None + reprfileloc = None + reprlocals = None + if data["reprfuncargs"]: + reprfuncargs = ReprFuncArgs(**data["reprfuncargs"]) + if data["reprfileloc"]: + reprfileloc = ReprFileLocation(**data["reprfileloc"]) + if data["reprlocals"]: + reprlocals = ReprLocals(data["reprlocals"]["lines"]) + + reprentry = ReprEntry( + lines=data["lines"], + reprfuncargs=reprfuncargs, + reprlocals=reprlocals, + filelocrepr=reprfileloc, + style=data["style"], + ) + elif entry_type == "ReprEntryNative": + reprentry = ReprEntryNative(data["lines"]) + else: + _report_unserialization_failure(entry_type, cls, reportdict) + unserialized_entries.append(reprentry) + reprtraceback["reprentries"] = unserialized_entries + + exception_info = ReprExceptionInfo( + reprtraceback=ReprTraceback(**reprtraceback), + reprcrash=ReprFileLocation(**reprcrash), + ) + + for section in reportdict["longrepr"]["sections"]: + exception_info.addsection(*section) + reportdict["longrepr"] = exception_info + + return cls(**reportdict) + + +def _report_unserialization_failure(type_name, report_class, reportdict): + url = "https://github.com/pytest-dev/pytest/issues" + stream = py.io.TextIO() + pprint("-" * 100, stream=stream) + pprint("INTERNALERROR: Unknown entry type returned: %s" % type_name, stream=stream) + pprint("report_name: %s" % report_class, stream=stream) + pprint(reportdict, stream=stream) + pprint("Please report this bug at %s" % url, stream=stream) + pprint("-" * 100, stream=stream) + raise RuntimeError(stream.getvalue()) + + class TestReport(BaseReport): """ Basic test report object (also used for setup and teardown calls if they fail). """ - __test__ = False - + __test__ = False + def __init__( self, nodeid, @@ -335,59 +335,59 @@ class TestReport(BaseReport): self.__dict__.update(extra) def __repr__(self): - return "<%s %r when=%r outcome=%r>" % ( - self.__class__.__name__, + return "<%s %r when=%r outcome=%r>" % ( + self.__class__.__name__, self.nodeid, self.when, self.outcome, ) - @classmethod - def from_item_and_call(cls, item, call): - """ - Factory method to create and fill a TestReport with standard item and call info. - """ - when = call.when - duration = call.stop - call.start - keywords = {x: 1 for x in item.keywords} - excinfo = call.excinfo - sections = [] - if not call.excinfo: - outcome = "passed" - longrepr = None - else: - if not isinstance(excinfo, ExceptionInfo): - outcome = "failed" - longrepr = excinfo - elif excinfo.errisinstance(skip.Exception): - outcome = "skipped" - r = excinfo._getreprcrash() - longrepr = (str(r.path), r.lineno, r.message) - else: - outcome = "failed" - if call.when == "call": - longrepr = item.repr_failure(excinfo) - else: # exception in setup or teardown - longrepr = item._repr_failure_py( - excinfo, style=item.config.getoption("tbstyle", "auto") - ) - for rwhen, key, content in item._report_sections: - sections.append(("Captured %s %s" % (key, rwhen), content)) - return cls( - item.nodeid, - item.location, - keywords, - outcome, - longrepr, - when, - sections, - duration, - user_properties=item.user_properties, - ) - - -class CollectReport(BaseReport): - when = "collect" + @classmethod + def from_item_and_call(cls, item, call): + """ + Factory method to create and fill a TestReport with standard item and call info. + """ + when = call.when + duration = call.stop - call.start + keywords = {x: 1 for x in item.keywords} + excinfo = call.excinfo + sections = [] + if not call.excinfo: + outcome = "passed" + longrepr = None + else: + if not isinstance(excinfo, ExceptionInfo): + outcome = "failed" + longrepr = excinfo + elif excinfo.errisinstance(skip.Exception): + outcome = "skipped" + r = excinfo._getreprcrash() + longrepr = (str(r.path), r.lineno, r.message) + else: + outcome = "failed" + if call.when == "call": + longrepr = item.repr_failure(excinfo) + else: # exception in setup or teardown + longrepr = item._repr_failure_py( + excinfo, style=item.config.getoption("tbstyle", "auto") + ) + for rwhen, key, content in item._report_sections: + sections.append(("Captured %s %s" % (key, rwhen), content)) + return cls( + item.nodeid, + item.location, + keywords, + outcome, + longrepr, + when, + sections, + duration, + user_properties=item.user_properties, + ) + + +class CollectReport(BaseReport): + when = "collect" def __init__(self, nodeid, outcome, longrepr, result, sections=(), **extra): self.nodeid = nodeid @@ -415,21 +415,21 @@ class CollectErrorRepr(TerminalRepr): def toterminal(self, out): out.line(self.longrepr, red=True) - - -def pytest_report_to_serializable(report): - if isinstance(report, (TestReport, CollectReport)): - data = report._to_json() - data["_report_type"] = report.__class__.__name__ - return data - - -def pytest_report_from_serializable(data): - if "_report_type" in data: - if data["_report_type"] == "TestReport": - return TestReport._from_json(data) - elif data["_report_type"] == "CollectReport": - return CollectReport._from_json(data) - assert False, "Unknown report_type unserialize data: {}".format( - data["_report_type"] - ) + + +def pytest_report_to_serializable(report): + if isinstance(report, (TestReport, CollectReport)): + data = report._to_json() + data["_report_type"] = report.__class__.__name__ + return data + + +def pytest_report_from_serializable(data): + if "_report_type" in data: + if data["_report_type"] == "TestReport": + return TestReport._from_json(data) + elif data["_report_type"] == "CollectReport": + return CollectReport._from_json(data) + assert False, "Unknown report_type unserialize data: {}".format( + data["_report_type"] + ) diff --git a/contrib/python/pytest/py2/_pytest/resultlog.py b/contrib/python/pytest/py2/_pytest/resultlog.py index bd30b5071e..1d86cf088e 100644 --- a/contrib/python/pytest/py2/_pytest/resultlog.py +++ b/contrib/python/pytest/py2/_pytest/resultlog.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ log machine-parseable test session result information in a plain text file. """ @@ -35,9 +35,9 @@ def pytest_configure(config): config.pluginmanager.register(config._resultlog) from _pytest.deprecated import RESULT_LOG - from _pytest.warnings import _issue_warning_captured + from _pytest.warnings import _issue_warning_captured - _issue_warning_captured(RESULT_LOG, config.hook, stacklevel=2) + _issue_warning_captured(RESULT_LOG, config.hook, stacklevel=2) def pytest_unconfigure(config): @@ -67,9 +67,9 @@ class ResultLog(object): def pytest_runtest_logreport(self, report): if report.when != "call" and report.passed: return - res = self.config.hook.pytest_report_teststatus( - report=report, config=self.config - ) + res = self.config.hook.pytest_report_teststatus( + report=report, config=self.config + ) code = res[1] if code == "x": longrepr = str(report.longrepr) diff --git a/contrib/python/pytest/py2/_pytest/runner.py b/contrib/python/pytest/py2/_pytest/runner.py index 34ae917738..076fb0f1bb 100644 --- a/contrib/python/pytest/py2/_pytest/runner.py +++ b/contrib/python/pytest/py2/_pytest/runner.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ basic collect and runtest protocol implementations """ from __future__ import absolute_import from __future__ import division @@ -9,15 +9,15 @@ import os import sys from time import time -import attr +import attr import six from .reports import CollectErrorRepr from .reports import CollectReport from .reports import TestReport from _pytest._code.code import ExceptionInfo -from _pytest.compat import safe_str -from _pytest.outcomes import Exit +from _pytest.compat import safe_str +from _pytest.outcomes import Exit from _pytest.outcomes import Skipped from _pytest.outcomes import TEST_OUTCOME @@ -88,9 +88,9 @@ def runtestprotocol(item, log=True, nextitem=None): rep = call_and_report(item, "setup", log) reports = [rep] if rep.passed: - if item.config.getoption("setupshow", False): + if item.config.getoption("setupshow", False): show_test_item(item) - if not item.config.getoption("setuponly", False): + if not item.config.getoption("setuponly", False): reports.append(call_and_report(item, "call", log)) reports.append(call_and_report(item, "teardown", log, nextitem=nextitem)) # after all teardown hooks have been called @@ -184,7 +184,7 @@ def call_and_report(item, when, log=True, **kwds): def check_interactive_exception(call, report): return call.excinfo and not ( hasattr(report, "wasxfail") - or call.excinfo.errisinstance(Skipped) + or call.excinfo.errisinstance(Skipped) or call.excinfo.errisinstance(bdb.BdbQuit) ) @@ -192,66 +192,66 @@ def check_interactive_exception(call, report): def call_runtest_hook(item, when, **kwds): hookname = "pytest_runtest_" + when ihook = getattr(item.ihook, hookname) - reraise = (Exit,) - if not item.config.getoption("usepdb", False): - reraise += (KeyboardInterrupt,) - return CallInfo.from_call( - lambda: ihook(item=item, **kwds), when=when, reraise=reraise + reraise = (Exit,) + if not item.config.getoption("usepdb", False): + reraise += (KeyboardInterrupt,) + return CallInfo.from_call( + lambda: ihook(item=item, **kwds), when=when, reraise=reraise ) -@attr.s(repr=False) +@attr.s(repr=False) class CallInfo(object): """ Result/Exception info a function invocation. """ - _result = attr.ib() - # Optional[ExceptionInfo] - excinfo = attr.ib() - start = attr.ib() - stop = attr.ib() - when = attr.ib() - - @property - def result(self): - if self.excinfo is not None: - raise AttributeError("{!r} has no valid result".format(self)) - return self._result - - @classmethod - def from_call(cls, func, when, reraise=None): + _result = attr.ib() + # Optional[ExceptionInfo] + excinfo = attr.ib() + start = attr.ib() + stop = attr.ib() + when = attr.ib() + + @property + def result(self): + if self.excinfo is not None: + raise AttributeError("{!r} has no valid result".format(self)) + return self._result + + @classmethod + def from_call(cls, func, when, reraise=None): #: context of invocation: one of "setup", "call", #: "teardown", "memocollect" - start = time() - excinfo = None + start = time() + excinfo = None try: - result = func() - except: # noqa - excinfo = ExceptionInfo.from_current() - if reraise is not None and excinfo.errisinstance(reraise): + result = func() + except: # noqa + excinfo = ExceptionInfo.from_current() + if reraise is not None and excinfo.errisinstance(reraise): raise - result = None - stop = time() - return cls(start=start, stop=stop, when=when, result=result, excinfo=excinfo) + result = None + stop = time() + return cls(start=start, stop=stop, when=when, result=result, excinfo=excinfo) def __repr__(self): - if self.excinfo is not None: - status = "exception" - value = self.excinfo.value + if self.excinfo is not None: + status = "exception" + value = self.excinfo.value else: - # TODO: investigate unification - value = repr(self._result) - status = "result" - return "<CallInfo when={when!r} {status}: {value}>".format( - when=self.when, value=safe_str(value), status=status - ) + # TODO: investigate unification + value = repr(self._result) + status = "result" + return "<CallInfo when={when!r} {status}: {value}>".format( + when=self.when, value=safe_str(value), status=status + ) def pytest_runtest_makereport(item, call): - return TestReport.from_item_and_call(item, call) + return TestReport.from_item_and_call(item, call) def pytest_make_collect_report(collector): - call = CallInfo.from_call(lambda: list(collector.collect()), "collect") + call = CallInfo.from_call(lambda: list(collector.collect()), "collect") longrepr = None if not call.excinfo: outcome = "passed" diff --git a/contrib/python/pytest/py2/_pytest/setuponly.py b/contrib/python/pytest/py2/_pytest/setuponly.py index 0859011241..9d4e43ccac 100644 --- a/contrib/python/pytest/py2/_pytest/setuponly.py +++ b/contrib/python/pytest/py2/_pytest/setuponly.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- from __future__ import absolute_import from __future__ import division from __future__ import print_function diff --git a/contrib/python/pytest/py2/_pytest/setupplan.py b/contrib/python/pytest/py2/_pytest/setupplan.py index 47b0fe82ef..a7271d39d5 100644 --- a/contrib/python/pytest/py2/_pytest/setupplan.py +++ b/contrib/python/pytest/py2/_pytest/setupplan.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- from __future__ import absolute_import from __future__ import division from __future__ import print_function diff --git a/contrib/python/pytest/py2/_pytest/skipping.py b/contrib/python/pytest/py2/_pytest/skipping.py index bc8b88e717..c0faa05a22 100644 --- a/contrib/python/pytest/py2/_pytest/skipping.py +++ b/contrib/python/pytest/py2/_pytest/skipping.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ support for skip/xfail functions and markers. """ from __future__ import absolute_import from __future__ import division @@ -18,7 +18,7 @@ def pytest_addoption(parser): action="store_true", dest="runxfail", default=False, - help="report the results of xfail tests as if they were not marked", + help="report the results of xfail tests as if they were not marked", ) parser.addini( @@ -181,6 +181,6 @@ def pytest_runtest_makereport(item, call): def pytest_report_teststatus(report): if hasattr(report, "wasxfail"): if report.skipped: - return "xfailed", "x", "XFAIL" + return "xfailed", "x", "XFAIL" elif report.passed: - return "xpassed", "X", "XPASS" + return "xpassed", "X", "XPASS" diff --git a/contrib/python/pytest/py2/_pytest/stepwise.py b/contrib/python/pytest/py2/_pytest/stepwise.py index 8890259589..83fb4727cd 100644 --- a/contrib/python/pytest/py2/_pytest/stepwise.py +++ b/contrib/python/pytest/py2/_pytest/stepwise.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- import pytest @@ -9,7 +9,7 @@ def pytest_addoption(parser): "--stepwise", action="store_true", dest="stepwise", - help="exit on test failure and continue from last failing test next time", + help="exit on test failure and continue from last failing test next time", ) group.addoption( "--stepwise-skip", @@ -29,7 +29,7 @@ class StepwisePlugin: self.config = config self.active = config.getvalue("stepwise") self.session = None - self.report_status = "" + self.report_status = "" if self.active: self.lastfailed = config.cache.get("cache/stepwise", None) @@ -39,11 +39,11 @@ class StepwisePlugin: self.session = session def pytest_collection_modifyitems(self, session, config, items): - if not self.active: - return - if not self.lastfailed: - self.report_status = "no previously failed tests, not skipping." + if not self.active: return + if not self.lastfailed: + self.report_status = "no previously failed tests, not skipping." + return already_passed = [] found = False @@ -59,12 +59,12 @@ class StepwisePlugin: # If the previously failed test was not found among the test items, # do not skip any tests. if not found: - self.report_status = "previously failed test not found, not skipping." + self.report_status = "previously failed test not found, not skipping." already_passed = [] - else: - self.report_status = "skipping {} already passed items.".format( - len(already_passed) - ) + else: + self.report_status = "skipping {} already passed items.".format( + len(already_passed) + ) for item in already_passed: items.remove(item) @@ -72,7 +72,7 @@ class StepwisePlugin: config.hook.pytest_deselected(items=already_passed) def pytest_runtest_logreport(self, report): - if not self.active: + if not self.active: return if report.failed: @@ -97,10 +97,10 @@ class StepwisePlugin: if report.nodeid == self.lastfailed: self.lastfailed = None - def pytest_report_collectionfinish(self): - if self.active and self.config.getoption("verbose") >= 0 and self.report_status: - return "stepwise: %s" % self.report_status - + def pytest_report_collectionfinish(self): + if self.active and self.config.getoption("verbose") >= 0 and self.report_status: + return "stepwise: %s" % self.report_status + def pytest_sessionfinish(self, session): if self.active: self.config.cache.set("cache/stepwise", self.lastfailed) diff --git a/contrib/python/pytest/py2/_pytest/terminal.py b/contrib/python/pytest/py2/_pytest/terminal.py index 4418338c65..1a80290b48 100644 --- a/contrib/python/pytest/py2/_pytest/terminal.py +++ b/contrib/python/pytest/py2/_pytest/terminal.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ terminal reporting of the full testing process. This is a good source for looking at the various reporting hooks. @@ -8,11 +8,11 @@ from __future__ import division from __future__ import print_function import argparse -import collections +import collections import platform import sys import time -from functools import partial +from functools import partial import attr import pluggy @@ -28,9 +28,9 @@ from _pytest.main import EXIT_OK from _pytest.main import EXIT_TESTSFAILED from _pytest.main import EXIT_USAGEERROR -REPORT_COLLECTING_RESOLUTION = 0.5 - +REPORT_COLLECTING_RESOLUTION = 0.5 + class MoreQuietAction(argparse.Action): """ a modified copy of the argparse count action which counts down and updates @@ -83,11 +83,11 @@ def pytest_addoption(parser): dest="reportchars", default="", metavar="chars", - help="show extra test summary info as specified by chars: (f)ailed, " - "(E)rror, (s)kipped, (x)failed, (X)passed, " - "(p)assed, (P)assed with output, (a)ll except passed (p/P), or (A)ll. " + help="show extra test summary info as specified by chars: (f)ailed, " + "(E)rror, (s)kipped, (x)failed, (X)passed, " + "(p)assed, (P)assed with output, (a)ll except passed (p/P), or (A)ll. " "Warnings are displayed at all times except when " - "--disable-warnings is set.", + "--disable-warnings is set.", ) group._addoption( "--disable-warnings", @@ -142,7 +142,7 @@ def pytest_addoption(parser): parser.addini( "console_output_style", - help='console output: "classic", or with additional progress information ("progress" (percentage) | "count").', + help='console output: "classic", or with additional progress information ("progress" (percentage) | "count").', default="progress", ) @@ -166,41 +166,41 @@ def getreportopt(config): reportchars += "w" elif config.option.disable_warnings and "w" in reportchars: reportchars = reportchars.replace("w", "") - aliases = {"F", "S"} - for char in reportchars: - # handle old aliases - if char in aliases: - char = char.lower() - if char == "a": - reportopts = "sxXwEf" - elif char == "A": - reportopts = "PpsxXwEf" - break - elif char not in reportopts: - reportopts += char + aliases = {"F", "S"} + for char in reportchars: + # handle old aliases + if char in aliases: + char = char.lower() + if char == "a": + reportopts = "sxXwEf" + elif char == "A": + reportopts = "PpsxXwEf" + break + elif char not in reportopts: + reportopts += char return reportopts -@pytest.hookimpl(trylast=True) # after _pytest.runner +@pytest.hookimpl(trylast=True) # after _pytest.runner def pytest_report_teststatus(report): - letter = "F" + letter = "F" if report.passed: letter = "." elif report.skipped: letter = "s" - outcome = report.outcome - if report.when in ("collect", "setup", "teardown") and outcome == "failed": - outcome = "error" - letter = "E" - - return outcome, letter, outcome.upper() - + outcome = report.outcome + if report.when in ("collect", "setup", "teardown") and outcome == "failed": + outcome = "error" + letter = "E" + return outcome, letter, outcome.upper() + + @attr.s class WarningReport(object): """ - Simple structure to hold warnings information captured by ``pytest_warning_captured``. + Simple structure to hold warnings information captured by ``pytest_warning_captured``. :ivar str message: user friendly message about the warning :ivar str|None nodeid: node id that generated the warning (see ``get_location``). @@ -211,7 +211,7 @@ class WarningReport(object): message = attr.ib() nodeid = attr.ib(default=None) fslocation = attr.ib(default=None) - count_towards_summary = True + count_towards_summary = True def get_location(self, config): """ @@ -239,10 +239,10 @@ class TerminalReporter(object): self.config = config self._numcollected = 0 self._session = None - self._showfspath = None + self._showfspath = None self.stats = {} - self.startdir = config.invocation_dir + self.startdir = config.invocation_dir if file is None: file = sys.stdout self._tw = _pytest.config.create_terminal_writer(config, file) @@ -260,47 +260,47 @@ class TerminalReporter(object): def _determine_show_progress_info(self): """Return True if we should display progress information based on the current config""" # do not show progress if we are not capturing output (#3038) - if self.config.getoption("capture", "no") == "no": + if self.config.getoption("capture", "no") == "no": return False # do not show progress if we are showing fixture setup/teardown - if self.config.getoption("setupshow", False): + if self.config.getoption("setupshow", False): return False - cfg = self.config.getini("console_output_style") - if cfg in ("progress", "count"): - return cfg - return False - - @property - def verbosity(self): - return self.config.option.verbose - - @property - def showheader(self): - return self.verbosity >= 0 - - @property - def showfspath(self): - if self._showfspath is None: - return self.verbosity >= 0 - return self._showfspath - - @showfspath.setter - def showfspath(self, value): - self._showfspath = value - - @property - def showlongtestinfo(self): - return self.verbosity > 0 - + cfg = self.config.getini("console_output_style") + if cfg in ("progress", "count"): + return cfg + return False + + @property + def verbosity(self): + return self.config.option.verbose + + @property + def showheader(self): + return self.verbosity >= 0 + + @property + def showfspath(self): + if self._showfspath is None: + return self.verbosity >= 0 + return self._showfspath + + @showfspath.setter + def showfspath(self, value): + self._showfspath = value + + @property + def showlongtestinfo(self): + return self.verbosity > 0 + def hasopt(self, char): char = {"xfailed": "x", "skipped": "s"}.get(char, char) return char in self.reportchars def write_fspath_result(self, nodeid, res, **markup): fspath = self.config.rootdir.join(nodeid.split("::")[0]) - # NOTE: explicitly check for None to work around py bug, and for less - # overhead in general (https://github.com/pytest-dev/py/pull/207). - if self.currentfspath is None or fspath != self.currentfspath: + # NOTE: explicitly check for None to work around py bug, and for less + # overhead in general (https://github.com/pytest-dev/py/pull/207). + if self.currentfspath is None or fspath != self.currentfspath: if self.currentfspath is not None and self._show_progress_info: self._write_progress_information_filling_space() self.currentfspath = fspath @@ -401,9 +401,9 @@ class TerminalReporter(object): self.write_fspath_result(fsid, "") def pytest_runtest_logreport(self, report): - self._tests_ran = True + self._tests_ran = True rep = report - res = self.config.hook.pytest_report_teststatus(report=rep, config=self.config) + res = self.config.hook.pytest_report_teststatus(report=rep, config=self.config) category, letter, word = res if isinstance(word, tuple): word, markup = word @@ -415,11 +415,11 @@ class TerminalReporter(object): return running_xdist = hasattr(rep, "node") if markup is None: - was_xfail = hasattr(report, "wasxfail") - if rep.passed and not was_xfail: + was_xfail = hasattr(report, "wasxfail") + if rep.passed and not was_xfail: markup = {"green": True} - elif rep.passed and was_xfail: - markup = {"yellow": True} + elif rep.passed and was_xfail: + markup = {"yellow": True} elif rep.failed: markup = {"red": True} elif rep.skipped: @@ -452,18 +452,18 @@ class TerminalReporter(object): self.currentfspath = -2 def pytest_runtest_logfinish(self, nodeid): - if self.verbosity <= 0 and self._show_progress_info: - if self._show_progress_info == "count": - num_tests = self._session.testscollected - progress_length = len(" [{}/{}]".format(str(num_tests), str(num_tests))) - else: - progress_length = len(" [100%]") + if self.verbosity <= 0 and self._show_progress_info: + if self._show_progress_info == "count": + num_tests = self._session.testscollected + progress_length = len(" [{}/{}]".format(str(num_tests), str(num_tests))) + else: + progress_length = len(" [100%]") self._progress_nodeids_reported.add(nodeid) - is_last_item = ( + is_last_item = ( len(self._progress_nodeids_reported) == self._session.testscollected ) - if is_last_item: + if is_last_item: self._write_progress_information_filling_space() else: w = self._width_of_current_line @@ -474,7 +474,7 @@ class TerminalReporter(object): def _get_progress_information_message(self): collected = self._session.testscollected - if self._show_progress_info == "count": + if self._show_progress_info == "count": if collected: progress = self._progress_nodeids_reported counter_format = "{{:{}d}}".format(len(str(collected))) @@ -529,7 +529,7 @@ class TerminalReporter(object): t = time.time() if ( self._collect_report_last_write is not None - and self._collect_report_last_write > t - REPORT_COLLECTING_RESOLUTION + and self._collect_report_last_write > t - REPORT_COLLECTING_RESOLUTION ): return self._collect_report_last_write = t @@ -537,7 +537,7 @@ class TerminalReporter(object): errors = len(self.stats.get("error", [])) skipped = len(self.stats.get("skipped", [])) deselected = len(self.stats.get("deselected", [])) - selected = self._numcollected - errors - skipped - deselected + selected = self._numcollected - errors - skipped - deselected if final: line = "collected " else: @@ -551,8 +551,8 @@ class TerminalReporter(object): line += " / %d deselected" % deselected if skipped: line += " / %d skipped" % skipped - if self._numcollected > selected > 0: - line += " / %d selected" % selected + if self._numcollected > selected > 0: + line += " / %d selected" % selected if self.isatty: self.rewrite(line, bold=True, erase=True) if final: @@ -595,39 +595,39 @@ class TerminalReporter(object): self.write_line(line) def pytest_report_header(self, config): - line = "rootdir: %s" % config.rootdir - + line = "rootdir: %s" % config.rootdir + if config.inifile: - line += ", inifile: " + config.rootdir.bestrelpath(config.inifile) - - testpaths = config.getini("testpaths") - if testpaths and config.args == testpaths: - rel_paths = [config.rootdir.bestrelpath(x) for x in testpaths] - line += ", testpaths: {}".format(", ".join(rel_paths)) - result = [line] - + line += ", inifile: " + config.rootdir.bestrelpath(config.inifile) + + testpaths = config.getini("testpaths") + if testpaths and config.args == testpaths: + rel_paths = [config.rootdir.bestrelpath(x) for x in testpaths] + line += ", testpaths: {}".format(", ".join(rel_paths)) + result = [line] + plugininfo = config.pluginmanager.list_plugin_distinfo() if plugininfo: - result.append("plugins: %s" % ", ".join(_plugin_nameversions(plugininfo))) - return result + result.append("plugins: %s" % ", ".join(_plugin_nameversions(plugininfo))) + return result - def pytest_collection_finish(self, session): - self.report_collect(True) + def pytest_collection_finish(self, session): + self.report_collect(True) - if self.config.getoption("collectonly"): + if self.config.getoption("collectonly"): self._printcollecteditems(session.items) - + lines = self.config.hook.pytest_report_collectionfinish( config=self.config, startdir=self.startdir, items=session.items ) self._write_report_lines_from_hooks(lines) - if self.config.getoption("collectonly"): - if self.stats.get("failed"): - self._tw.sep("!", "collection failures") - for rep in self.stats.get("failed"): - rep.toterminal(self._tw) - + if self.config.getoption("collectonly"): + if self.stats.get("failed"): + self._tw.sep("!", "collection failures") + for rep in self.stats.get("failed"): + rep.toterminal(self._tw) + def _printcollecteditems(self, items): # to print out items and their parent collectors # we take care to leave out Instances aka () @@ -658,10 +658,10 @@ class TerminalReporter(object): continue indent = (len(stack) - 1) * " " self._tw.line("%s%s" % (indent, col)) - if self.config.option.verbose >= 1: - if hasattr(col, "_obj") and col._obj.__doc__: - for line in col._obj.__doc__.strip().splitlines(): - self._tw.line("%s%s" % (indent + " ", line.strip())) + if self.config.option.verbose >= 1: + if hasattr(col, "_obj") and col._obj.__doc__: + for line in col._obj.__doc__.strip().splitlines(): + self._tw.line("%s%s" % (indent + " ", line.strip())) @pytest.hookimpl(hookwrapper=True) def pytest_sessionfinish(self, exitstatus): @@ -677,7 +677,7 @@ class TerminalReporter(object): ) if exitstatus in summary_exit_codes: self.config.hook.pytest_terminal_summary( - terminalreporter=self, exitstatus=exitstatus, config=self.config + terminalreporter=self, exitstatus=exitstatus, config=self.config ) if exitstatus == EXIT_INTERRUPTED: self._report_keyboardinterrupt() @@ -689,9 +689,9 @@ class TerminalReporter(object): self.summary_errors() self.summary_failures() self.summary_warnings() - self.summary_passes() + self.summary_passes() yield - self.short_test_summary() + self.short_test_summary() # Display any extra warnings from teardown here (if any). self.summary_warnings() @@ -739,10 +739,10 @@ class TerminalReporter(object): return res + " " def _getfailureheadline(self, rep): - head_line = rep.head_line - if head_line: - return head_line - return "test session" # XXX? + head_line = rep.head_line + if head_line: + return head_line + return "test session" # XXX? def _getcrashline(self, rep): try: @@ -771,33 +771,33 @@ class TerminalReporter(object): final = hasattr(self, "_already_displayed_warnings") if final: - warning_reports = all_warnings[self._already_displayed_warnings :] + warning_reports = all_warnings[self._already_displayed_warnings :] else: - warning_reports = all_warnings - self._already_displayed_warnings = len(warning_reports) - if not warning_reports: + warning_reports = all_warnings + self._already_displayed_warnings = len(warning_reports) + if not warning_reports: return - reports_grouped_by_message = collections.OrderedDict() - for wr in warning_reports: - reports_grouped_by_message.setdefault(wr.message, []).append(wr) + reports_grouped_by_message = collections.OrderedDict() + for wr in warning_reports: + reports_grouped_by_message.setdefault(wr.message, []).append(wr) title = "warnings summary (final)" if final else "warnings summary" self.write_sep("=", title, yellow=True, bold=False) - for message, warning_reports in reports_grouped_by_message.items(): - has_any_location = False - for w in warning_reports: - location = w.get_location(self.config) + for message, warning_reports in reports_grouped_by_message.items(): + has_any_location = False + for w in warning_reports: + location = w.get_location(self.config) if location: - self._tw.line(str(location)) - has_any_location = True - if has_any_location: - lines = message.splitlines() - indented = "\n".join(" " + x for x in lines) - message = indented.rstrip() - else: - message = message.rstrip() - self._tw.line(message) + self._tw.line(str(location)) + has_any_location = True + if has_any_location: + lines = message.splitlines() + indented = "\n".join(" " + x for x in lines) + message = indented.rstrip() + else: + message = message.rstrip() + self._tw.line(message) self._tw.line() self._tw.line("-- Docs: https://docs.pytest.org/en/latest/warnings.html") @@ -811,7 +811,7 @@ class TerminalReporter(object): for rep in reports: if rep.sections: msg = self._getfailureheadline(rep) - self.write_sep("_", msg, green=True, bold=True) + self.write_sep("_", msg, green=True, bold=True) self._outrep_summary(rep) def print_teardown_sections(self, rep): @@ -833,22 +833,22 @@ class TerminalReporter(object): if not reports: return self.write_sep("=", "FAILURES") - if self.config.option.tbstyle == "line": - for rep in reports: + if self.config.option.tbstyle == "line": + for rep in reports: line = self._getcrashline(rep) self.write_line(line) - else: - teardown_sections = {} - for report in self.getreports(""): - if report.when == "teardown": - teardown_sections.setdefault(report.nodeid, []).append(report) - - for rep in reports: + else: + teardown_sections = {} + for report in self.getreports(""): + if report.when == "teardown": + teardown_sections.setdefault(report.nodeid, []).append(report) + + for rep in reports: msg = self._getfailureheadline(rep) self.write_sep("_", msg, red=True, bold=True) self._outrep_summary(rep) - for report in teardown_sections.get(rep.nodeid, []): - self.print_teardown_sections(report) + for report in teardown_sections.get(rep.nodeid, []): + self.print_teardown_sections(report) def summary_errors(self): if self.config.option.tbstyle != "no": @@ -858,10 +858,10 @@ class TerminalReporter(object): self.write_sep("=", "ERRORS") for rep in self.stats["error"]: msg = self._getfailureheadline(rep) - if rep.when == "collect": + if rep.when == "collect": msg = "ERROR collecting " + msg - else: - msg = "ERROR at %s of %s" % (rep.when, msg) + else: + msg = "ERROR at %s of %s" % (rep.when, msg) self.write_sep("_", msg, red=True, bold=True) self._outrep_summary(rep) @@ -889,174 +889,174 @@ class TerminalReporter(object): if self.verbosity == -1: self.write_line(msg, **markup) - def short_test_summary(self): - if not self.reportchars: - return - - def show_simple(stat, lines): - failed = self.stats.get(stat, []) - if not failed: - return - termwidth = self.writer.fullwidth - config = self.config - for rep in failed: - line = _get_line_with_reprcrash_message(config, rep, termwidth) - lines.append(line) - - def show_xfailed(lines): - xfailed = self.stats.get("xfailed", []) - for rep in xfailed: - verbose_word = rep._get_verbose_word(self.config) - pos = _get_pos(self.config, rep) - lines.append("%s %s" % (verbose_word, pos)) - reason = rep.wasxfail - if reason: - lines.append(" " + str(reason)) - - def show_xpassed(lines): - xpassed = self.stats.get("xpassed", []) - for rep in xpassed: - verbose_word = rep._get_verbose_word(self.config) - pos = _get_pos(self.config, rep) - reason = rep.wasxfail - lines.append("%s %s %s" % (verbose_word, pos, reason)) - - def show_skipped(lines): - skipped = self.stats.get("skipped", []) - fskips = _folded_skips(skipped) if skipped else [] - if not fskips: - return - verbose_word = skipped[0]._get_verbose_word(self.config) - for num, fspath, lineno, reason in fskips: - if reason.startswith("Skipped: "): - reason = reason[9:] - if lineno is not None: - lines.append( - "%s [%d] %s:%d: %s" - % (verbose_word, num, fspath, lineno + 1, reason) - ) - else: - lines.append("%s [%d] %s: %s" % (verbose_word, num, fspath, reason)) - - REPORTCHAR_ACTIONS = { - "x": show_xfailed, - "X": show_xpassed, - "f": partial(show_simple, "failed"), - "s": show_skipped, - "p": partial(show_simple, "passed"), - "E": partial(show_simple, "error"), - } - - lines = [] - for char in self.reportchars: - action = REPORTCHAR_ACTIONS.get(char) - if action: # skipping e.g. "P" (passed with output) here. - action(lines) - - if lines: - self.write_sep("=", "short test summary info") - for line in lines: - self.write_line(line) - - -def _get_pos(config, rep): - nodeid = config.cwd_relative_nodeid(rep.nodeid) - return nodeid - - -def _get_line_with_reprcrash_message(config, rep, termwidth): - """Get summary line for a report, trying to add reprcrash message.""" - from wcwidth import wcswidth - - verbose_word = rep._get_verbose_word(config) - pos = _get_pos(config, rep) - - line = "%s %s" % (verbose_word, pos) - len_line = wcswidth(line) - ellipsis, len_ellipsis = "...", 3 - if len_line > termwidth - len_ellipsis: - # No space for an additional message. - return line - + def short_test_summary(self): + if not self.reportchars: + return + + def show_simple(stat, lines): + failed = self.stats.get(stat, []) + if not failed: + return + termwidth = self.writer.fullwidth + config = self.config + for rep in failed: + line = _get_line_with_reprcrash_message(config, rep, termwidth) + lines.append(line) + + def show_xfailed(lines): + xfailed = self.stats.get("xfailed", []) + for rep in xfailed: + verbose_word = rep._get_verbose_word(self.config) + pos = _get_pos(self.config, rep) + lines.append("%s %s" % (verbose_word, pos)) + reason = rep.wasxfail + if reason: + lines.append(" " + str(reason)) + + def show_xpassed(lines): + xpassed = self.stats.get("xpassed", []) + for rep in xpassed: + verbose_word = rep._get_verbose_word(self.config) + pos = _get_pos(self.config, rep) + reason = rep.wasxfail + lines.append("%s %s %s" % (verbose_word, pos, reason)) + + def show_skipped(lines): + skipped = self.stats.get("skipped", []) + fskips = _folded_skips(skipped) if skipped else [] + if not fskips: + return + verbose_word = skipped[0]._get_verbose_word(self.config) + for num, fspath, lineno, reason in fskips: + if reason.startswith("Skipped: "): + reason = reason[9:] + if lineno is not None: + lines.append( + "%s [%d] %s:%d: %s" + % (verbose_word, num, fspath, lineno + 1, reason) + ) + else: + lines.append("%s [%d] %s: %s" % (verbose_word, num, fspath, reason)) + + REPORTCHAR_ACTIONS = { + "x": show_xfailed, + "X": show_xpassed, + "f": partial(show_simple, "failed"), + "s": show_skipped, + "p": partial(show_simple, "passed"), + "E": partial(show_simple, "error"), + } + + lines = [] + for char in self.reportchars: + action = REPORTCHAR_ACTIONS.get(char) + if action: # skipping e.g. "P" (passed with output) here. + action(lines) + + if lines: + self.write_sep("=", "short test summary info") + for line in lines: + self.write_line(line) + + +def _get_pos(config, rep): + nodeid = config.cwd_relative_nodeid(rep.nodeid) + return nodeid + + +def _get_line_with_reprcrash_message(config, rep, termwidth): + """Get summary line for a report, trying to add reprcrash message.""" + from wcwidth import wcswidth + + verbose_word = rep._get_verbose_word(config) + pos = _get_pos(config, rep) + + line = "%s %s" % (verbose_word, pos) + len_line = wcswidth(line) + ellipsis, len_ellipsis = "...", 3 + if len_line > termwidth - len_ellipsis: + # No space for an additional message. + return line + try: - msg = rep.longrepr.reprcrash.message - except AttributeError: - pass - else: - # Only use the first line. - i = msg.find("\n") - if i != -1: - msg = msg[:i] - len_msg = wcswidth(msg) - - sep, len_sep = " - ", 3 - max_len_msg = termwidth - len_line - len_sep - if max_len_msg >= len_ellipsis: - if len_msg > max_len_msg: - max_len_msg -= len_ellipsis - msg = msg[:max_len_msg] - while wcswidth(msg) > max_len_msg: - msg = msg[:-1] - if six.PY2: - # on python 2 systems with narrow unicode compilation, trying to - # get a single character out of a multi-byte unicode character such as - # u'😄' will result in a High Surrogate (U+D83D) character, which is - # rendered as u'�'; in this case we just strip that character out as it - # serves no purpose being rendered - try: - surrogate = six.unichr(0xD83D) - msg = msg.rstrip(surrogate) - except ValueError: # pragma: no cover - # Jython cannot represent this lone surrogate at all (#5256): - # ValueError: unichr() arg is a lone surrogate in range - # (0xD800, 0xDFFF) (Jython UTF-16 encoding) - # ignore this case as it shouldn't appear in the string anyway - pass - msg += ellipsis - line += sep + msg - return line - - -def _folded_skips(skipped): - d = {} - for event in skipped: - key = event.longrepr - assert len(key) == 3, (event, key) - keywords = getattr(event, "keywords", {}) - # folding reports with global pytestmark variable - # this is workaround, because for now we cannot identify the scope of a skip marker - # TODO: revisit after marks scope would be fixed - if ( - event.when == "setup" - and "skip" in keywords - and "pytestmark" not in keywords - ): - key = (key[0], None, key[2]) - d.setdefault(key, []).append(event) - values = [] - for key, events in d.items(): - values.append((len(events),) + key) - return values - - + msg = rep.longrepr.reprcrash.message + except AttributeError: + pass + else: + # Only use the first line. + i = msg.find("\n") + if i != -1: + msg = msg[:i] + len_msg = wcswidth(msg) + + sep, len_sep = " - ", 3 + max_len_msg = termwidth - len_line - len_sep + if max_len_msg >= len_ellipsis: + if len_msg > max_len_msg: + max_len_msg -= len_ellipsis + msg = msg[:max_len_msg] + while wcswidth(msg) > max_len_msg: + msg = msg[:-1] + if six.PY2: + # on python 2 systems with narrow unicode compilation, trying to + # get a single character out of a multi-byte unicode character such as + # u'😄' will result in a High Surrogate (U+D83D) character, which is + # rendered as u'�'; in this case we just strip that character out as it + # serves no purpose being rendered + try: + surrogate = six.unichr(0xD83D) + msg = msg.rstrip(surrogate) + except ValueError: # pragma: no cover + # Jython cannot represent this lone surrogate at all (#5256): + # ValueError: unichr() arg is a lone surrogate in range + # (0xD800, 0xDFFF) (Jython UTF-16 encoding) + # ignore this case as it shouldn't appear in the string anyway + pass + msg += ellipsis + line += sep + msg + return line + + +def _folded_skips(skipped): + d = {} + for event in skipped: + key = event.longrepr + assert len(key) == 3, (event, key) + keywords = getattr(event, "keywords", {}) + # folding reports with global pytestmark variable + # this is workaround, because for now we cannot identify the scope of a skip marker + # TODO: revisit after marks scope would be fixed + if ( + event.when == "setup" + and "skip" in keywords + and "pytestmark" not in keywords + ): + key = (key[0], None, key[2]) + d.setdefault(key, []).append(event) + values = [] + for key, events in d.items(): + values.append((len(events),) + key) + return values + + def build_summary_stats_line(stats): - known_types = ( - "failed passed skipped deselected xfailed xpassed warnings error".split() - ) - unknown_type_seen = False - for found_type in stats: - if found_type not in known_types: - if found_type: # setup/teardown reports have an empty key, ignore them - known_types.append(found_type) - unknown_type_seen = True + known_types = ( + "failed passed skipped deselected xfailed xpassed warnings error".split() + ) + unknown_type_seen = False + for found_type in stats: + if found_type not in known_types: + if found_type: # setup/teardown reports have an empty key, ignore them + known_types.append(found_type) + unknown_type_seen = True parts = [] - for key in known_types: - reports = stats.get(key, None) - if reports: - count = sum( - 1 for rep in reports if getattr(rep, "count_towards_summary", True) - ) - parts.append("%d %s" % (count, key)) + for key in known_types: + reports = stats.get(key, None) + if reports: + count = sum( + 1 for rep in reports if getattr(rep, "count_towards_summary", True) + ) + parts.append("%d %s" % (count, key)) if parts: line = ", ".join(parts) @@ -1065,14 +1065,14 @@ def build_summary_stats_line(stats): if "failed" in stats or "error" in stats: color = "red" - elif "warnings" in stats or unknown_type_seen: + elif "warnings" in stats or unknown_type_seen: color = "yellow" elif "passed" in stats: color = "green" else: color = "yellow" - return line, color + return line, color def _plugin_nameversions(plugininfo): diff --git a/contrib/python/pytest/py2/_pytest/tmpdir.py b/contrib/python/pytest/py2/_pytest/tmpdir.py index a8a7037713..7502540a87 100644 --- a/contrib/python/pytest/py2/_pytest/tmpdir.py +++ b/contrib/python/pytest/py2/_pytest/tmpdir.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ support for providing temporary directories to test functions. """ from __future__ import absolute_import from __future__ import division @@ -60,30 +60,30 @@ class TempPathFactory(object): def getbasetemp(self): """ return base temporary directory. """ - if self._basetemp is not None: + if self._basetemp is not None: return self._basetemp - if self._given_basetemp is not None: - basetemp = self._given_basetemp - ensure_reset_dir(basetemp) - basetemp = basetemp.resolve() - else: - from_env = os.environ.get("PYTEST_DEBUG_TEMPROOT") - temproot = Path(from_env or tempfile.gettempdir()).resolve() - user = get_user() or "unknown" - # use a sub-directory in the temproot to speed-up - # make_numbered_dir() call - rootdir = temproot.joinpath("pytest-of-{}".format(user)) - rootdir.mkdir(exist_ok=True) - basetemp = make_numbered_dir_with_cleanup( - prefix="pytest-", root=rootdir, keep=3, lock_timeout=LOCK_TIMEOUT - ) - assert basetemp is not None, basetemp - self._basetemp = t = basetemp - self._trace("new basetemp", t) - return t - - + if self._given_basetemp is not None: + basetemp = self._given_basetemp + ensure_reset_dir(basetemp) + basetemp = basetemp.resolve() + else: + from_env = os.environ.get("PYTEST_DEBUG_TEMPROOT") + temproot = Path(from_env or tempfile.gettempdir()).resolve() + user = get_user() or "unknown" + # use a sub-directory in the temproot to speed-up + # make_numbered_dir() call + rootdir = temproot.joinpath("pytest-of-{}".format(user)) + rootdir.mkdir(exist_ok=True) + basetemp = make_numbered_dir_with_cleanup( + prefix="pytest-", root=rootdir, keep=3, lock_timeout=LOCK_TIMEOUT + ) + assert basetemp is not None, basetemp + self._basetemp = t = basetemp + self._trace("new basetemp", t) + return t + + @attr.s class TempdirFactory(object): """ @@ -169,7 +169,7 @@ def _mk_tmp(request, factory): @pytest.fixture -def tmpdir(tmp_path): +def tmpdir(tmp_path): """Return a temporary directory path object which is unique to each test function invocation, created as a sub directory of the base temporary @@ -178,7 +178,7 @@ def tmpdir(tmp_path): .. _`py.path.local`: https://py.readthedocs.io/en/latest/path.html """ - return py.path.local(tmp_path) + return py.path.local(tmp_path) @pytest.fixture diff --git a/contrib/python/pytest/py2/_pytest/unittest.py b/contrib/python/pytest/py2/_pytest/unittest.py index 3ff6f45d8d..8445a6785c 100644 --- a/contrib/python/pytest/py2/_pytest/unittest.py +++ b/contrib/python/pytest/py2/_pytest/unittest.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """ discovery and running of std-library "unittest" style tests. """ from __future__ import absolute_import from __future__ import division @@ -8,7 +8,7 @@ import sys import traceback import _pytest._code -import pytest +import pytest from _pytest.compat import getimfunc from _pytest.config import hookimpl from _pytest.outcomes import fail @@ -40,12 +40,12 @@ class UnitTestCase(Class): cls = self.obj if not getattr(cls, "__test__", True): return - - skipped = getattr(cls, "__unittest_skip__", False) - if not skipped: - self._inject_setup_teardown_fixtures(cls) - self._inject_setup_class_fixture() - + + skipped = getattr(cls, "__unittest_skip__", False) + if not skipped: + self._inject_setup_teardown_fixtures(cls) + self._inject_setup_class_fixture() + self.session._fixturemanager.parsefactories(self, unittest=True) loader = TestLoader() foundsomething = False @@ -64,48 +64,48 @@ class UnitTestCase(Class): if ut is None or runtest != ut.TestCase.runTest: yield TestCaseFunction("runTest", parent=self) - def _inject_setup_teardown_fixtures(self, cls): - """Injects a hidden auto-use fixture to invoke setUpClass/setup_method and corresponding - teardown functions (#517)""" - class_fixture = _make_xunit_fixture( - cls, "setUpClass", "tearDownClass", scope="class", pass_self=False - ) - if class_fixture: - cls.__pytest_class_setup = class_fixture - - method_fixture = _make_xunit_fixture( - cls, "setup_method", "teardown_method", scope="function", pass_self=True - ) - if method_fixture: - cls.__pytest_method_setup = method_fixture - - -def _make_xunit_fixture(obj, setup_name, teardown_name, scope, pass_self): - setup = getattr(obj, setup_name, None) - teardown = getattr(obj, teardown_name, None) - if setup is None and teardown is None: - return None - - @pytest.fixture(scope=scope, autouse=True) - def fixture(self, request): - if getattr(self, "__unittest_skip__", None): - reason = self.__unittest_skip_why__ - pytest.skip(reason) - if setup is not None: - if pass_self: - setup(self, request.function) - else: - setup() - yield - if teardown is not None: - if pass_self: - teardown(self, request.function) - else: - teardown() - - return fixture - - + def _inject_setup_teardown_fixtures(self, cls): + """Injects a hidden auto-use fixture to invoke setUpClass/setup_method and corresponding + teardown functions (#517)""" + class_fixture = _make_xunit_fixture( + cls, "setUpClass", "tearDownClass", scope="class", pass_self=False + ) + if class_fixture: + cls.__pytest_class_setup = class_fixture + + method_fixture = _make_xunit_fixture( + cls, "setup_method", "teardown_method", scope="function", pass_self=True + ) + if method_fixture: + cls.__pytest_method_setup = method_fixture + + +def _make_xunit_fixture(obj, setup_name, teardown_name, scope, pass_self): + setup = getattr(obj, setup_name, None) + teardown = getattr(obj, teardown_name, None) + if setup is None and teardown is None: + return None + + @pytest.fixture(scope=scope, autouse=True) + def fixture(self, request): + if getattr(self, "__unittest_skip__", None): + reason = self.__unittest_skip_why__ + pytest.skip(reason) + if setup is not None: + if pass_self: + setup(self, request.function) + else: + setup() + yield + if teardown is not None: + if pass_self: + teardown(self, request.function) + else: + teardown() + + return fixture + + class TestCaseFunction(Function): nofuncargs = True _excinfo = None @@ -143,10 +143,10 @@ class TestCaseFunction(Function): rawexcinfo = getattr(rawexcinfo, "_rawexcinfo", rawexcinfo) try: excinfo = _pytest._code.ExceptionInfo(rawexcinfo) - # invoke the attributes to trigger storing the traceback - # trial causes some issue there - excinfo.value - excinfo.traceback + # invoke the attributes to trigger storing the traceback + # trial causes some issue there + excinfo.value + excinfo.traceback except TypeError: try: try: @@ -168,7 +168,7 @@ class TestCaseFunction(Function): except KeyboardInterrupt: raise except fail.Exception: - excinfo = _pytest._code.ExceptionInfo.from_current() + excinfo = _pytest._code.ExceptionInfo.from_current() self.__dict__.setdefault("_excinfo", []).append(excinfo) def addError(self, testcase, rawexcinfo): diff --git a/contrib/python/pytest/py2/_pytest/warning_types.py b/contrib/python/pytest/py2/_pytest/warning_types.py index 861010a127..ef10d82809 100644 --- a/contrib/python/pytest/py2/_pytest/warning_types.py +++ b/contrib/python/pytest/py2/_pytest/warning_types.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- import attr @@ -10,46 +10,46 @@ class PytestWarning(UserWarning): """ -class PytestAssertRewriteWarning(PytestWarning): +class PytestAssertRewriteWarning(PytestWarning): """ - Bases: :class:`PytestWarning`. + Bases: :class:`PytestWarning`. - Warning emitted by the pytest assert rewrite module. + Warning emitted by the pytest assert rewrite module. """ -class PytestCacheWarning(PytestWarning): +class PytestCacheWarning(PytestWarning): """ - Bases: :class:`PytestWarning`. + Bases: :class:`PytestWarning`. - Warning emitted by the cache plugin in various situations. - """ - - -class PytestConfigWarning(PytestWarning): - """ - Bases: :class:`PytestWarning`. - - Warning emitted for configuration issues. - """ - - -class PytestCollectionWarning(PytestWarning): - """ - Bases: :class:`PytestWarning`. - - Warning emitted when pytest is not able to collect a file or symbol in a module. - """ - - -class PytestDeprecationWarning(PytestWarning, DeprecationWarning): - """ - Bases: :class:`pytest.PytestWarning`, :class:`DeprecationWarning`. - - Warning class for features that will be removed in a future version. + Warning emitted by the cache plugin in various situations. """ +class PytestConfigWarning(PytestWarning): + """ + Bases: :class:`PytestWarning`. + + Warning emitted for configuration issues. + """ + + +class PytestCollectionWarning(PytestWarning): + """ + Bases: :class:`PytestWarning`. + + Warning emitted when pytest is not able to collect a file or symbol in a module. + """ + + +class PytestDeprecationWarning(PytestWarning, DeprecationWarning): + """ + Bases: :class:`pytest.PytestWarning`, :class:`DeprecationWarning`. + + Warning class for features that will be removed in a future version. + """ + + class PytestExperimentalApiWarning(PytestWarning, FutureWarning): """ Bases: :class:`pytest.PytestWarning`, :class:`FutureWarning`. @@ -67,33 +67,33 @@ class PytestExperimentalApiWarning(PytestWarning, FutureWarning): ) -class PytestUnhandledCoroutineWarning(PytestWarning): - """ - Bases: :class:`PytestWarning`. - - Warning emitted when pytest encounters a test function which is a coroutine, - but it was not handled by any async-aware plugin. Coroutine test functions - are not natively supported. - """ - - -class PytestUnknownMarkWarning(PytestWarning): - """ - Bases: :class:`PytestWarning`. - - Warning emitted on use of unknown markers. - See https://docs.pytest.org/en/latest/mark.html for details. - """ - - -class RemovedInPytest4Warning(PytestDeprecationWarning): - """ - Bases: :class:`pytest.PytestDeprecationWarning`. - - Warning class for features scheduled to be removed in pytest 4.0. - """ - - +class PytestUnhandledCoroutineWarning(PytestWarning): + """ + Bases: :class:`PytestWarning`. + + Warning emitted when pytest encounters a test function which is a coroutine, + but it was not handled by any async-aware plugin. Coroutine test functions + are not natively supported. + """ + + +class PytestUnknownMarkWarning(PytestWarning): + """ + Bases: :class:`PytestWarning`. + + Warning emitted on use of unknown markers. + See https://docs.pytest.org/en/latest/mark.html for details. + """ + + +class RemovedInPytest4Warning(PytestDeprecationWarning): + """ + Bases: :class:`pytest.PytestDeprecationWarning`. + + Warning class for features scheduled to be removed in pytest 4.0. + """ + + @attr.s class UnformattedWarning(object): """Used to hold warnings that need to format their message at runtime, as opposed to a direct message. diff --git a/contrib/python/pytest/py2/_pytest/warnings.py b/contrib/python/pytest/py2/_pytest/warnings.py index a3debae462..7c1240d501 100644 --- a/contrib/python/pytest/py2/_pytest/warnings.py +++ b/contrib/python/pytest/py2/_pytest/warnings.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- from __future__ import absolute_import from __future__ import division from __future__ import print_function @@ -104,9 +104,9 @@ def catch_warnings_for_item(config, ihook, when, item): def warning_record_to_str(warning_message): - """Convert a warnings.WarningMessage to a string. + """Convert a warnings.WarningMessage to a string. - This takes lot of unicode shenaningans into account for Python 2. + This takes lot of unicode shenaningans into account for Python 2. When Python 2 support is dropped this function can be greatly simplified. """ warn_msg = warning_message.message @@ -162,19 +162,19 @@ def pytest_terminal_summary(terminalreporter): yield -def _issue_warning_captured(warning, hook, stacklevel): +def _issue_warning_captured(warning, hook, stacklevel): """ This function should be used instead of calling ``warnings.warn`` directly when we are in the "configure" stage: at this point the actual options might not have been set, so we manually trigger the pytest_warning_captured hook so we can display this warnings in the terminal. This is a hack until we can sort out #2891. :param warning: the warning instance. - :param hook: the hook caller + :param hook: the hook caller :param stacklevel: stacklevel forwarded to warnings.warn """ with warnings.catch_warnings(record=True) as records: warnings.simplefilter("always", type(warning)) warnings.warn(warning, stacklevel=stacklevel) - hook.pytest_warning_captured.call_historic( + hook.pytest_warning_captured.call_historic( kwargs=dict(warning_message=records[0], when="config", item=None) ) |