Small cleanups on _pytest.compat (#5451)

Small cleanups on _pytest.compat
This commit is contained in:
Bruno Oliveira 2019-06-16 10:42:07 -03:00 committed by GitHub
commit 689ce112e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 29 additions and 58 deletions

View File

@ -244,17 +244,9 @@ def _compare_eq_iterable(left, right, verbose=0):
# dynamic import to speedup pytest
import difflib
try:
left_formatting = pprint.pformat(left).splitlines()
right_formatting = pprint.pformat(right).splitlines()
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 = ["Full diff (fallback to calling repr on each item):"]
left_formatting = pprint.pformat(left).splitlines()
right_formatting = pprint.pformat(right).splitlines()
explanation = ["Full diff:"]
explanation.extend(
line.strip() for line in difflib.ndiff(left_formatting, right_formatting)
)

View File

@ -10,6 +10,7 @@ from contextlib import contextmanager
from inspect import Parameter
from inspect import signature
import attr
import py
import _pytest
@ -29,10 +30,6 @@ def _format_args(func):
return str(signature(func))
isfunction = inspect.isfunction
isclass = inspect.isclass
# used to work around a python2 exception info leak
exc_clear = getattr(sys, "exc_clear", lambda: None)
# The type of re.compile objects is not exposed in Python.
REGEX_TYPE = type(re.compile(""))
@ -129,11 +126,15 @@ def getfuncargnames(function, is_method=False, cls=None):
return arg_names
@contextmanager
def dummy_context_manager():
"""Context manager that does nothing, useful in situations where you might need an actual context manager or not
depending on some condition. Using this allow to keep the same code"""
yield
if sys.version_info < (3, 7):
@contextmanager
def nullcontext():
yield
else:
from contextlib import nullcontext # noqa
def get_default_arg_names(function):
@ -191,6 +192,7 @@ def ascii_escaped(val):
return _translate_non_printable(ret)
@attr.s
class _PytestWrapper:
"""Dummy wrapper around a function object for internal use only.
@ -199,8 +201,7 @@ class _PytestWrapper:
to issue warnings when the fixture function is called directly.
"""
def __init__(self, obj):
self.obj = obj
obj = attr.ib()
def get_real_func(obj):
@ -280,7 +281,7 @@ def safe_getattr(object, name, default):
def safe_isclass(obj):
"""Ignore any exception via isinstance on Python 3."""
try:
return isclass(obj)
return inspect.isclass(obj)
except Exception:
return False
@ -304,8 +305,8 @@ def _setup_collect_fakemodule():
pytest.collect = ModuleType("pytest.collect")
pytest.collect.__all__ = [] # used for setns
for attr in COLLECT_FAKEMODULE_ATTRIBUTES:
setattr(pytest.collect, attr, getattr(pytest, attr))
for attr_name in COLLECT_FAKEMODULE_ATTRIBUTES:
setattr(pytest.collect, attr_name, getattr(pytest, attr_name))
class CaptureIO(io.TextIOWrapper):

View File

@ -16,7 +16,6 @@ from _pytest._code.code import FormattedExcinfo
from _pytest._code.code import TerminalRepr
from _pytest.compat import _format_args
from _pytest.compat import _PytestWrapper
from _pytest.compat import exc_clear
from _pytest.compat import FuncargnamesCompatAttr
from _pytest.compat import get_real_func
from _pytest.compat import get_real_method
@ -25,7 +24,6 @@ from _pytest.compat import getfuncargnames
from _pytest.compat import getimfunc
from _pytest.compat import getlocation
from _pytest.compat import is_generator
from _pytest.compat import isclass
from _pytest.compat import NOTSET
from _pytest.compat import safe_getattr
from _pytest.deprecated import FIXTURE_FUNCTION_CALL
@ -572,10 +570,6 @@ class FixtureRequest(FuncargnamesCompatAttr):
# check if a higher-level scoped fixture accesses a lower level one
subrequest._check_scope(argname, self.scope, scope)
# clear sys.exc_info before invoking the fixture (python bug?)
# if it's not explicitly cleared it will leak into the call
exc_clear()
try:
# call the fixture function
fixturedef.execute(request=subrequest)
@ -970,7 +964,7 @@ class FixtureFunctionMarker:
name = attr.ib(default=None)
def __call__(self, function):
if isclass(function):
if inspect.isclass(function):
raise ValueError("class fixtures not supported (maybe in the future)")
if getattr(function, "_pytestfixturefunction", False):

View File

@ -6,7 +6,7 @@ from contextlib import contextmanager
import py
import pytest
from _pytest.compat import dummy_context_manager
from _pytest.compat import nullcontext
from _pytest.config import create_terminal_writer
from _pytest.pathlib import Path
@ -436,7 +436,7 @@ class LoggingPlugin:
self.log_cli_handler = None
self.live_logs_context = lambda: dummy_context_manager()
self.live_logs_context = lambda: nullcontext()
# Note that the lambda for the live_logs_context is needed because
# live_logs_context can otherwise not be entered multiple times due
# to limitations of contextlib.contextmanager.
@ -676,7 +676,7 @@ class _LiveLoggingStreamHandler(logging.StreamHandler):
ctx_manager = (
self.capture_manager.global_and_fixture_disabled()
if self.capture_manager
else dummy_context_manager()
else nullcontext()
)
with ctx_manager:
if not self._first_record_emitted:

View File

@ -23,8 +23,6 @@ from _pytest.compat import getfslineno
from _pytest.compat import getimfunc
from _pytest.compat import getlocation
from _pytest.compat import is_generator
from _pytest.compat import isclass
from _pytest.compat import isfunction
from _pytest.compat import NOTSET
from _pytest.compat import REGEX_TYPE
from _pytest.compat import safe_getattr
@ -207,7 +205,7 @@ def pytest_pycollect_makeitem(collector, name, obj):
# We need to try and unwrap the function if it's a functools.partial
# or a funtools.wrapped.
# We musn't if it's been wrapped with mock.patch (python 2 only)
if not (isfunction(obj) or isfunction(get_real_func(obj))):
if not (inspect.isfunction(obj) or inspect.isfunction(get_real_func(obj))):
filename, lineno = getfslineno(obj)
warnings.warn_explicit(
message=PytestCollectionWarning(
@ -1172,8 +1170,6 @@ def _idval(val, argname, idx, idfn, item, config):
# See issue https://github.com/pytest-dev/pytest/issues/2169
msg = "{}: error raised while trying to determine id of parameter '{}' at position {}\n"
msg = msg.format(item.nodeid, argname, idx)
# we only append the exception type and message because on Python 2 reraise does nothing
msg += " {}: {}\n".format(type(e).__name__, e)
raise ValueError(msg) from e
elif config:
hook_id = config.hook.pytest_make_parametrize_id(
@ -1190,7 +1186,7 @@ def _idval(val, argname, idx, idfn, item, config):
return ascii_escaped(val.pattern)
elif enum is not None and isinstance(val, enum.Enum):
return str(val)
elif (isclass(val) or isfunction(val)) and hasattr(val, "__name__"):
elif (inspect.isclass(val) or inspect.isfunction(val)) and hasattr(val, "__name__"):
return val.__name__
return str(argname) + str(idx)

View File

@ -1,3 +1,4 @@
import inspect
import math
import pprint
import sys
@ -13,27 +14,12 @@ from more_itertools.more import always_iterable
import _pytest._code
from _pytest import deprecated
from _pytest.compat import isclass
from _pytest.compat import STRING_TYPES
from _pytest.outcomes import fail
BASE_TYPE = (type, STRING_TYPES)
def _cmp_raises_type_error(self, other):
"""__cmp__ implementation which raises TypeError. Used
by Approx base classes to implement only == and != and raise a
TypeError for other comparisons.
Needed in Python 2 only, Python 3 all it takes is not implementing the
other operators at all.
"""
__tracebackhide__ = True
raise TypeError(
"Comparison operators other than == and != not supported by approx objects"
)
def _non_numeric_type_error(value, at):
at_str = " at {}".format(at) if at else ""
return TypeError(
@ -658,7 +644,9 @@ def raises(expected_exception, *args, **kwargs):
"""
__tracebackhide__ = True
for exc in filterfalse(isclass, always_iterable(expected_exception, BASE_TYPE)):
for exc in filterfalse(
inspect.isclass, always_iterable(expected_exception, BASE_TYPE)
):
msg = (
"exceptions must be old-style classes or"
" derived from BaseException, not %s"

View File

@ -506,8 +506,8 @@ class TestMetafunc:
result = testdir.runpytest()
result.stdout.fnmatch_lines(
[
"*test_foo: error raised while trying to determine id of parameter 'arg' at position 0",
"*Exception: bad ids",
"*test_foo: error raised while trying to determine id of parameter 'arg' at position 0",
]
)