Merge branch 'master' into merge-master-into-features
This commit is contained in:
@@ -4,7 +4,6 @@ from bisect import bisect_right
|
||||
import sys
|
||||
import inspect, tokenize
|
||||
import py
|
||||
from types import ModuleType
|
||||
cpy_compile = compile
|
||||
|
||||
try:
|
||||
@@ -192,14 +191,6 @@ class Source(object):
|
||||
if flag & _AST_FLAG:
|
||||
return co
|
||||
lines = [(x + "\n") for x in self.lines]
|
||||
if sys.version_info[0] >= 3:
|
||||
# XXX py3's inspect.getsourcefile() checks for a module
|
||||
# and a pep302 __loader__ ... we don't have a module
|
||||
# at code compile-time so we need to fake it here
|
||||
m = ModuleType("_pycodecompile_pseudo_module")
|
||||
py.std.inspect.modulesbyfile[filename] = None
|
||||
py.std.sys.modules[None] = m
|
||||
m.__loader__ = 1
|
||||
py.std.linecache.cache[filename] = (1, None, lines, filename)
|
||||
return co
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ def pytest_namespace():
|
||||
|
||||
|
||||
def register_assert_rewrite(*names):
|
||||
"""Register a module name to be rewritten on import.
|
||||
"""Register one or more module names to be rewritten on import.
|
||||
|
||||
This function will make sure that this module or all modules inside
|
||||
the package will get their assert statements rewritten.
|
||||
|
||||
@@ -186,16 +186,15 @@ class AssertionRewritingHook(object):
|
||||
"""
|
||||
already_imported = set(names).intersection(set(sys.modules))
|
||||
if already_imported:
|
||||
for name in names:
|
||||
for name in already_imported:
|
||||
if name not in self._rewritten_names:
|
||||
self._warn_already_imported(already_imported)
|
||||
self._warn_already_imported(name)
|
||||
self._must_rewrite.update(names)
|
||||
|
||||
def _warn_already_imported(self, names):
|
||||
def _warn_already_imported(self, name):
|
||||
self.config.warn(
|
||||
'P1',
|
||||
'Modules are already imported so can not be re-written: %s' %
|
||||
','.join(names))
|
||||
'Module already imported so can not be re-written: %s' % name)
|
||||
|
||||
def load_module(self, name):
|
||||
# If there is an existing module object named 'fullname' in
|
||||
|
||||
@@ -19,6 +19,7 @@ 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
|
||||
|
||||
@@ -26,6 +27,9 @@ _PY2 = not _PY3
|
||||
NoneType = type(None)
|
||||
NOTSET = object()
|
||||
|
||||
PY36 = sys.version_info[:2] >= (3, 6)
|
||||
MODULE_NOT_FOUND_ERROR = 'ModuleNotFoundError' if PY36 else 'ImportError'
|
||||
|
||||
if hasattr(inspect, 'signature'):
|
||||
def _format_args(func):
|
||||
return str(inspect.signature(func))
|
||||
@@ -42,11 +46,18 @@ REGEX_TYPE = type(re.compile(''))
|
||||
|
||||
|
||||
def is_generator(func):
|
||||
try:
|
||||
return _pytest._code.getrawcode(func).co_flags & 32 # generator function
|
||||
except AttributeError: # builtin functions have no bytecode
|
||||
# assume them to not be generators
|
||||
return False
|
||||
genfunc = inspect.isgeneratorfunction(func)
|
||||
return genfunc and not iscoroutinefunction(func)
|
||||
|
||||
|
||||
def iscoroutinefunction(func):
|
||||
"""Return True if func is a decorated coroutine function.
|
||||
|
||||
Note: copied and modified from Python 3.5's builtin couroutines.py to avoid import asyncio directly,
|
||||
which in turns also initializes the "logging" module as side-effect (see issue #8).
|
||||
"""
|
||||
return (getattr(func, '_is_coroutine', False) or
|
||||
(hasattr(inspect, 'iscoroutinefunction') and inspect.iscoroutinefunction(func)))
|
||||
|
||||
|
||||
def getlocation(function, curdir):
|
||||
|
||||
@@ -70,6 +70,28 @@ class UsageError(Exception):
|
||||
""" error in pytest usage or invocation"""
|
||||
|
||||
|
||||
def filename_arg(path, optname):
|
||||
""" Argparse type validator for filename arguments.
|
||||
|
||||
:path: path of filename
|
||||
:optname: name of the option
|
||||
"""
|
||||
if os.path.isdir(path):
|
||||
raise UsageError("{0} must be a filename, given: {1}".format(optname, path))
|
||||
return path
|
||||
|
||||
|
||||
def directory_arg(path, optname):
|
||||
"""Argparse type validator for directory arguments.
|
||||
|
||||
:path: path of directory
|
||||
:optname: name of the option
|
||||
"""
|
||||
if not os.path.isdir(path):
|
||||
raise UsageError("{0} must be a directory, given: {1}".format(optname, path))
|
||||
return path
|
||||
|
||||
|
||||
_preinit = []
|
||||
|
||||
default_plugins = (
|
||||
@@ -996,7 +1018,6 @@ class Config(object):
|
||||
"(are you using python -O?)\n")
|
||||
|
||||
def _preparse(self, args, addopts=True):
|
||||
import pytest
|
||||
self._initini(args)
|
||||
if addopts:
|
||||
args[:] = shlex.split(os.environ.get('PYTEST_ADDOPTS', '')) + args
|
||||
@@ -1009,9 +1030,7 @@ class Config(object):
|
||||
self.pluginmanager.consider_env()
|
||||
self.known_args_namespace = ns = self._parser.parse_known_args(args, namespace=self.option.copy())
|
||||
confcutdir = self.known_args_namespace.confcutdir
|
||||
if confcutdir and not os.path.isdir(confcutdir):
|
||||
raise pytest.UsageError('--confcutdir must be a directory, given: {0}'.format(confcutdir))
|
||||
if confcutdir is None and self.inifile:
|
||||
if self.known_args_namespace.confcutdir is None and self.inifile:
|
||||
confcutdir = py.path.local(self.inifile).dirname
|
||||
self.known_args_namespace.confcutdir = confcutdir
|
||||
try:
|
||||
@@ -1130,7 +1149,10 @@ class Config(object):
|
||||
if self.getoption("override_ini", None):
|
||||
for ini_config_list in self.option.override_ini:
|
||||
for ini_config in ini_config_list:
|
||||
(key, user_ini_value) = ini_config.split("=", 1)
|
||||
try:
|
||||
(key, user_ini_value) = ini_config.split("=", 1)
|
||||
except ValueError:
|
||||
raise UsageError("-o/--override-ini expects option=value style.")
|
||||
if key == name:
|
||||
value = user_ini_value
|
||||
return value
|
||||
@@ -1206,25 +1228,20 @@ def getcfg(args, warnfunc=None):
|
||||
return None, None, None
|
||||
|
||||
|
||||
def get_common_ancestor(args):
|
||||
# args are what we get after early command line parsing (usually
|
||||
# strings, but can be py.path.local objects as well)
|
||||
def get_common_ancestor(paths):
|
||||
common_ancestor = None
|
||||
for arg in args:
|
||||
if str(arg)[0] == "-":
|
||||
continue
|
||||
p = py.path.local(arg)
|
||||
if not p.exists():
|
||||
for path in paths:
|
||||
if not path.exists():
|
||||
continue
|
||||
if common_ancestor is None:
|
||||
common_ancestor = p
|
||||
common_ancestor = path
|
||||
else:
|
||||
if p.relto(common_ancestor) or p == common_ancestor:
|
||||
if path.relto(common_ancestor) or path == common_ancestor:
|
||||
continue
|
||||
elif common_ancestor.relto(p):
|
||||
common_ancestor = p
|
||||
elif common_ancestor.relto(path):
|
||||
common_ancestor = path
|
||||
else:
|
||||
shared = p.common(common_ancestor)
|
||||
shared = path.common(common_ancestor)
|
||||
if shared is not None:
|
||||
common_ancestor = shared
|
||||
if common_ancestor is None:
|
||||
@@ -1235,9 +1252,29 @@ def get_common_ancestor(args):
|
||||
|
||||
|
||||
def get_dirs_from_args(args):
|
||||
return [d for d in (py.path.local(x) for x in args
|
||||
if not str(x).startswith("-"))
|
||||
if d.exists()]
|
||||
def is_option(x):
|
||||
return str(x).startswith('-')
|
||||
|
||||
def get_file_part_from_node_id(x):
|
||||
return str(x).split('::')[0]
|
||||
|
||||
def get_dir_from_path(path):
|
||||
if path.isdir():
|
||||
return path
|
||||
return py.path.local(path.dirname)
|
||||
|
||||
# These look like paths but may not exist
|
||||
possible_paths = (
|
||||
py.path.local(get_file_part_from_node_id(arg))
|
||||
for arg in args
|
||||
if not is_option(arg)
|
||||
)
|
||||
|
||||
return [
|
||||
get_dir_from_path(path)
|
||||
for path in possible_paths
|
||||
if path.exists()
|
||||
]
|
||||
|
||||
|
||||
def determine_setup(inifile, args, warnfunc=None):
|
||||
|
||||
@@ -23,7 +23,7 @@ def pytest_addoption(parser):
|
||||
group._addoption(
|
||||
'-o', '--override-ini', nargs='*', dest="override_ini",
|
||||
action="append",
|
||||
help="override config option, e.g. `-o xfail_strict=True`.")
|
||||
help="override config option with option=value style, e.g. `-o xfail_strict=True`.")
|
||||
|
||||
|
||||
@pytest.hookimpl(hookwrapper=True)
|
||||
|
||||
@@ -8,12 +8,14 @@ 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
|
||||
|
||||
import functools
|
||||
import py
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import pytest
|
||||
from _pytest.config import filename_arg
|
||||
|
||||
# Python 2.X and 3.X compatibility
|
||||
if sys.version_info[0] < 3:
|
||||
@@ -216,6 +218,7 @@ def pytest_addoption(parser):
|
||||
action="store",
|
||||
dest="xmlpath",
|
||||
metavar="path",
|
||||
type=functools.partial(filename_arg, optname="--junitxml"),
|
||||
default=None,
|
||||
help="create junit-xml style report file at given path.")
|
||||
group.addoption(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
""" core implementation of testing process: init, session, runtest loop. """
|
||||
import functools
|
||||
import os
|
||||
import sys
|
||||
|
||||
@@ -11,6 +12,7 @@ try:
|
||||
except ImportError:
|
||||
from UserDict import DictMixin as MappingMixin
|
||||
|
||||
from _pytest.config import directory_arg
|
||||
from _pytest.runner import collect_one_node
|
||||
|
||||
tracebackcutdir = py.path.local(_pytest.__file__).dirpath()
|
||||
@@ -58,7 +60,7 @@ def pytest_addoption(parser):
|
||||
# when changing this to --conf-cut-dir, config.py Conftest.setinitial
|
||||
# needs upgrading as well
|
||||
group.addoption('--confcutdir', dest="confcutdir", default=None,
|
||||
metavar="dir",
|
||||
metavar="dir", type=functools.partial(directory_arg, optname="--confcutdir"),
|
||||
help="only load conftest.py's relative to specified dir.")
|
||||
group.addoption('--noconftest', action="store_true",
|
||||
dest="noconftest", default=False,
|
||||
@@ -533,7 +535,6 @@ class Session(FSCollector):
|
||||
def __init__(self, config):
|
||||
FSCollector.__init__(self, config.rootdir, parent=None,
|
||||
config=config, session=self)
|
||||
self._fs2hookproxy = {}
|
||||
self.testsfailed = 0
|
||||
self.testscollected = 0
|
||||
self.shouldstop = False
|
||||
@@ -564,23 +565,18 @@ class Session(FSCollector):
|
||||
return path in self._initialpaths
|
||||
|
||||
def gethookproxy(self, fspath):
|
||||
try:
|
||||
return self._fs2hookproxy[fspath]
|
||||
except KeyError:
|
||||
# check if we have the common case of running
|
||||
# hooks with all conftest.py filesall conftest.py
|
||||
pm = self.config.pluginmanager
|
||||
my_conftestmodules = pm._getconftestmodules(fspath)
|
||||
remove_mods = pm._conftest_plugins.difference(my_conftestmodules)
|
||||
if remove_mods:
|
||||
# one or more conftests are not in use at this fspath
|
||||
proxy = FSHookProxy(fspath, pm, remove_mods)
|
||||
else:
|
||||
# all plugis are active for this fspath
|
||||
proxy = self.config.hook
|
||||
|
||||
self._fs2hookproxy[fspath] = proxy
|
||||
return proxy
|
||||
# check if we have the common case of running
|
||||
# hooks with all conftest.py filesall conftest.py
|
||||
pm = self.config.pluginmanager
|
||||
my_conftestmodules = pm._getconftestmodules(fspath)
|
||||
remove_mods = pm._conftest_plugins.difference(my_conftestmodules)
|
||||
if remove_mods:
|
||||
# one or more conftests are not in use at this fspath
|
||||
proxy = FSHookProxy(fspath, pm, remove_mods)
|
||||
else:
|
||||
# all plugis are active for this fspath
|
||||
proxy = self.config.hook
|
||||
return proxy
|
||||
|
||||
def perform_collect(self, args=None, genitems=True):
|
||||
hook = self.config.hook
|
||||
|
||||
@@ -481,6 +481,7 @@ class Testdir:
|
||||
ret = None
|
||||
for name, value in items:
|
||||
p = self.tmpdir.join(name).new(ext=ext)
|
||||
p.dirpath().ensure_dir()
|
||||
source = Source(value)
|
||||
|
||||
def my_totext(s, encoding="utf-8"):
|
||||
|
||||
@@ -1431,16 +1431,10 @@ class ApproxNonIterable(object):
|
||||
except ValueError:
|
||||
vetted_tolerance = '???'
|
||||
|
||||
plus_minus = u'{0} \u00b1 {1}'.format(self.expected, vetted_tolerance)
|
||||
|
||||
# In python2, __repr__() must return a string (i.e. not a unicode
|
||||
# object). In python3, __repr__() must return a unicode object
|
||||
# (although now strings are unicode objects and bytes are what
|
||||
# strings were).
|
||||
if sys.version_info[0] == 2:
|
||||
return plus_minus.encode('utf-8')
|
||||
return '{0} +- {1}'.format(self.expected, vetted_tolerance)
|
||||
else:
|
||||
return plus_minus
|
||||
return u'{0} \u00b1 {1}'.format(self.expected, vetted_tolerance)
|
||||
|
||||
def __eq__(self, actual):
|
||||
# Short-circuit exact equality.
|
||||
|
||||
@@ -218,4 +218,7 @@ class WarningsChecker(WarningsRecorder):
|
||||
if self.expected_warning is not None:
|
||||
if not any(r.category in self.expected_warning for r in self):
|
||||
__tracebackhide__ = True
|
||||
pytest.fail("DID NOT WARN")
|
||||
pytest.fail("DID NOT WARN. No warnings of type {0} was emitted. "
|
||||
"The list of emitted warnings is: {1}.".format(
|
||||
self.expected_warning,
|
||||
[each.message for each in self]))
|
||||
|
||||
@@ -5,9 +5,9 @@ import sys
|
||||
def pytest_addoption(parser):
|
||||
group = parser.getgroup("debugconfig")
|
||||
group.addoption('--setuponly', '--setup-only', action="store_true",
|
||||
help="only setup fixtures, don't execute the tests.")
|
||||
help="only setup fixtures, do not execute tests.")
|
||||
group.addoption('--setupshow', '--setup-show', action="store_true",
|
||||
help="show setup fixtures while executing the tests.")
|
||||
help="show setup of fixtures while executing tests.")
|
||||
|
||||
|
||||
@pytest.hookimpl(hookwrapper=True)
|
||||
|
||||
Reference in New Issue
Block a user