Merge remote-tracking branch 'upstream/master' into release-3.10.0

This commit is contained in:
Bruno Oliveira
2018-11-03 13:42:20 +00:00
33 changed files with 315 additions and 41 deletions

View File

@@ -8,14 +8,13 @@ import linecache
import sys
import textwrap
import tokenize
import warnings
from ast import PyCF_ONLY_AST as _AST_FLAG
from bisect import bisect_right
import py
import six
cpy_compile = compile
class Source(object):
""" an immutable object holding a source code fragment,
@@ -161,7 +160,7 @@ class Source(object):
filename = base + "%r %s:%d>" % (filename, fn, lineno)
source = "\n".join(self.lines) + "\n"
try:
co = cpy_compile(source, filename, mode, flag)
co = compile(source, filename, mode, flag)
except SyntaxError:
ex = sys.exc_info()[1]
# re-represent syntax errors from parsing python strings
@@ -195,7 +194,7 @@ def compile_(source, filename=None, mode="exec", flags=0, dont_inherit=0):
"""
if isinstance(source, ast.AST):
# XXX should Source support having AST?
return cpy_compile(source, filename, mode, flags, dont_inherit)
return compile(source, filename, mode, flags, dont_inherit)
_genframe = sys._getframe(1) # the caller
s = Source(source)
co = s.compile(filename, mode, flags, _genframe=_genframe)
@@ -290,7 +289,11 @@ def get_statement_startend2(lineno, node):
def getstatementrange_ast(lineno, source, assertion=False, astnode=None):
if astnode is None:
content = str(source)
astnode = compile(content, "source", "exec", 1024) # 1024 for AST
# See #4260:
# don't produce duplicate warnings when compiling source to find ast
with warnings.catch_warnings():
warnings.simplefilter("ignore")
astnode = compile(content, "source", "exec", _AST_FLAG)
start, end = get_statement_startend2(lineno, astnode)
# we need to correct the end:

View File

@@ -127,7 +127,7 @@ class CaptureManager(object):
def read_global_capture(self):
return self._global_capturing.readouterr()
# Fixture Control (its just forwarding, think about removing this later)
# Fixture Control (it's just forwarding, think about removing this later)
def activate_fixture(self, item):
"""If the current item is using ``capsys`` or ``capfd``, activate them so they take precedence over

View File

@@ -334,6 +334,14 @@ def safe_getattr(object, name, default):
return default
def safe_isclass(obj):
"""Ignore any exception via isinstance on Python 3."""
try:
return isclass(obj)
except Exception:
return False
def _is_unittest_unexpected_success_a_failure():
"""Return if the test suite should fail if an @expectedFailure unittest test PASSES.

View File

@@ -152,7 +152,7 @@ class ArgumentError(Exception):
class Argument(object):
"""class that mimics the necessary behaviour of optparse.Option
its currently a least effort implementation
it's currently a least effort implementation
and ignoring choices and integer prefixes
https://docs.python.org/3/library/optparse.html#optparse-standard-option-types
"""

View File

@@ -619,7 +619,7 @@ class FixtureRequest(FuncargnamesCompatAttr):
subrequest._check_scope(argname, self.scope, scope)
# clear sys.exc_info before invoking the fixture (python bug?)
# if its not explicitly cleared it will leak into the call
# if it's not explicitly cleared it will leak into the call
exc_clear()
try:
# call the fixture function
@@ -1197,6 +1197,7 @@ class FixtureManager(object):
nodeid = p.dirpath().relto(self.config.rootdir)
if p.sep != nodes.SEP:
nodeid = nodeid.replace(p.sep, nodes.SEP)
self.parsefactories(plugin, nodeid)
def _getautousenames(self, nodeid):
@@ -1301,11 +1302,18 @@ class FixtureManager(object):
nodeid = node_or_obj.nodeid
if holderobj in self._holderobjseen:
return
from _pytest.nodes import _CompatProperty
self._holderobjseen.add(holderobj)
autousenames = []
for name in dir(holderobj):
# The attribute can be an arbitrary descriptor, so the attribute
# access below can raise. safe_getatt() ignores such exceptions.
maybe_property = safe_getattr(type(holderobj), name, None)
if isinstance(maybe_property, _CompatProperty):
# deprecated
continue
obj = safe_getattr(holderobj, name, None)
marker = getfixturemarker(obj)
# fixture functions have a pytest_funcarg__ prefix (pre-2.3 style)

View File

