shift config initialization to own "config" plugin
--HG-- branch : trunk
This commit is contained in:
parent
7453fc107c
commit
04c41cb672
|
@ -114,12 +114,10 @@ py.test loads plugin modules at tool startup in the following way:
|
||||||
* by recursively loading all plugins specified by the
|
* by recursively loading all plugins specified by the
|
||||||
``pytest_plugins`` variable in a ``conftest.py`` file
|
``pytest_plugins`` variable in a ``conftest.py`` file
|
||||||
|
|
||||||
Specifying plugins in a test module or plugin
|
Requiring/Loading plugins in a test module or plugin
|
||||||
-----------------------------------------------
|
-------------------------------------------------------------
|
||||||
|
|
||||||
You can specify plugins in a test module or a plugin like this:
|
You can specify plugins in a test module or a plugin like this::
|
||||||
|
|
||||||
.. sourcecode:: python
|
|
||||||
|
|
||||||
pytest_plugins = "name1", "name2",
|
pytest_plugins = "name1", "name2",
|
||||||
|
|
||||||
|
@ -138,11 +136,7 @@ The purpose of ``conftest.py`` files is to allow `project-specific
|
||||||
test configuration`_. They thus make for a good place to implement
|
test configuration`_. They thus make for a good place to implement
|
||||||
project-specific test related features through hooks. For example you may
|
project-specific test related features through hooks. For example you may
|
||||||
set the `collect_ignore`_ variable depending on a command line option
|
set the `collect_ignore`_ variable depending on a command line option
|
||||||
by defining the following hook in a ``conftest.py`` file:
|
by defining the following hook in a ``conftest.py`` file::
|
||||||
|
|
||||||
.. _`exclude-file-example`:
|
|
||||||
|
|
||||||
.. sourcecode:: python
|
|
||||||
|
|
||||||
# ./conftest.py in your root or package dir
|
# ./conftest.py in your root or package dir
|
||||||
collect_ignore = ['hello', 'test_world.py']
|
collect_ignore = ['hello', 'test_world.py']
|
||||||
|
@ -223,6 +217,7 @@ initialisation, command line and configuration hooks
|
||||||
|
|
||||||
.. currentmodule:: pytest.hookspec
|
.. currentmodule:: pytest.hookspec
|
||||||
|
|
||||||
|
.. autofunction:: pytest_cmdline_parse
|
||||||
.. autofunction:: pytest_namespace
|
.. autofunction:: pytest_namespace
|
||||||
.. autofunction:: pytest_addoption
|
.. autofunction:: pytest_addoption
|
||||||
.. autofunction:: pytest_cmdline_main
|
.. autofunction:: pytest_cmdline_main
|
||||||
|
@ -340,7 +335,7 @@ name. Given a filesystem ``fspath`` it is constructed as follows:
|
||||||
Reference of important objects involved in hooks
|
Reference of important objects involved in hooks
|
||||||
===========================================================
|
===========================================================
|
||||||
|
|
||||||
.. autoclass:: pytest._core.Config
|
.. autoclass:: pytest.plugin.config.Config
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
.. autoclass:: pytest.plugin.session.Item
|
.. autoclass:: pytest.plugin.session.Item
|
||||||
|
|
|
@ -10,8 +10,6 @@ __version__ = "2.0.0dev0"
|
||||||
__all__ = ['config', 'cmdline']
|
__all__ = ['config', 'cmdline']
|
||||||
|
|
||||||
from pytest import _core as cmdline
|
from pytest import _core as cmdline
|
||||||
from pytest._core import Config
|
|
||||||
config = Config()
|
|
||||||
|
|
||||||
def __main__():
|
def __main__():
|
||||||
raise SystemExit(cmdline.main())
|
raise SystemExit(cmdline.main())
|
||||||
|
|
427
pytest/_core.py
427
pytest/_core.py
|
@ -2,410 +2,10 @@ import py
|
||||||
import sys, os
|
import sys, os
|
||||||
|
|
||||||
default_plugins = (
|
default_plugins = (
|
||||||
"session terminal python runner pdb capture mark skipping tmpdir monkeypatch "
|
"config session terminal python runner pdb capture mark skipping tmpdir "
|
||||||
"recwarn pastebin unittest helpconfig nose assertion genscript "
|
"monkeypatch recwarn pastebin unittest helpconfig nose assertion genscript "
|
||||||
"junitxml doctest").split()
|
"junitxml doctest").split()
|
||||||
|
|
||||||
def main(args=None):
|
|
||||||
import sys
|
|
||||||
if args is None:
|
|
||||||
args = sys.argv[1:]
|
|
||||||
config = py.test.config
|
|
||||||
config.parse(args)
|
|
||||||
try:
|
|
||||||
exitstatus = config.hook.pytest_cmdline_main(config=config)
|
|
||||||
except config.Error:
|
|
||||||
e = sys.exc_info()[1]
|
|
||||||
sys.stderr.write("ERROR: %s\n" %(e.args[0],))
|
|
||||||
exitstatus = 3
|
|
||||||
py.test.config = config.__class__()
|
|
||||||
return exitstatus
|
|
||||||
|
|
||||||
class Parser:
|
|
||||||
""" Parser for command line arguments. """
|
|
||||||
|
|
||||||
def __init__(self, usage=None, processopt=None):
|
|
||||||
self._anonymous = OptionGroup("custom options", parser=self)
|
|
||||||
self._groups = []
|
|
||||||
self._processopt = processopt
|
|
||||||
self._usage = usage
|
|
||||||
self.hints = []
|
|
||||||
|
|
||||||
def processoption(self, option):
|
|
||||||
if self._processopt:
|
|
||||||
if option.dest:
|
|
||||||
self._processopt(option)
|
|
||||||
|
|
||||||
def addnote(self, note):
|
|
||||||
self._notes.append(note)
|
|
||||||
|
|
||||||
def getgroup(self, name, description="", after=None):
|
|
||||||
for group in self._groups:
|
|
||||||
if group.name == name:
|
|
||||||
return group
|
|
||||||
group = OptionGroup(name, description, parser=self)
|
|
||||||
i = 0
|
|
||||||
for i, grp in enumerate(self._groups):
|
|
||||||
if grp.name == after:
|
|
||||||
break
|
|
||||||
self._groups.insert(i+1, group)
|
|
||||||
return group
|
|
||||||
|
|
||||||
addgroup = getgroup
|
|
||||||
def addgroup(self, name, description=""):
|
|
||||||
py.log._apiwarn("1.1", "use getgroup() which gets-or-creates")
|
|
||||||
return self.getgroup(name, description)
|
|
||||||
|
|
||||||
def addoption(self, *opts, **attrs):
|
|
||||||
""" add an optparse-style option. """
|
|
||||||
self._anonymous.addoption(*opts, **attrs)
|
|
||||||
|
|
||||||
def parse(self, args):
|
|
||||||
optparser = MyOptionParser(self)
|
|
||||||
groups = self._groups + [self._anonymous]
|
|
||||||
for group in groups:
|
|
||||||
if group.options:
|
|
||||||
desc = group.description or group.name
|
|
||||||
optgroup = py.std.optparse.OptionGroup(optparser, desc)
|
|
||||||
optgroup.add_options(group.options)
|
|
||||||
optparser.add_option_group(optgroup)
|
|
||||||
return optparser.parse_args([str(x) for x in args])
|
|
||||||
|
|
||||||
def parse_setoption(self, args, option):
|
|
||||||
parsedoption, args = self.parse(args)
|
|
||||||
for name, value in parsedoption.__dict__.items():
|
|
||||||
setattr(option, name, value)
|
|
||||||
return args
|
|
||||||
|
|
||||||
|
|
||||||
class OptionGroup:
|
|
||||||
def __init__(self, name, description="", parser=None):
|
|
||||||
self.name = name
|
|
||||||
self.description = description
|
|
||||||
self.options = []
|
|
||||||
self.parser = parser
|
|
||||||
|
|
||||||
def addoption(self, *optnames, **attrs):
|
|
||||||
""" add an option to this group. """
|
|
||||||
option = py.std.optparse.Option(*optnames, **attrs)
|
|
||||||
self._addoption_instance(option, shortupper=False)
|
|
||||||
|
|
||||||
def _addoption(self, *optnames, **attrs):
|
|
||||||
option = py.std.optparse.Option(*optnames, **attrs)
|
|
||||||
self._addoption_instance(option, shortupper=True)
|
|
||||||
|
|
||||||
def _addoption_instance(self, option, shortupper=False):
|
|
||||||
if not shortupper:
|
|
||||||
for opt in option._short_opts:
|
|
||||||
if opt[0] == '-' and opt[1].islower():
|
|
||||||
raise ValueError("lowercase shortoptions reserved")
|
|
||||||
if self.parser:
|
|
||||||
self.parser.processoption(option)
|
|
||||||
self.options.append(option)
|
|
||||||
|
|
||||||
|
|
||||||
class MyOptionParser(py.std.optparse.OptionParser):
|
|
||||||
def __init__(self, parser):
|
|
||||||
self._parser = parser
|
|
||||||
py.std.optparse.OptionParser.__init__(self, usage=parser._usage)
|
|
||||||
def format_epilog(self, formatter):
|
|
||||||
hints = self._parser.hints
|
|
||||||
if hints:
|
|
||||||
s = "\n".join(["hint: " + x for x in hints]) + "\n"
|
|
||||||
s = "\n" + s + "\n"
|
|
||||||
return s
|
|
||||||
return ""
|
|
||||||
|
|
||||||
class Conftest(object):
|
|
||||||
""" the single place for accessing values and interacting
|
|
||||||
towards conftest modules from py.test objects.
|
|
||||||
"""
|
|
||||||
def __init__(self, onimport=None, confcutdir=None):
|
|
||||||
self._path2confmods = {}
|
|
||||||
self._onimport = onimport
|
|
||||||
self._conftestpath2mod = {}
|
|
||||||
self._confcutdir = confcutdir
|
|
||||||
self._md5cache = {}
|
|
||||||
|
|
||||||
def setinitial(self, args):
|
|
||||||
""" try to find a first anchor path for looking up global values
|
|
||||||
from conftests. This function is usually called _before_
|
|
||||||
argument parsing. conftest files may add command line options
|
|
||||||
and we thus have no completely safe way of determining
|
|
||||||
which parts of the arguments are actually related to options
|
|
||||||
and which are file system paths. We just try here to get
|
|
||||||
bootstrapped ...
|
|
||||||
"""
|
|
||||||
current = py.path.local()
|
|
||||||
opt = '--confcutdir'
|
|
||||||
for i in range(len(args)):
|
|
||||||
opt1 = str(args[i])
|
|
||||||
if opt1.startswith(opt):
|
|
||||||
if opt1 == opt:
|
|
||||||
if len(args) > i:
|
|
||||||
p = current.join(args[i+1], abs=True)
|
|
||||||
elif opt1.startswith(opt + "="):
|
|
||||||
p = current.join(opt1[len(opt)+1:], abs=1)
|
|
||||||
self._confcutdir = p
|
|
||||||
break
|
|
||||||
for arg in args + [current]:
|
|
||||||
if hasattr(arg, 'startswith') and arg.startswith("--"):
|
|
||||||
continue
|
|
||||||
anchor = current.join(arg, abs=1)
|
|
||||||
if anchor.check(): # we found some file object
|
|
||||||
self._path2confmods[None] = self.getconftestmodules(anchor)
|
|
||||||
# let's also consider test* dirs
|
|
||||||
if anchor.check(dir=1):
|
|
||||||
for x in anchor.listdir("test*"):
|
|
||||||
if x.check(dir=1):
|
|
||||||
self.getconftestmodules(x)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
assert 0, "no root of filesystem?"
|
|
||||||
|
|
||||||
def getconftestmodules(self, path):
|
|
||||||
""" return a list of imported conftest modules for the given path. """
|
|
||||||
try:
|
|
||||||
clist = self._path2confmods[path]
|
|
||||||
except KeyError:
|
|
||||||
if path is None:
|
|
||||||
raise ValueError("missing default confest.")
|
|
||||||
dp = path.dirpath()
|
|
||||||
clist = []
|
|
||||||
if dp != path:
|
|
||||||
cutdir = self._confcutdir
|
|
||||||
if cutdir and path != cutdir and not path.relto(cutdir):
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
conftestpath = path.join("conftest.py")
|
|
||||||
if conftestpath.check(file=1):
|
|
||||||
key = conftestpath.computehash()
|
|
||||||
# XXX logging about conftest loading
|
|
||||||
if key not in self._md5cache:
|
|
||||||
clist.append(self.importconftest(conftestpath))
|
|
||||||
self._md5cache[key] = conftestpath
|
|
||||||
else:
|
|
||||||
# use some kind of logging
|
|
||||||
print ("WARN: not loading %s" % conftestpath)
|
|
||||||
clist[:0] = self.getconftestmodules(dp)
|
|
||||||
self._path2confmods[path] = clist
|
|
||||||
# be defensive: avoid changes from caller side to
|
|
||||||
# affect us by always returning a copy of the actual list
|
|
||||||
return clist[:]
|
|
||||||
|
|
||||||
def rget(self, name, path=None):
|
|
||||||
mod, value = self.rget_with_confmod(name, path)
|
|
||||||
return value
|
|
||||||
|
|
||||||
def rget_with_confmod(self, name, path=None):
|
|
||||||
modules = self.getconftestmodules(path)
|
|
||||||
modules.reverse()
|
|
||||||
for mod in modules:
|
|
||||||
try:
|
|
||||||
return mod, getattr(mod, name)
|
|
||||||
except AttributeError:
|
|
||||||
continue
|
|
||||||
raise KeyError(name)
|
|
||||||
|
|
||||||
def importconftest(self, conftestpath):
|
|
||||||
assert conftestpath.check(), conftestpath
|
|
||||||
try:
|
|
||||||
return self._conftestpath2mod[conftestpath]
|
|
||||||
except KeyError:
|
|
||||||
if not conftestpath.dirpath('__init__.py').check(file=1):
|
|
||||||
# HACK: we don't want any "globally" imported conftest.py,
|
|
||||||
# prone to conflicts and subtle problems
|
|
||||||
modname = str(conftestpath).replace('.', conftestpath.sep)
|
|
||||||
mod = conftestpath.pyimport(modname=modname)
|
|
||||||
else:
|
|
||||||
mod = conftestpath.pyimport()
|
|
||||||
self._conftestpath2mod[conftestpath] = mod
|
|
||||||
dirpath = conftestpath.dirpath()
|
|
||||||
if dirpath in self._path2confmods:
|
|
||||||
for path, mods in self._path2confmods.items():
|
|
||||||
if path and path.relto(dirpath) or path == dirpath:
|
|
||||||
assert mod not in mods
|
|
||||||
mods.append(mod)
|
|
||||||
self._postimport(mod)
|
|
||||||
return mod
|
|
||||||
|
|
||||||
def _postimport(self, mod):
|
|
||||||
if self._onimport:
|
|
||||||
self._onimport(mod)
|
|
||||||
return mod
|
|
||||||
|
|
||||||
|
|
||||||
class CmdOptions(object):
|
|
||||||
""" holds cmdline options as attributes."""
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
self.__dict__.update(kwargs)
|
|
||||||
def __repr__(self):
|
|
||||||
return "<CmdOptions %r>" %(self.__dict__,)
|
|
||||||
|
|
||||||
class Error(Exception):
|
|
||||||
""" Test Configuration Error. """
|
|
||||||
|
|
||||||
class Config(object):
|
|
||||||
""" access to configuration values, pluginmanager and plugin hooks. """
|
|
||||||
Option = py.std.optparse.Option
|
|
||||||
Error = Error
|
|
||||||
basetemp = None
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
#: command line option values
|
|
||||||
self.option = CmdOptions()
|
|
||||||
self._parser = Parser(
|
|
||||||
usage="usage: %prog [options] [file_or_dir] [file_or_dir] [...]",
|
|
||||||
processopt=self._processopt,
|
|
||||||
)
|
|
||||||
#: a pluginmanager instance
|
|
||||||
self.pluginmanager = PluginManager()
|
|
||||||
self._conftest = Conftest(onimport=self._onimportconftest)
|
|
||||||
self.hook = self.pluginmanager.hook
|
|
||||||
|
|
||||||
def _onimportconftest(self, conftestmodule):
|
|
||||||
self.trace("loaded conftestmodule %r" %(conftestmodule,))
|
|
||||||
self.pluginmanager.consider_conftest(conftestmodule)
|
|
||||||
|
|
||||||
def _getmatchingplugins(self, fspath):
|
|
||||||
allconftests = self._conftest._conftestpath2mod.values()
|
|
||||||
plugins = [x for x in self.pluginmanager.getplugins()
|
|
||||||
if x not in allconftests]
|
|
||||||
plugins += self._conftest.getconftestmodules(fspath)
|
|
||||||
return plugins
|
|
||||||
|
|
||||||
def trace(self, msg):
|
|
||||||
if getattr(self.option, 'traceconfig', None):
|
|
||||||
self.hook.pytest_trace(category="config", msg=msg)
|
|
||||||
|
|
||||||
def _processopt(self, opt):
|
|
||||||
if hasattr(opt, 'default') and opt.dest:
|
|
||||||
val = os.environ.get("PYTEST_OPTION_" + opt.dest.upper(), None)
|
|
||||||
if val is not None:
|
|
||||||
if opt.type == "int":
|
|
||||||
val = int(val)
|
|
||||||
elif opt.type == "long":
|
|
||||||
val = long(val)
|
|
||||||
elif opt.type == "float":
|
|
||||||
val = float(val)
|
|
||||||
elif not opt.type and opt.action in ("store_true", "store_false"):
|
|
||||||
val = eval(val)
|
|
||||||
opt.default = val
|
|
||||||
else:
|
|
||||||
name = "option_" + opt.dest
|
|
||||||
try:
|
|
||||||
opt.default = self._conftest.rget(name)
|
|
||||||
except (ValueError, KeyError):
|
|
||||||
pass
|
|
||||||
if not hasattr(self.option, opt.dest):
|
|
||||||
setattr(self.option, opt.dest, opt.default)
|
|
||||||
|
|
||||||
def _preparse(self, args):
|
|
||||||
self.pluginmanager.consider_setuptools_entrypoints()
|
|
||||||
self.pluginmanager.consider_env()
|
|
||||||
self.pluginmanager.consider_preparse(args)
|
|
||||||
self._conftest.setinitial(args)
|
|
||||||
self.pluginmanager.do_addoption(self._parser)
|
|
||||||
|
|
||||||
def parse(self, args):
|
|
||||||
# cmdline arguments into this config object.
|
|
||||||
# Note that this can only be called once per testing process.
|
|
||||||
assert not hasattr(self, 'args'), (
|
|
||||||
"can only parse cmdline args at most once per Config object")
|
|
||||||
self._preparse(args)
|
|
||||||
self._parser.hints.extend(self.pluginmanager._hints)
|
|
||||||
args = self._parser.parse_setoption(args, self.option)
|
|
||||||
if not args:
|
|
||||||
args.append(py.std.os.getcwd())
|
|
||||||
self.args = args
|
|
||||||
|
|
||||||
def ensuretemp(self, string, dir=True):
|
|
||||||
return self.getbasetemp().ensure(string, dir=dir)
|
|
||||||
|
|
||||||
def getbasetemp(self):
|
|
||||||
if self.basetemp is None:
|
|
||||||
basetemp = self.option.basetemp
|
|
||||||
if basetemp:
|
|
||||||
basetemp = py.path.local(basetemp)
|
|
||||||
if not basetemp.check(dir=1):
|
|
||||||
basetemp.mkdir()
|
|
||||||
else:
|
|
||||||
basetemp = py.path.local.make_numbered_dir(prefix='pytest-')
|
|
||||||
self.basetemp = basetemp
|
|
||||||
return self.basetemp
|
|
||||||
|
|
||||||
def mktemp(self, basename, numbered=False):
|
|
||||||
basetemp = self.getbasetemp()
|
|
||||||
if not numbered:
|
|
||||||
return basetemp.mkdir(basename)
|
|
||||||
else:
|
|
||||||
return py.path.local.make_numbered_dir(prefix=basename,
|
|
||||||
keep=0, rootdir=basetemp, lock_timeout=None)
|
|
||||||
|
|
||||||
def _getcollectclass(self, name, path):
|
|
||||||
try:
|
|
||||||
cls = self._conftest.rget(name, path)
|
|
||||||
except KeyError:
|
|
||||||
return getattr(py.test.collect, name)
|
|
||||||
else:
|
|
||||||
py.log._apiwarn(">1.1", "%r was found in a conftest.py file, "
|
|
||||||
"use pytest_collect hooks instead." % (cls,))
|
|
||||||
return cls
|
|
||||||
|
|
||||||
def getconftest_pathlist(self, name, path=None):
|
|
||||||
""" return a matching value, which needs to be sequence
|
|
||||||
of filenames that will be returned as a list of Path
|
|
||||||
objects (they can be relative to the location
|
|
||||||
where they were found).
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
mod, relroots = self._conftest.rget_with_confmod(name, path)
|
|
||||||
except KeyError:
|
|
||||||
return None
|
|
||||||
modpath = py.path.local(mod.__file__).dirpath()
|
|
||||||
l = []
|
|
||||||
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
|
|
||||||
|
|
||||||
def addoptions(self, groupname, *specs):
|
|
||||||
# add a named group of options to the current testing session.
|
|
||||||
# This function gets invoked during testing session initialization.
|
|
||||||
py.log._apiwarn("1.0",
|
|
||||||
"define pytest_addoptions(parser) to add options", stacklevel=2)
|
|
||||||
group = self._parser.getgroup(groupname)
|
|
||||||
for opt in specs:
|
|
||||||
group._addoption_instance(opt)
|
|
||||||
return self.option
|
|
||||||
|
|
||||||
def addoption(self, *optnames, **attrs):
|
|
||||||
return self._parser.addoption(*optnames, **attrs)
|
|
||||||
|
|
||||||
def getvalueorskip(self, name, path=None):
|
|
||||||
""" return getvalue(name) or call py.test.skip if no value exists. """
|
|
||||||
try:
|
|
||||||
val = self.getvalue(name, path)
|
|
||||||
if val is None:
|
|
||||||
raise KeyError(name)
|
|
||||||
return val
|
|
||||||
except KeyError:
|
|
||||||
py.test.skip("no %r value found" %(name,))
|
|
||||||
|
|
||||||
def getvalue(self, name, path=None):
|
|
||||||
""" return 'name' value looked up from the 'options'
|
|
||||||
and then from the first conftest file found up
|
|
||||||
the path (including the path itself).
|
|
||||||
if path is None, lookup the value in the initial
|
|
||||||
conftest modules found during command line parsing.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
return getattr(self.option, name)
|
|
||||||
except AttributeError:
|
|
||||||
return self._conftest.rget(name, path)
|
|
||||||
|
|
||||||
class PluginManager(object):
|
class PluginManager(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
from pytest import hookspec
|
from pytest import hookspec
|
||||||
|
@ -530,8 +130,12 @@ class PluginManager(object):
|
||||||
mod = importplugin(modname)
|
mod = importplugin(modname)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except py.test.skip.Exception:
|
except:
|
||||||
e = py.std.sys.exc_info()[1]
|
e = py.std.sys.exc_info()[1]
|
||||||
|
if not hasattr(py.test, 'skip'):
|
||||||
|
raise
|
||||||
|
elif not isinstance(e, py.test.skip.Exception):
|
||||||
|
raise
|
||||||
self._hints.append("skipped plugin %r: %s" %((modname, e.msg)))
|
self._hints.append("skipped plugin %r: %s" %((modname, e.msg)))
|
||||||
else:
|
else:
|
||||||
self.register(mod, modname)
|
self.register(mod, modname)
|
||||||
|
@ -771,3 +375,20 @@ class HookCaller:
|
||||||
mc = MultiCall(methods, kwargs, firstresult=self.firstresult)
|
mc = MultiCall(methods, kwargs, firstresult=self.firstresult)
|
||||||
return self.hookrelay._performcall(self.name, mc)
|
return self.hookrelay._performcall(self.name, mc)
|
||||||
|
|
||||||
|
pluginmanager = PluginManager() # will trigger default plugin loading
|
||||||
|
|
||||||
|
def main(args=None):
|
||||||
|
global pluginmanager
|
||||||
|
if args is None:
|
||||||
|
args = sys.argv[1:]
|
||||||
|
hook = pluginmanager.hook
|
||||||
|
config = hook.pytest_cmdline_parse(pluginmanager=pluginmanager, args=args)
|
||||||
|
try:
|
||||||
|
exitstatus = hook.pytest_cmdline_main(config=config)
|
||||||
|
except config.Error:
|
||||||
|
e = sys.exc_info()[1]
|
||||||
|
sys.stderr.write("ERROR: %s\n" %(e.args[0],))
|
||||||
|
exitstatus = 3
|
||||||
|
pluginmanager = PluginManager()
|
||||||
|
return exitstatus
|
||||||
|
|
||||||
|
|
|
@ -8,16 +8,21 @@ def pytest_namespace():
|
||||||
"""return dict of name->object to be made globally available in
|
"""return dict of name->object to be made globally available in
|
||||||
the py.test/pytest namespace. This hook is called before command
|
the py.test/pytest namespace. This hook is called before command
|
||||||
line options are fully parsed. If you want to provide helper functions
|
line options are fully parsed. If you want to provide helper functions
|
||||||
that can interact with a test function invocation, please refer to
|
that can interact with a test function invocation, please refer to
|
||||||
:ref:`funcarg mechanism`.
|
:ref:`funcarg mechanism`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def pytest_cmdline_parse(pluginmanager, args):
|
||||||
|
"""return initialized config object, parsing the specified args. """
|
||||||
|
pytest_cmdline_parse.firstresult = True
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
"""allows to add optparse-style command line options via a call to
|
"""allows to add optparse-style command line options via a call to
|
||||||
``parser.addoption(...)``."""
|
``parser.addoption(...)``."""
|
||||||
|
|
||||||
def pytest_addhooks(pluginmanager):
|
def pytest_addhooks(pluginmanager):
|
||||||
"allows to add new hooks via pluginmanager.registerhooks(module)"
|
"""called at plugin load time to allow adding new hooks via a call to
|
||||||
|
pluginmanager.registerhooks(module)."""
|
||||||
|
|
||||||
def pytest_cmdline_main(config):
|
def pytest_cmdline_main(config):
|
||||||
""" called for performing the main command line action. The default
|
""" called for performing the main command line action. The default
|
||||||
|
|
|
@ -0,0 +1,395 @@
|
||||||
|
|
||||||
|
import py
|
||||||
|
import os
|
||||||
|
from pytest._core import PluginManager
|
||||||
|
|
||||||
|
def pytest_cmdline_parse(pluginmanager, args):
|
||||||
|
config = Config(pluginmanager)
|
||||||
|
config.parse(args)
|
||||||
|
return config
|
||||||
|
|
||||||
|
class Parser:
|
||||||
|
""" Parser for command line arguments. """
|
||||||
|
|
||||||
|
def __init__(self, usage=None, processopt=None):
|
||||||
|
self._anonymous = OptionGroup("custom options", parser=self)
|
||||||
|
self._groups = []
|
||||||
|
self._processopt = processopt
|
||||||
|
self._usage = usage
|
||||||
|
self.hints = []
|
||||||
|
|
||||||
|
def processoption(self, option):
|
||||||
|
if self._processopt:
|
||||||
|
if option.dest:
|
||||||
|
self._processopt(option)
|
||||||
|
|
||||||
|
def addnote(self, note):
|
||||||
|
self._notes.append(note)
|
||||||
|
|
||||||
|
def getgroup(self, name, description="", after=None):
|
||||||
|
for group in self._groups:
|
||||||
|
if group.name == name:
|
||||||
|
return group
|
||||||
|
group = OptionGroup(name, description, parser=self)
|
||||||
|
i = 0
|
||||||
|
for i, grp in enumerate(self._groups):
|
||||||
|
if grp.name == after:
|
||||||
|
break
|
||||||
|
self._groups.insert(i+1, group)
|
||||||
|
return group
|
||||||
|
|
||||||
|
addgroup = getgroup
|
||||||
|
def addgroup(self, name, description=""):
|
||||||
|
py.log._apiwarn("1.1", "use getgroup() which gets-or-creates")
|
||||||
|
return self.getgroup(name, description)
|
||||||
|
|
||||||
|
def addoption(self, *opts, **attrs):
|
||||||
|
""" add an optparse-style option. """
|
||||||
|
self._anonymous.addoption(*opts, **attrs)
|
||||||
|
|
||||||
|
def parse(self, args):
|
||||||
|
optparser = MyOptionParser(self)
|
||||||
|
groups = self._groups + [self._anonymous]
|
||||||
|
for group in groups:
|
||||||
|
if group.options:
|
||||||
|
desc = group.description or group.name
|
||||||
|
optgroup = py.std.optparse.OptionGroup(optparser, desc)
|
||||||
|
optgroup.add_options(group.options)
|
||||||
|
optparser.add_option_group(optgroup)
|
||||||
|
return optparser.parse_args([str(x) for x in args])
|
||||||
|
|
||||||
|
def parse_setoption(self, args, option):
|
||||||
|
parsedoption, args = self.parse(args)
|
||||||
|
for name, value in parsedoption.__dict__.items():
|
||||||
|
setattr(option, name, value)
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
class OptionGroup:
|
||||||
|
def __init__(self, name, description="", parser=None):
|
||||||
|
self.name = name
|
||||||
|
self.description = description
|
||||||
|
self.options = []
|
||||||
|
self.parser = parser
|
||||||
|
|
||||||
|
def addoption(self, *optnames, **attrs):
|
||||||
|
""" add an option to this group. """
|
||||||
|
option = py.std.optparse.Option(*optnames, **attrs)
|
||||||
|
self._addoption_instance(option, shortupper=False)
|
||||||
|
|
||||||
|
def _addoption(self, *optnames, **attrs):
|
||||||
|
option = py.std.optparse.Option(*optnames, **attrs)
|
||||||
|
self._addoption_instance(option, shortupper=True)
|
||||||
|
|
||||||
|
def _addoption_instance(self, option, shortupper=False):
|
||||||
|
if not shortupper:
|
||||||
|
for opt in option._short_opts:
|
||||||
|
if opt[0] == '-' and opt[1].islower():
|
||||||
|
raise ValueError("lowercase shortoptions reserved")
|
||||||
|
if self.parser:
|
||||||
|
self.parser.processoption(option)
|
||||||
|
self.options.append(option)
|
||||||
|
|
||||||
|
|
||||||
|
class MyOptionParser(py.std.optparse.OptionParser):
|
||||||
|
def __init__(self, parser):
|
||||||
|
self._parser = parser
|
||||||
|
py.std.optparse.OptionParser.__init__(self, usage=parser._usage)
|
||||||
|
def format_epilog(self, formatter):
|
||||||
|
hints = self._parser.hints
|
||||||
|
if hints:
|
||||||
|
s = "\n".join(["hint: " + x for x in hints]) + "\n"
|
||||||
|
s = "\n" + s + "\n"
|
||||||
|
return s
|
||||||
|
return ""
|
||||||
|
|
||||||
|
class Conftest(object):
|
||||||
|
""" the single place for accessing values and interacting
|
||||||
|
towards conftest modules from py.test objects.
|
||||||
|
"""
|
||||||
|
def __init__(self, onimport=None, confcutdir=None):
|
||||||
|
self._path2confmods = {}
|
||||||
|
self._onimport = onimport
|
||||||
|
self._conftestpath2mod = {}
|
||||||
|
self._confcutdir = confcutdir
|
||||||
|
self._md5cache = {}
|
||||||
|
|
||||||
|
def setinitial(self, args):
|
||||||
|
""" try to find a first anchor path for looking up global values
|
||||||
|
from conftests. This function is usually called _before_
|
||||||
|
argument parsing. conftest files may add command line options
|
||||||
|
and we thus have no completely safe way of determining
|
||||||
|
which parts of the arguments are actually related to options
|
||||||
|
and which are file system paths. We just try here to get
|
||||||
|
bootstrapped ...
|
||||||
|
"""
|
||||||
|
current = py.path.local()
|
||||||
|
opt = '--confcutdir'
|
||||||
|
for i in range(len(args)):
|
||||||
|
opt1 = str(args[i])
|
||||||
|
if opt1.startswith(opt):
|
||||||
|
if opt1 == opt:
|
||||||
|
if len(args) > i:
|
||||||
|
p = current.join(args[i+1], abs=True)
|
||||||
|
elif opt1.startswith(opt + "="):
|
||||||
|
p = current.join(opt1[len(opt)+1:], abs=1)
|
||||||
|
self._confcutdir = p
|
||||||
|
break
|
||||||
|
for arg in args + [current]:
|
||||||
|
if hasattr(arg, 'startswith') and arg.startswith("--"):
|
||||||
|
continue
|
||||||
|
anchor = current.join(arg, abs=1)
|
||||||
|
if anchor.check(): # we found some file object
|
||||||
|
self._path2confmods[None] = self.getconftestmodules(anchor)
|
||||||
|
# let's also consider test* dirs
|
||||||
|
if anchor.check(dir=1):
|
||||||
|
for x in anchor.listdir("test*"):
|
||||||
|
if x.check(dir=1):
|
||||||
|
self.getconftestmodules(x)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
assert 0, "no root of filesystem?"
|
||||||
|
|
||||||
|
def getconftestmodules(self, path):
|
||||||
|
""" return a list of imported conftest modules for the given path. """
|
||||||
|
try:
|
||||||
|
clist = self._path2confmods[path]
|
||||||
|
except KeyError:
|
||||||
|
if path is None:
|
||||||
|
raise ValueError("missing default confest.")
|
||||||
|
dp = path.dirpath()
|
||||||
|
clist = []
|
||||||
|
if dp != path:
|
||||||
|
cutdir = self._confcutdir
|
||||||
|
if cutdir and path != cutdir and not path.relto(cutdir):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
conftestpath = path.join("conftest.py")
|
||||||
|
if conftestpath.check(file=1):
|
||||||
|
key = conftestpath.computehash()
|
||||||
|
# XXX logging about conftest loading
|
||||||
|
if key not in self._md5cache:
|
||||||
|
clist.append(self.importconftest(conftestpath))
|
||||||
|
self._md5cache[key] = conftestpath
|
||||||
|
else:
|
||||||
|
# use some kind of logging
|
||||||
|
print ("WARN: not loading %s" % conftestpath)
|
||||||
|
clist[:0] = self.getconftestmodules(dp)
|
||||||
|
self._path2confmods[path] = clist
|
||||||
|
# be defensive: avoid changes from caller side to
|
||||||
|
# affect us by always returning a copy of the actual list
|
||||||
|
return clist[:]
|
||||||
|
|
||||||
|
def rget(self, name, path=None):
|
||||||
|
mod, value = self.rget_with_confmod(name, path)
|
||||||
|
return value
|
||||||
|
|
||||||
|
def rget_with_confmod(self, name, path=None):
|
||||||
|
modules = self.getconftestmodules(path)
|
||||||
|
modules.reverse()
|
||||||
|
for mod in modules:
|
||||||
|
try:
|
||||||
|
return mod, getattr(mod, name)
|
||||||
|
except AttributeError:
|
||||||
|
continue
|
||||||
|
raise KeyError(name)
|
||||||
|
|
||||||
|
def importconftest(self, conftestpath):
|
||||||
|
assert conftestpath.check(), conftestpath
|
||||||
|
try:
|
||||||
|
return self._conftestpath2mod[conftestpath]
|
||||||
|
except KeyError:
|
||||||
|
if not conftestpath.dirpath('__init__.py').check(file=1):
|
||||||
|
# HACK: we don't want any "globally" imported conftest.py,
|
||||||
|
# prone to conflicts and subtle problems
|
||||||
|
modname = str(conftestpath).replace('.', conftestpath.sep)
|
||||||
|
mod = conftestpath.pyimport(modname=modname)
|
||||||
|
else:
|
||||||
|
mod = conftestpath.pyimport()
|
||||||
|
self._conftestpath2mod[conftestpath] = mod
|
||||||
|
dirpath = conftestpath.dirpath()
|
||||||
|
if dirpath in self._path2confmods:
|
||||||
|
for path, mods in self._path2confmods.items():
|
||||||
|
if path and path.relto(dirpath) or path == dirpath:
|
||||||
|
assert mod not in mods
|
||||||
|
mods.append(mod)
|
||||||
|
self._postimport(mod)
|
||||||
|
return mod
|
||||||
|
|
||||||
|
def _postimport(self, mod):
|
||||||
|
if self._onimport:
|
||||||
|
self._onimport(mod)
|
||||||
|
return mod
|
||||||
|
|
||||||
|
|
||||||
|
class CmdOptions(object):
|
||||||
|
""" holds cmdline options as attributes."""
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.__dict__.update(kwargs)
|
||||||
|
def __repr__(self):
|
||||||
|
return "<CmdOptions %r>" %(self.__dict__,)
|
||||||
|
|
||||||
|
class Error(Exception):
|
||||||
|
""" Test Configuration Error. """
|
||||||
|
|
||||||
|
class Config(object):
|
||||||
|
""" access to configuration values, pluginmanager and plugin hooks. """
|
||||||
|
Option = py.std.optparse.Option
|
||||||
|
Error = Error
|
||||||
|
basetemp = None
|
||||||
|
|
||||||
|
def __init__(self, pluginmanager=None):
|
||||||
|
#: command line option values
|
||||||
|
self.option = CmdOptions()
|
||||||
|
self._parser = Parser(
|
||||||
|
usage="usage: %prog [options] [file_or_dir] [file_or_dir] [...]",
|
||||||
|
processopt=self._processopt,
|
||||||
|
)
|
||||||
|
#: a pluginmanager instance
|
||||||
|
self.pluginmanager = pluginmanager or PluginManager()
|
||||||
|
self._conftest = Conftest(onimport=self._onimportconftest)
|
||||||
|
self.hook = self.pluginmanager.hook
|
||||||
|
|
||||||
|
def _onimportconftest(self, conftestmodule):
|
||||||
|
self.trace("loaded conftestmodule %r" %(conftestmodule,))
|
||||||
|
self.pluginmanager.consider_conftest(conftestmodule)
|
||||||
|
|
||||||
|
def _getmatchingplugins(self, fspath):
|
||||||
|
allconftests = self._conftest._conftestpath2mod.values()
|
||||||
|
plugins = [x for x in self.pluginmanager.getplugins()
|
||||||
|
if x not in allconftests]
|
||||||
|
plugins += self._conftest.getconftestmodules(fspath)
|
||||||
|
return plugins
|
||||||
|
|
||||||
|
def trace(self, msg):
|
||||||
|
if getattr(self.option, 'traceconfig', None):
|
||||||
|
self.hook.pytest_trace(category="config", msg=msg)
|
||||||
|
|
||||||
|
def _processopt(self, opt):
|
||||||
|
if hasattr(opt, 'default') and opt.dest:
|
||||||
|
val = os.environ.get("PYTEST_OPTION_" + opt.dest.upper(), None)
|
||||||
|
if val is not None:
|
||||||
|
if opt.type == "int":
|
||||||
|
val = int(val)
|
||||||
|
elif opt.type == "long":
|
||||||
|
val = long(val)
|
||||||
|
elif opt.type == "float":
|
||||||
|
val = float(val)
|
||||||
|
elif not opt.type and opt.action in ("store_true", "store_false"):
|
||||||
|
val = eval(val)
|
||||||
|
opt.default = val
|
||||||
|
else:
|
||||||
|
name = "option_" + opt.dest
|
||||||
|
try:
|
||||||
|
opt.default = self._conftest.rget(name)
|
||||||
|
except (ValueError, KeyError):
|
||||||
|
pass
|
||||||
|
if not hasattr(self.option, opt.dest):
|
||||||
|
setattr(self.option, opt.dest, opt.default)
|
||||||
|
|
||||||
|
def _preparse(self, args):
|
||||||
|
self.pluginmanager.consider_setuptools_entrypoints()
|
||||||
|
self.pluginmanager.consider_env()
|
||||||
|
self.pluginmanager.consider_preparse(args)
|
||||||
|
self._conftest.setinitial(args)
|
||||||
|
self.pluginmanager.do_addoption(self._parser)
|
||||||
|
|
||||||
|
def parse(self, args):
|
||||||
|
# cmdline arguments into this config object.
|
||||||
|
# Note that this can only be called once per testing process.
|
||||||
|
assert not hasattr(self, 'args'), (
|
||||||
|
"can only parse cmdline args at most once per Config object")
|
||||||
|
self._preparse(args)
|
||||||
|
self._parser.hints.extend(self.pluginmanager._hints)
|
||||||
|
args = self._parser.parse_setoption(args, self.option)
|
||||||
|
if not args:
|
||||||
|
args.append(py.std.os.getcwd())
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def ensuretemp(self, string, dir=True):
|
||||||
|
return self.getbasetemp().ensure(string, dir=dir)
|
||||||
|
|
||||||
|
def getbasetemp(self):
|
||||||
|
if self.basetemp is None:
|
||||||
|
basetemp = self.option.basetemp
|
||||||
|
if basetemp:
|
||||||
|
basetemp = py.path.local(basetemp)
|
||||||
|
if not basetemp.check(dir=1):
|
||||||
|
basetemp.mkdir()
|
||||||
|
else:
|
||||||
|
basetemp = py.path.local.make_numbered_dir(prefix='pytest-')
|
||||||
|
self.basetemp = basetemp
|
||||||
|
return self.basetemp
|
||||||
|
|
||||||
|
def mktemp(self, basename, numbered=False):
|
||||||
|
basetemp = self.getbasetemp()
|
||||||
|
if not numbered:
|
||||||
|
return basetemp.mkdir(basename)
|
||||||
|
else:
|
||||||
|
return py.path.local.make_numbered_dir(prefix=basename,
|
||||||
|
keep=0, rootdir=basetemp, lock_timeout=None)
|
||||||
|
|
||||||
|
def _getcollectclass(self, name, path):
|
||||||
|
try:
|
||||||
|
cls = self._conftest.rget(name, path)
|
||||||
|
except KeyError:
|
||||||
|
return getattr(py.test.collect, name)
|
||||||
|
else:
|
||||||
|
py.log._apiwarn(">1.1", "%r was found in a conftest.py file, "
|
||||||
|
"use pytest_collect hooks instead." % (cls,))
|
||||||
|
return cls
|
||||||
|
|
||||||
|
def getconftest_pathlist(self, name, path=None):
|
||||||
|
""" return a matching value, which needs to be sequence
|
||||||
|
of filenames that will be returned as a list of Path
|
||||||
|
objects (they can be relative to the location
|
||||||
|
where they were found).
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
mod, relroots = self._conftest.rget_with_confmod(name, path)
|
||||||
|
except KeyError:
|
||||||
|
return None
|
||||||
|
modpath = py.path.local(mod.__file__).dirpath()
|
||||||
|
l = []
|
||||||
|
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
|
||||||
|
|
||||||
|
def addoptions(self, groupname, *specs):
|
||||||
|
# add a named group of options to the current testing session.
|
||||||
|
# This function gets invoked during testing session initialization.
|
||||||
|
py.log._apiwarn("1.0",
|
||||||
|
"define pytest_addoptions(parser) to add options", stacklevel=2)
|
||||||
|
group = self._parser.getgroup(groupname)
|
||||||
|
for opt in specs:
|
||||||
|
group._addoption_instance(opt)
|
||||||
|
return self.option
|
||||||
|
|
||||||
|
def addoption(self, *optnames, **attrs):
|
||||||
|
return self._parser.addoption(*optnames, **attrs)
|
||||||
|
|
||||||
|
def getvalueorskip(self, name, path=None):
|
||||||
|
""" return getvalue(name) or call py.test.skip if no value exists. """
|
||||||
|
try:
|
||||||
|
val = self.getvalue(name, path)
|
||||||
|
if val is None:
|
||||||
|
raise KeyError(name)
|
||||||
|
return val
|
||||||
|
except KeyError:
|
||||||
|
py.test.skip("no %r value found" %(name,))
|
||||||
|
|
||||||
|
def getvalue(self, name, path=None):
|
||||||
|
""" return 'name' value looked up from the 'options'
|
||||||
|
and then from the first conftest file found up
|
||||||
|
the path (including the path itself).
|
||||||
|
if path is None, lookup the value in the initial
|
||||||
|
conftest modules found during command line parsing.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return getattr(self.option, name)
|
||||||
|
except AttributeError:
|
||||||
|
return self._conftest.rget(name, path)
|
||||||
|
|
|
@ -8,7 +8,6 @@ import re
|
||||||
import inspect
|
import inspect
|
||||||
import time
|
import time
|
||||||
from fnmatch import fnmatch
|
from fnmatch import fnmatch
|
||||||
from pytest._core import Config as pytestConfig
|
|
||||||
from pytest.plugin.session import Collection
|
from pytest.plugin.session import Collection
|
||||||
from py.builtin import print_
|
from py.builtin import print_
|
||||||
from pytest._core import HookRelay
|
from pytest._core import HookRelay
|
||||||
|
@ -109,7 +108,7 @@ class HookRecorder:
|
||||||
from py.builtin import print_
|
from py.builtin import print_
|
||||||
i = 0
|
i = 0
|
||||||
entries = list(entries)
|
entries = list(entries)
|
||||||
backlocals = py.std.sys._getframe(1).f_locals
|
backlocals = py.std.sys._getframe(1).f_locals
|
||||||
while entries:
|
while entries:
|
||||||
name, check = entries.pop(0)
|
name, check = entries.pop(0)
|
||||||
for ind, call in enumerate(self.calls[i:]):
|
for ind, call in enumerate(self.calls[i:]):
|
||||||
|
@ -173,6 +172,7 @@ class RunResult:
|
||||||
class TmpTestdir:
|
class TmpTestdir:
|
||||||
def __init__(self, request):
|
def __init__(self, request):
|
||||||
self.request = request
|
self.request = request
|
||||||
|
self.Config = request.config.__class__
|
||||||
self._pytest = request.getfuncargvalue("_pytest")
|
self._pytest = request.getfuncargvalue("_pytest")
|
||||||
# XXX remove duplication with tmpdir plugin
|
# XXX remove duplication with tmpdir plugin
|
||||||
basetmp = request.config.ensuretemp("testdir")
|
basetmp = request.config.ensuretemp("testdir")
|
||||||
|
@ -195,9 +195,6 @@ class TmpTestdir:
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<TmpTestdir %r>" % (self.tmpdir,)
|
return "<TmpTestdir %r>" % (self.tmpdir,)
|
||||||
|
|
||||||
def Config(self):
|
|
||||||
return pytestConfig()
|
|
||||||
|
|
||||||
def finalize(self):
|
def finalize(self):
|
||||||
for p in self._syspathremove:
|
for p in self._syspathremove:
|
||||||
py.std.sys.path.remove(p)
|
py.std.sys.path.remove(p)
|
||||||
|
@ -341,9 +338,9 @@ class TmpTestdir:
|
||||||
""" this is used from tests that want to re-invoke parse(). """
|
""" this is used from tests that want to re-invoke parse(). """
|
||||||
if not args:
|
if not args:
|
||||||
args = [self.tmpdir]
|
args = [self.tmpdir]
|
||||||
oldconfig = py.test.config
|
oldconfig = getattr(py.test, 'config', None)
|
||||||
try:
|
try:
|
||||||
c = py.test.config = pytestConfig()
|
c = py.test.config = self.Config()
|
||||||
c.basetemp = oldconfig.mktemp("reparse", numbered=True)
|
c.basetemp = oldconfig.mktemp("reparse", numbered=True)
|
||||||
c.parse(args)
|
c.parse(args)
|
||||||
return c
|
return c
|
||||||
|
|
|
@ -37,7 +37,7 @@ def pytest_namespace():
|
||||||
File=File, Directory=Directory))
|
File=File, Directory=Directory))
|
||||||
|
|
||||||
def pytest_configure(config):
|
def pytest_configure(config):
|
||||||
# compat
|
py.test.config = config # compatibiltiy
|
||||||
if config.getvalue("exitfirst"):
|
if config.getvalue("exitfirst"):
|
||||||
config.option.maxfail = 1
|
config.option.maxfail = 1
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import py
|
import py
|
||||||
from pytest._core import Conftest
|
from pytest.plugin.config import Conftest
|
||||||
|
|
||||||
def pytest_generate_tests(metafunc):
|
def pytest_generate_tests(metafunc):
|
||||||
if "basedir" in metafunc.funcargnames:
|
if "basedir" in metafunc.funcargnames:
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import py
|
import py
|
||||||
from pytest import main as parseopt
|
from pytest.plugin import config as parseopt
|
||||||
|
|
||||||
class TestParser:
|
class TestParser:
|
||||||
def test_init(self, capsys):
|
def test_init(self, capsys):
|
||||||
|
|
Loading…
Reference in New Issue