summaryrefslogtreecommitdiff
path: root/lib/spack/external/_pytest/config.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/spack/external/_pytest/config.py')
-rw-r--r--lib/spack/external/_pytest/config.py320
1 files changed, 197 insertions, 123 deletions
diff --git a/lib/spack/external/_pytest/config.py b/lib/spack/external/_pytest/config.py
index fe386ed0b1..19835d2c39 100644
--- a/lib/spack/external/_pytest/config.py
+++ b/lib/spack/external/_pytest/config.py
@@ -1,4 +1,5 @@
""" command line options, ini-file and conftest.py processing. """
+from __future__ import absolute_import, division, print_function
import argparse
import shlex
import traceback
@@ -7,7 +8,8 @@ import warnings
import py
# DON't import pytest here because it causes import cycle troubles
-import sys, os
+import sys
+import os
import _pytest._code
import _pytest.hookspec # the extension point definitions
import _pytest.assertion
@@ -53,15 +55,15 @@ def main(args=None, plugins=None):
return 4
else:
try:
- config.pluginmanager.check_pending()
return config.hook.pytest_cmdline_main(config=config)
finally:
config._ensure_unconfigure()
except UsageError as e:
for msg in e.args:
- sys.stderr.write("ERROR: %s\n" %(msg,))
+ sys.stderr.write("ERROR: %s\n" % (msg,))
return 4
+
class cmdline: # compatibility namespace
main = staticmethod(main)
@@ -70,6 +72,12 @@ class UsageError(Exception):
""" error in pytest usage or invocation"""
+class PrintHelp(Exception):
+ """Raised when pytest should print it's help to skip the rest of the
+ argument parsing and validation."""
+ pass
+
+
def filename_arg(path, optname):
""" Argparse type validator for filename arguments.
@@ -95,10 +103,11 @@ def directory_arg(path, optname):
_preinit = []
default_plugins = (
- "mark main terminal runner python fixtures debugging unittest capture skipping "
- "tmpdir monkeypatch recwarn pastebin helpconfig nose assertion "
- "junitxml resultlog doctest cacheprovider freeze_support "
- "setuponly setupplan").split()
+ "mark main terminal runner python fixtures debugging unittest capture skipping "
+ "tmpdir monkeypatch recwarn pastebin helpconfig nose assertion "
+ "junitxml resultlog doctest cacheprovider freeze_support "
+ "setuponly setupplan warnings").split()
+
builtin_plugins = set(default_plugins)
builtin_plugins.add("pytester")
@@ -108,6 +117,7 @@ def _preloadplugins():
assert not _preinit
_preinit.append(get_config())
+
def get_config():
if _preinit:
return _preinit.pop(0)
@@ -118,6 +128,7 @@ def get_config():
pluginmanager.import_plugin(spec)
return config
+
def get_plugin_manager():
"""
Obtain a new instance of the
@@ -129,6 +140,7 @@ def get_plugin_manager():
"""
return get_config().pluginmanager
+
def _prepareconfig(args=None, plugins=None):
warning = None
if args is None:
@@ -153,7 +165,7 @@ def _prepareconfig(args=None, plugins=None):
if warning:
config.warn('C1', warning)
return pluginmanager.hook.pytest_cmdline_parse(
- pluginmanager=pluginmanager, args=args)
+ pluginmanager=pluginmanager, args=args)
except BaseException:
config._ensure_unconfigure()
raise
@@ -161,13 +173,14 @@ def _prepareconfig(args=None, plugins=None):
class PytestPluginManager(PluginManager):
"""
- Overwrites :py:class:`pluggy.PluginManager` to add pytest-specific
+ Overwrites :py:class:`pluggy.PluginManager <_pytest.vendored_packages.pluggy.PluginManager>` to add pytest-specific
functionality:
* loading plugins from the command line, ``PYTEST_PLUGIN`` env variable and
``pytest_plugins`` global variables found in plugins being loaded;
* ``conftest.py`` loading during start-up;
"""
+
def __init__(self):
super(PytestPluginManager, self).__init__("pytest", implprefix="pytest_")
self._conftest_plugins = set()
@@ -198,7 +211,8 @@ class PytestPluginManager(PluginManager):
"""
.. deprecated:: 2.8
- Use :py:meth:`pluggy.PluginManager.add_hookspecs` instead.
+ Use :py:meth:`pluggy.PluginManager.add_hookspecs <_pytest.vendored_packages.pluggy.PluginManager.add_hookspecs>`
+ instead.
"""
warning = dict(code="I2",
fslocation=_pytest._code.getfslineno(sys._getframe(1)),
@@ -227,7 +241,7 @@ class PytestPluginManager(PluginManager):
def parse_hookspec_opts(self, module_or_class, name):
opts = super(PytestPluginManager, self).parse_hookspec_opts(
- module_or_class, name)
+ module_or_class, name)
if opts is None:
method = getattr(module_or_class, name)
if name.startswith("pytest_"):
@@ -250,7 +264,10 @@ class PytestPluginManager(PluginManager):
ret = super(PytestPluginManager, self).register(plugin, name)
if ret:
self.hook.pytest_plugin_registered.call_historic(
- kwargs=dict(plugin=plugin, manager=self))
+ kwargs=dict(plugin=plugin, manager=self))
+
+ if isinstance(plugin, types.ModuleType):
+ self.consider_module(plugin)
return ret
def getplugin(self, name):
@@ -265,11 +282,11 @@ class PytestPluginManager(PluginManager):
# XXX now that the pluginmanager exposes hookimpl(tryfirst...)
# we should remove tryfirst/trylast as markers
config.addinivalue_line("markers",
- "tryfirst: mark a hook implementation function such that the "
- "plugin machinery will try to call it first/as early as possible.")
+ "tryfirst: mark a hook implementation function such that the "
+ "plugin machinery will try to call it first/as early as possible.")
config.addinivalue_line("markers",
- "trylast: mark a hook implementation function such that the "
- "plugin machinery will try to call it last/as late as possible.")
+ "trylast: mark a hook implementation function such that the "
+ "plugin machinery will try to call it last/as late as possible.")
def _warn(self, message):
kwargs = message if isinstance(message, dict) else {
@@ -293,7 +310,7 @@ class PytestPluginManager(PluginManager):
"""
current = py.path.local()
self._confcutdir = current.join(namespace.confcutdir, abs=True) \
- if namespace.confcutdir else None
+ if namespace.confcutdir else None
self._noconftest = namespace.noconftest
testpaths = namespace.file_or_dir
foundanchor = False
@@ -304,7 +321,7 @@ class PytestPluginManager(PluginManager):
if i != -1:
path = path[:i]
anchor = current.join(path, abs=1)
- if exists(anchor): # we found some file object
+ if exists(anchor): # we found some file object
self._try_load_conftest(anchor)
foundanchor = True
if not foundanchor:
@@ -371,7 +388,7 @@ class PytestPluginManager(PluginManager):
if path and path.relto(dirpath) or path == dirpath:
assert mod not in mods
mods.append(mod)
- self.trace("loaded conftestmodule %r" %(mod))
+ self.trace("loaded conftestmodule %r" % (mod))
self.consider_conftest(mod)
return mod
@@ -381,7 +398,7 @@ class PytestPluginManager(PluginManager):
#
def consider_preparse(self, args):
- for opt1,opt2 in zip(args, args[1:]):
+ for opt1, opt2 in zip(args, args[1:]):
if opt1 == "-p":
self.consider_pluginarg(opt2)
@@ -395,38 +412,33 @@ class PytestPluginManager(PluginManager):
self.import_plugin(arg)
def consider_conftest(self, conftestmodule):
- if self.register(conftestmodule, name=conftestmodule.__file__):
- self.consider_module(conftestmodule)
+ self.register(conftestmodule, name=conftestmodule.__file__)
def consider_env(self):
self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS"))
def consider_module(self, mod):
- plugins = getattr(mod, 'pytest_plugins', [])
- if isinstance(plugins, str):
- plugins = [plugins]
- self.rewrite_hook.mark_rewrite(*plugins)
- self._import_plugin_specs(plugins)
+ self._import_plugin_specs(getattr(mod, 'pytest_plugins', []))
def _import_plugin_specs(self, spec):
- if spec:
- if isinstance(spec, str):
- spec = spec.split(",")
- for import_spec in spec:
- self.import_plugin(import_spec)
+ plugins = _get_plugin_specs_as_list(spec)
+ for import_spec in plugins:
+ self.import_plugin(import_spec)
def import_plugin(self, modname):
# most often modname refers to builtin modules, e.g. "pytester",
# "terminal" or "capture". Those plugins are registered under their
# basename for historic purposes but must be imported with the
# _pytest prefix.
- assert isinstance(modname, str)
+ assert isinstance(modname, (py.builtin.text, str)), "module name as text required, got %r" % modname
+ modname = str(modname)
if self.get_plugin(modname) is not None:
return
if modname in builtin_plugins:
importspec = "_pytest." + modname
else:
importspec = modname
+ self.rewrite_hook.mark_rewrite(importspec)
try:
__import__(importspec)
except ImportError as e:
@@ -440,11 +452,28 @@ class PytestPluginManager(PluginManager):
import pytest
if not hasattr(pytest, 'skip') or not isinstance(e, pytest.skip.Exception):
raise
- self._warn("skipped plugin %r: %s" %((modname, e.msg)))
+ self._warn("skipped plugin %r: %s" % ((modname, e.msg)))
else:
mod = sys.modules[importspec]
self.register(mod, modname)
- self.consider_module(mod)
+
+
+def _get_plugin_specs_as_list(specs):
+ """
+ Parses a list of "plugin specs" and returns a list of plugin names.
+
+ Plugin specs can be given as a list of strings separated by "," or already as a list/tuple in
+ which case it is returned as a list. Specs can also be `None` in which case an
+ empty list is returned.
+ """
+ if specs is not None:
+ if isinstance(specs, str):
+ specs = specs.split(',') if specs else []
+ if not isinstance(specs, (list, tuple)):
+ raise UsageError("Plugin specs must be a ','-separated string or a "
+ "list/tuple of strings for plugin names. Given: %r" % specs)
+ return list(specs)
+ return []
class Parser:
@@ -488,7 +517,7 @@ class Parser:
for i, grp in enumerate(self._groups):
if grp.name == after:
break
- self._groups.insert(i+1, group)
+ self._groups.insert(i + 1, group)
return group
def addoption(self, *opts, **attrs):
@@ -526,7 +555,7 @@ class Parser:
a = option.attrs()
arggroup.add_argument(*n, **a)
# bash like autocompletion for dirs (appending '/')
- optparser.add_argument(FILE_OR_DIR, nargs='*').completer=filescompleter
+ optparser.add_argument(FILE_OR_DIR, nargs='*').completer = filescompleter
return optparser
def parse_setoption(self, args, option, namespace=None):
@@ -670,7 +699,7 @@ class Argument:
if self._attrs.get('help'):
a = self._attrs['help']
a = a.replace('%default', '%(default)s')
- #a = a.replace('%prog', '%(prog)s')
+ # a = a.replace('%prog', '%(prog)s')
self._attrs['help'] = a
return self._attrs
@@ -754,7 +783,7 @@ class MyOptionParser(argparse.ArgumentParser):
extra_info = {}
self._parser = parser
argparse.ArgumentParser.__init__(self, usage=parser._usage,
- add_help=False, formatter_class=DropShorterLongHelpFormatter)
+ add_help=False, formatter_class=DropShorterLongHelpFormatter)
# extra_info is a dict of (param -> value) to display if there's
# an usage error to provide more contextual information to the user
self.extra_info = extra_info
@@ -782,9 +811,10 @@ class DropShorterLongHelpFormatter(argparse.HelpFormatter):
- shortcut if there are only two options and one of them is a short one
- cache result on action object as this is called at least 2 times
"""
+
def _format_action_invocation(self, action):
orgstr = argparse.HelpFormatter._format_action_invocation(self, action)
- if orgstr and orgstr[0] != '-': # only optional arguments
+ if orgstr and orgstr[0] != '-': # only optional arguments
return orgstr
res = getattr(action, '_formatted_action_invocation', None)
if res:
@@ -795,7 +825,7 @@ class DropShorterLongHelpFormatter(argparse.HelpFormatter):
action._formatted_action_invocation = orgstr
return orgstr
return_list = []
- option_map = getattr(action, 'map_long_option', {})
+ option_map = getattr(action, 'map_long_option', {})
if option_map is None:
option_map = {}
short_long = {}
@@ -813,7 +843,7 @@ class DropShorterLongHelpFormatter(argparse.HelpFormatter):
short_long[shortened] = xxoption
# now short_long has been filled out to the longest with dashes
# **and** we keep the right option ordering from add_argument
- for option in options: #
+ for option in options:
if len(option) == 2 or option[2] == ' ':
return_list.append(option)
if option[2:] == short_long.get(option.replace('-', '')):
@@ -822,22 +852,26 @@ class DropShorterLongHelpFormatter(argparse.HelpFormatter):
return action._formatted_action_invocation
-
def _ensure_removed_sysmodule(modname):
try:
del sys.modules[modname]
except KeyError:
pass
+
class CmdOptions(object):
""" holds cmdline options as attributes."""
+
def __init__(self, values=()):
self.__dict__.update(values)
+
def __repr__(self):
- return "<CmdOptions %r>" %(self.__dict__,)
+ return "<CmdOptions %r>" % (self.__dict__,)
+
def copy(self):
return CmdOptions(self.__dict__)
+
class Notset:
def __repr__(self):
return "<NOTSET>"
@@ -847,6 +881,18 @@ notset = Notset()
FILE_OR_DIR = 'file_or_dir'
+def _iter_rewritable_modules(package_files):
+ for fn in package_files:
+ is_simple_module = '/' not in fn and fn.endswith('.py')
+ is_package = fn.count('/') == 1 and fn.endswith('__init__.py')
+ if is_simple_module:
+ module_name, _ = os.path.splitext(fn)
+ yield module_name
+ elif is_package:
+ package_name = os.path.dirname(fn)
+ yield package_name
+
+
class Config(object):
""" access to configuration values, pluginmanager and plugin hooks. """
@@ -864,6 +910,7 @@ class Config(object):
self.trace = self.pluginmanager.trace.root.get("config")
self.hook = self.pluginmanager.hook
self._inicache = {}
+ self._override_ini = ()
self._opt2dest = {}
self._cleanup = []
self._warn = self.pluginmanager._warn
@@ -896,11 +943,11 @@ class Config(object):
fin = self._cleanup.pop()
fin()
- def warn(self, code, message, fslocation=None):
+ def warn(self, code, message, fslocation=None, nodeid=None):
""" generate a warning for this test session. """
self.hook.pytest_logwarning.call_historic(kwargs=dict(
code=code, message=message,
- fslocation=fslocation, nodeid=None))
+ fslocation=fslocation, nodeid=nodeid))
def get_terminal_writer(self):
return self.pluginmanager.get_plugin("terminalreporter")._tw
@@ -916,14 +963,14 @@ class Config(object):
else:
style = "native"
excrepr = excinfo.getrepr(funcargs=True,
- showlocals=getattr(option, 'showlocals', False),
- style=style,
- )
+ showlocals=getattr(option, 'showlocals', False),
+ style=style,
+ )
res = self.hook.pytest_internalerror(excrepr=excrepr,
excinfo=excinfo)
if not py.builtin.any(res):
for line in str(excrepr).split("\n"):
- sys.stderr.write("INTERNALERROR> %s\n" %line)
+ sys.stderr.write("INTERNALERROR> %s\n" % line)
sys.stderr.flush()
def cwd_relative_nodeid(self, nodeid):
@@ -964,8 +1011,9 @@ class Config(object):
self.invocation_dir = py.path.local()
self._parser.addini('addopts', 'extra command line options', 'args')
self._parser.addini('minversion', 'minimally required pytest version')
+ self._override_ini = ns.override_ini or ()
- def _consider_importhook(self, args, entrypoint_name):
+ def _consider_importhook(self, args):
"""Install the PEP 302 import hook if using assertion re-writing.
Needs to parse the --assert=<mode> option from the commandline
@@ -980,26 +1028,34 @@ class Config(object):
except SystemError:
mode = 'plain'
else:
- import pkg_resources
- self.pluginmanager.rewrite_hook = hook
- for entrypoint in pkg_resources.iter_entry_points('pytest11'):
- # 'RECORD' available for plugins installed normally (pip install)
- # 'SOURCES.txt' available for plugins installed in dev mode (pip install -e)
- # for installed plugins 'SOURCES.txt' returns an empty list, and vice-versa
- # so it shouldn't be an issue
- for metadata in ('RECORD', 'SOURCES.txt'):
- for entry in entrypoint.dist._get_metadata(metadata):
- fn = entry.split(',')[0]
- is_simple_module = os.sep not in fn and fn.endswith('.py')
- is_package = fn.count(os.sep) == 1 and fn.endswith('__init__.py')
- if is_simple_module:
- module_name, ext = os.path.splitext(fn)
- hook.mark_rewrite(module_name)
- elif is_package:
- package_name = os.path.dirname(fn)
- hook.mark_rewrite(package_name)
+ self._mark_plugins_for_rewrite(hook)
self._warn_about_missing_assertion(mode)
+ def _mark_plugins_for_rewrite(self, hook):
+ """
+ Given an importhook, mark for rewrite any top-level
+ modules or packages in the distribution package for
+ all pytest plugins.
+ """
+ import pkg_resources
+ self.pluginmanager.rewrite_hook = hook
+
+ # 'RECORD' available for plugins installed normally (pip install)
+ # 'SOURCES.txt' available for plugins installed in dev mode (pip install -e)
+ # for installed plugins 'SOURCES.txt' returns an empty list, and vice-versa
+ # so it shouldn't be an issue
+ metadata_files = 'RECORD', 'SOURCES.txt'
+
+ package_files = (
+ entry.split(',')[0]
+ for entrypoint in pkg_resources.iter_entry_points('pytest11')
+ for metadata in metadata_files
+ for entry in entrypoint.dist._get_metadata(metadata)
+ )
+
+ for name in _iter_rewritable_modules(package_files):
+ hook.mark_rewrite(name)
+
def _warn_about_missing_assertion(self, mode):
try:
assert False
@@ -1023,19 +1079,17 @@ class Config(object):
args[:] = shlex.split(os.environ.get('PYTEST_ADDOPTS', '')) + args
args[:] = self.getini("addopts") + args
self._checkversion()
- entrypoint_name = 'pytest11'
- self._consider_importhook(args, entrypoint_name)
+ self._consider_importhook(args)
self.pluginmanager.consider_preparse(args)
- self.pluginmanager.load_setuptools_entrypoints(entrypoint_name)
+ self.pluginmanager.load_setuptools_entrypoints('pytest11')
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 self.known_args_namespace.confcutdir is None and self.inifile:
confcutdir = py.path.local(self.inifile).dirname
self.known_args_namespace.confcutdir = confcutdir
try:
self.hook.pytest_load_initial_conftests(early_config=self,
- args=args, parser=self._parser)
+ args=args, parser=self._parser)
except ConftestImportFailure:
e = sys.exc_info()[1]
if ns.help or ns.version:
@@ -1053,28 +1107,32 @@ class Config(object):
myver = pytest.__version__.split(".")
if myver < ver:
raise pytest.UsageError(
- "%s:%d: requires pytest-%s, actual pytest-%s'" %(
- self.inicfg.config.path, self.inicfg.lineof('minversion'),
- minver, pytest.__version__))
+ "%s:%d: requires pytest-%s, actual pytest-%s'" % (
+ self.inicfg.config.path, self.inicfg.lineof('minversion'),
+ minver, pytest.__version__))
def parse(self, args, addopts=True):
# parse given cmdline arguments into this config object.
assert not hasattr(self, 'args'), (
- "can only parse cmdline args at most once per Config object")
+ "can only parse cmdline args at most once per Config object")
self._origargs = args
self.hook.pytest_addhooks.call_historic(
- kwargs=dict(pluginmanager=self.pluginmanager))
+ kwargs=dict(pluginmanager=self.pluginmanager))
self._preparse(args, addopts=addopts)
# XXX deprecated hook:
self.hook.pytest_cmdline_preparse(config=self, args=args)
- args = self._parser.parse_setoption(args, self.option, namespace=self.option)
- if not args:
- cwd = os.getcwd()
- if cwd == self.rootdir:
- args = self.getini('testpaths')
+ self._parser.after_preparse = True
+ try:
+ args = self._parser.parse_setoption(args, self.option, namespace=self.option)
if not args:
- args = [cwd]
- self.args = args
+ cwd = os.getcwd()
+ if cwd == self.rootdir:
+ args = self.getini('testpaths')
+ if not args:
+ args = [cwd]
+ self.args = args
+ except PrintHelp:
+ pass
def addinivalue_line(self, name, line):
""" add a line to an ini-file option. The option must have been
@@ -1082,12 +1140,12 @@ class Config(object):
the first line in its value. """
x = self.getini(name)
assert isinstance(x, list)
- x.append(line) # modifies the cached list inline
+ x.append(line) # modifies the cached list inline
def getini(self, name):
""" return configuration value from an :ref:`ini file <inifiles>`. If the
specified name hasn't been registered through a prior
- :py:func:`parser.addini <pytest.config.Parser.addini>`
+ :py:func:`parser.addini <_pytest.config.Parser.addini>`
call (usually from a plugin), a ValueError is raised. """
try:
return self._inicache[name]
@@ -1099,7 +1157,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" % (name,))
value = self._get_override_ini_value(name)
if value is None:
try:
@@ -1112,10 +1170,10 @@ class Config(object):
return []
if type == "pathlist":
dp = py.path.local(self.inicfg.config.path).dirpath()
- l = []
+ values = []
for relpath in shlex.split(value):
- l.append(dp.join(relpath, abs=True))
- return l
+ values.append(dp.join(relpath, abs=True))
+ return values
elif type == "args":
return shlex.split(value)
elif type == "linelist":
@@ -1132,13 +1190,13 @@ class Config(object):
except KeyError:
return None
modpath = py.path.local(mod.__file__).dirpath()
- l = []
+ values = []
for relroot in relroots:
if not isinstance(relroot, py.path.local):
relroot = relroot.replace("/", py.path.local.sep)
relroot = modpath.join(relroot, abs=True)
- l.append(relroot)
- return l
+ values.append(relroot)
+ return values
def _get_override_ini_value(self, name):
value = None
@@ -1146,15 +1204,14 @@ class Config(object):
# and -o foo1=bar1 -o foo2=bar2 options
# always use the last item if multiple value set for same ini-name,
# e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2
- if self.getoption("override_ini", None):
- for ini_config_list in self.option.override_ini:
- for ini_config in ini_config_list:
- 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
+ for ini_config_list in self._override_ini:
+ for ini_config in ini_config_list:
+ 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
def getoption(self, name, default=notset, skip=False):
@@ -1177,7 +1234,7 @@ class Config(object):
return default
if skip:
import pytest
- pytest.skip("no %r option found" %(name,))
+ pytest.skip("no %r option found" % (name,))
raise ValueError("no option named %r" % (name,))
def getvalue(self, name, path=None):
@@ -1188,12 +1245,14 @@ class Config(object):
""" (deprecated, use getoption(skip=True)) """
return self.getoption(name, skip=True)
+
def exists(path, ignore=EnvironmentError):
try:
return path.check()
except ignore:
return False
+
def getcfg(args, warnfunc=None):
"""
Search the list of arguments for a valid ini-file for pytest,
@@ -1228,25 +1287,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:
@@ -1257,9 +1311,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):
@@ -1282,7 +1356,7 @@ def determine_setup(inifile, args, warnfunc=None):
rootdir, inifile, inicfg = getcfg(dirs, warnfunc=warnfunc)
if rootdir is None:
rootdir = get_common_ancestor([py.path.local(), ancestor])
- is_fs_root = os.path.splitdrive(str(rootdir))[1] == os.sep
+ is_fs_root = os.path.splitdrive(str(rootdir))[1] == '/'
if is_fs_root:
rootdir = ancestor
return rootdir, inifile, inicfg or {}
@@ -1304,7 +1378,7 @@ def setns(obj, dic):
else:
setattr(obj, name, value)
obj.__all__.append(name)
- #if obj != pytest:
+ # if obj != pytest:
# pytest.__all__.append(name)
setattr(pytest, name, value)