@@ -41,10 +41,10 @@ def pytest_namespace():
Plugins whose users depend on the current namespace functionality should prepare to migrate to a
namespace they actually own.
To support the migration its suggested to trigger ``DeprecationWarnings`` for objects they put into the
To support the migration it's suggested to trigger ``DeprecationWarnings`` for objects they put into the
pytest namespace.
An stopgap measure to avoid the warning is to monkeypatch the ``pytest`` module, but just as the
A stopgap measure to avoid the warning is to monkeypatch the ``pytest`` module, but just as the
``pytest_namespace`` hook this should be seen as a temporary measure to be removed in future versions after
an appropriate transition period.
"""

View File

@@ -559,7 +559,15 @@ class Session(nodes.FSCollector):
col = root._collectfile(argpath)
if col:
self._node_cache[argpath] = col
for y in self.matchnodes(col, names):
m = self.matchnodes(col, names)
# If __init__.py was the only file requested, then the matched node will be
# the corresponding Package, and the first yielded item will be the __init__
# Module itself, so just use that. If this special case isn't taken, then all
# the files in the package will be yielded.
if argpath.basename == "__init__.py":
yield next(m[0].collect())
return
for y in m:
yield y
def _collectfile(self, path):

View File

@@ -443,7 +443,7 @@ class NodeKeywords(MappingMixin):
@attr.s(cmp=False, hash=False)
class NodeMarkers(object):
"""
internal strucutre for storing marks belongong to a node
internal structure for storing marks belonging to a node
..warning::

View File

@@ -230,10 +230,12 @@ class MonkeyPatch(object):
if not isinstance(value, str):
warnings.warn(
pytest.PytestWarning(
"Environment variable value {!r} should be str, converted to str implicitly".format(
value
"Value of environment variable {name} type should be str, but got "
"{value!r} (type: {type}); converted to str implicitly".format(
name=name, value=value, type=type(value).__name__
)
)
),
stacklevel=2,
)
value = str(value)
if prepend and name in os.environ:

View File

@@ -35,7 +35,7 @@ get_lock_path = operator.methodcaller("joinpath", ".lock")
def ensure_reset_dir(path):
"""
ensures the given path is a empty directory
ensures the given path is an empty directory
"""
if path.exists():
rmtree(path, force=True)
@@ -98,8 +98,8 @@ else:
def _force_symlink(root, target, link_to):
"""helper to create the current symlink
its full of race conditions that are reasonably ok to ignore
for the contex of best effort linking to the latest testrun
it's full of race conditions that are reasonably ok to ignore
for the context of best effort linking to the latest testrun
the presumption being thatin case of much parallelism
the inaccuracy is going to be acceptable
@@ -116,7 +116,7 @@ def _force_symlink(root, target, link_to):
def make_numbered_dir(root, prefix):
"""create a directory with a increased number as suffix for the given 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)
@@ -156,7 +156,7 @@ def create_cleanup_lock(p):
os.write(fd, spid)
os.close(fd)
if not lock_path.is_file():
raise EnvironmentError("lock path got renamed after sucessfull creation")
raise EnvironmentError("lock path got renamed after successful creation")
return lock_path
@@ -178,19 +178,29 @@ def register_cleanup_lock_removal(lock_path, register=atexit.register):
def maybe_delete_a_numbered_dir(path):
"""removes a numbered directory if its lock can be obtained"""
"""removes a numbered directory if its lock can be obtained and it does not seem to be in use"""
lock_path = None
try:
create_cleanup_lock(path)
lock_path = create_cleanup_lock(path)
parent = path.parent
garbage = parent.joinpath("garbage-{}".format(uuid.uuid4()))
path.rename(garbage)
rmtree(garbage, force=True)
except (OSError, EnvironmentError):
# known races:
# * other process did a cleanup at the same time
# * deletable folder was found
# * process cwd (Windows)
return
parent = path.parent
garbage = parent.joinpath("garbage-{}".format(uuid.uuid4()))
path.rename(garbage)
rmtree(garbage, force=True)
finally:
# if we created the lock, ensure we remove it even if we failed
# to properly remove the numbered dir
if lock_path is not None:
try:
lock_path.unlink()
except (OSError, IOError):
pass
def ensure_deletable(path, consider_lock_dead_if_created_before):
@@ -213,7 +223,7 @@ def ensure_deletable(path, consider_lock_dead_if_created_before):
def try_cleanup(path, consider_lock_dead_if_created_before):
"""tries to cleanup a folder if we can ensure its deletable"""
"""tries to cleanup a folder if we can ensure it's deletable"""
if ensure_deletable(path, consider_lock_dead_if_created_before):
maybe_delete_a_numbered_dir(path)

View File

@@ -33,6 +33,7 @@ from _pytest.compat import NoneType
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
@@ -196,7 +197,7 @@ def pytest_pycollect_makeitem(collector, name, obj):
if res is not None:
return
# nothing was collected elsewhere, let's do it here
if isclass(obj):
if safe_isclass(obj):
if collector.istestclass(obj, name):
Class = collector._getcustomclass("Class")
outcome.force_result(Class(name, parent=collector))
@@ -652,7 +653,7 @@ class Instance(PyCollector):
_ALLOW_MARKERS = False # hack, destroy later
# instances share the object with their parents in a way
# that duplicates markers instances if not taken out
# can be removed at node strucutre reorganization time
# can be removed at node structure reorganization time
def _getobj(self):
return self.parent.obj()
@@ -1334,7 +1335,7 @@ class Function(FunctionMixin, nodes.Item, fixtures.FuncargnamesCompatAttr):
"""
_genid = None
# disable since functions handle it themselfes
# disable since functions handle it themselves
_ALLOW_MARKERS = False
def __init__(