summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBruno Oliveira <bruno@esss.com.br>2019-06-03 13:14:31 -0300
committerGitHub <noreply@github.com>2019-06-03 13:14:31 -0300
commit4f57d40a43e0c27d82b8996d70e170ba9c879ee9 (patch)
tree9140d4be613b4d6e4e59b725577f68ab5f5d148d /src
parent693c3b7f61d4d32f8927a74f34ce8ac56d63958e (diff)
parent5976f36240c2990b3f2b0dca44e8b5435a1040dc (diff)
downloadpytest-4f57d40a43e0c27d82b8996d70e170ba9c879ee9.tar.gz
Merge master into features (#5374)
Merge master into features
Diffstat (limited to 'src')
-rw-r--r--src/_pytest/__init__.py1
-rw-r--r--src/_pytest/_argcomplete.py7
-rw-r--r--src/_pytest/_code/__init__.py5
-rw-r--r--src/_pytest/_code/_py2traceback.py95
-rw-r--r--src/_pytest/_code/code.py181
-rw-r--r--src/_pytest/_code/source.py10
-rw-r--r--src/_pytest/_io/saferepr.py17
-rw-r--r--src/_pytest/assertion/__init__.py17
-rw-r--r--src/_pytest/assertion/rewrite.py186
-rw-r--r--src/_pytest/assertion/truncate.py9
-rw-r--r--src/_pytest/assertion/util.py91
-rwxr-xr-xsrc/_pytest/cacheprovider.py23
-rw-r--r--src/_pytest/capture.py79
-rw-r--r--src/_pytest/compat.py202
-rw-r--r--src/_pytest/config/__init__.py60
-rw-r--r--src/_pytest/config/argparsing.py18
-rw-r--r--src/_pytest/config/exceptions.py1
-rw-r--r--src/_pytest/config/findpaths.py1
-rw-r--r--src/_pytest/debugging.py27
-rw-r--r--src/_pytest/deprecated.py5
-rw-r--r--src/_pytest/doctest.py40
-rw-r--r--src/_pytest/fixtures.py49
-rw-r--r--src/_pytest/freeze_support.py4
-rw-r--r--src/_pytest/helpconfig.py21
-rw-r--r--src/_pytest/hookspec.py1
-rw-r--r--src/_pytest/junitxml.py30
-rw-r--r--src/_pytest/logging.py78
-rw-r--r--src/_pytest/main.py100
-rw-r--r--src/_pytest/mark/__init__.py5
-rw-r--r--src/_pytest/mark/evaluate.py7
-rw-r--r--src/_pytest/mark/legacy.py5
-rw-r--r--src/_pytest/mark/structures.py30
-rw-r--r--src/_pytest/monkeypatch.py45
-rw-r--r--src/_pytest/nodes.py20
-rw-r--r--src/_pytest/nose.py9
-rw-r--r--src/_pytest/outcomes.py11
-rw-r--r--src/_pytest/pastebin.py28
-rw-r--r--src/_pytest/pathlib.py32
-rw-r--r--src/_pytest/pytester.py102
-rw-r--r--src/_pytest/python.py53
-rw-r--r--src/_pytest/python_api.py29
-rw-r--r--src/_pytest/recwarn.py50
-rw-r--r--src/_pytest/reports.py28
-rw-r--r--src/_pytest/resultlog.py9
-rw-r--r--src/_pytest/runner.py21
-rw-r--r--src/_pytest/setuponly.py5
-rw-r--r--src/_pytest/setupplan.py5
-rw-r--r--src/_pytest/skipping.py13
-rw-r--r--src/_pytest/stepwise.py1
-rw-r--r--src/_pytest/terminal.py61
-rw-r--r--src/_pytest/tmpdir.py14
-rw-r--r--src/_pytest/unittest.py26
-rw-r--r--src/_pytest/warning_types.py3
-rw-r--r--src/_pytest/warnings.py32
-rw-r--r--src/pytest.py1
55 files changed, 559 insertions, 1444 deletions
diff --git a/src/_pytest/__init__.py b/src/_pytest/__init__.py
index 17cc20b61..46c7827ed 100644
--- a/src/_pytest/__init__.py
+++ b/src/_pytest/__init__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
__all__ = ["__version__"]
try:
diff --git a/src/_pytest/_argcomplete.py b/src/_pytest/_argcomplete.py
index c6cf1d8fd..1ebf7432c 100644
--- a/src/_pytest/_argcomplete.py
+++ b/src/_pytest/_argcomplete.py
@@ -1,4 +1,3 @@
-# -*- 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
@@ -54,16 +53,12 @@ If things do not work right away:
which should throw a KeyError: 'COMPLINE' (which is properly set by the
global argcomplete script).
"""
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import os
import sys
from glob import glob
-class FastFilesCompleter(object):
+class FastFilesCompleter:
"Fast file completer class"
def __init__(self, directories=True):
diff --git a/src/_pytest/_code/__init__.py b/src/_pytest/_code/__init__.py
index 1394b2b10..370e41dc9 100644
--- a/src/_pytest/_code/__init__.py
+++ b/src/_pytest/_code/__init__.py
@@ -1,9 +1,4 @@
-# -*- coding: utf-8 -*-
""" python inspection/code generation API """
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
from .code import Code # noqa
from .code import ExceptionInfo # noqa
from .code import filter_traceback # noqa
diff --git a/src/_pytest/_code/_py2traceback.py b/src/_pytest/_code/_py2traceback.py
deleted file mode 100644
index faacc0216..000000000
--- a/src/_pytest/_code/_py2traceback.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# -*- coding: utf-8 -*-
-# copied from python-2.7.3's traceback.py
-# CHANGES:
-# - some_str is replaced, trying to create unicode strings
-#
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-from __future__ import unicode_literals
-
-import types
-
-from six import text_type
-
-
-def format_exception_only(etype, value):
- """Format the exception part of a traceback.
-
- The arguments are the exception type and value such as given by
- sys.last_type and sys.last_value. The return value is a list of
- strings, each ending in a newline.
-
- Normally, the list contains a single string; however, for
- SyntaxError exceptions, it contains several lines that (when
- printed) display detailed information about where the syntax
- error occurred.
-
- The message indicating which exception occurred is always the last
- string in the list.
-
- """
-
- # An instance should not have a meaningful value parameter, but
- # sometimes does, particularly for string exceptions, such as
- # >>> raise string1, string2 # deprecated
- #
- # Clear these out first because issubtype(string1, SyntaxError)
- # would throw another exception and mask the original problem.
- if (
- isinstance(etype, BaseException)
- or isinstance(etype, types.InstanceType)
- or etype is None
- or type(etype) is str
- ):
- return [_format_final_exc_line(etype, value)]
-
- stype = etype.__name__
-
- if not issubclass(etype, SyntaxError):
- return [_format_final_exc_line(stype, value)]
-
- # It was a syntax error; show exactly where the problem was found.
- lines = []
- try:
- msg, (filename, lineno, offset, badline) = value.args
- except Exception:
- pass
- else:
- filename = filename or "<string>"
- lines.append(' File "{}", line {}\n'.format(filename, lineno))
- if badline is not None:
- if isinstance(badline, bytes): # python 2 only
- badline = badline.decode("utf-8", "replace")
- lines.append(" {}\n".format(badline.strip()))
- if offset is not None:
- caretspace = badline.rstrip("\n")[:offset].lstrip()
- # non-space whitespace (likes tabs) must be kept for alignment
- caretspace = ((c.isspace() and c or " ") for c in caretspace)
- # only three spaces to account for offset1 == pos 0
- lines.append(" {}^\n".format("".join(caretspace)))
- value = msg
-
- lines.append(_format_final_exc_line(stype, value))
- return lines
-
-
-def _format_final_exc_line(etype, value):
- """Return a list of a single line -- normal case for format_exception_only"""
- valuestr = _some_str(value)
- if value is None or not valuestr:
- line = "{}\n".format(etype)
- else:
- line = "{}: {}\n".format(etype, valuestr)
- return line
-
-
-def _some_str(value):
- try:
- return text_type(value)
- except Exception:
- try:
- return bytes(value).decode("UTF-8", "replace")
- except Exception:
- pass
- return "<unprintable {} object>".format(type(value).__name__)
diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py
index 8c73ccc6a..c4ed961ac 100644
--- a/src/_pytest/_code/code.py
+++ b/src/_pytest/_code/code.py
@@ -1,36 +1,22 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import inspect
import re
import sys
import traceback
from inspect import CO_VARARGS
from inspect import CO_VARKEYWORDS
+from traceback import format_exception_only
from weakref import ref
import attr
import pluggy
import py
-from six import text_type
import _pytest
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
-from _pytest.compat import safe_str
-
-if _PY3:
- from traceback import format_exception_only
-else:
- from ._py2traceback import format_exception_only
-class Code(object):
+class Code:
""" wrapper around Python code objects """
def __init__(self, rawcode):
@@ -41,7 +27,7 @@ class Code(object):
self.firstlineno = rawcode.co_firstlineno - 1
self.name = rawcode.co_name
except AttributeError:
- raise TypeError("not a code object: %r" % (rawcode,))
+ raise TypeError("not a code object: {!r}".format(rawcode))
self.raw = rawcode
def __eq__(self, other):
@@ -100,7 +86,7 @@ class Code(object):
return raw.co_varnames[:argcount]
-class Frame(object):
+class Frame:
"""Wrapper around a Python frame holding f_locals and f_globals
in which expressions can be evaluated."""
@@ -163,7 +149,7 @@ class Frame(object):
return retval
-class TracebackEntry(object):
+class TracebackEntry:
""" a single entry in a traceback """
_repr_style = None
@@ -208,8 +194,7 @@ class TracebackEntry(object):
locals = property(getlocals, None, None, "locals of underlaying frame")
def getfirstlinesource(self):
- # on Jython this firstlineno can be -1 apparently
- return max(self.frame.code.firstlineno, 0)
+ return self.frame.code.firstlineno
def getsource(self, astcache=None):
""" return failing source code. """
@@ -324,7 +309,7 @@ class Traceback(list):
return self
def __getitem__(self, key):
- val = super(Traceback, self).__getitem__(key)
+ val = super().__getitem__(key)
if isinstance(key, type(slice(0))):
val = self.__class__(val)
return val
@@ -386,14 +371,12 @@ co_equal = compile(
@attr.s(repr=False)
-class ExceptionInfo(object):
+class ExceptionInfo:
""" wraps sys.exc_info() objects and offers
help for navigating the traceback.
"""
- _assert_start_repr = (
- "AssertionError(u'assert " if _PY2 else "AssertionError('assert "
- )
+ _assert_start_repr = "AssertionError('assert "
_excinfo = attr.ib()
_striptext = attr.ib(default="")
@@ -558,11 +541,6 @@ class ExceptionInfo(object):
loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly())
return str(loc)
- def __unicode__(self):
- entry = self.traceback[-1]
- loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly())
- return text_type(loc)
-
def match(self, regexp):
"""
Check whether the regular expression 'regexp' is found in the string
@@ -578,7 +556,7 @@ class ExceptionInfo(object):
@attr.s
-class FormattedExcinfo(object):
+class FormattedExcinfo:
""" presenting information about failing Functions and Generators. """
# for traceback entries
@@ -677,7 +655,7 @@ class FormattedExcinfo(object):
str_repr = safeformat(value)
# if len(str_repr) < 70 or not isinstance(value,
# (list, tuple, dict)):
- lines.append("%-10s = %s" % (name, str_repr))
+ lines.append("{:<10} = {}".format(name, str_repr))
# else:
# self._line("%-10s =\\" % (name,))
# # XXX
@@ -692,8 +670,7 @@ class FormattedExcinfo(object):
source = _pytest._code.Source("???")
line_index = 0
else:
- # entry.getfirstlinesource() can be -1, should be 0 on jython
- line_index = entry.lineno - max(entry.getfirstlinesource(), 0)
+ line_index = entry.lineno - entry.getfirstlinesource()
lines = []
style = entry._repr_style
@@ -733,7 +710,7 @@ class FormattedExcinfo(object):
if self.tbfilter:
traceback = traceback.filter()
- if is_recursion_error(excinfo):
+ if excinfo.errisinstance(RecursionError):
traceback, extraline = self._truncate_recursive_traceback(traceback)
else:
extraline = None
@@ -769,7 +746,7 @@ class FormattedExcinfo(object):
" Displaying first and last {max_frames} stack frames out of {total}."
).format(
exc_type=type(e).__name__,
- exc_msg=safe_str(e),
+ exc_msg=str(e),
max_frames=max_frames,
total=len(traceback),
)
@@ -784,64 +761,51 @@ class FormattedExcinfo(object):
return traceback, extraline
def repr_excinfo(self, excinfo):
- if _PY2:
- reprtraceback = self.repr_traceback(excinfo)
- reprcrash = excinfo._getreprcrash()
- return ReprExceptionInfo(reprtraceback, reprcrash)
- else:
- repr_chain = []
- e = excinfo.value
- descr = None
- seen = set()
- while e is not None and id(e) not in seen:
- seen.add(id(e))
- if excinfo:
- reprtraceback = self.repr_traceback(excinfo)
- reprcrash = excinfo._getreprcrash()
- else:
- # fallback to native repr if the exception doesn't have a traceback:
- # ExceptionInfo objects require a full traceback to work
- reprtraceback = ReprTracebackNative(
- traceback.format_exception(type(e), e, None)
- )
- reprcrash = None
-
- repr_chain += [(reprtraceback, reprcrash, descr)]
- if e.__cause__ is not None and self.chain:
- e = e.__cause__
- excinfo = (
- ExceptionInfo((type(e), e, e.__traceback__))
- if e.__traceback__
- else None
- )
- descr = "The above exception was the direct cause of the following exception:"
- elif (
- e.__context__ is not None
- and not e.__suppress_context__
- and self.chain
- ):
- e = e.__context__
- excinfo = (
- ExceptionInfo((type(e), e, e.__traceback__))
- if e.__traceback__
- else None
- )
- descr = "During handling of the above exception, another exception occurred:"
- else:
- e = None
- repr_chain.reverse()
- return ExceptionChainRepr(repr_chain)
+ repr_chain = []
+ e = excinfo.value
+ descr = None
+ seen = set()
+ while e is not None and id(e) not in seen:
+ seen.add(id(e))
+ if excinfo:
+ reprtraceback = self.repr_traceback(excinfo)
+ reprcrash = excinfo._getreprcrash()
+ else:
+ # fallback to native repr if the exception doesn't have a traceback:
+ # ExceptionInfo objects require a full traceback to work
+ reprtraceback = ReprTracebackNative(
+ traceback.format_exception(type(e), e, None)
+ )
+ reprcrash = None
+
+ repr_chain += [(reprtraceback, reprcrash, descr)]
+ if e.__cause__ is not None and self.chain:
+ e = e.__cause__
+ excinfo = (
+ ExceptionInfo((type(e), e, e.__traceback__))
+ if e.__traceback__
+ else None
+ )
+ descr = "The above exception was the direct cause of the following exception:"
+ elif (
+ e.__context__ is not None and not e.__suppress_context__ and self.chain
+ ):
+ e = e.__context__
+ excinfo = (
+ ExceptionInfo((type(e), e, e.__traceback__))
+ if e.__traceback__
+ else None
+ )
+ descr = "During handling of the above exception, another exception occurred:"
+ else:
+ e = None
+ repr_chain.reverse()
+ return ExceptionChainRepr(repr_chain)
-class TerminalRepr(object):
+class TerminalRepr:
def __str__(self):
- s = self.__unicode__()
- if _PY2:
- s = s.encode("utf-8")
- return s
-
- def __unicode__(self):
# FYI this is called from pytest-xdist's serialization of exception
# information.
io = py.io.TextIO()
@@ -850,7 +814,7 @@ class TerminalRepr(object):
return io.getvalue().strip()
def __repr__(self):
- return "<%s instance at %0x>" % (self.__class__, id(self))
+ return "<{} instance at {:0x}>".format(self.__class__, id(self))
class ExceptionRepr(TerminalRepr):
@@ -868,7 +832,7 @@ class ExceptionRepr(TerminalRepr):
class ExceptionChainRepr(ExceptionRepr):
def __init__(self, chain):
- super(ExceptionChainRepr, self).__init__()
+ super().__init__()
self.chain = chain
# reprcrash and reprtraceback of the outermost (the newest) exception
# in the chain
@@ -881,18 +845,18 @@ class ExceptionChainRepr(ExceptionRepr):
if element[2] is not None:
tw.line("")
tw.line(element[2], yellow=True)
- super(ExceptionChainRepr, self).toterminal(tw)
+ super().toterminal(tw)
class ReprExceptionInfo(ExceptionRepr):
def __init__(self, reprtraceback, reprcrash):
- super(ReprExceptionInfo, self).__init__()
+ super().__init__()
self.reprtraceback = reprtraceback
self.reprcrash = reprcrash
def toterminal(self, tw):
self.reprtraceback.toterminal(tw)
- super(ReprExceptionInfo, self).toterminal(tw)
+ super().toterminal(tw)
class ReprTraceback(TerminalRepr):
@@ -969,7 +933,9 @@ class ReprEntry(TerminalRepr):
self.reprfileloc.toterminal(tw)
def __str__(self):
- return "%s\n%s\n%s" % ("\n".join(self.lines), self.reprlocals, self.reprfileloc)
+ return "{}\n{}\n{}".format(
+ "\n".join(self.lines), self.reprlocals, self.reprfileloc
+ )
class ReprFileLocation(TerminalRepr):
@@ -986,7 +952,7 @@ class ReprFileLocation(TerminalRepr):
if i != -1:
msg = msg[:i]
tw.write(self.path, bold=True, red=True)
- tw.line(":%s: %s" % (self.lineno, msg))
+ tw.line(":{}: {}".format(self.lineno, msg))
class ReprLocals(TerminalRepr):
@@ -1006,7 +972,7 @@ class ReprFuncArgs(TerminalRepr):
if self.args:
linesofar = ""
for name, value in self.args:
- ns = "%s = %s" % (safe_str(name), safe_str(value))
+ ns = "{} = {}".format(name, value)
if len(ns) + len(linesofar) + 2 > tw.fullwidth:
if linesofar:
tw.line(linesofar)
@@ -1038,23 +1004,6 @@ def getrawcode(obj, trycall=True):
return obj
-if PY35: # RecursionError introduced in 3.5
-
- def is_recursion_error(excinfo):
- return excinfo.errisinstance(RecursionError) # noqa
-
-
-else:
-
- def is_recursion_error(excinfo):
- if not excinfo.errisinstance(RuntimeError):
- return False
- try:
- return "maximum recursion depth exceeded" in str(excinfo.value)
- except UnicodeError:
- return False
-
-
# relative paths that we use to filter traceback entries from appearing to the user;
# see filter_traceback
# note: if we need to add more paths than what we have now we should probably use a list
diff --git a/src/_pytest/_code/source.py b/src/_pytest/_code/source.py
index c8a4b6adf..70d5f8fcd 100644
--- a/src/_pytest/_code/source.py
+++ b/src/_pytest/_code/source.py
@@ -1,8 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import ast
import inspect
import linecache
@@ -14,10 +9,9 @@ from ast import PyCF_ONLY_AST as _AST_FLAG
from bisect import bisect_right
import py
-import six
-class Source(object):
+class Source:
""" an immutable object holding a source code fragment,
possibly deindenting it.
"""
@@ -34,7 +28,7 @@ class Source(object):
partlines = part.lines
elif isinstance(part, (tuple, list)):
partlines = [x.rstrip("\n") for x in part]
- elif isinstance(part, six.string_types):
+ elif isinstance(part, str):
partlines = part.split("\n")
else:
partlines = getsource(part, deindent=de).lines
diff --git a/src/_pytest/_io/saferepr.py b/src/_pytest/_io/saferepr.py
index 9b412dcca..74f75124f 100644
--- a/src/_pytest/_io/saferepr.py
+++ b/src/_pytest/_io/saferepr.py
@@ -1,7 +1,5 @@
-# -*- coding: utf-8 -*-
import pprint
-
-from six.moves import reprlib
+import reprlib
def _call_and_format_exception(call, x, *args):
@@ -14,11 +12,8 @@ def _call_and_format_exception(call, x, *args):
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),
+ return '<[{}("{}") raised in repr()] {} object at 0x{:x}>'.format(
+ exc_name, exc_info, x.__class__.__name__, id(x)
)
@@ -34,11 +29,11 @@ class SafeRepr(reprlib.Repr):
# Strictly speaking wrong on narrow builds
def repr(u):
if "'" not in u:
- return u"'%s'" % u
+ return "'%s'" % u
elif '"' not in u:
- return u'"%s"' % u
+ return '"%s"' % u
else:
- return u"'%s'" % u.replace("'", r"\'")
+ return "'%s'" % u.replace("'", r"\'")
s = repr(x[: self.maxstring])
if len(s) > self.maxstring:
diff --git a/src/_pytest/assertion/__init__.py b/src/_pytest/assertion/__init__.py
index 6b6abb863..e52101c9f 100644
--- a/src/_pytest/assertion/__init__.py
+++ b/src/_pytest/assertion/__init__.py
@@ -1,15 +1,8 @@
-# -*- coding: utf-8 -*-
"""
support for presenting detailed information in failing assertions.
"""
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import sys
-import six
-
from _pytest.assertion import rewrite
from _pytest.assertion import truncate
from _pytest.assertion import util
@@ -56,14 +49,14 @@ def register_assert_rewrite(*names):
importhook.mark_rewrite(*names)
-class DummyRewriteHook(object):
+class DummyRewriteHook:
"""A no-op import hook for when rewriting is disabled."""
def mark_rewrite(self, *names):
pass
-class AssertionState(object):
+class AssertionState:
"""State for the assertion plugin."""
def __init__(self, config, mode):
@@ -74,10 +67,6 @@ class AssertionState(object):
def install_importhook(config):
"""Try to install the rewrite hook, raise SystemError if it fails."""
- # Jython has an AST bug that make the assertion rewriting hook malfunction.
- if sys.platform.startswith("java"):
- raise SystemError("rewrite not supported")
-
config._assertstate = AssertionState(config, "rewrite")
config._assertstate.hook = hook = rewrite.AssertionRewritingHook(config)
sys.meta_path.insert(0, hook)
@@ -133,7 +122,7 @@ def pytest_runtest_setup(item):
if new_expl:
new_expl = truncate.truncate_if_required(new_expl, item)
new_expl = [line.replace("\n", "\\n") for line in new_expl]
- res = six.text_type("\n~").join(new_expl)
+ res = "\n~".join(new_expl)
if item.config.getvalue("assertmode") == "rewrite":
res = res.replace("%", "%%")
return res
diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py
index 5e2c5397b..9b431b984 100644
--- a/src/_pytest/assertion/rewrite.py
+++ b/src/_pytest/assertion/rewrite.py
@@ -1,9 +1,4 @@
-# -*- 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 ast
import errno
import imp
@@ -15,17 +10,16 @@ import string
import struct
import sys
import types
+from importlib.util import spec_from_file_location
import atomicwrites
import py
-import six
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.compat import spec_from_file_location
from _pytest.pathlib import fnmatch_ex
from _pytest.pathlib import PurePath
@@ -35,28 +29,17 @@ if hasattr(imp, "get_tag"):
else:
if hasattr(sys, "pypy_version_info"):
impl = "pypy"
- elif sys.platform == "java":
- impl = "jython"
else:
impl = "cpython"
ver = sys.version_info
- PYTEST_TAG = "%s-%s%s-PYTEST" % (impl, ver[0], ver[1])
+ PYTEST_TAG = "{}-{}{}-PYTEST".format(impl, ver[0], ver[1])
del ver, impl
PYC_EXT = ".py" + (__debug__ and "c" or "o")
PYC_TAIL = "." + PYTEST_TAG + PYC_EXT
-ASCII_IS_DEFAULT_ENCODING = sys.version_info[0] < 3
-
-if sys.version_info >= (3, 5):
- ast_Call = ast.Call
-else:
-
- def ast_Call(a, b, c):
- return ast.Call(a, b, c, None, None)
-
-class AssertionRewritingHook(object):
+class AssertionRewritingHook:
"""PEP302 Import hook which rewrites asserts."""
def __init__(self, config):
@@ -165,7 +148,7 @@ class AssertionRewritingHook(object):
# to check for a cached pyc. This may not be optimal...
co = _read_pyc(fn_pypath, pyc, state.trace)
if co is None:
- state.trace("rewriting %r" % (fn,))
+ state.trace("rewriting {!r}".format(fn))
source_stat, co = _rewrite_test(self.config, fn_pypath)
if co is None:
# Probably a SyntaxError in the test.
@@ -177,7 +160,7 @@ class AssertionRewritingHook(object):
finally:
self._writing_pyc = False
else:
- state.trace("found cached rewritten pyc for %r" % (fn,))
+ state.trace("found cached rewritten pyc for {!r}".format(fn))
self.modules[name] = co, pyc
return self
@@ -216,26 +199,28 @@ class AssertionRewritingHook(object):
if self._is_marked_for_rewrite(name, state):
return False
- state.trace("early skip of rewriting module: %s" % (name,))
+ state.trace("early skip of rewriting module: {}".format(name))
return True
def _should_rewrite(self, name, fn_pypath, state):
# always rewrite conftest files
fn = str(fn_pypath)
if fn_pypath.basename == "conftest.py":
- state.trace("rewriting conftest file: %r" % (fn,))
+ state.trace("rewriting conftest file: {!r}".format(fn))
return True
if self.session is not None:
if self.session.isinitpath(fn):
- state.trace("matched test file (was specified on cmdline): %r" % (fn,))
+ state.trace(
+ "matched test file (was specified on cmdline): {!r}".format(fn)
+ )
return True
# modules not passed explicitly on the command line are only
# rewritten if they match the naming convention for test files
for pat in self.fnpats:
if fn_pypath.fnmatch(pat):
- state.trace("matched test file %r" % (fn,))
+ state.trace("matched test file {!r}".format(fn))
return True
return self._is_marked_for_rewrite(name, state)
@@ -246,7 +231,9 @@ class AssertionRewritingHook(object):
except KeyError:
for marked in self._must_rewrite:
if name == marked or name.startswith(marked + "."):
- state.trace("matched marked file %r (from %r)" % (name, marked))
+ state.trace(
+ "matched marked file {!r} (from {!r})".format(name, marked)
+ )
self._marked_for_rewrite_cache[name] = True
return True
@@ -341,7 +328,7 @@ def _write_pyc(state, co, source_stat, pyc):
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))
+ state.trace("error writing pyc file at {}: errno={}".format(pyc, e.errno))
# we ignore any failure to write the cache file
# there are many reasons, permission-denied, __pycache__ being a
# file etc.
@@ -349,8 +336,8 @@ def _write_pyc(state, co, source_stat, pyc):
return True
-RN = "\r\n".encode("utf-8")
-N = "\n".encode("utf-8")
+RN = "\r\n".encode()
+N = "\n".encode()
cookie_re = re.compile(r"^[ \t\f]*#.*coding[:=][ \t]*[-\w.]+")
BOM_UTF8 = "\xef\xbb\xbf"
@@ -364,42 +351,11 @@ def _rewrite_test(config, fn):
source = fn.read("rb")
except EnvironmentError:
return None, None
- if ASCII_IS_DEFAULT_ENCODING:
- # ASCII is the default encoding in Python 2. Without a coding
- # declaration, Python 2 will complain about any bytes in the file
- # outside the ASCII range. Sadly, this behavior does not extend to
- # compile() or ast.parse(), which prefer to interpret the bytes as
- # latin-1. (At least they properly handle explicit coding cookies.) To
- # preserve this error behavior, we could force ast.parse() to use ASCII
- # as the encoding by inserting a coding cookie. Unfortunately, that
- # messes up line numbers. Thus, we have to check ourselves if anything
- # is outside the ASCII range in the case no encoding is explicitly
- # declared. For more context, see issue #269. Yay for Python 3 which
- # gets this right.
- end1 = source.find("\n")
- end2 = source.find("\n", end1 + 1)
- if (
- not source.startswith(BOM_UTF8)
- and cookie_re.match(source[0:end1]) is None
- and cookie_re.match(source[end1 + 1 : end2]) is None
- ):
- if hasattr(state, "_indecode"):
- # encodings imported us again, so don't rewrite.
- return None, None
- state._indecode = True
- try:
- try:
- source.decode("ascii")
- except UnicodeDecodeError:
- # Let it fail in real import.
- return None, None
- finally:
- del state._indecode
try:
tree = ast.parse(source, filename=fn.strpath)
except SyntaxError:
# Let this pop up again in the real import.
- state.trace("failed to parse: %r" % (fn,))
+ state.trace("failed to parse: {!r}".format(fn))
return None, None
rewrite_asserts(tree, fn, config)
try:
@@ -407,7 +363,7 @@ def _rewrite_test(config, fn):
except SyntaxError:
# It's possible that this error is from some bug in the
# assertion rewriting, but I don't know of a fast way to tell.
- state.trace("failed to compile: %r" % (fn,))
+ state.trace("failed to compile: {!r}".format(fn))
return None, None
return stat, co
@@ -427,7 +383,7 @@ def _read_pyc(source, pyc, trace=lambda x: None):
size = source.size()
data = fp.read(12)
except EnvironmentError as e:
- trace("_read_pyc(%s): EnvironmentError %s" % (source, e))
+ trace("_read_pyc({}): EnvironmentError {}".format(source, e))
return None
# Check for invalid or out of date pyc file.
if (
@@ -440,7 +396,7 @@ def _read_pyc(source, pyc, trace=lambda x: None):
try:
co = marshal.load(fp)
except Exception as e:
- trace("_read_pyc(%s): marshal.load error %s" % (source, e))
+ trace("_read_pyc({}): marshal.load error {}".format(source, e))
return None
if not isinstance(co, types.CodeType):
trace("_read_pyc(%s): not a code object" % source)
@@ -468,11 +424,11 @@ def _saferepr(obj):
# only occurs in python2.x, repr must return text in python3+
if isinstance(r, bytes):
# Represent unprintable bytes as `\x##`
- r = u"".join(
- u"\\x{:x}".format(ord(c)) if c not in string.printable else c.decode()
+ r = "".join(
+ "\\x{:x}".format(ord(c)) if c not in string.printable else c.decode()
for c in r
)
- return r.replace(u"\n", u"\\n")
+ return r.replace("\n", "\\n")
def _format_assertmsg(obj):
@@ -487,10 +443,10 @@ def _format_assertmsg(obj):
# contains a newline it gets escaped, however if an object has a
# .__repr__() which contains newlines it does not get escaped.
# However in either case we want to preserve the newline.
- replaces = [(u"\n", u"\n~"), (u"%", u"%%")]
- if not isinstance(obj, six.string_types):
+ replaces = [("\n", "\n~"), ("%", "%%")]
+ if not isinstance(obj, str):
obj = saferepr(obj)
- replaces.append((u"\\n", u"\n~"))
+ replaces.append(("\\n", "\n~"))
if isinstance(obj, bytes):
replaces = [(r1.encode(), r2.encode()) for r1, r2 in replaces]
@@ -513,8 +469,8 @@ def _should_repr_global_name(obj):
def _format_boolop(explanations, is_or):
explanation = "(" + (is_or and " or " or " and ").join(explanations) + ")"
- if isinstance(explanation, six.text_type):
- return explanation.replace(u"%", u"%%")
+ if isinstance(explanation, str):
+ return explanation.replace("%", "%%")
else:
return explanation.replace(b"%", b"%%")
@@ -643,7 +599,7 @@ class AssertionRewriter(ast.NodeVisitor):
"""
def __init__(self, module_path, config):
- super(AssertionRewriter, self).__init__()
+ super().__init__()
self.module_path = module_path
self.config = config
@@ -655,7 +611,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("builtins", "@py_builtins"),
ast.alias("_pytest.assertion.rewrite", "@pytest_ar"),
]
doc = getattr(mod, "docstring", None)
@@ -737,7 +693,7 @@ class AssertionRewriter(ast.NodeVisitor):
"""Call a helper in this module."""
py_name = ast.Name("@pytest_ar", ast.Load())
attr = ast.Attribute(py_name, name, ast.Load())
- return ast_Call(attr, list(args), [])
+ return ast.Call(attr, list(args), [])
def builtin(self, name):
"""Return the builtin called *name*."""
@@ -847,11 +803,9 @@ class AssertionRewriter(ast.NodeVisitor):
msg = self.pop_format_context(template)
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:
- raise_ = ast.Raise(exc, None)
- else:
- raise_ = ast.Raise(exc, None, None)
+ exc = ast.Call(err_name, [fmt], [])
+ raise_ = ast.Raise(exc, None)
+
body.append(raise_)
# Clear temporary variables by setting them to None.
if self.variables:
@@ -893,7 +847,7 @@ warn_explicit(
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"), [], [])
+ locs = ast.Call(self.builtin("locals"), [], [])
inlocs = ast.Compare(ast.Str(name.id), [ast.In()], [locs])
dorepr = self.helper("_should_repr_global_name", name)
test = ast.BoolOp(ast.Or(), [inlocs, dorepr])
@@ -920,7 +874,7 @@ warn_explicit(
res, expl = self.visit(v)
body.append(ast.Assign([ast.Name(res_var, ast.Store())], res))
expl_format = self.pop_format_context(ast.Str(expl))
- call = ast_Call(app, [expl_format], [])
+ call = ast.Call(app, [expl_format], [])
self.on_failure.append(ast.Expr(call))
if i < levels:
cond = res
@@ -945,15 +899,25 @@ warn_explicit(
symbol = binop_map[binop.op.__class__]
left_expr, left_expl = self.visit(binop.left)
right_expr, right_expl = self.visit(binop.right)
- explanation = "(%s %s %s)" % (left_expl, symbol, right_expl)
+ explanation = "({} {} {})".format(left_expl, symbol, right_expl)
res = self.assign(ast.BinOp(left_expr, binop.op, right_expr))
return res, explanation
- def visit_Call_35(self, call):
+ @staticmethod
+ def _is_any_call_with_generator_or_list_comprehension(call):
+ """Return True if the Call node is an 'any' call with a generator or list comprehension"""
+ return (
+ isinstance(call.func, ast.Name)
+ and call.func.id == "all"
+ and len(call.args) == 1
+ and isinstance(call.args[0], (ast.GeneratorExp, ast.ListComp))
+ )
+
+ def visit_Call(self, call):
"""
- visit `ast.Call` nodes on Python3.5 and after
+ visit `ast.Call` nodes
"""
- if isinstance(call.func, ast.Name) and call.func.id == "all":
+ if self._is_any_call_with_generator_or_list_comprehension(call):
return self._visit_all(call)
new_func, func_expl = self.visit(call.func)
arg_expls = []
@@ -971,17 +935,15 @@ warn_explicit(
else: # **args have `arg` keywords with an .arg of None
arg_expls.append("**" + expl)
- expl = "%s(%s)" % (func_expl, ", ".join(arg_expls))
+ expl = "{}({})".format(func_expl, ", ".join(arg_expls))
new_call = ast.Call(new_func, new_args, new_kwargs)
res = self.assign(new_call)
res_expl = self.explanation_param(self.display(res))
- outer_expl = "%s\n{%s = %s\n}" % (res_expl, res_expl, expl)
+ outer_expl = "{}\n{{{} = {}\n}}".format(res_expl, res_expl, expl)
return res, outer_expl
def _visit_all(self, call):
"""Special rewrite for the builtin all function, see #5062"""
- if not isinstance(call.args[0], (ast.GeneratorExp, ast.ListComp)):
- return
gen_exp = call.args[0]
assertion_module = ast.Module(
body=[ast.Assert(test=gen_exp.elt, lineno=1, msg="", col_offset=1)]
@@ -1005,46 +967,6 @@ warn_explicit(
new_starred = ast.Starred(res, starred.ctx)
return new_starred, "*" + expl
- def visit_Call_legacy(self, call):
- """
- visit `ast.Call nodes on 3.4 and below`
- """
- if isinstance(call.func, ast.Name) and call.func.id == "all":
- return self._visit_all(call)
- new_func, func_expl = self.visit(call.func)
- arg_expls = []
- new_args = []
- new_kwargs = []
- new_star = new_kwarg = None
- for arg in call.args:
- res, expl = self.visit(arg)
- new_args.append(res)
- arg_expls.append(expl)
- for keyword in call.keywords:
- res, expl = self.visit(keyword.value)
- new_kwargs.append(ast.keyword(keyword.arg, res))
- arg_expls.append(keyword.arg + "=" + expl)
- if call.starargs:
- new_star, expl = self.visit(call.starargs)
- arg_expls.append("*" + expl)
- if call.kwargs:
- new_kwarg, expl = self.visit(call.kwargs)
- arg_expls.append("**" + expl)
- expl = "%s(%s)" % (func_expl, ", ".join(arg_expls))
- new_call = ast.Call(new_func, new_args, new_kwargs, new_star, new_kwarg)
- res = self.assign(new_call)
- res_expl = self.explanation_param(self.display(res))
- outer_expl = "%s\n{%s = %s\n}" % (res_expl, res_expl, expl)
- return res, outer_expl
-
- # ast.Call signature changed on 3.5,
- # conditionally change which methods is named
- # visit_Call depending on Python version
- if sys.version_info >= (3, 5):
- visit_Call = visit_Call_35
- else:
- visit_Call = visit_Call_legacy
-
def visit_Attribute(self, attr):
if not isinstance(attr.ctx, ast.Load):
return self.generic_visit(attr)
@@ -1074,7 +996,7 @@ warn_explicit(
results.append(next_res)
sym = binop_map[op.__class__]
syms.append(ast.Str(sym))
- expl = "%s %s %s" % (left_expl, sym, next_expl)
+ expl = "{} {} {}".format(left_expl, sym, next_expl)
expls.append(ast.Str(expl))
res_expr = ast.Compare(left_res, [op], [next_res])
self.statements.append(ast.Assign([store_names[i]], res_expr))
diff --git a/src/_pytest/assertion/truncate.py b/src/_pytest/assertion/truncate.py
index 525896ea9..d97b05b44 100644
--- a/src/_pytest/assertion/truncate.py
+++ b/src/_pytest/assertion/truncate.py
@@ -1,18 +1,11 @@
-# -*- coding: utf-8 -*-
"""
Utilities for truncating assertion output.
Current default behaviour is to truncate assertion explanations at
~8 terminal lines, unless running in "-vv" mode or running on CI.
"""
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import os
-import six
-
DEFAULT_MAX_LINES = 8
DEFAULT_MAX_CHARS = 8 * 80
USAGE_MSG = "use '-vv' to show"
@@ -76,7 +69,7 @@ def _truncate_explanation(input_lines, max_lines=None, max_chars=None):
else:
msg += " ({} lines hidden)".format(truncated_line_count)
msg += ", {}".format(USAGE_MSG)
- truncated_explanation.extend([six.text_type(""), six.text_type(msg)])
+ truncated_explanation.extend(["", str(msg)])
return truncated_explanation
diff --git a/src/_pytest/assertion/util.py b/src/_pytest/assertion/util.py
index 1fee64ce0..2be759bac 100644
--- a/src/_pytest/assertion/util.py
+++ b/src/_pytest/assertion/util.py
@@ -1,15 +1,8 @@
-# -*- coding: utf-8 -*-
"""Utilities for assertion debugging"""
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import pprint
-
-import six
+from collections.abc import Sequence
import _pytest._code
-from ..compat import Sequence
from _pytest import outcomes
from _pytest._io.saferepr import saferepr
@@ -42,7 +35,7 @@ def format_explanation(explanation):
explanation = ecu(explanation)
lines = _split_explanation(explanation)
result = _format_lines(lines)
- return u"\n".join(result)
+ return "\n".join(result)
def _split_explanation(explanation):
@@ -52,7 +45,7 @@ def _split_explanation(explanation):
Any other newlines will be escaped and appear in the line as the
literal '\n' characters.
"""
- raw_lines = (explanation or u"").split("\n")
+ raw_lines = (explanation or "").split("\n")
lines = [raw_lines[0]]
for values in raw_lines[1:]:
if values and values[0] in ["{", "}", "~", ">"]:
@@ -77,13 +70,13 @@ def _format_lines(lines):
for line in lines[1:]:
if line.startswith("{"):
if stackcnt[-1]:
- s = u"and "
+ s = "and "
else:
- s = u"where "
+ s = "where "
stack.append(len(result))
stackcnt[-1] += 1
stackcnt.append(0)
- result.append(u" +" + u" " * (len(stack) - 1) + s + line[1:])
+ result.append(" +" + " " * (len(stack) - 1) + s + line[1:])
elif line.startswith("}"):
stack.pop()
stackcnt.pop()
@@ -92,7 +85,7 @@ def _format_lines(lines):
assert line[0] in ["~", ">"]
stack[-1] += 1
indent = len(stack) if line.startswith("~") else len(stack) - 1
- result.append(u" " * indent + line[1:])
+ result.append(" " * indent + line[1:])
assert len(stack) == 1
return result
@@ -142,7 +135,7 @@ def assertrepr_compare(config, op, left, right):
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))
+ summary = "{} {} {}".format(ecu(left_repr), op, ecu(right_repr))
verbose = config.getoption("verbose")
explanation = None
@@ -175,9 +168,9 @@ def assertrepr_compare(config, op, left, right):
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()),
+ "(pytest_assertion plugin: representation of details failed. "
+ "Probably an object has a faulty __repr__.)",
+ str(_pytest._code.ExceptionInfo.from_current()),
]
if not explanation:
@@ -204,7 +197,7 @@ def _diff_text(left, right, verbose=0):
This is done using repr() which then needs post-processing to fix the encompassing quotes and un-escape
newlines and carriage returns (#429).
"""
- r = six.text_type(repr(binary_text)[1:-1])
+ r = str(repr(binary_text)[1:-1])
r = r.replace(r"\n", "\n")
r = r.replace(r"\r", "\r")
return r
@@ -221,7 +214,7 @@ def _diff_text(left, right, verbose=0):
if i > 42:
i -= 10 # Provide some context
explanation = [
- u"Skipping %s identical leading characters in diff, use -v to show" % i
+ "Skipping %s identical leading characters in diff, use -v to show" % i
]
left = left[i:]
right = right[i:]
@@ -232,8 +225,8 @@ def _diff_text(left, right, verbose=0):
if i > 42:
i -= 10 # Provide some context
explanation += [
- u"Skipping {} identical trailing "
- u"characters in diff, use -v to show".format(i)
+ "Skipping {} identical trailing "
+ "characters in diff, use -v to show".format(i)
]
left = left[:-i]
right = right[:-i]
@@ -241,7 +234,7 @@ def _diff_text(left, right, verbose=0):
if left.isspace() or right.isspace():
left = repr(str(left))
right = repr(str(right))
- explanation += [u"Strings contain only whitespace, escaping them using repr()"]
+ explanation += ["Strings contain only whitespace, escaping them using repr()"]
explanation += [
line.strip("\n")
for line in ndiff(left.splitlines(keepends), right.splitlines(keepends))
@@ -255,29 +248,29 @@ def _compare_eq_verbose(left, right):
right_lines = repr(right).splitlines(keepends)
explanation = []
- explanation += [u"-" + line for line in left_lines]
- explanation += [u"+" + line for line in right_lines]
+ explanation += ["-" + line for line in left_lines]
+ explanation += ["+" + 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"]
+ return ["Use -v to get the full diff"]
# dynamic import to speedup pytest
import difflib
try:
left_formatting = pprint.pformat(left).splitlines()
right_formatting = pprint.pformat(right).splitlines()
- explanation = [u"Full diff:"]
+ explanation = ["Full diff:"]
except Exception:
# hack: PrettyPrinter.pformat() in python 2 fails when formatting items that can't be sorted(), ie, calling
# sorted() on a list would raise. See issue #718.
# As a workaround, the full diff is generated by using the repr() string of each item of each container.
left_formatting = sorted(repr(x) for x in left)
right_formatting = sorted(repr(x) for x in right)
- explanation = [u"Full diff (fallback to calling repr on each item):"]
+ explanation = ["Full diff (fallback to calling repr on each item):"]
explanation.extend(
line.strip() for line in difflib.ndiff(left_formatting, right_formatting)
)
@@ -290,7 +283,9 @@ def _compare_eq_sequence(left, right, verbose=0):
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])]
+ explanation += [
+ "At index {} diff: {!r} != {!r}".format(i, left[i], right[i])
+ ]
break
len_diff = len_left - len_right
@@ -304,10 +299,12 @@ def _compare_eq_sequence(left, right, verbose=0):
extra = saferepr(right[len_left])
if len_diff == 1:
- explanation += [u"%s contains one more item: %s" % (dir_with_more, extra)]
+ explanation += [
+ "{} contains one more item: {}".format(dir_with_more, extra)
+ ]
else:
explanation += [
- u"%s contains %d more items, first extra item: %s"
+ "%s contains %d more items, first extra item: %s"
% (dir_with_more, len_diff, extra)
]
return explanation
@@ -318,11 +315,11 @@ def _compare_eq_set(left, right, verbose=0):
diff_left = left - right
diff_right = right - left
if diff_left:
- explanation.append(u"Extra items in the left set:")
+ explanation.append("Extra items in the left set:")
for item in diff_left:
explanation.append(saferepr(item))
if diff_right:
- explanation.append(u"Extra items in the right set:")
+ explanation.append("Extra items in the right set:")
for item in diff_right:
explanation.append(saferepr(item))
return explanation
@@ -335,20 +332,20 @@ def _compare_eq_dict(left, right, verbose=0):
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)]
+ explanation += ["Omitting %s identical items, use -vv to show" % len(same)]
elif same:
- explanation += [u"Common items:"]
+ explanation += ["Common items:"]
explanation += pprint.pformat(same).splitlines()
diff = {k for k in common if left[k] != right[k]}
if diff:
- explanation += [u"Differing items:"]
+ explanation += ["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:"
+ "Left contains %d more item%s:"
% (len_extra_left, "" if len_extra_left == 1 else "s")
)
explanation.extend(
@@ -358,7 +355,7 @@ def _compare_eq_dict(left, right, verbose=0):
len_extra_right = len(extra_right)
if len_extra_right:
explanation.append(
- u"Right contains %d more item%s:"
+ "Right contains %d more item%s:"
% (len_extra_right, "" if len_extra_right == 1 else "s")
)
explanation.extend(
@@ -386,15 +383,15 @@ def _compare_eq_cls(left, right, verbose, type_fns):
explanation = []
if same and verbose < 2:
- explanation.append(u"Omitting %s identical items, use -vv to show" % len(same))
+ explanation.append("Omitting %s identical items, use -vv to show" % len(same))
elif same:
- explanation += [u"Matching attributes:"]
+ explanation += ["Matching attributes:"]
explanation += pprint.pformat(same).splitlines()
if diff:
- explanation += [u"Differing attributes:"]
+ explanation += ["Differing attributes:"]
for field in diff:
explanation += [
- (u"%s: %r != %r") % (field, getattr(left, field), getattr(right, field))
+ ("%s: %r != %r") % (field, getattr(left, field), getattr(right, field))
]
return explanation
@@ -405,14 +402,14 @@ def _notin_text(term, text, verbose=0):
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 = ["%s is contained here:" % saferepr(term, maxsize=42)]
for line in diff:
- if line.startswith(u"Skipping"):
+ if line.startswith("Skipping"):
continue
- if line.startswith(u"- "):
+ if line.startswith("- "):
continue
- if line.startswith(u"+ "):
- newdiff.append(u" " + line[2:])
+ if line.startswith("+ "):
+ newdiff.append(" " + line[2:])
else:
newdiff.append(line)
return newdiff
diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py
index 045248cb7..17463959f 100755
--- a/src/_pytest/cacheprovider.py
+++ b/src/_pytest/cacheprovider.py
@@ -1,29 +1,22 @@
-# -*- coding: utf-8 -*-
"""
merged implementation of the cache provider
the name cache was not chosen to ensure pluggy automatically
ignores the external pytest-cache
"""
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import json
import os
from collections import OrderedDict
import attr
import py
-import six
import pytest
-from .compat import _PY2 as PY2
from .pathlib import Path
from .pathlib import resolve_from_str
from .pathlib import rmtree
-README_CONTENT = u"""\
+README_CONTENT = """\
# pytest cache directory #
This directory contains data from the pytest's cache plugin,
@@ -43,7 +36,7 @@ Signature: 8a477f597d28d172789f06886806bc55
@attr.s
-class Cache(object):
+class Cache:
_cachedir = attr.ib(repr=False)
_config = attr.ib(repr=False)
@@ -129,7 +122,7 @@ class Cache(object):
if not cache_dir_exists_already:
self._ensure_supporting_files()
try:
- f = path.open("wb" if PY2 else "w")
+ f = path.open("w")
except (IOError, OSError):
self.warn("cache could not write path {path}", path=path)
else:
@@ -142,14 +135,14 @@ class Cache(object):
readme_path.write_text(README_CONTENT)
gitignore_path = self._cachedir.joinpath(".gitignore")
- msg = u"# Created by pytest automatically.\n*"
+ msg = "# 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):
+class LFPlugin:
""" Plugin which implements the --lf (run last-failing) option """
def __init__(self, config):
@@ -262,7 +255,7 @@ class LFPlugin(object):
config.cache.set("cache/lastfailed", self.lastfailed)
-class NFPlugin(object):
+class NFPlugin:
""" Plugin which implements the --nf (run new-first) option """
def __init__(self, config):
@@ -281,8 +274,8 @@ class NFPlugin(object):
other_items[item.nodeid] = item
items[:] = self._get_increasing_order(
- six.itervalues(new_items)
- ) + self._get_increasing_order(six.itervalues(other_items))
+ new_items.values()
+ ) + self._get_increasing_order(other_items.values())
self.cached_nodeids = [x.nodeid for x in items if isinstance(x, pytest.Item)]
def _get_increasing_order(self, items):
diff --git a/src/_pytest/capture.py b/src/_pytest/capture.py
index 68c17772f..302979ef4 100644
--- a/src/_pytest/capture.py
+++ b/src/_pytest/capture.py
@@ -1,12 +1,7 @@
-# -*- coding: utf-8 -*-
"""
per-test stdout/stderr capturing mechanism.
"""
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import collections
import contextlib
import io
@@ -15,10 +10,7 @@ import sys
from io import UnsupportedOperation
from tempfile import TemporaryFile
-import six
-
import pytest
-from _pytest.compat import _PY3
from _pytest.compat import CaptureIO
patchsysdict = {0: "stdin", 1: "stdout", 2: "stderr"}
@@ -67,7 +59,7 @@ def pytest_load_initial_conftests(early_config, parser, args):
sys.stderr.write(err)
-class CaptureManager(object):
+class CaptureManager:
"""
Capture plugin, manages that the appropriate capture method is enabled/disabled during collection and each
test phase (setup, call, teardown). After each of those points, the captured output is obtained and
@@ -86,10 +78,8 @@ class CaptureManager(object):
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,
+ return "<CaptureManager _method={!r} _global_capturing={!r} _current_item={!r}>".format(
+ self._method, self._global_capturing, self._current_item
)
def _getcapture(self, method):
@@ -283,10 +273,6 @@ def capsysbinary(request):
``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")
with _install_capture_fixture_on_item(request, SysCaptureBinary) as fixture:
yield fixture
@@ -345,7 +331,7 @@ def _install_capture_fixture_on_item(request, capture_class):
del request.node._capture_fixture
-class CaptureFixture(object):
+class CaptureFixture:
"""
Object returned by :py:func:`capsys`, :py:func:`capsysbinary`, :py:func:`capfd` and :py:func:`capfdbinary`
fixtures.
@@ -424,7 +410,7 @@ def safe_text_dupfile(f, mode, default_encoding="UTF8"):
return EncodedFile(f, encoding or default_encoding)
-class EncodedFile(object):
+class EncodedFile:
errors = "strict" # possibly needed by py3 code (issue555)
def __init__(self, buffer, encoding):
@@ -432,9 +418,9 @@ class EncodedFile(object):
self.encoding = encoding
def write(self, obj):
- if isinstance(obj, six.text_type):
+ if isinstance(obj, str):
obj = obj.encode(self.encoding, "replace")
- elif _PY3:
+ else:
raise TypeError(
"write() argument must be str, not {}".format(type(obj).__name__)
)
@@ -460,7 +446,7 @@ class EncodedFile(object):
CaptureResult = collections.namedtuple("CaptureResult", ["out", "err"])
-class MultiCapture(object):
+class MultiCapture:
out = err = in_ = None
_state = None
@@ -473,7 +459,7 @@ class MultiCapture(object):
self.err = Capture(2)
def __repr__(self):
- return "<MultiCapture out=%r err=%r in_=%r _state=%r _in_suspended=%r>" % (
+ return "<MultiCapture out={!r} err={!r} in_={!r} _state={!r} _in_suspended={!r}>".format(
self.out,
self.err,
self.in_,
@@ -539,12 +525,12 @@ class MultiCapture(object):
)
-class NoCapture(object):
+class NoCapture:
EMPTY_BUFFER = None
__init__ = start = done = suspend = resume = lambda *args: None
-class FDCaptureBinary(object):
+class FDCaptureBinary:
"""Capture IO to/from a given os-level filedescriptor.
snap() produces `bytes`
@@ -578,10 +564,8 @@ 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 {} oldfd={} _state={!r}>".format(
+ self.targetfd, getattr(self, "targetfd_save", None), self._state
)
def start(self):
@@ -608,7 +592,7 @@ class FDCaptureBinary(object):
os.dup2(targetfd_save, self.targetfd)
os.close(targetfd_save)
self.syscapture.done()
- _attempt_to_close_capture_file(self.tmpfile)
+ self.tmpfile.close()
self._state = "done"
def suspend(self):
@@ -623,7 +607,7 @@ class FDCaptureBinary(object):
def writeorg(self, data):
""" write to original file descriptor. """
- if isinstance(data, six.text_type):
+ if isinstance(data, str):
data = data.encode("utf8") # XXX use encoding of original stream
os.write(self.targetfd_save, data)
@@ -637,14 +621,14 @@ class FDCapture(FDCaptureBinary):
EMPTY_BUFFER = str()
def snap(self):
- res = super(FDCapture, self).snap()
+ res = super().snap()
enc = getattr(self.tmpfile, "encoding", None)
if enc and isinstance(res, bytes):
- res = six.text_type(res, enc, "replace")
+ res = str(res, enc, "replace")
return res
-class SysCapture(object):
+class SysCapture:
EMPTY_BUFFER = str()
_state = None
@@ -661,11 +645,8 @@ class SysCapture(object):
self.tmpfile = tmpfile
def __repr__(self):
- return "<SysCapture %s _old=%r, tmpfile=%r _state=%r>" % (
- self.name,
- self._old,
- self.tmpfile,
- self._state,
+ return "<SysCapture {} _old={!r}, tmpfile={!r} _state={!r}>".format(
+ self.name, self._old, self.tmpfile, self._state
)
def start(self):
@@ -681,7 +662,7 @@ class SysCapture(object):
def done(self):
setattr(sys, self.name, self._old)
del self._old
- _attempt_to_close_capture_file(self.tmpfile)
+ self.tmpfile.close()
self._state = "done"
def suspend(self):
@@ -707,7 +688,7 @@ class SysCaptureBinary(SysCapture):
return res
-class DontReadFromInput(six.Iterator):
+class DontReadFromInput:
"""Temporary stub class. Ideally when stdin is accessed, the
capturing should be turned off, with possibly all data captured
so far sent to the screen. This should be configurable, though,
@@ -738,10 +719,7 @@ class DontReadFromInput(six.Iterator):
@property
def buffer(self):
- if sys.version_info >= (3, 0):
- return self
- else:
- raise AttributeError("redirected stdin has no attribute buffer")
+ return self
def _colorama_workaround():
@@ -837,14 +815,3 @@ def _py36_windowsconsoleio_workaround(stream):
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):
- """Suppress IOError when closing the temporary file used for capturing streams in py27 (#2370)"""
- if six.PY2:
- try:
- f.close()
- except IOError:
- pass
- else:
- f.close()
diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py
index 7668c3a94..a4f3bc003 100644
--- a/src/_pytest/compat.py
+++ b/src/_pytest/compat.py
@@ -1,64 +1,28 @@
-# -*- coding: utf-8 -*-
"""
python version compatibility code
"""
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
-import codecs
import functools
import inspect
+import io
import re
import sys
from contextlib import contextmanager
+from inspect import Parameter
+from inspect import signature
import py
-import six
-from six import text_type
import _pytest
from _pytest._io.saferepr import saferepr
from _pytest.outcomes import fail
from _pytest.outcomes import TEST_OUTCOME
-try:
- import enum
-except ImportError: # pragma: no cover
- # Only available in Python 3.4+ or as a backport
- enum = None
-
-_PY3 = sys.version_info > (3, 0)
-_PY2 = not _PY3
-
-
-if _PY3:
- from inspect import signature, Parameter as Parameter
-else:
- from funcsigs import signature, Parameter as Parameter
NOTSET = object()
-PY35 = sys.version_info[:2] >= (3, 5)
-PY36 = sys.version_info[:2] >= (3, 6)
-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
-else:
- # those raise DeprecationWarnings in Python >=3.7
- from collections import MutableMapping as MappingMixin # noqa
- from collections import Iterable, Mapping, Sequence, Sized # noqa
-
-
-if sys.version_info >= (3, 4):
- from importlib.util import spec_from_file_location
-else:
-
- def spec_from_file_location(*_, **__):
- return None
+MODULE_NOT_FOUND_ERROR = (
+ "ModuleNotFoundError" if sys.version_info[:2] >= (3, 6) else "ImportError"
+)
def _format_args(func):
@@ -184,10 +148,10 @@ 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)
+ i: "\\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"}
+ {ord("\t"): "\\t", ord("\r"): "\\r", ord("\n"): "\\n"}
)
@@ -195,75 +159,39 @@ def _translate_non_printable(s):
return s.translate(_non_printable_ascii_translate_table)
-if _PY3:
- STRING_TYPES = bytes, str
- UNICODE_TYPES = six.text_type
+STRING_TYPES = bytes, str
- if PY35:
- def _bytes_to_ascii(val):
- return val.decode("ascii", "backslashreplace")
+def _bytes_to_ascii(val):
+ return val.decode("ascii", "backslashreplace")
- else:
-
- def _bytes_to_ascii(val):
- if val:
- # source: http://goo.gl/bGsnwC
- encoded_bytes, _ = codecs.escape_encode(val)
- return encoded_bytes.decode("ascii")
- else:
- # empty bytes crashes codecs.escape_encode (#1087)
- return ""
-
- def ascii_escaped(val):
- """If val is pure ascii, returns it as a str(). Otherwise, escapes
- bytes objects into a sequence of escaped bytes:
-
- b'\xc3\xb4\xc5\xd6' -> u'\\xc3\\xb4\\xc5\\xd6'
-
- and escapes unicode objects into a sequence of escaped unicode
- ids, e.g.:
- '4\\nV\\U00043efa\\x0eMXWB\\x1e\\u3028\\u15fd\\xcd\\U0007d944'
+def ascii_escaped(val):
+ """If val is pure ascii, returns it as a str(). Otherwise, escapes
+ bytes objects into a sequence of escaped bytes:
- note:
- the obvious "v.decode('unicode-escape')" will return
- valid utf-8 unicode if it finds them in bytes, but we
- want to return escaped bytes for any byte, even if they match
- a utf-8 string.
+ b'\xc3\xb4\xc5\xd6' -> u'\\xc3\\xb4\\xc5\\xd6'
- """
- if isinstance(val, bytes):
- ret = _bytes_to_ascii(val)
- else:
- ret = val.encode("unicode_escape").decode("ascii")
- return _translate_non_printable(ret)
+ and escapes unicode objects into a sequence of escaped unicode
+ ids, e.g.:
+ '4\\nV\\U00043efa\\x0eMXWB\\x1e\\u3028\\u15fd\\xcd\\U0007d944'
-else:
- STRING_TYPES = six.string_types
- UNICODE_TYPES = six.text_type
+ note:
+ the obvious "v.decode('unicode-escape')" will return
+ valid utf-8 unicode if it finds them in bytes, but we
+ want to return escaped bytes for any byte, even if they match
+ a utf-8 string.
- def ascii_escaped(val):
- """In py2 bytes and str are the same type, so return if it's a bytes
- object, return it unchanged if it is a full ascii string,
- otherwise escape it into its binary form.
-
- If it's a unicode string, change the unicode characters into
- unicode escapes.
-
- """
- if isinstance(val, bytes):
- try:
- ret = val.decode("ascii")
- except UnicodeDecodeError:
- ret = val.encode("string-escape").decode("ascii")
- else:
- ret = val.encode("unicode-escape").decode("ascii")
- return _translate_non_printable(ret)
+ """
+ if isinstance(val, bytes):
+ ret = _bytes_to_ascii(val)
+ else:
+ ret = val.encode("unicode_escape").decode("ascii")
+ return _translate_non_printable(ret)
-class _PytestWrapper(object):
+class _PytestWrapper:
"""Dummy wrapper around a function object for internal use only.
Used to correctly unwrap the underlying function object
@@ -357,36 +285,6 @@ def safe_isclass(obj):
return False
-def _is_unittest_unexpected_success_a_failure():
- """Return if the test suite should fail if an @expectedFailure unittest test PASSES.
-
- From https://docs.python.org/3/library/unittest.html?highlight=unittest#unittest.TestResult.wasSuccessful:
- Changed in version 3.4: Returns False if there were any
- unexpectedSuccesses from tests marked with the expectedFailure() decorator.
- """
- return sys.version_info >= (3, 4)
-
-
-if _PY3:
-
- def safe_str(v):
- """returns v as string"""
- return str(v)
-
-
-else:
-
- def safe_str(v):
- """returns v as string, converting to ascii if necessary"""
- try:
- return str(v)
- except UnicodeError:
- if not isinstance(v, text_type):
- v = text_type(v)
- errors = "replace"
- return v.encode("utf-8", errors)
-
-
COLLECT_FAKEMODULE_ATTRIBUTES = (
"Collector",
"Module",
@@ -410,30 +308,15 @@ def _setup_collect_fakemodule():
setattr(pytest.collect, attr, getattr(pytest, attr))
-if _PY2:
- # Without this the test_dupfile_on_textio will fail, otherwise CaptureIO could directly inherit from StringIO.
- from py.io import TextIO
-
- class CaptureIO(TextIO):
- @property
- def encoding(self):
- return getattr(self, "_encoding", "UTF-8")
+class CaptureIO(io.TextIOWrapper):
+ def __init__(self):
+ super().__init__(io.BytesIO(), encoding="UTF-8", newline="", write_through=True)
+ def getvalue(self):
+ return self.buffer.getvalue().decode("UTF-8")
-else:
- import io
- class CaptureIO(io.TextIOWrapper):
- def __init__(self):
- super(CaptureIO, self).__init__(
- io.BytesIO(), encoding="UTF-8", newline="", write_through=True
- )
-
- def getvalue(self):
- return self.buffer.getvalue().decode("UTF-8")
-
-
-class FuncargnamesCompatAttr(object):
+class FuncargnamesCompatAttr:
""" helper class so that Metafunc, Function and FixtureRequest
don't need to each define the "funcargnames" compatibility attribute.
"""
@@ -442,16 +325,3 @@ class FuncargnamesCompatAttr(object):
def funcargnames(self):
""" alias attribute for ``fixturenames`` for pre-2.3 compatibility"""
return self.fixturenames
-
-
-if six.PY2:
-
- def lru_cache(*_, **__):
- def dec(fn):
- return fn
-
- return dec
-
-
-else:
- from functools import lru_cache # noqa: F401
diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py
index 7a5deb13f..40f37480b 100644
--- a/src/_pytest/config/__init__.py
+++ b/src/_pytest/config/__init__.py
@@ -1,9 +1,4 @@
-# -*- coding: utf-8 -*-
""" command line options, ini-file and conftest.py processing. """
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import argparse
import copy
import inspect
@@ -12,10 +7,10 @@ import shlex
import sys
import types
import warnings
+from functools import lru_cache
import importlib_metadata
import py
-import six
from packaging.version import Version
from pluggy import HookimplMarker
from pluggy import HookspecMarker
@@ -31,8 +26,6 @@ from .findpaths import exists
from _pytest import deprecated
from _pytest._code import ExceptionInfo
from _pytest._code import filter_traceback
-from _pytest.compat import lru_cache
-from _pytest.compat import safe_str
from _pytest.outcomes import fail
from _pytest.outcomes import Skipped
from _pytest.warning_types import PytestConfigWarning
@@ -73,7 +66,7 @@ def main(args=None, plugins=None):
if exc_info.traceback
else exc_info.exconly()
)
- formatted_tb = safe_str(exc_repr)
+ formatted_tb = str(exc_repr)
for line in formatted_tb.splitlines():
tw.line(line.rstrip(), red=True)
return 4
@@ -89,7 +82,7 @@ def main(args=None, plugins=None):
return EXIT_USAGEERROR
-class cmdline(object): # compatibility namespace
+class cmdline: # compatibility namespace
main = staticmethod(main)
@@ -195,7 +188,7 @@ def _prepareconfig(args=None, plugins=None):
try:
if plugins:
for plugin in plugins:
- if isinstance(plugin, six.string_types):
+ if isinstance(plugin, str):
pluginmanager.consider_pluginarg(plugin)
else:
pluginmanager.register(plugin)
@@ -222,7 +215,7 @@ class PytestPluginManager(PluginManager):
"""
def __init__(self):
- super(PytestPluginManager, self).__init__("pytest")
+ super().__init__("pytest")
self._conftest_plugins = set()
# state related to local conftest plugins
@@ -270,7 +263,7 @@ class PytestPluginManager(PluginManager):
return
method = getattr(plugin, name)
- opts = super(PytestPluginManager, self).parse_hookimpl_opts(plugin, name)
+ opts = super().parse_hookimpl_opts(plugin, name)
# consider only actual functions for hooks (#3775)
if not inspect.isroutine(method):
@@ -289,9 +282,7 @@ class PytestPluginManager(PluginManager):
return opts
def parse_hookspec_opts(self, module_or_class, name):
- opts = super(PytestPluginManager, self).parse_hookspec_opts(
- module_or_class, name
- )
+ opts = super().parse_hookspec_opts(module_or_class, name)
if opts is None:
method = getattr(module_or_class, name)
@@ -318,7 +309,7 @@ class PytestPluginManager(PluginManager):
)
)
return
- ret = super(PytestPluginManager, self).register(plugin, name)
+ ret = super().register(plugin, name)
if ret:
self.hook.pytest_plugin_registered.call_historic(
kwargs=dict(plugin=plugin, manager=self)
@@ -403,12 +394,6 @@ class PytestPluginManager(PluginManager):
else:
directory = path
- if six.PY2: # py2 is not using lru_cache.
- try:
- return self._dirpath2confmods[directory]
- except KeyError:
- pass
-
# XXX these days we may rather want to use config.rootdir
# and allow users to opt into looking into the rootdir parent
# directories instead of requiring to specify confcutdir
@@ -485,7 +470,7 @@ class PytestPluginManager(PluginManager):
while i < n:
opt = args[i]
i += 1
- if isinstance(opt, six.string_types):
+ if isinstance(opt, str):
if opt == "-p":
try:
parg = args[i]
@@ -546,7 +531,7 @@ class PytestPluginManager(PluginManager):
# "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, str), (
"module name as text required, got %r" % modname
)
modname = str(modname)
@@ -564,20 +549,19 @@ class PytestPluginManager(PluginManager):
try:
__import__(importspec)
except ImportError as e:
- new_exc_message = 'Error importing plugin "%s": %s' % (
- modname,
- safe_str(e.args[0]),
+ new_exc_message = 'Error importing plugin "{}": {}'.format(
+ modname, str(e.args[0])
)
new_exc = ImportError(new_exc_message)
tb = sys.exc_info()[2]
- six.reraise(ImportError, new_exc, tb)
+ raise new_exc.with_traceback(tb)
except Skipped as e:
from _pytest.warnings import _issue_warning_captured
_issue_warning_captured(
- PytestConfigWarning("skipped plugin %r: %s" % (modname, e.msg)),
+ PytestConfigWarning("skipped plugin {!r}: {}".format(modname, e.msg)),
self.hook,
stacklevel=1,
)
@@ -595,7 +579,7 @@ def _get_plugin_specs_as_list(specs):
empty list is returned.
"""
if specs is not None and not isinstance(specs, types.ModuleType):
- if isinstance(specs, six.string_types):
+ if isinstance(specs, str):
specs = specs.split(",") if specs else []
if not isinstance(specs, (list, tuple)):
raise UsageError(
@@ -613,7 +597,7 @@ def _ensure_removed_sysmodule(modname):
pass
-class Notset(object):
+class Notset:
def __repr__(self):
return "<NOTSET>"
@@ -633,7 +617,7 @@ def _iter_rewritable_modules(package_files):
yield package_name
-class Config(object):
+class Config:
""" access to configuration values, pluginmanager and plugin hooks. """
def __init__(self, pluginmanager):
@@ -644,7 +628,7 @@ class Config(object):
_a = FILE_OR_DIR
self._parser = Parser(
- usage="%%(prog)s [options] [%s] [%s] [...]" % (_a, _a),
+ usage="%(prog)s [options] [{}] [{}] [...]".format(_a, _a),
processopt=self._processopt,
)
#: a pluginmanager instance
@@ -932,7 +916,7 @@ class Config(object):
try:
description, type, default = self._parser._inidict[name]
except KeyError:
- raise ValueError("unknown configuration value: %r" % (name,))
+ raise ValueError("unknown configuration value: {!r}".format(name))
value = self._get_override_ini_value(name)
if value is None:
try:
@@ -1009,8 +993,8 @@ class Config(object):
if skip:
import pytest
- pytest.skip("no %r option found" % (name,))
- raise ValueError("no option named %r" % (name,))
+ pytest.skip("no {!r} option found".format(name))
+ raise ValueError("no option named {!r}".format(name))
def getvalue(self, name, path=None):
""" (deprecated, use getoption()) """
@@ -1098,4 +1082,4 @@ def _strtobool(val):
elif val in ("n", "no", "f", "false", "off", "0"):
return 0
else:
- raise ValueError("invalid truth value %r" % (val,))
+ raise ValueError("invalid truth value {!r}".format(val))
diff --git a/src/_pytest/config/argparsing.py b/src/_pytest/config/argparsing.py
index 37fb772db..fb36c7985 100644
--- a/src/_pytest/config/argparsing.py
+++ b/src/_pytest/config/argparsing.py
@@ -1,16 +1,14 @@
-# -*- coding: utf-8 -*-
import argparse
import warnings
import py
-import six
from _pytest.config.exceptions import UsageError
FILE_OR_DIR = "file_or_dir"
-class Parser(object):
+class Parser:
""" Parser for command line arguments and ini-file values.
:ivar extra_info: dict of generic param -> value to display in case
@@ -145,12 +143,12 @@ class ArgumentError(Exception):
def __str__(self):
if self.option_id:
- return "option %s: %s" % (self.option_id, self.msg)
+ return "option {}: {}".format(self.option_id, self.msg)
else:
return self.msg
-class Argument(object):
+class Argument:
"""class that mimics the necessary behaviour of optparse.Option
it's currently a least effort implementation
@@ -179,7 +177,7 @@ class Argument(object):
pass
else:
# this might raise a keyerror as well, don't want to catch that
- if isinstance(typ, six.string_types):
+ if isinstance(typ, str):
if typ == "choice":
warnings.warn(
"`type` argument to addoption() is the string %r."
@@ -282,7 +280,7 @@ class Argument(object):
return "Argument({})".format(", ".join(args))
-class OptionGroup(object):
+class OptionGroup:
def __init__(self, name, description="", parser=None):
self.name = name
self.description = description
@@ -337,10 +335,10 @@ class MyOptionParser(argparse.ArgumentParser):
def error(self, message):
"""Transform argparse error message into UsageError."""
- msg = "%s: error: %s" % (self.prog, message)
+ msg = "{}: error: {}".format(self.prog, message)
if hasattr(self._parser, "_config_source_hint"):
- msg = "%s (%s)" % (msg, self._parser._config_source_hint)
+ msg = "{} ({})".format(msg, self._parser._config_source_hint)
raise UsageError(self.format_usage() + msg)
@@ -352,7 +350,7 @@ class MyOptionParser(argparse.ArgumentParser):
if arg and arg[0] == "-":
lines = ["unrecognized arguments: %s" % (" ".join(argv))]
for k, v in sorted(self.extra_info.items()):
- lines.append(" %s: %s" % (k, v))
+ lines.append(" {}: {}".format(k, v))
self.error("\n".join(lines))
getattr(args, FILE_OR_DIR).extend(argv)
return args
diff --git a/src/_pytest/config/exceptions.py b/src/_pytest/config/exceptions.py
index bf58fde5d..19fe5cb08 100644
--- a/src/_pytest/config/exceptions.py
+++ b/src/_pytest/config/exceptions.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
class UsageError(Exception):
""" error in pytest usage or invocation"""
diff --git a/src/_pytest/config/findpaths.py b/src/_pytest/config/findpaths.py
index 3ece3bdc1..fa2024470 100644
--- a/src/_pytest/config/findpaths.py
+++ b/src/_pytest/config/findpaths.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
import os
import py
diff --git a/src/_pytest/debugging.py b/src/_pytest/debugging.py
index 99d35a5ab..891630b43 100644
--- a/src/_pytest/debugging.py
+++ b/src/_pytest/debugging.py
@@ -1,9 +1,4 @@
-# -*- 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 argparse
import pdb
import sys
@@ -74,7 +69,7 @@ def pytest_configure(config):
config._cleanup.append(fin)
-class pytestPDB(object):
+class pytestPDB:
""" Pseudo PDB that defers to the real pdb. """
_pluginmanager = None
@@ -128,18 +123,18 @@ class pytestPDB(object):
def _get_pdb_wrapper_class(cls, pdb_cls, capman):
import _pytest.config
- class PytestPdbWrapper(pdb_cls, object):
+ class PytestPdbWrapper(pdb_cls):
_pytest_capman = capman
_continued = False
def do_debug(self, arg):
cls._recursive_debug += 1
- ret = super(PytestPdbWrapper, self).do_debug(arg)
+ ret = super().do_debug(arg)
cls._recursive_debug -= 1
return ret
def do_continue(self, arg):
- ret = super(PytestPdbWrapper, self).do_continue(arg)
+ ret = super().do_continue(arg)
if cls._recursive_debug == 0:
tw = _pytest.config.create_terminal_writer(cls._config)
tw.line()
@@ -171,7 +166,7 @@ class pytestPDB(object):
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)
+ ret = super().do_quit(arg)
if cls._recursive_debug == 0:
outcomes.exit("Quitting debugger")
@@ -187,7 +182,7 @@ class pytestPDB(object):
Needed after do_continue resumed, and entering another
breakpoint again.
"""
- ret = super(PytestPdbWrapper, self).setup(f, tb)
+ ret = super().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.
@@ -196,7 +191,7 @@ class pytestPDB(object):
return ret
def get_stack(self, f, t):
- stack, i = super(PytestPdbWrapper, self).get_stack(f, t)
+ stack, i = super().get_stack(f, t)
if f is None:
# Find last non-hidden frame.
i = max(0, len(stack) - 1)
@@ -230,7 +225,7 @@ class pytestPDB(object):
else:
capturing = cls._is_capturing(capman)
if capturing == "global":
- tw.sep(">", "PDB %s (IO-capturing turned off)" % (method,))
+ tw.sep(">", "PDB {} (IO-capturing turned off)".format(method))
elif capturing:
tw.sep(
">",
@@ -238,7 +233,7 @@ class pytestPDB(object):
% (method, capturing),
)
else:
- tw.sep(">", "PDB %s" % (method,))
+ tw.sep(">", "PDB {}".format(method))
_pdb = cls._import_pdb_cls(capman)(**kwargs)
@@ -254,7 +249,7 @@ class pytestPDB(object):
_pdb.set_trace(frame)
-class PdbInvoke(object):
+class PdbInvoke:
def pytest_exception_interact(self, node, call, report):
capman = node.config.pluginmanager.getplugin("capturemanager")
if capman:
@@ -269,7 +264,7 @@ class PdbInvoke(object):
post_mortem(tb)
-class PdbTrace(object):
+class PdbTrace:
@hookimpl(hookwrapper=True)
def pytest_pyfunc_call(self, pyfuncitem):
_test_pytest_function(pyfuncitem)
diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py
index 6283a8402..f80773fe5 100644
--- a/src/_pytest/deprecated.py
+++ b/src/_pytest/deprecated.py
@@ -1,4 +1,3 @@
-# -*- 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.
@@ -9,10 +8,6 @@ be removed when the time comes.
All constants defined in this module should be either PytestWarning instances or UnformattedWarning
in case of warnings which need to format their messages.
"""
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
from _pytest.warning_types import PytestDeprecationWarning
from _pytest.warning_types import RemovedInPytest4Warning
from _pytest.warning_types import UnformattedWarning
diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py
index 95a80bb4f..afb7ede4c 100644
--- a/src/_pytest/doctest.py
+++ b/src/_pytest/doctest.py
@@ -1,9 +1,4 @@
-# -*- 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 platform
import sys
@@ -126,7 +121,7 @@ class ReprFailDoctest(TerminalRepr):
class MultipleDoctestFailures(Exception):
def __init__(self, failures):
- super(MultipleDoctestFailures, self).__init__()
+ super().__init__()
self.failures = failures
@@ -181,7 +176,7 @@ def _get_runner(checker=None, verbose=None, optionflags=0, continue_on_failure=T
class DoctestItem(pytest.Item):
def __init__(self, name, parent, runner=None, dtest=None):
- super(DoctestItem, self).__init__(name, parent)
+ super().__init__(name, parent)
self.runner = runner
self.dtest = dtest
self.obj = None
@@ -258,7 +253,7 @@ class DoctestItem(pytest.Item):
]
indent = ">>>"
for line in example.source.splitlines():
- lines.append("??? %s %s" % (indent, line))
+ lines.append("??? {} {}".format(indent, line))
indent = "..."
if isinstance(failure, doctest.DocTestFailure):
lines += checker.output_difference(
@@ -271,7 +266,7 @@ class DoctestItem(pytest.Item):
reprlocation_lines.append((reprlocation, lines))
return ReprFailDoctest(reprlocation_lines)
else:
- return super(DoctestItem, self).repr_failure(excinfo)
+ return super().repr_failure(excinfo)
def reportinfo(self):
return self.fspath, self.dtest.lineno, "[doctest] %s" % self.name
@@ -333,7 +328,6 @@ class DoctestTextfile(pytest.Module):
checker=_get_checker(),
continue_on_failure=_get_continue_on_failure(self.config),
)
- _fix_spoof_python2(runner, encoding)
parser = doctest.DocTestParser()
test = parser.get_doctest(text, globs, name, filename, 0)
@@ -539,32 +533,6 @@ def _get_report_choice(key):
}[key]
-def _fix_spoof_python2(runner, encoding):
- """
- Installs a "SpoofOut" into the given DebugRunner so it properly deals with unicode output. This
- should patch only doctests for text files because they don't have a way to declare their
- encoding. Doctests in docstrings from Python modules don't have the same problem given that
- Python already decoded the strings.
-
- This fixes the problem related in issue #2434.
- """
- from _pytest.compat import _PY2
-
- if not _PY2:
- return
-
- from doctest import _SpoofOut
-
- class UnicodeSpoof(_SpoofOut):
- def getvalue(self):
- result = _SpoofOut.getvalue(self)
- if encoding and isinstance(result, bytes):
- result = result.decode(encoding)
- return result
-
- runner._fakeout = UnicodeSpoof()
-
-
@pytest.fixture(scope="session")
def doctest_namespace():
"""
diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py
index 280a48608..2f9b10b85 100644
--- a/src/_pytest/fixtures.py
+++ b/src/_pytest/fixtures.py
@@ -1,8 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import functools
import inspect
import itertools
@@ -14,7 +9,6 @@ from collections import OrderedDict
import attr
import py
-import six
import _pytest
from _pytest import nodes
@@ -41,7 +35,7 @@ from _pytest.outcomes import TEST_OUTCOME
@attr.s(frozen=True)
-class PseudoFixtureDef(object):
+class PseudoFixtureDef:
cached_result = attr.ib()
scope = attr.ib()
@@ -81,7 +75,7 @@ def scopeproperty(name=None, doc=None):
if func.__name__ in scope2props[self.scope]:
return func(self)
raise AttributeError(
- "%s not available in %s-scoped context" % (scopename, self.scope)
+ "{} not available in {}-scoped context".format(scopename, self.scope)
)
return property(provide, None, None, func.__doc__)
@@ -94,7 +88,7 @@ def get_scope_package(node, fixturedef):
cls = pytest.Package
current = node
- fixture_package_name = "%s/%s" % (fixturedef.baseid, "__init__.py")
+ fixture_package_name = "{}/{}".format(fixturedef.baseid, "__init__.py")
while current and (
type(current) is not cls or fixture_package_name != current.nodeid
):
@@ -301,7 +295,7 @@ def get_direct_param_fixture_func(request):
@attr.s(slots=True)
-class FuncFixtureInfo(object):
+class FuncFixtureInfo:
# original function argument names
argnames = attr.ib(type=tuple)
# argnames that function immediately requires. These include argnames +
@@ -657,7 +651,7 @@ class SubRequest(FixtureRequest):
self._fixturemanager = request._fixturemanager
def __repr__(self):
- return "<SubRequest %r for %r>" % (self.fixturename, self._pyfuncitem)
+ return "<SubRequest {!r} for {!r}>".format(self.fixturename, self._pyfuncitem)
def addfinalizer(self, finalizer):
self._fixturedef.addfinalizer(finalizer)
@@ -670,7 +664,7 @@ class SubRequest(FixtureRequest):
fixturedef.addfinalizer(
functools.partial(self._fixturedef.finish, request=self)
)
- super(SubRequest, self)._schedule_finalizers(fixturedef, subrequest)
+ super()._schedule_finalizers(fixturedef, subrequest)
scopes = "session package module class function".split()
@@ -723,7 +717,7 @@ class FixtureLookupError(LookupError):
error_msg = "file %s, line %s: source code not available"
addline(error_msg % (fspath, lineno + 1))
else:
- addline("file %s, line %s" % (fspath, lineno + 1))
+ addline("file {}, line {}".format(fspath, lineno + 1))
for i, line in enumerate(lines):
line = line.rstrip()
addline(" " + line)
@@ -779,7 +773,7 @@ class FixtureLookupErrorRepr(TerminalRepr):
def fail_fixturefunc(fixturefunc, msg):
fs, lineno = getfslineno(fixturefunc)
- location = "%s:%s" % (fs, lineno + 1)
+ location = "{}:{}".format(fs, lineno + 1)
source = _pytest._code.Source(fixturefunc)
fail(msg + ":\n\n" + str(source.indent()) + "\n" + location, pytrace=False)
@@ -809,7 +803,7 @@ def _teardown_yield_fixture(fixturefunc, it):
)
-class FixtureDef(object):
+class FixtureDef:
""" A container for a factory definition. """
def __init__(
@@ -853,10 +847,10 @@ class FixtureDef(object):
except: # noqa
exceptions.append(sys.exc_info())
if exceptions:
- e = exceptions[0]
+ _, val, tb = exceptions[0]
# Ensure to not keep frame references through traceback.
del exceptions
- six.reraise(*e)
+ raise val.with_traceback(tb)
finally:
hook = self._fixturemanager.session.gethookproxy(request.node.fspath)
hook.pytest_fixture_post_finalizer(fixturedef=self, request=request)
@@ -882,7 +876,8 @@ class FixtureDef(object):
result, cache_key, err = cached_result
if my_cache_key == cache_key:
if err is not None:
- six.reraise(*err)
+ _, val, tb = err
+ raise val.with_traceback(tb)
else:
return result
# we have a previous but differently parametrized fixture instance
@@ -894,10 +889,8 @@ class FixtureDef(object):
return hook.pytest_fixture_setup(fixturedef=self, request=request)
def __repr__(self):
- return "<FixtureDef argname=%r scope=%r baseid=%r>" % (
- self.argname,
- self.scope,
- self.baseid,
+ return "<FixtureDef argname={!r} scope={!r} baseid={!r}>".format(
+ self.argname, self.scope, self.baseid
)
@@ -957,7 +950,7 @@ def wrap_function_to_error_out_if_called_directly(function, fixture_marker):
name=fixture_marker.name or function.__name__
)
- @six.wraps(function)
+ @functools.wraps(function)
def result(*args, **kwargs):
fail(message, pytrace=False)
@@ -969,7 +962,7 @@ def wrap_function_to_error_out_if_called_directly(function, fixture_marker):
@attr.s(frozen=True)
-class FixtureFunctionMarker(object):
+class FixtureFunctionMarker:
scope = attr.ib()
params = attr.ib(converter=attr.converters.optional(tuple))
autouse = attr.ib(default=False)
@@ -1083,7 +1076,7 @@ def pytest_addoption(parser):
)
-class FixtureManager(object):
+class FixtureManager:
"""
pytest fixtures definitions and information is stored and managed
from this class.
@@ -1303,11 +1296,7 @@ class FixtureManager(object):
# 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
- if six.PY2 and unittest:
- # hack on Python 2 because of the unbound methods
- obj = get_real_func(obj)
- else:
- obj = get_real_method(obj, holderobj)
+ obj = get_real_method(obj, holderobj)
fixture_def = FixtureDef(
self,
diff --git a/src/_pytest/freeze_support.py b/src/_pytest/freeze_support.py
index aeeec2a56..f9d613a2b 100644
--- a/src/_pytest/freeze_support.py
+++ b/src/_pytest/freeze_support.py
@@ -1,11 +1,7 @@
-# -*- coding: utf-8 -*-
"""
Provides a function to report all internal modules for using freezing tools
pytest
"""
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
def freeze_includes():
diff --git a/src/_pytest/helpconfig.py b/src/_pytest/helpconfig.py
index 568116016..b379fae01 100644
--- a/src/_pytest/helpconfig.py
+++ b/src/_pytest/helpconfig.py
@@ -1,9 +1,4 @@
-# -*- coding: utf-8 -*-
""" version info, help messages, tracing configuration. """
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import os
import sys
from argparse import Action
@@ -24,7 +19,7 @@ class HelpAction(Action):
"""
def __init__(self, option_strings, dest=None, default=False, help=None):
- super(HelpAction, self).__init__(
+ super().__init__(
option_strings=option_strings,
dest=dest,
const=True,
@@ -122,7 +117,7 @@ def pytest_cmdline_parse():
def showversion(config):
p = py.path.local(pytest.__file__)
sys.stderr.write(
- "This is pytest version %s, imported from %s\n" % (pytest.__version__, p)
+ "This is pytest version {}, imported from {}\n".format(pytest.__version__, p)
)
plugininfo = getpluginversioninfo(config)
if plugininfo:
@@ -160,7 +155,7 @@ def showhelp(config):
help, type, default = config._parser._inidict[name]
if type is None:
type = "string"
- spec = "%s (%s):" % (name, type)
+ spec = "{} ({}):".format(name, type)
tw.write(" %s" % spec)
spec_len = len(spec)
if spec_len > (indent_len - 3):
@@ -194,7 +189,7 @@ def showhelp(config):
("PYTEST_DEBUG", "set to enable debug tracing of pytest's internals"),
]
for name, help in vars:
- tw.line(" %-24s %s" % (name, help))
+ tw.line(" {:<24} {}".format(name, help))
tw.line()
tw.line()
@@ -221,7 +216,7 @@ def getpluginversioninfo(config):
lines.append("setuptools registered plugins:")
for plugin, dist in plugininfo:
loc = getattr(plugin, "__file__", repr(plugin))
- content = "%s-%s at %s" % (dist.project_name, dist.version, loc)
+ content = "{}-{} at {}".format(dist.project_name, dist.version, loc)
lines.append(" " + content)
return lines
@@ -229,7 +224,9 @@ def getpluginversioninfo(config):
def pytest_report_header(config):
lines = []
if config.option.debug or config.option.traceconfig:
- lines.append("using: pytest-%s pylib-%s" % (pytest.__version__, py.__version__))
+ lines.append(
+ "using: pytest-{} pylib-{}".format(pytest.__version__, py.__version__)
+ )
verinfo = getpluginversioninfo(config)
if verinfo:
@@ -243,5 +240,5 @@ def pytest_report_header(config):
r = plugin.__file__
else:
r = repr(plugin)
- lines.append(" %-20s: %s" % (name, r))
+ lines.append(" {:<20}: {}".format(name, r))
return lines
diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py
index 7ab6154b1..d40a36811 100644
--- a/src/_pytest/hookspec.py
+++ b/src/_pytest/hookspec.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
""" hook specifications for pytest plugins, invoked from main.py and builtin plugins. """
from pluggy import HookspecMarker
diff --git a/src/_pytest/junitxml.py b/src/_pytest/junitxml.py
index 3972113cb..ea33e606c 100644
--- a/src/_pytest/junitxml.py
+++ b/src/_pytest/junitxml.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
"""
report test results in JUnit-XML format,
for use with Jenkins and build integration servers.
@@ -9,10 +8,6 @@ Based on initial code from Ross Lawley.
Output conforms to https://github.com/jenkinsci/xunit-plugin/blob/master/
src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd
"""
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import functools
import os
import re
@@ -20,16 +15,11 @@ import sys
import time
import py
-import six
import pytest
from _pytest import nodes
from _pytest.config import filename_arg
-# Python 2.X and 3.X compatibility
-if sys.version_info[0] < 3:
- from codecs import open
-
class Junit(py.xml.Namespace):
pass
@@ -43,12 +33,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))
+ "{}-{}".format(chr(low), chr(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 = [chr(x) for x in _legal_chars] + _legal_xml_re
+illegal_xml_re = re.compile("[^%s]" % "".join(_legal_xml_re))
del _legal_chars
del _legal_ranges
del _legal_xml_re
@@ -60,9 +50,9 @@ def bin_xml_escape(arg):
def repl(matchobj):
i = ord(matchobj.group())
if i <= 0xFF:
- return u"#x%02X" % i
+ return "#x%02X" % i
else:
- return u"#x%04X" % i
+ return "#x%04X" % i
return py.xml.raw(illegal_xml_re.sub(repl, py.xml.escape(arg)))
@@ -89,7 +79,7 @@ merge_family(families["xunit1"], families["_base_legacy"])
families["xunit2"] = families["_base"]
-class _NodeReporter(object):
+class _NodeReporter:
def __init__(self, nodeid, xml):
self.id = nodeid
self.xml = xml
@@ -229,7 +219,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, str):
message = report.longrepr
else:
message = str(report.longrepr)
@@ -268,7 +258,7 @@ class _NodeReporter(object):
filename, lineno, skipreason = report.longrepr
if skipreason.startswith("Skipped: "):
skipreason = skipreason[9:]
- details = "%s:%s: %s" % (filename, lineno, skipreason)
+ details = "{}:{}: {}".format(filename, lineno, skipreason)
self.append(
Junit.skipped(
@@ -353,7 +343,7 @@ 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):
+ if not isinstance(v, str):
msg = "{param} parameter needs to be a string, but {g} given"
raise TypeError(msg.format(param=param, g=type(v).__name__))
@@ -473,7 +463,7 @@ def mangle_test_address(address):
return names
-class LogXML(object):
+class LogXML:
def __init__(
self,
logfile,
diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py
index 577a5407b..ac0c4c2b3 100644
--- a/src/_pytest/logging.py
+++ b/src/_pytest/logging.py
@@ -1,15 +1,9 @@
-# -*- coding: utf-8 -*-
""" Access and control log capturing. """
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import logging
import re
from contextlib import contextmanager
import py
-import six
import pytest
from _pytest.compat import dummy_context_manager
@@ -39,14 +33,11 @@ class ColoredLevelFormatter(logging.Formatter):
logging.DEBUG: {"purple"},
logging.NOTSET: set(),
}
- LEVELNAME_FMT_REGEX = re.compile(r"%\(levelname\)([+-]?\d*s)")
+ LEVELNAME_FMT_REGEX = re.compile(r"%\(levelname\)([+-.]?\d*s)")
def __init__(self, terminalwriter, *args, **kwargs):
- super(ColoredLevelFormatter, self).__init__(*args, **kwargs)
- if six.PY2:
- self._original_fmt = self._fmt
- else:
- self._original_fmt = self._style._fmt
+ super().__init__(*args, **kwargs)
+ self._original_fmt = self._style._fmt
self._level_to_fmt_mapping = {}
levelname_fmt_match = self.LEVELNAME_FMT_REGEX.search(self._fmt)
@@ -70,41 +61,35 @@ class ColoredLevelFormatter(logging.Formatter):
def format(self, record):
fmt = self._level_to_fmt_mapping.get(record.levelno, self._original_fmt)
- if six.PY2:
- self._fmt = fmt
- else:
- self._style._fmt = fmt
- return super(ColoredLevelFormatter, self).format(record)
+ self._style._fmt = fmt
+ return super().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.
- 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.
+ """
- 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
- @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 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):
@@ -246,7 +231,7 @@ class LogCaptureHandler(logging.StreamHandler):
self.stream = py.io.TextIO()
-class LogCaptureFixture(object):
+class LogCaptureFixture:
"""Provides access and control of log capturing."""
def __init__(self, item):
@@ -393,7 +378,7 @@ def get_actual_log_level(config, *setting_names):
else:
return
- if isinstance(log_level, six.string_types):
+ if isinstance(log_level, str):
log_level = log_level.upper()
try:
return int(getattr(logging, log_level, log_level))
@@ -412,7 +397,7 @@ def pytest_configure(config):
config.pluginmanager.register(LoggingPlugin(config), "logging-plugin")
-class LoggingPlugin(object):
+class LoggingPlugin:
"""Attaches to the logging module and captures log messages for each test.
"""
@@ -475,8 +460,7 @@ class LoggingPlugin(object):
else:
formatter = logging.Formatter(log_format, log_date_format)
- if not six.PY2:
- formatter._style = PercentStyleMultiline(formatter._style._fmt)
+ formatter._style = PercentStyleMultiline(formatter._style._fmt)
return formatter
def _setup_cli_logging(self):
diff --git a/src/_pytest/main.py b/src/_pytest/main.py
index fa4d8d3d5..735d60bd6 100644
--- a/src/_pytest/main.py
+++ b/src/_pytest/main.py
@@ -1,10 +1,4 @@
-# -*- 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 functools
import os
@@ -14,7 +8,6 @@ import warnings
import attr
import py
-import six
import _pytest._code
from _pytest import nodes
@@ -172,7 +165,7 @@ def pytest_addoption(parser):
)
-class _ConfigDeprecated(object):
+class _ConfigDeprecated:
def __init__(self, config):
self.__dict__["_config"] = config
@@ -311,10 +304,7 @@ def pytest_ignore_collect(path, config):
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
- ):
+ if any(fnmatch.fnmatch(str(path), str(glob)) for glob in ignore_globs):
return True
allow_in_venv = config.getoption("collect_in_virtualenv")
@@ -342,47 +332,7 @@ def pytest_collection_modifyitems(items, config):
items[:] = remaining
-@contextlib.contextmanager
-def _patched_find_module():
- """Patch bug in pkgutil.ImpImporter.find_module
-
- When using pkgutil.find_loader on python<3.4 it removes symlinks
- from the path due to a call to os.path.realpath. This is not consistent
- with actually doing the import (in these versions, pkgutil and __import__
- did not share the same underlying code). This can break conftest
- discovery for pytest where symlinks are involved.
-
- The only supported python<3.4 by pytest is python 2.7.
- """
- if six.PY2: # python 3.4+ uses importlib instead
-
- def find_module_patched(self, fullname, path=None):
- # Note: we ignore 'path' argument since it is only used via meta_path
- subname = fullname.split(".")[-1]
- if subname != fullname and self.path is None:
- return None
- if self.path is None:
- path = None
- else:
- # original: path = [os.path.realpath(self.path)]
- path = [self.path]
- try:
- file, filename, etc = pkgutil.imp.find_module(subname, path)
- except ImportError:
- return None
- return pkgutil.ImpLoader(fullname, file, filename, etc)
-
- old_find_module = pkgutil.ImpImporter.find_module
- pkgutil.ImpImporter.find_module = find_module_patched
- try:
- yield
- finally:
- pkgutil.ImpImporter.find_module = old_find_module
- else:
- yield
-
-
-class FSHookProxy(object):
+class FSHookProxy:
def __init__(self, fspath, pm, remove_mods):
self.fspath = fspath
self.pm = pm
@@ -522,8 +472,8 @@ class Session(nodes.FSCollector):
if self._notfound:
errors = []
for arg, exc in self._notfound:
- line = "(no name %r in any of %r)" % (arg, exc.args[0])
- errors.append("not found: %s\n%s" % (arg, line))
+ line = "(no name {!r} in any of {!r})".format(arg, exc.args[0])
+ errors.append("not found: {}\n{}".format(arg, line))
# XXX: test this
raise UsageError(*errors)
if not genitems:
@@ -540,8 +490,7 @@ class Session(nodes.FSCollector):
self.trace("processing argument", arg)
self.trace.root.indent += 1
try:
- for x in self._collect(arg):
- yield x
+ yield from self._collect(arg)
except NoMatch:
# we are inside a make_report hook so
# we cannot directly pass through the exception
@@ -578,7 +527,7 @@ class Session(nodes.FSCollector):
# If it's a directory argument, recurse and look for any Subpackages.
# Let the Package collector deal with subnodes, don't collect here.
if argpath.check(dir=1):
- assert not names, "invalid arg %r" % (arg,)
+ assert not names, "invalid arg {!r}".format(arg)
seen_dirs = set()
for path in argpath.visit(
@@ -623,15 +572,13 @@ class Session(nodes.FSCollector):
if argpath.basename == "__init__.py":
yield next(m[0].collect())
return
- for y in m:
- yield y
+ yield from m
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})".format(
+ path, path.isdir(), path.exists(), path.islink()
)
ihook = self.gethookproxy(path)
if not self.isinitpath(path):
@@ -662,23 +609,14 @@ 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)
+ @staticmethod
+ def _visit_filter(f):
+ return f.check(file=1)
def _tryconvertpyarg(self, x):
"""Convert a dotted module name to path."""
try:
- with _patched_find_module():
- loader = pkgutil.find_loader(x)
+ loader = pkgutil.find_loader(x)
except ImportError:
return x
if loader is None:
@@ -686,8 +624,7 @@ class Session(nodes.FSCollector):
# This method is sometimes invoked when AssertionRewritingHook, which
# does not define a get_filename method, is already in place:
try:
- with _patched_find_module():
- path = loader.get_filename(x)
+ path = loader.get_filename(x)
except AttributeError:
# Retrieve path from AssertionRewritingHook:
path = loader.modules[x][0].co_filename
@@ -769,6 +706,5 @@ class Session(nodes.FSCollector):
rep = collect_one_node(node)
if rep.passed:
for subnode in rep.result:
- for x in self.genitems(subnode):
- yield x
+ yield from self.genitems(subnode)
node.ihook.pytest_collectreport(report=rep)
diff --git a/src/_pytest/mark/__init__.py b/src/_pytest/mark/__init__.py
index 6bc22fe27..30c6e0048 100644
--- a/src/_pytest/mark/__init__.py
+++ b/src/_pytest/mark/__init__.py
@@ -1,9 +1,4 @@
-# -*- coding: utf-8 -*-
""" generic mechanism for marking and selecting python functions. """
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
from .legacy import matchkeyword
from .legacy import matchmark
from .structures import EMPTY_PARAMETERSET_OPTION
diff --git a/src/_pytest/mark/evaluate.py b/src/_pytest/mark/evaluate.py
index 506546e25..898278e30 100644
--- a/src/_pytest/mark/evaluate.py
+++ b/src/_pytest/mark/evaluate.py
@@ -1,11 +1,8 @@
-# -*- coding: utf-8 -*-
import os
import platform
import sys
import traceback
-import six
-
from ..outcomes import fail
from ..outcomes import TEST_OUTCOME
@@ -23,7 +20,7 @@ def cached_eval(config, expr, d):
return x
-class MarkEvaluator(object):
+class MarkEvaluator:
def __init__(self, item, name):
self.item = item
self._marks = None
@@ -87,7 +84,7 @@ class MarkEvaluator(object):
for expr in args:
self.expr = expr
- if isinstance(expr, six.string_types):
+ if isinstance(expr, str):
d = self._getglobals()
result = cached_eval(self.item.config, expr, d)
else:
diff --git a/src/_pytest/mark/legacy.py b/src/_pytest/mark/legacy.py
index c56482f14..d14ea3a82 100644
--- a/src/_pytest/mark/legacy.py
+++ b/src/_pytest/mark/legacy.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
"""
this is a place where we put datastructures used by legacy apis
we hope ot remove
@@ -11,7 +10,7 @@ from _pytest.config import UsageError
@attr.s
-class MarkMapping(object):
+class MarkMapping:
"""Provides a local mapping for markers where item access
resolves to True if the marker is present. """
@@ -26,7 +25,7 @@ class MarkMapping(object):
return name in self.own_mark_names
-class KeywordMapping(object):
+class KeywordMapping:
"""Provides a local mapping for keywords.
Given a list of names, map any substring of one of these names to True.
"""
diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py
index 561ccc3f4..39cdb57e4 100644
--- a/src/_pytest/mark/structures.py
+++ b/src/_pytest/mark/structures.py
@@ -1,15 +1,13 @@
-# -*- coding: utf-8 -*-
import inspect
import warnings
from collections import namedtuple
+from collections.abc import MutableMapping
from operator import attrgetter
import attr
-import six
from ..compat import ascii_escaped
from ..compat import getfslineno
-from ..compat import MappingMixin
from ..compat import NOTSET
from _pytest.deprecated import PYTEST_PARAM_UNKNOWN_KWARGS
from _pytest.outcomes import fail
@@ -72,7 +70,7 @@ class ParameterSet(namedtuple("ParameterSet", "values, marks, id")):
id_ = kwargs.pop("id", None)
if id_ is not None:
- if not isinstance(id_, six.string_types):
+ if not isinstance(id_, str):
raise TypeError(
"Expected id to be a string, got {}: {!r}".format(type(id_), id_)
)
@@ -113,14 +111,18 @@ class ParameterSet(namedtuple("ParameterSet", "values, marks, id")):
force_tuple = len(argnames) == 1
else:
force_tuple = False
- parameters = [
+ 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, parameters
@classmethod
def _for_parametrize(cls, argnames, argvalues, func, config, function_definition):
- argnames, parameters = cls._parse_parametrize_args(argnames, argvalues)
+ argnames, force_tuple = cls._parse_parametrize_args(argnames, argvalues)
+ parameters = cls._parse_parametrize_parameters(argvalues, force_tuple)
del argvalues
if parameters:
@@ -154,7 +156,7 @@ class ParameterSet(namedtuple("ParameterSet", "values, marks, id")):
@attr.s(frozen=True)
-class Mark(object):
+class Mark:
#: name of the mark
name = attr.ib(type=str)
#: positional arguments of the mark decorator
@@ -177,7 +179,7 @@ class Mark(object):
@attr.s
-class MarkDecorator(object):
+class MarkDecorator:
""" A decorator for test functions and test classes. When applied
it will create :class:`MarkInfo` objects which may be
:ref:`retrieved by hooks as item keywords <excontrolskip>`.
@@ -225,7 +227,7 @@ class MarkDecorator(object):
return self.mark == other.mark if isinstance(other, MarkDecorator) else False
def __repr__(self):
- return "<MarkDecorator %r>" % (self.mark,)
+ return "<MarkDecorator {!r}>".format(self.mark)
def with_args(self, *args, **kwargs):
""" return a MarkDecorator with extra arguments added
@@ -286,7 +288,7 @@ def store_mark(obj, mark):
obj.pytestmark = get_unpacked_marks(obj) + [mark]
-class MarkGenerator(object):
+class MarkGenerator:
""" Factory for :class:`MarkDecorator` objects - exposed as
a ``pytest.mark`` singleton instance. Example::
@@ -339,7 +341,7 @@ class MarkGenerator(object):
MARK_GEN = MarkGenerator()
-class NodeKeywords(MappingMixin):
+class NodeKeywords(MutableMapping):
def __init__(self, node):
self.node = node
self.parent = node.parent
@@ -373,11 +375,11 @@ class NodeKeywords(MappingMixin):
return len(self._seen())
def __repr__(self):
- return "<NodeKeywords for node %s>" % (self.node,)
+ return "<NodeKeywords for node {}>".format(self.node)
@attr.s(cmp=False, hash=False)
-class NodeMarkers(object):
+class NodeMarkers:
"""
internal structure for storing marks belonging to a node
diff --git a/src/_pytest/monkeypatch.py b/src/_pytest/monkeypatch.py
index e8671b0c7..090bf61d6 100644
--- a/src/_pytest/monkeypatch.py
+++ b/src/_pytest/monkeypatch.py
@@ -1,17 +1,10 @@
-# -*- coding: utf-8 -*-
""" monkeypatching and mocking functionality. """
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import os
import re
import sys
import warnings
from contextlib import contextmanager
-import six
-
import pytest
from _pytest.fixtures import fixture
from _pytest.pathlib import Path
@@ -67,7 +60,7 @@ def resolve(name):
if expected == used:
raise
else:
- raise ImportError("import error in %s: %s" % (used, ex))
+ raise ImportError("import error in {}: {}".format(used, ex))
found = annotated_getattr(found, part, used)
return found
@@ -77,14 +70,18 @@ def annotated_getattr(obj, name, ann):
obj = getattr(obj, name)
except AttributeError:
raise AttributeError(
- "%r object at %s has no attribute %r" % (type(obj).__name__, ann, name)
+ "{!r} object at {} has no attribute {!r}".format(
+ type(obj).__name__, ann, name
+ )
)
return obj
def derive_importpath(import_path, raising):
- if not isinstance(import_path, six.string_types) or "." not in import_path:
- raise TypeError("must be absolute import path string, not %r" % (import_path,))
+ if not isinstance(import_path, str) or "." not in import_path:
+ raise TypeError(
+ "must be absolute import path string, not {!r}".format(import_path)
+ )
module, attr = import_path.rsplit(".", 1)
target = resolve(module)
if raising:
@@ -92,7 +89,7 @@ def derive_importpath(import_path, raising):
return attr, target
-class Notset(object):
+class Notset:
def __repr__(self):
return "<notset>"
@@ -100,7 +97,7 @@ class Notset(object):
notset = Notset()
-class MonkeyPatch(object):
+class MonkeyPatch:
""" Object returned by the ``monkeypatch`` fixture keeping a record of setattr/item/env/syspath changes.
"""
@@ -151,7 +148,7 @@ class MonkeyPatch(object):
import inspect
if value is notset:
- if not isinstance(target, six.string_types):
+ if not isinstance(target, str):
raise TypeError(
"use setattr(target, name, value) or "
"setattr(target, value) with target being a dotted "
@@ -162,7 +159,7 @@ class MonkeyPatch(object):
oldval = getattr(target, name, notset)
if raising and oldval is notset:
- raise AttributeError("%r has no attribute %r" % (target, name))
+ raise AttributeError("{!r} has no attribute {!r}".format(target, name))
# avoid class descriptors like staticmethod/classmethod
if inspect.isclass(target):
@@ -185,7 +182,7 @@ class MonkeyPatch(object):
import inspect
if name is notset:
- if not isinstance(target, six.string_types):
+ if not isinstance(target, str):
raise TypeError(
"use delattr(target, name) or "
"delattr(target) with target being a dotted "
@@ -222,15 +219,6 @@ class MonkeyPatch(object):
self._setitem.append((dic, name, dic.get(name, notset)))
del dic[name]
- def _warn_if_env_name_is_not_str(self, name):
- """On Python 2, warn if the given environment variable name is not a native str (#4056)"""
- if six.PY2 and not isinstance(name, str):
- warnings.warn(
- pytest.PytestWarning(
- "Environment variable name {!r} should be str".format(name)
- )
- )
-
def setenv(self, name, value, prepend=None):
""" Set environment variable ``name`` to ``value``. If ``prepend``
is a character, read the current environment variable value
@@ -248,7 +236,6 @@ class MonkeyPatch(object):
value = str(value)
if prepend and name in os.environ:
value = value + prepend + os.environ[name]
- self._warn_if_env_name_is_not_str(name)
self.setitem(os.environ, name, value)
def delenv(self, name, raising=True):
@@ -258,7 +245,6 @@ class MonkeyPatch(object):
If ``raising`` is set to False, no exception will be raised if the
environment variable is missing.
"""
- self._warn_if_env_name_is_not_str(name)
self.delitem(os.environ, name, raising=raising)
def syspath_prepend(self, path):
@@ -279,10 +265,9 @@ class MonkeyPatch(object):
# 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
+ from importlib import invalidate_caches
- invalidate_caches()
+ invalidate_caches()
def chdir(self, path):
""" Change the current working directory to the specified path.
diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py
index 7342d9607..f476e4141 100644
--- a/src/_pytest/nodes.py
+++ b/src/_pytest/nodes.py
@@ -1,13 +1,7 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import os
import warnings
import py
-import six
import _pytest._code
from _pytest.compat import getfslineno
@@ -55,7 +49,7 @@ def ischildnode(baseid, nodeid):
return node_parts[: len(base_parts)] == base_parts
-class Node(object):
+class Node:
""" base class for Collector and Item the test collection tree.
Collector subclasses have children, Items are terminal nodes."""
@@ -103,7 +97,7 @@ class Node(object):
return self.session.gethookproxy(self.fspath)
def __repr__(self):
- return "<%s %s>" % (self.__class__.__name__, getattr(self, "name", None))
+ return "<{} {}>".format(self.__class__.__name__, getattr(self, "name", None))
def warn(self, warning):
"""Issue a warning for this item.
@@ -173,7 +167,7 @@ class Node(object):
"""
from _pytest.mark import MarkDecorator, MARK_GEN
- if isinstance(marker, six.string_types):
+ if isinstance(marker, str):
marker = getattr(MARK_GEN, marker)
elif not isinstance(marker, MarkDecorator):
raise ValueError("is not a string or pytest.mark.* Marker")
@@ -244,7 +238,7 @@ class Node(object):
def _repr_failure_py(self, excinfo, style=None):
if excinfo.errisinstance(fail.Exception):
if not excinfo.value.pytrace:
- return six.text_type(excinfo.value)
+ return str(excinfo.value)
fm = self.session._fixturemanager
if excinfo.errisinstance(fm.FixtureLookupError):
return excinfo.value.formatrepr()
@@ -371,9 +365,7 @@ class FSCollector(Collector):
if nodeid and os.sep != SEP:
nodeid = nodeid.replace(os.sep, SEP)
- super(FSCollector, self).__init__(
- name, parent, config, session, nodeid=nodeid, fspath=fspath
- )
+ super().__init__(name, parent, config, session, nodeid=nodeid, fspath=fspath)
class File(FSCollector):
@@ -388,7 +380,7 @@ class Item(Node):
nextitem = None
def __init__(self, name, parent=None, config=None, session=None, nodeid=None):
- super(Item, self).__init__(name, parent, config, session, nodeid=nodeid)
+ super().__init__(name, parent, config, session, nodeid=nodeid)
self._report_sections = []
#: user properties is a list of tuples (name, value) that holds user
diff --git a/src/_pytest/nose.py b/src/_pytest/nose.py
index fbab91da2..bb5ca198c 100644
--- a/src/_pytest/nose.py
+++ b/src/_pytest/nose.py
@@ -1,13 +1,6 @@
-# -*- coding: utf-8 -*-
""" run test suites written for nose. """
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import sys
-import six
-
import pytest
from _pytest import python
from _pytest import runner
@@ -28,7 +21,7 @@ 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
+ lambda: pytest.skip(str(call.excinfo.value)), call.when
)
call.excinfo = call2.excinfo
diff --git a/src/_pytest/outcomes.py b/src/_pytest/outcomes.py
index 4620f957c..c63c80e10 100644
--- a/src/_pytest/outcomes.py
+++ b/src/_pytest/outcomes.py
@@ -1,12 +1,7 @@
-# -*- coding: utf-8 -*-
"""
exception classes and constants handling test outcomes
as well as functions creating them
"""
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import sys
from packaging.version import Version
@@ -28,7 +23,7 @@ class OutcomeException(BaseException):
if isinstance(val, bytes):
val = val.decode("UTF-8", errors="replace")
return val
- return "<%s instance>" % (self.__class__.__name__,)
+ return "<{} instance>".format(self.__class__.__name__)
__str__ = __repr__
@@ -58,7 +53,7 @@ class Exit(Exception):
def __init__(self, msg="unknown reason", returncode=None):
self.msg = msg
self.returncode = returncode
- super(Exit, self).__init__(msg)
+ super().__init__(msg)
# exposed helper methods
@@ -171,7 +166,7 @@ def importorskip(modname, minversion=None, reason=None):
import_exc = exc
if import_exc:
if reason is None:
- reason = "could not import %r: %s" % (modname, import_exc)
+ reason = "could not import {!r}: {}".format(modname, import_exc)
raise Skipped(reason, allow_module_level=True)
mod = sys.modules[modname]
if minversion is None:
diff --git a/src/_pytest/pastebin.py b/src/_pytest/pastebin.py
index 3f4171207..ce0e73acc 100644
--- a/src/_pytest/pastebin.py
+++ b/src/_pytest/pastebin.py
@@ -1,14 +1,6 @@
-# -*- coding: utf-8 -*-
""" submit failure or test session information to a pastebin service. """
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
-import sys
import tempfile
-import six
-
import pytest
@@ -39,7 +31,7 @@ def pytest_configure(config):
def tee_write(s, **kwargs):
oldwrite(s, **kwargs)
- if isinstance(s, six.text_type):
+ if isinstance(s, str):
s = s.encode("utf-8")
config._pastebinfile.write(s)
@@ -70,23 +62,15 @@ def create_new_paste(contents):
:returns: url to the pasted contents
"""
import re
+ from urllib.request import urlopen
+ from urllib.parse import urlencode
- if sys.version_info < (3, 0):
- from urllib import urlopen, urlencode
- else:
- from urllib.request import urlopen
- from urllib.parse import urlencode
-
- params = {
- "code": contents,
- "lexer": "python3" if sys.version_info[0] == 3 else "python",
- "expiry": "1week",
- }
+ params = {"code": contents, "lexer": "python3", "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"))
if m:
- return "%s/show/%s" % (url, m.group(1))
+ return "{}/show/{}".format(url, m.group(1))
else:
return "bad response: " + response
@@ -111,4 +95,4 @@ def pytest_terminal_summary(terminalreporter):
s = tw.stringio.getvalue()
assert len(s)
pastebinurl = create_new_paste(s)
- tr.write_line("%s --> %s" % (msg, pastebinurl))
+ tr.write_line("{} --> {}".format(msg, pastebinurl))
diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py
index 729c41797..09b1bb3d5 100644
--- a/src/_pytest/pathlib.py
+++ b/src/_pytest/pathlib.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
import atexit
import errno
import fnmatch
@@ -8,19 +7,14 @@ import os
import shutil
import sys
import uuid
-from functools import reduce
from os.path import expanduser
from os.path import expandvars
from os.path import isabs
from os.path import sep
from posixpath import sep as posix_sep
-import six
-from six.moves import map
-from .compat import PY36
-
-if PY36:
+if sys.version_info[:2] >= (3, 6):
from pathlib import Path, PurePath
else:
from pathlib2 import Path, PurePath
@@ -84,17 +78,6 @@ def parse_num(maybe_num):
return -1
-if six.PY2:
-
- def _max(iterable, default):
- """needed due to python2.7 lacking the default argument for max"""
- return reduce(max, iterable, default)
-
-
-else:
- _max = max
-
-
def _force_symlink(root, target, link_to):
"""helper to create the current symlink
@@ -119,7 +102,7 @@ def make_numbered_dir(root, prefix):
"""create a directory with an increased number as suffix for the given prefix"""
for i in range(10):
# try up to 10 times to create the folder
- max_existing = _max(map(parse_num, find_suffixes(root, prefix)), default=-1)
+ max_existing = max(map(parse_num, find_suffixes(root, prefix)), default=-1)
new_number = max_existing + 1
new_path = root.joinpath("{}{}".format(prefix, new_number))
try:
@@ -143,9 +126,10 @@ def create_cleanup_lock(p):
fd = os.open(str(lock_path), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644)
except OSError as e:
if e.errno == errno.EEXIST:
- six.raise_from(
- EnvironmentError("cannot create lockfile in {path}".format(path=p)), e
- )
+ raise EnvironmentError(
+ "cannot create lockfile in {path}".format(path=p)
+ ) from e
+
else:
raise
else:
@@ -230,7 +214,7 @@ def try_cleanup(path, consider_lock_dead_if_created_before):
def cleanup_candidates(root, prefix, keep):
"""lists candidates for numbered directories to be removed - follows py.path"""
- max_existing = _max(map(parse_num, find_suffixes(root, prefix)), default=-1)
+ max_existing = max(map(parse_num, find_suffixes(root, prefix)), default=-1)
max_delete = max_existing - keep
paths = find_prefixed(root, prefix)
paths, paths2 = itertools.tee(paths)
@@ -311,7 +295,7 @@ def fnmatch_ex(pattern, path):
if sep not in pattern:
name = path.name
else:
- name = six.text_type(path)
+ name = str(path)
return fnmatch.fnmatch(name, pattern)
diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py
index 605451630..c7a8ca693 100644
--- a/src/_pytest/pytester.py
+++ b/src/_pytest/pytester.py
@@ -1,10 +1,4 @@
-# -*- coding: utf-8 -*-
"""(disabled by default) support for testing pytest and pytest plugins."""
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
-import codecs
import gc
import os
import platform
@@ -13,11 +7,11 @@ import subprocess
import sys
import time
import traceback
+from collections.abc import Sequence
from fnmatch import fnmatch
from weakref import WeakKeyDictionary
import py
-import six
import pytest
from _pytest._code import Source
@@ -25,8 +19,6 @@ 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.main import EXIT_INTERRUPTED
from _pytest.main import EXIT_OK
from _pytest.main import Session
@@ -34,7 +26,7 @@ from _pytest.monkeypatch import MonkeyPatch
from _pytest.pathlib import Path
IGNORE_PAM = [ # filenames added when obtaining details about the current user
- u"/var/lib/sss/mc/passwd"
+ "/var/lib/sss/mc/passwd"
]
@@ -84,7 +76,7 @@ def raise_on_kwargs(kwargs):
)
-class LsofFdLeakChecker(object):
+class LsofFdLeakChecker:
def get_open_files(self):
out = self._exec_lsof()
open_files = self._parse_lsof_output(out)
@@ -166,7 +158,7 @@ def _pytest(request):
return PytestArg(request)
-class PytestArg(object):
+class PytestArg:
def __init__(self, request):
self.request = request
@@ -181,7 +173,7 @@ def get_public_names(values):
return [x for x in values if x[0] != "_"]
-class ParsedCall(object):
+class ParsedCall:
def __init__(self, name, kwargs):
self.__dict__.update(kwargs)
self._name = name
@@ -189,10 +181,10 @@ class ParsedCall(object):
def __repr__(self):
d = self.__dict__.copy()
del d["_name"]
- return "<ParsedCall %r(**%r)>" % (self._name, d)
+ return "<ParsedCall {!r}(**{!r})>".format(self._name, d)
-class HookRecorder(object):
+class HookRecorder:
"""Record all hooks called in a plugin manager.
This wraps all the hook calls in the plugin manager, recording each call
@@ -239,7 +231,7 @@ class HookRecorder(object):
break
print("NONAMEMATCH", name, "with", call)
else:
- pytest.fail("could not find %r check %r" % (name, check))
+ pytest.fail("could not find {!r} check {!r}".format(name, check))
def popcall(self, name):
__tracebackhide__ = True
@@ -247,7 +239,7 @@ class HookRecorder(object):
if call._name == name:
del self.calls[i]
return call
- lines = ["could not find call %r, in:" % (name,)]
+ lines = ["could not find call {!r}, in:".format(name)]
lines.extend([" %s" % x for x in self.calls])
pytest.fail("\n".join(lines))
@@ -284,7 +276,9 @@ class HookRecorder(object):
)
if len(values) > 1:
raise ValueError(
- "found 2 or more testreports matching %r: %s" % (inamepart, values)
+ "found 2 or more testreports matching {!r}: {}".format(
+ inamepart, values
+ )
)
return values[0]
@@ -358,7 +352,7 @@ def _config_for_test():
rex_outcome = re.compile(r"(\d+) ([\w-]+)")
-class RunResult(object):
+class RunResult:
"""The result of running a command.
Attributes:
@@ -430,7 +424,7 @@ class RunResult(object):
assert obtained == expected
-class CwdSnapshot(object):
+class CwdSnapshot:
def __init__(self):
self.__saved = os.getcwd()
@@ -438,7 +432,7 @@ class CwdSnapshot(object):
os.chdir(self.__saved)
-class SysModulesSnapshot(object):
+class SysModulesSnapshot:
def __init__(self, preserve=None):
self.__preserve = preserve
self.__saved = dict(sys.modules)
@@ -452,7 +446,7 @@ class SysModulesSnapshot(object):
sys.modules.update(self.__saved)
-class SysPathsSnapshot(object):
+class SysPathsSnapshot:
def __init__(self):
self.__saved = list(sys.path), list(sys.meta_path)
@@ -460,7 +454,7 @@ class SysPathsSnapshot(object):
sys.path[:], sys.meta_path[:] = self.__saved
-class Testdir(object):
+class Testdir:
"""Temporary test directory with tools to test/run pytest itself.
This is based on the ``tmpdir`` fixture but provides a number of methods
@@ -513,7 +507,7 @@ class Testdir(object):
self._env_run_update = {"HOME": tmphome, "USERPROFILE": tmphome}
def __repr__(self):
- return "<Testdir %r>" % (self.tmpdir,)
+ return "<Testdir {!r}>".format(self.tmpdir)
def __str__(self):
return str(self.tmpdir)
@@ -558,10 +552,10 @@ class Testdir(object):
items = list(kwargs.items())
def to_text(s):
- return s.decode(encoding) if isinstance(s, bytes) else six.text_type(s)
+ return s.decode(encoding) if isinstance(s, bytes) else str(s)
if args:
- source = u"\n".join(to_text(x) for x in args)
+ source = "\n".join(to_text(x) for x in args)
basename = self.request.function.__name__
items.insert(0, (basename, source))
@@ -570,7 +564,7 @@ class Testdir(object):
p = self.tmpdir.join(basename).new(ext=ext)
p.dirpath().ensure_dir()
source = Source(value)
- source = u"\n".join(to_text(line) for line in source.lines)
+ source = "\n".join(to_text(line) for line in source.lines)
p.write(source.strip().encode(encoding), "wb")
if ret is None:
ret = p
@@ -839,7 +833,7 @@ class Testdir(object):
rec = []
- class Collect(object):
+ class Collect:
def pytest_configure(x, config):
rec.append(self.make_hook_recorder(config.pluginmanager))
@@ -849,7 +843,7 @@ class Testdir(object):
reprec = rec.pop()
else:
- class reprec(object):
+ class reprec:
pass
reprec.ret = ret
@@ -881,13 +875,13 @@ class Testdir(object):
reprec = self.inline_run(*args, **kwargs)
except SystemExit as e:
- class reprec(object):
+ class reprec:
ret = e.args[0]
except Exception:
traceback.print_exc()
- class reprec(object):
+ class reprec:
ret = 3
finally:
@@ -911,7 +905,7 @@ class Testdir(object):
def _ensure_basetemp(self, args):
args = list(args)
for x in args:
- if safe_str(x).startswith("--basetemp"):
+ if str(x).startswith("--basetemp"):
break
else:
args.append("--basetemp=%s" % self.tmpdir.dirpath("basetemp"))
@@ -969,10 +963,8 @@ class Testdir(object):
for item in items:
if item.name == funcname:
return item
- assert 0, "%r item not found in module:\n%s\nitems: %s" % (
- funcname,
- source,
- items,
+ assert 0, "{!r} item not found in module:\n{}\nitems: {}".format(
+ funcname, source, items
)
def getitems(self, source):
@@ -1096,8 +1088,8 @@ class Testdir(object):
p2 = self.tmpdir.join("stderr")
print("running:", *cmdargs)
print(" in:", py.path.local())
- f1 = codecs.open(str(p1), "w", encoding="utf8")
- f2 = codecs.open(str(p2), "w", encoding="utf8")
+ f1 = open(str(p1), "w", encoding="utf8")
+ f2 = open(str(p2), "w", encoding="utf8")
try:
now = time.time()
popen = self.popen(
@@ -1124,30 +1116,16 @@ class Testdir(object):
if timeout is None:
ret = popen.wait()
- elif six.PY3:
+ else:
try:
ret = popen.wait(timeout)
except subprocess.TimeoutExpired:
handle_timeout()
- else:
- end = time.time() + timeout
-
- resolution = min(0.1, timeout / 10)
-
- while True:
- ret = popen.poll()
- if ret is not None:
- break
-
- if time.time() > end:
- handle_timeout()
-
- time.sleep(resolution)
finally:
f1.close()
f2.close()
- f1 = codecs.open(str(p1), "r", encoding="utf8")
- f2 = codecs.open(str(p2), "r", encoding="utf8")
+ f1 = open(str(p1), "r", encoding="utf8")
+ f2 = open(str(p2), "r", encoding="utf8")
try:
out = f1.read().splitlines()
err = f2.read().splitlines()
@@ -1163,7 +1141,7 @@ class Testdir(object):
for line in lines:
print(line, file=fp)
except UnicodeEncodeError:
- print("couldn't print to %s because of encoding" % (fp,))
+ print("couldn't print to {} because of encoding".format(fp))
def _getpytestargs(self):
return sys.executable, "-mpytest"
@@ -1220,7 +1198,7 @@ class Testdir(object):
"""
basetemp = self.tmpdir.mkdir("temp-pexpect")
invoke = " ".join(map(str, self._getpytestargs()))
- cmd = "%s --basetemp=%s %s" % (invoke, basetemp, string)
+ cmd = "{} --basetemp={} {}".format(invoke, basetemp, string)
return self.spawn(cmd, expect_timeout=expect_timeout)
def spawn(self, cmd, expect_timeout=10.0):
@@ -1250,10 +1228,12 @@ 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{}".format(
+ saferepr(out)
+ )
-class LineComp(object):
+class LineComp:
def __init__(self):
self.stringio = py.io.TextIO()
@@ -1271,7 +1251,7 @@ class LineComp(object):
return LineMatcher(lines1).fnmatch_lines(lines2)
-class LineMatcher(object):
+class LineMatcher:
"""Flexible matching of text.
This is a convenience class to test large texts like the output of
@@ -1409,5 +1389,5 @@ class LineMatcher(object):
self._log(" and:", repr(nextline))
extralines.append(nextline)
else:
- self._log("remains unmatched: %r" % (line,))
+ self._log("remains unmatched: {!r}".format(line))
pytest.fail(self._log_text)
diff --git a/src/_pytest/python.py b/src/_pytest/python.py
index 5f1e6885b..b4d8f5ae0 100644
--- a/src/_pytest/python.py
+++ b/src/_pytest/python.py
@@ -1,10 +1,6 @@
-# -*- coding: utf-8 -*-
""" Python test discovery, setup and run of test functions. """
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import collections
+import enum
import fnmatch
import inspect
import os
@@ -14,7 +10,6 @@ from functools import partial
from textwrap import dedent
import py
-import six
import _pytest
from _pytest import deprecated
@@ -22,7 +17,6 @@ from _pytest import fixtures
from _pytest import nodes
from _pytest._code import filter_traceback
from _pytest.compat import ascii_escaped
-from _pytest.compat import enum
from _pytest.compat import get_default_arg_names
from _pytest.compat import get_real_func
from _pytest.compat import getfslineno
@@ -35,7 +29,6 @@ from _pytest.compat import NOTSET
from _pytest.compat import REGEX_TYPE
from _pytest.compat import safe_getattr
from _pytest.compat import safe_isclass
-from _pytest.compat import safe_str
from _pytest.compat import STRING_TYPES
from _pytest.config import hookimpl
from _pytest.main import FSHookProxy
@@ -55,8 +48,8 @@ def pyobj_property(name):
if node is not None:
return node.obj
- doc = "python %s object this node was collected from (can be None)." % (
- name.lower(),
+ doc = "python {} object this node was collected from (can be None).".format(
+ name.lower()
)
return property(get, None, None, doc)
@@ -239,7 +232,7 @@ def pytest_make_parametrize_id(config, val, argname=None):
return None
-class PyobjContext(object):
+class PyobjContext:
module = pyobj_property("Module")
cls = pyobj_property("Class")
instance = pyobj_property("Instance")
@@ -249,7 +242,7 @@ class PyobjMixin(PyobjContext):
_ALLOW_MARKERS = True
def __init__(self, *k, **kw):
- super(PyobjMixin, self).__init__(*k, **kw)
+ super().__init__(*k, **kw)
@property
def obj(self):
@@ -421,7 +414,7 @@ class PyCollector(PyobjMixin, nodes.Collector):
fixtureinfo.prune_dependency_tree()
for callspec in metafunc._calls:
- subname = "%s[%s]" % (name, callspec.id)
+ subname = "{}[{}]".format(name, callspec.id)
yield Function(
name=subname,
parent=self,
@@ -443,7 +436,7 @@ class Module(nodes.File, PyCollector):
self._inject_setup_module_fixture()
self._inject_setup_function_fixture()
self.session._fixturemanager.parsefactories(self)
- return super(Module, self).collect()
+ return super().collect()
def _inject_setup_module_fixture(self):
"""Injects a hidden autouse, module scoped fixture into the collected module object
@@ -531,7 +524,7 @@ class Module(nodes.File, PyCollector):
if exc_info.traceback
else exc_info.exconly()
)
- formatted_tb = safe_str(exc_repr)
+ formatted_tb = str(exc_repr)
raise self.CollectError(
"ImportError while importing test module '{fspath}'.\n"
"Hint: make sure your test modules/packages have valid Python names.\n"
@@ -606,11 +599,10 @@ 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})".format(
+ path, path.isdir(), path.exists(), path.islink()
)
ihook = self.gethookproxy(path)
if not self.isinitpath(path):
@@ -657,8 +649,7 @@ class Package(Module):
continue
if is_file:
- for x in self._collectfile(path):
- yield x
+ yield from self._collectfile(path)
elif not path.isdir():
# Broken symlink or invalid/missing file.
continue
@@ -800,7 +791,7 @@ class Instance(PyCollector):
def collect(self):
self.session._fixturemanager.parsefactories(self)
- return super(Instance, self).collect()
+ return super().collect()
def newinstance(self):
self.obj = self._getobj()
@@ -858,7 +849,7 @@ def hasnew(obj):
return new != object.__new__
-class CallSpec2(object):
+class CallSpec2:
def __init__(self, metafunc):
self.metafunc = metafunc
self.funcargs = {}
@@ -884,7 +875,7 @@ class CallSpec2(object):
def _checkargnotcontained(self, arg):
if arg in self.params or arg in self.funcargs:
- raise ValueError("duplicate %r" % (arg,))
+ raise ValueError("duplicate {!r}".format(arg))
def getparam(self, name):
try:
@@ -1060,7 +1051,7 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
msg = "In {}: {} parameter sets specified, with different number of ids: {}"
fail(msg.format(func_name, len(parameters), len(ids)), pytrace=False)
for id_value in ids:
- if id_value is not None and not isinstance(id_value, six.string_types):
+ if id_value is not None and not isinstance(id_value, str):
msg = "In {}: ids must be list of strings, found: {} (type: {!r})"
fail(
msg.format(func_name, saferepr(id_value), type(id_value)),
@@ -1183,7 +1174,7 @@ def _idval(val, argname, idx, idfn, item, config):
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)
+ raise ValueError(msg) from e
elif config:
hook_id = config.hook.pytest_make_parametrize_id(
config=config, val=val, argname=argname
@@ -1335,7 +1326,7 @@ def _showfixtures_main(config, session):
if currentmodule != module:
if not module.startswith("_pytest."):
tw.line()
- tw.sep("-", "fixtures defined from %s" % (module,))
+ tw.sep("-", "fixtures defined from {}".format(module))
currentmodule = module
if verbose <= 0 and argname[0] == "_":
continue
@@ -1350,7 +1341,7 @@ def _showfixtures_main(config, session):
if doc:
write_docstring(tw, doc)
else:
- tw.line(" %s: no docstring available" % (loc,), red=True)
+ tw.line(" {}: no docstring available".format(loc), red=True)
tw.line()
@@ -1390,7 +1381,7 @@ class Function(FunctionMixin, nodes.Item, fixtures.FuncargnamesCompatAttr):
fixtureinfo=None,
originalname=None,
):
- super(Function, self).__init__(name, parent, config=config, session=session)
+ super().__init__(name, parent, config=config, session=session)
self._args = args
if callobj is not NOTSET:
self.obj = callobj
@@ -1464,7 +1455,7 @@ class Function(FunctionMixin, nodes.Item, fixtures.FuncargnamesCompatAttr):
self.ihook.pytest_pyfunc_call(pyfuncitem=self)
def setup(self):
- super(Function, self).setup()
+ super().setup()
fixtures.fillfixtures(self)
diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py
index df09aa32d..011181a40 100644
--- a/src/_pytest/python_api.py
+++ b/src/_pytest/python_api.py
@@ -1,23 +1,19 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import
-
import math
import pprint
import sys
import warnings
+from collections.abc import Iterable
+from collections.abc import Mapping
+from collections.abc import Sized
from decimal import Decimal
+from itertools import filterfalse
from numbers import Number
from more_itertools.more import always_iterable
-from six.moves import filterfalse
-from six.moves import zip
import _pytest._code
from _pytest import deprecated
from _pytest.compat import isclass
-from _pytest.compat import Iterable
-from _pytest.compat import Mapping
-from _pytest.compat import Sized
from _pytest.compat import STRING_TYPES
from _pytest.outcomes import fail
@@ -50,7 +46,7 @@ def _non_numeric_type_error(value, at):
# builtin pytest.approx helper
-class ApproxBase(object):
+class ApproxBase:
"""
Provide shared utilities for making approximate comparisons between numbers
or sequences of numbers.
@@ -81,9 +77,6 @@ class ApproxBase(object):
def __ne__(self, actual):
return not (actual == self)
- if sys.version_info[0] == 2:
- __cmp__ = _cmp_raises_type_error
-
def _approx_scalar(self, x):
return ApproxScalar(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok)
@@ -122,9 +115,6 @@ class ApproxNumpy(ApproxBase):
list_scalars = _recursive_list_map(self._approx_scalar, self.expected.tolist())
return "approx({!r})".format(list_scalars)
- if sys.version_info[0] == 2:
- __cmp__ = _cmp_raises_type_error
-
def __eq__(self, actual):
import numpy as np
@@ -251,10 +241,7 @@ class ApproxScalar(ApproxBase):
except ValueError:
vetted_tolerance = "???"
- if sys.version_info[0] == 2:
- return "{} +- {}".format(self.expected, vetted_tolerance)
- else:
- return u"{} \u00b1 {}".format(self.expected, vetted_tolerance)
+ return "{} \u00b1 {}".format(self.expected, vetted_tolerance)
def __eq__(self, actual):
"""
@@ -719,7 +706,7 @@ def raises(expected_exception, *args, **kwargs):
raises.Exception = fail.Exception
-class RaisesContext(object):
+class RaisesContext:
def __init__(self, expected_exception, message, match_expr):
self.expected_exception = expected_exception
self.message = message
@@ -736,8 +723,6 @@ class RaisesContext(object):
fail(self.message)
self.excinfo.__init__(tp)
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:
self.excinfo.match(self.match_expr)
return suppress_exception
diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py
index 574c6a1cc..006d97e7f 100644
--- a/src/_pytest/recwarn.py
+++ b/src/_pytest/recwarn.py
@@ -1,16 +1,9 @@
-# -*- coding: utf-8 -*-
""" recording warnings during test function execution. """
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import inspect
import re
import sys
import warnings
-import six
-
import _pytest._code
from _pytest.deprecated import PYTEST_WARNS_UNKNOWN_KWARGS
from _pytest.deprecated import WARNS_EXEC
@@ -117,7 +110,7 @@ class WarningsRecorder(warnings.catch_warnings):
"""
def __init__(self):
- super(WarningsRecorder, self).__init__(record=True)
+ super().__init__(record=True)
self._entered = False
self._list = []
@@ -154,47 +147,16 @@ class WarningsRecorder(warnings.catch_warnings):
if self._entered:
__tracebackhide__ = True
raise RuntimeError("Cannot enter %r twice" % self)
- self._list = super(WarningsRecorder, self).__enter__()
+ self._list = super().__enter__()
warnings.simplefilter("always")
- # python3 keeps track of a "filter version", when the filters are
- # updated previously seen warnings can be re-warned. python2 has no
- # concept of this so we must reset the warnings registry manually.
- # trivial patching of `warnings.warn` seems to be enough somehow?
- if six.PY2:
-
- def warn(message, category=None, stacklevel=1):
- # duplicate the stdlib logic due to
- # bad handing in the c version of warnings
- if isinstance(message, Warning):
- category = message.__class__
- # Check category argument
- if category is None:
- category = UserWarning
- assert issubclass(category, Warning)
-
- # emulate resetting the warn registry
- f_globals = sys._getframe(stacklevel).f_globals
- if "__warningregistry__" in f_globals:
- orig = f_globals["__warningregistry__"]
- f_globals["__warningregistry__"] = None
- try:
- return self._saved_warn(message, category, stacklevel + 1)
- finally:
- f_globals["__warningregistry__"] = orig
- else:
- return self._saved_warn(message, category, stacklevel + 1)
-
- warnings.warn, self._saved_warn = warn, warnings.warn
return self
def __exit__(self, *exc_info):
if not self._entered:
__tracebackhide__ = True
raise RuntimeError("Cannot exit %r without entering first" % self)
- # see above where `self._saved_warn` is assigned
- if six.PY2:
- warnings.warn = self._saved_warn
- super(WarningsRecorder, self).__exit__(*exc_info)
+
+ super().__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.
@@ -203,7 +165,7 @@ class WarningsRecorder(warnings.catch_warnings):
class WarningsChecker(WarningsRecorder):
def __init__(self, expected_warning=None, match_expr=None):
- super(WarningsChecker, self).__init__()
+ super().__init__()
msg = "exceptions must be old-style classes or derived from Warning, not %s"
if isinstance(expected_warning, tuple):
@@ -219,7 +181,7 @@ class WarningsChecker(WarningsRecorder):
self.match_expr = match_expr
def __exit__(self, *exc_info):
- super(WarningsChecker, self).__exit__(*exc_info)
+ super().__exit__(*exc_info)
__tracebackhide__ = True
diff --git a/src/_pytest/reports.py b/src/_pytest/reports.py
index 0bba6762c..d2f1f33e2 100644
--- a/src/_pytest/reports.py
+++ b/src/_pytest/reports.py
@@ -1,8 +1,6 @@
-# -*- coding: utf-8 -*-
from pprint import pprint
import py
-import six
from _pytest._code.code import ExceptionInfo
from _pytest._code.code import ReprEntry
@@ -23,16 +21,13 @@ def getslaveinfoline(node):
except AttributeError:
d = node.slaveinfo
ver = "%s.%s.%s" % d["version_info"][:3]
- node._slaveinfocache = s = "[%s] %s -- Python %s %s" % (
- d["id"],
- d["sysplatform"],
- ver,
- d["executable"],
+ node._slaveinfocache = s = "[{}] {} -- Python {} {}".format(
+ d["id"], d["sysplatform"], ver, d["executable"]
)
return s
-class BaseReport(object):
+class BaseReport:
when = None
location = None
@@ -195,7 +190,7 @@ class BaseReport(object):
):
d["longrepr"] = disassembled_report(self)
else:
- d["longrepr"] = six.text_type(self.longrepr)
+ d["longrepr"] = str(self.longrepr)
else:
d["longrepr"] = self.longrepr
for name in d:
@@ -335,11 +330,8 @@ class TestReport(BaseReport):
self.__dict__.update(extra)
def __repr__(self):
- return "<%s %r when=%r outcome=%r>" % (
- self.__class__.__name__,
- self.nodeid,
- self.when,
- self.outcome,
+ return "<{} {!r} when={!r} outcome={!r}>".format(
+ self.__class__.__name__, self.nodeid, self.when, self.outcome
)
@classmethod
@@ -372,7 +364,7 @@ class TestReport(BaseReport):
excinfo, style=item.config.getoption("tbstyle", "auto")
)
for rwhen, key, content in item._report_sections:
- sections.append(("Captured %s %s" % (key, rwhen), content))
+ sections.append(("Captured {} {}".format(key, rwhen), content))
return cls(
item.nodeid,
item.location,
@@ -402,10 +394,8 @@ class CollectReport(BaseReport):
return (self.fspath, None, self.fspath)
def __repr__(self):
- return "<CollectReport %r lenresult=%s outcome=%r>" % (
- self.nodeid,
- len(self.result),
- self.outcome,
+ return "<CollectReport {!r} lenresult={} outcome={!r}>".format(
+ self.nodeid, len(self.result), self.outcome
)
diff --git a/src/_pytest/resultlog.py b/src/_pytest/resultlog.py
index bd30b5071..a977b29da 100644
--- a/src/_pytest/resultlog.py
+++ b/src/_pytest/resultlog.py
@@ -1,11 +1,6 @@
-# -*- coding: utf-8 -*-
""" log machine-parseable test session result information in a plain
text file.
"""
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import os
import py
@@ -48,13 +43,13 @@ def pytest_unconfigure(config):
config.pluginmanager.unregister(resultlog)
-class ResultLog(object):
+class ResultLog:
def __init__(self, config, logfile):
self.config = config
self.logfile = logfile # preferably line buffered
def write_log_entry(self, testpath, lettercode, longrepr):
- print("%s %s" % (lettercode, testpath), file=self.logfile)
+ print("{} {}".format(lettercode, testpath), file=self.logfile)
for line in longrepr.splitlines():
print(" %s" % line, file=self.logfile)
diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py
index d51e859f1..9c91a49a5 100644
--- a/src/_pytest/runner.py
+++ b/src/_pytest/runner.py
@@ -1,16 +1,10 @@
-# -*- coding: utf-8 -*-
""" basic collect and runtest protocol implementations """
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import bdb
import os
import sys
from time import time
import attr
-import six
from .reports import CollectErrorRepr
from .reports import CollectReport
@@ -62,7 +56,7 @@ def pytest_terminal_summary(terminalreporter):
tr.write_line("")
tr.write_line("(0.00 durations hidden. Use -vv to show these durations.)")
break
- tr.write_line("%02.2fs %-8s %s" % (rep.duration, rep.when, rep.nodeid))
+ tr.write_line("{:02.2f}s {:<8} {}".format(rep.duration, rep.when, rep.nodeid))
def pytest_sessionstart(session):
@@ -200,7 +194,7 @@ def call_runtest_hook(item, when, **kwds):
@attr.s(repr=False)
-class CallInfo(object):
+class CallInfo:
""" Result/Exception info a function invocation. """
_result = attr.ib()
@@ -275,7 +269,7 @@ def pytest_make_collect_report(collector):
return rep
-class SetupState(object):
+class SetupState:
""" shared state for setting up/tearing down test items or collectors. """
def __init__(self):
@@ -309,7 +303,8 @@ class SetupState(object):
if exc is None:
exc = sys.exc_info()
if exc:
- six.reraise(*exc)
+ _, val, tb = exc
+ raise val.with_traceback(tb)
def _teardown_with_finalization(self, colitem):
self._callfinalizers(colitem)
@@ -344,7 +339,8 @@ class SetupState(object):
if exc is None:
exc = sys.exc_info()
if exc:
- six.reraise(*exc)
+ _, val, tb = exc
+ raise val.with_traceback(tb)
def prepare(self, colitem):
""" setup objects along the collector chain to the test-method
@@ -355,7 +351,8 @@ class SetupState(object):
# check if the last collection node has raised an error
for col in self.stack:
if hasattr(col, "_prepare_exc"):
- six.reraise(*col._prepare_exc)
+ _, val, tb = col._prepare_exc
+ raise val.with_traceback(tb)
for col in needed_collectors[len(self.stack) :]:
self.stack.append(col)
try:
diff --git a/src/_pytest/setuponly.py b/src/_pytest/setuponly.py
index 085901124..70d6ed12f 100644
--- a/src/_pytest/setuponly.py
+++ b/src/_pytest/setuponly.py
@@ -1,8 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import sys
import pytest
diff --git a/src/_pytest/setupplan.py b/src/_pytest/setupplan.py
index 47b0fe82e..697746f20 100644
--- a/src/_pytest/setupplan.py
+++ b/src/_pytest/setupplan.py
@@ -1,8 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import pytest
diff --git a/src/_pytest/skipping.py b/src/_pytest/skipping.py
index bc8b88e71..53737816f 100644
--- a/src/_pytest/skipping.py
+++ b/src/_pytest/skipping.py
@@ -1,9 +1,4 @@
-# -*- coding: utf-8 -*-
""" support for skip/xfail functions and markers. """
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
from _pytest.config import hookimpl
from _pytest.mark.evaluate import MarkEvaluator
from _pytest.outcomes import fail
@@ -129,17 +124,13 @@ def pytest_runtest_makereport(item, call):
evalxfail = getattr(item, "_evalxfail", None)
# unitttest special case, see setting of _unexpectedsuccess
if hasattr(item, "_unexpectedsuccess") and rep.when == "call":
- from _pytest.compat import _is_unittest_unexpected_success_a_failure
if item._unexpectedsuccess:
rep.longrepr = "Unexpected success: {}".format(item._unexpectedsuccess)
else:
rep.longrepr = "Unexpected success"
- if _is_unittest_unexpected_success_a_failure():
- rep.outcome = "failed"
- else:
- rep.outcome = "passed"
- rep.wasxfail = rep.longrepr
+ rep.outcome = "failed"
+
elif item.config.option.runxfail:
pass # don't interefere
elif call.excinfo and call.excinfo.errisinstance(xfail.Exception):
diff --git a/src/_pytest/stepwise.py b/src/_pytest/stepwise.py
index 0427cd0ea..68e53a31c 100644
--- a/src/_pytest/stepwise.py
+++ b/src/_pytest/stepwise.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
import pytest
diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py
index eb1970d51..6f9890301 100644
--- a/src/_pytest/terminal.py
+++ b/src/_pytest/terminal.py
@@ -1,12 +1,7 @@
-# -*- coding: utf-8 -*-
""" terminal reporting of the full testing process.
This is a good source for looking at the various reporting hooks.
"""
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import argparse
import collections
import platform
@@ -17,7 +12,6 @@ from functools import partial
import attr
import pluggy
import py
-import six
from more_itertools import collapse
import pytest
@@ -40,7 +34,7 @@ class MoreQuietAction(argparse.Action):
"""
def __init__(self, option_strings, dest, default=None, required=False, help=None):
- super(MoreQuietAction, self).__init__(
+ super().__init__(
option_strings=option_strings,
dest=dest,
nargs=0,
@@ -191,7 +185,7 @@ def pytest_report_teststatus(report):
@attr.s
-class WarningReport(object):
+class WarningReport:
"""
Simple structure to hold warnings information captured by ``pytest_warning_captured``.
@@ -219,13 +213,13 @@ class WarningReport(object):
relpath = py.path.local(filename).relto(config.invocation_dir)
if not relpath:
relpath = str(filename)
- return "%s:%s" % (relpath, linenum)
+ return "{}:{}".format(relpath, linenum)
else:
return str(self.fslocation)
return None
-class TerminalReporter(object):
+class TerminalReporter:
def __init__(self, config, file=None):
import _pytest.config
@@ -320,8 +314,8 @@ class TerminalReporter(object):
self._tw.write(content, **markup)
def write_line(self, line, **markup):
- if not isinstance(line, six.text_type):
- line = six.text_type(line, errors="replace")
+ if not isinstance(line, str):
+ line = str(line, errors="replace")
self.ensure_newline()
self._tw.line(line, **markup)
@@ -354,7 +348,7 @@ class TerminalReporter(object):
self._tw.line(msg, **kw)
def pytest_internalerror(self, excrepr):
- for line in six.text_type(excrepr).split("\n"):
+ for line in str(excrepr).split("\n"):
self.write_line("INTERNALERROR> " + line)
return 1
@@ -374,7 +368,7 @@ class TerminalReporter(object):
def pytest_plugin_registered(self, plugin):
if self.config.option.traceconfig:
- msg = "PLUGIN registered: %s" % (plugin,)
+ msg = "PLUGIN registered: {}".format(plugin)
# XXX this event may happen during setup/teardown time
# which unfortunately captures our output here
# which garbles our output if we use self.write_line
@@ -561,14 +555,12 @@ class TerminalReporter(object):
return
self.write_sep("=", "test session starts", bold=True)
verinfo = platform.python_version()
- msg = "platform %s -- Python %s" % (sys.platform, verinfo)
+ msg = "platform {} -- Python {}".format(sys.platform, verinfo)
if hasattr(sys, "pypy_version_info"):
verinfo = ".".join(map(str, sys.pypy_version_info[:3]))
- msg += "[pypy-%s-%s]" % (verinfo, sys.pypy_version_info[3])
- msg += ", pytest-%s, py-%s, pluggy-%s" % (
- pytest.__version__,
- py.__version__,
- pluggy.__version__,
+ msg += "[pypy-{}-{}]".format(verinfo, sys.pypy_version_info[3])
+ msg += ", pytest-{}, py-{}, pluggy-{}".format(
+ pytest.__version__, py.__version__, pluggy.__version__
)
if (
self.verbosity > 0
@@ -650,11 +642,11 @@ class TerminalReporter(object):
if col.name == "()": # Skip Instances.
continue
indent = (len(stack) - 1) * " "
- self._tw.line("%s%s" % (indent, col))
+ self._tw.line("{}{}".format(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()))
+ self._tw.line("{}{}".format(indent + " ", line.strip()))
@pytest.hookimpl(hookwrapper=True)
def pytest_sessionfinish(self, exitstatus):
@@ -854,7 +846,7 @@ class TerminalReporter(object):
if rep.when == "collect":
msg = "ERROR collecting " + msg
else:
- msg = "ERROR at %s of %s" % (rep.when, msg)
+ msg = "ERROR at {} of {}".format(rep.when, msg)
self.write_sep("_", msg, red=True, bold=True)
self._outrep_summary(rep)
@@ -874,7 +866,7 @@ class TerminalReporter(object):
def summary_stats(self):
session_duration = time.time() - self._sessionstarttime
(line, color) = build_summary_stats_line(self.stats)
- msg = "%s in %.2f seconds" % (line, session_duration)
+ msg = "{} in {:.2f} seconds".format(line, session_duration)
markup = {color: True, "bold": True}
if self.verbosity >= 0:
@@ -901,7 +893,7 @@ class TerminalReporter(object):
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))
+ lines.append("{} {}".format(verbose_word, pos))
reason = rep.wasxfail
if reason:
lines.append(" " + str(reason))
@@ -912,7 +904,7 @@ class TerminalReporter(object):
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))
+ lines.append("{} {} {}".format(verbose_word, pos, reason))
def show_skipped(lines):
skipped = self.stats.get("skipped", [])
@@ -966,7 +958,7 @@ def _get_line_with_reprcrash_message(config, rep, termwidth):
verbose_word = rep._get_verbose_word(config)
pos = _get_pos(config, rep)
- line = "%s %s" % (verbose_word, pos)
+ line = "{} {}".format(verbose_word, pos)
len_line = wcswidth(line)
ellipsis, len_ellipsis = "...", 3
if len_line > termwidth - len_ellipsis:
@@ -992,21 +984,6 @@ def _get_line_with_reprcrash_message(config, rep, termwidth):
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
diff --git a/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py
index a8a703771..f2c4d905c 100644
--- a/src/_pytest/tmpdir.py
+++ b/src/_pytest/tmpdir.py
@@ -1,9 +1,4 @@
-# -*- coding: utf-8 -*-
""" support for providing temporary directories to test functions. """
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import os
import re
import tempfile
@@ -11,7 +6,6 @@ import warnings
import attr
import py
-import six
import pytest
from .pathlib import ensure_reset_dir
@@ -23,7 +17,7 @@ from _pytest.monkeypatch import MonkeyPatch
@attr.s
-class TempPathFactory(object):
+class TempPathFactory:
"""Factory for temporary directories under the common base temp directory.
The base directory can be configured using the ``--basetemp`` option."""
@@ -32,9 +26,7 @@ class TempPathFactory(object):
# using os.path.abspath() to get absolute path instead of resolve() as it
# does not work the same in all platforms (see #4427)
# Path.absolute() exists, but it is not public (see https://bugs.python.org/issue25012)
- converter=attr.converters.optional(
- lambda p: Path(os.path.abspath(six.text_type(p)))
- )
+ converter=attr.converters.optional(lambda p: Path(os.path.abspath(str(p))))
)
_trace = attr.ib()
_basetemp = attr.ib(default=None)
@@ -85,7 +77,7 @@ class TempPathFactory(object):
@attr.s
-class TempdirFactory(object):
+class TempdirFactory:
"""
backward comptibility wrapper that implements
:class:``py.path.local`` for :class:``TempPathFactory``
diff --git a/src/_pytest/unittest.py b/src/_pytest/unittest.py
index e2e7efdc5..da45e312f 100644
--- a/src/_pytest/unittest.py
+++ b/src/_pytest/unittest.py
@@ -1,9 +1,4 @@
-# -*- coding: utf-8 -*-
""" discovery and running of std-library "unittest" style tests. """
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import sys
import traceback
@@ -113,23 +108,9 @@ class TestCaseFunction(Function):
def setup(self):
self._testcase = self.parent.obj(self.name)
- self._fix_unittest_skip_decorator()
if hasattr(self, "_request"):
self._request._fillfixtures()
- def _fix_unittest_skip_decorator(self):
- """
- The @unittest.skip decorator calls functools.wraps(self._testcase)
- The call to functools.wraps() fails unless self._testcase
- has a __name__ attribute. This is usually automatically supplied
- if the test is a function or method, but we need to add manually
- here.
-
- See issue #1169
- """
- if sys.version_info[0] == 2:
- setattr(self._testcase, "__name__", self.name)
-
def teardown(self):
self._testcase = None
@@ -208,12 +189,7 @@ class TestCaseFunction(Function):
skip_why = getattr(
self._testcase.__class__, "__unittest_skip_why__", ""
) or getattr(testMethod, "__unittest_skip_why__", "")
- try: # PY3, unittest2 on PY2
- self._testcase._addSkip(self, self._testcase, skip_why)
- except TypeError: # PY2
- if sys.version_info[0] != 2:
- raise
- self._testcase._addSkip(self, skip_why)
+ self._testcase._addSkip(self, self._testcase, skip_why)
return True
return False
diff --git a/src/_pytest/warning_types.py b/src/_pytest/warning_types.py
index 861010a12..d7d37b4bb 100644
--- a/src/_pytest/warning_types.py
+++ b/src/_pytest/warning_types.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
import attr
@@ -95,7 +94,7 @@ class RemovedInPytest4Warning(PytestDeprecationWarning):
@attr.s
-class UnformattedWarning(object):
+class UnformattedWarning:
"""Used to hold warnings that need to format their message at runtime, as opposed to a direct message.
Using this class avoids to keep all the warning types and messages in this module, avoiding misuse.
diff --git a/src/_pytest/warnings.py b/src/_pytest/warnings.py
index a3debae46..2f1086225 100644
--- a/src/_pytest/warnings.py
+++ b/src/_pytest/warnings.py
@@ -1,14 +1,8 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import sys
import warnings
from contextlib import contextmanager
import pytest
-from _pytest import compat
SHOW_PYTEST_WARNINGS_ARG = "-Walways::pytest.RemovedInPytest4Warning"
@@ -19,7 +13,7 @@ def _setoption(wmod, arg):
"""
parts = arg.split(":")
if len(parts) > 5:
- raise wmod._OptionError("too many fields (max 5): %r" % (arg,))
+ raise wmod._OptionError("too many fields (max 5): {!r}".format(arg))
while len(parts) < 5:
parts.append("")
action, message, category, module, lineno = [s.strip() for s in parts]
@@ -31,7 +25,7 @@ def _setoption(wmod, arg):
if lineno < 0:
raise ValueError
except (ValueError, OverflowError):
- raise wmod._OptionError("invalid lineno %r" % (lineno,))
+ raise wmod._OptionError("invalid lineno {!r}".format(lineno))
else:
lineno = 0
wmod.filterwarnings(action, message, category, module, lineno)
@@ -104,22 +98,8 @@ def catch_warnings_for_item(config, ihook, when, item):
def warning_record_to_str(warning_message):
- """Convert a warnings.WarningMessage to a string.
-
- This takes lot of unicode shenaningans into account for Python 2.
- When Python 2 support is dropped this function can be greatly simplified.
- """
+ """Convert a warnings.WarningMessage to a string."""
warn_msg = warning_message.message
- unicode_warning = False
- if compat._PY2 and any(isinstance(m, compat.UNICODE_TYPES) for m in warn_msg.args):
- new_args = []
- for m in warn_msg.args:
- new_args.append(
- compat.ascii_escaped(m) if isinstance(m, compat.UNICODE_TYPES) else m
- )
- unicode_warning = list(warn_msg.args) != new_args
- warn_msg.args = new_args
-
msg = warnings.formatwarning(
warn_msg,
warning_message.category,
@@ -127,12 +107,6 @@ def warning_record_to_str(warning_message):
warning_message.lineno,
warning_message.line,
)
- if unicode_warning:
- warnings.warn(
- "Warning is using unicode non convertible to ascii, "
- "converting to a safe representation:\n {!r}".format(compat.safe_str(msg)),
- UnicodeWarning,
- )
return msg
diff --git a/src/pytest.py b/src/pytest.py
index ccc77b476..a6376843d 100644
--- a/src/pytest.py
+++ b/src/pytest.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
# PYTHON_ARGCOMPLETE_OK
"""
pytest: unit and functional testing with Python.