1
""" command line options, ini-file and conftest.py processing. """
9
# DON't import pytest here because it causes import cycle troubles
12
import _pytest.hookspec # the extension point definitions
13
import _pytest.assertion
14
from _pytest._pluggy import PluginManager, HookimplMarker, HookspecMarker
16
hookimpl = HookimplMarker("pytest")
17
hookspec = HookspecMarker("pytest")
23
class ConftestImportFailure(Exception):
24
def __init__(self, path, excinfo):
25
Exception.__init__(self, path, excinfo)
27
self.excinfo = excinfo
30
etype, evalue, etb = self.excinfo
31
formatted = traceback.format_tb(etb)
32
# The level of the tracebacks we want to print is hand crafted :(
33
return repr(evalue) + '\n' + ''.join(formatted[2:])
36
def main(args=None, plugins=None):
37
""" return exit code, after performing an in-process test run.
39
:arg args: list of command line arguments.
41
:arg plugins: list of plugin objects to be auto-registered during
46
config = _prepareconfig(args, plugins)
47
except ConftestImportFailure as e:
48
tw = py.io.TerminalWriter(sys.stderr)
49
for line in traceback.format_exception(*e.excinfo):
50
tw.line(line.rstrip(), red=True)
51
tw.line("ERROR: could not load %s\n" % (e.path), red=True)
55
config.pluginmanager.check_pending()
56
return config.hook.pytest_cmdline_main(config=config)
58
config._ensure_unconfigure()
59
except UsageError as e:
61
sys.stderr.write("ERROR: %s\n" %(msg,))
64
class cmdline: # compatibility namespace
65
main = staticmethod(main)
67
class UsageError(Exception):
68
""" error in pytest usage or invocation"""
73
"mark main terminal runner python fixtures debugging unittest capture skipping "
74
"tmpdir monkeypatch recwarn pastebin helpconfig nose assertion "
75
"junitxml resultlog doctest cacheprovider freeze_support "
76
"setuponly setupplan").split()
78
builtin_plugins = set(default_plugins)
79
builtin_plugins.add("pytester")
82
def _preloadplugins():
84
_preinit.append(get_config())
88
return _preinit.pop(0)
89
# subsequent calls to main will create a fresh instance
90
pluginmanager = PytestPluginManager()
91
config = Config(pluginmanager)
92
for spec in default_plugins:
93
pluginmanager.import_plugin(spec)
96
def get_plugin_manager():
98
Obtain a new instance of the
99
:py:class:`_pytest.config.PytestPluginManager`, with default plugins
102
This function can be used by integration with other tools, like hooking
103
into pytest to run tests into an IDE.
105
return get_config().pluginmanager
107
def _prepareconfig(args=None, plugins=None):
111
elif isinstance(args, py.path.local):
113
elif not isinstance(args, (tuple, list)):
114
if not isinstance(args, str):
115
raise ValueError("not a string or argument list: %r" % (args,))
116
args = shlex.split(args, posix=sys.platform != "win32")
117
from _pytest import deprecated
118
warning = deprecated.MAIN_STR_ARGS
119
config = get_config()
120
pluginmanager = config.pluginmanager
123
for plugin in plugins:
124
if isinstance(plugin, py.builtin._basestring):
125
pluginmanager.consider_pluginarg(plugin)
127
pluginmanager.register(plugin)
129
config.warn('C1', warning)
130
return pluginmanager.hook.pytest_cmdline_parse(
131
pluginmanager=pluginmanager, args=args)
132
except BaseException:
133
config._ensure_unconfigure()
137
class PytestPluginManager(PluginManager):
139
Overwrites :py:class:`pluggy.PluginManager` to add pytest-specific
142
* loading plugins from the command line, ``PYTEST_PLUGIN`` env variable and
143
``pytest_plugins`` global variables found in plugins being loaded;
144
* ``conftest.py`` loading during start-up;
147
super(PytestPluginManager, self).__init__("pytest", implprefix="pytest_")
148
self._conftest_plugins = set()
150
# state related to local conftest plugins
151
self._path2confmods = {}
152
self._conftestpath2mod = {}
153
self._confcutdir = None
154
self._noconftest = False
155
self._duplicatepaths = set()
157
self.add_hookspecs(_pytest.hookspec)
159
if os.environ.get('PYTEST_DEBUG'):
161
encoding = getattr(err, 'encoding', 'utf8')
163
err = py.io.dupfile(err, encoding=encoding)
166
self.trace.root.setwriter(err.write)
167
self.enable_tracing()
169
# Config._consider_importhook will set a real object if required.
170
self.rewrite_hook = _pytest.assertion.DummyRewriteHook()
172
def addhooks(self, module_or_class):
176
Use :py:meth:`pluggy.PluginManager.add_hookspecs` instead.
178
warning = dict(code="I2",
179
fslocation=_pytest._code.getfslineno(sys._getframe(1)),
181
message="use pluginmanager.add_hookspecs instead of "
182
"deprecated addhooks() method.")
184
return self.add_hookspecs(module_or_class)
186
def parse_hookimpl_opts(self, plugin, name):
187
# pytest hooks are always prefixed with pytest_
188
# so we avoid accessing possibly non-readable attributes
190
if not name.startswith("pytest_"):
192
# ignore some historic special names which can not be hooks anyway
193
if name == "pytest_plugins" or name.startswith("pytest_funcarg__"):
196
method = getattr(plugin, name)
197
opts = super(PytestPluginManager, self).parse_hookimpl_opts(plugin, name)
199
for name in ("tryfirst", "trylast", "optionalhook", "hookwrapper"):
200
opts.setdefault(name, hasattr(method, name))
203
def parse_hookspec_opts(self, module_or_class, name):
204
opts = super(PytestPluginManager, self).parse_hookspec_opts(
205
module_or_class, name)
207
method = getattr(module_or_class, name)
208
if name.startswith("pytest_"):
209
opts = {"firstresult": hasattr(method, "firstresult"),
210
"historic": hasattr(method, "historic")}
213
def _verify_hook(self, hook, hookmethod):
214
super(PytestPluginManager, self)._verify_hook(hook, hookmethod)
215
if "__multicall__" in hookmethod.argnames:
216
fslineno = _pytest._code.getfslineno(hookmethod.function)
217
warning = dict(code="I1",
220
message="%r hook uses deprecated __multicall__ "
221
"argument" % (hook.name))
224
def register(self, plugin, name=None):
225
ret = super(PytestPluginManager, self).register(plugin, name)
227
self.hook.pytest_plugin_registered.call_historic(
228
kwargs=dict(plugin=plugin, manager=self))
231
def getplugin(self, name):
232
# support deprecated naming because plugins (xdist e.g.) use it
233
return self.get_plugin(name)
235
def hasplugin(self, name):
236
"""Return True if the plugin with the given name is registered."""
237
return bool(self.get_plugin(name))
239
def pytest_configure(self, config):
240
# XXX now that the pluginmanager exposes hookimpl(tryfirst...)
241
# we should remove tryfirst/trylast as markers
242
config.addinivalue_line("markers",
243
"tryfirst: mark a hook implementation function such that the "
244
"plugin machinery will try to call it first/as early as possible.")
245
config.addinivalue_line("markers",
246
"trylast: mark a hook implementation function such that the "
247
"plugin machinery will try to call it last/as late as possible.")
249
def _warn(self, message):
250
kwargs = message if isinstance(message, dict) else {
256
self.hook.pytest_logwarning.call_historic(kwargs=kwargs)
259
# internal API for local conftest plugin handling
261
def _set_initial_conftests(self, namespace):
262
""" load initial conftest files given a preparsed "namespace".
263
As conftest files may add their own command line options
264
which have arguments ('--my-opt somepath') we might get some
265
false positives. All builtin and 3rd party plugins will have
266
been loaded, however, so common options will not confuse our logic
269
current = py.path.local()
270
self._confcutdir = current.join(namespace.confcutdir, abs=True) \
271
if namespace.confcutdir else None
272
self._noconftest = namespace.noconftest
273
testpaths = namespace.file_or_dir
275
for path in testpaths:
277
# remove node-id syntax
281
anchor = current.join(path, abs=1)
282
if exists(anchor): # we found some file object
283
self._try_load_conftest(anchor)
286
self._try_load_conftest(current)
288
def _try_load_conftest(self, anchor):
289
self._getconftestmodules(anchor)
290
# let's also consider test* subdirs
291
if anchor.check(dir=1):
292
for x in anchor.listdir("test*"):
294
self._getconftestmodules(x)
296
def _getconftestmodules(self, path):
300
return self._path2confmods[path]
303
clist = self._getconftestmodules(path.dirpath())
305
# XXX these days we may rather want to use config.rootdir
306
# and allow users to opt into looking into the rootdir parent
307
# directories instead of requiring to specify confcutdir
309
for parent in path.parts():
310
if self._confcutdir and self._confcutdir.relto(parent):
312
conftestpath = parent.join("conftest.py")
313
if conftestpath.isfile():
314
mod = self._importconftest(conftestpath)
317
self._path2confmods[path] = clist
320
def _rget_with_confmod(self, name, path):
321
modules = self._getconftestmodules(path)
322
for mod in reversed(modules):
324
return mod, getattr(mod, name)
325
except AttributeError:
329
def _importconftest(self, conftestpath):
331
return self._conftestpath2mod[conftestpath]
333
pkgpath = conftestpath.pypkgpath()
335
_ensure_removed_sysmodule(conftestpath.purebasename)
337
mod = conftestpath.pyimport()
339
raise ConftestImportFailure(conftestpath, sys.exc_info())
341
self._conftest_plugins.add(mod)
342
self._conftestpath2mod[conftestpath] = mod
343
dirpath = conftestpath.dirpath()
344
if dirpath in self._path2confmods:
345
for path, mods in self._path2confmods.items():
346
if path and path.relto(dirpath) or path == dirpath:
347
assert mod not in mods
349
self.trace("loaded conftestmodule %r" %(mod))
350
self.consider_conftest(mod)
354
# API for bootstrapping plugin loading
358
def consider_preparse(self, args):
359
for opt1,opt2 in zip(args, args[1:]):
361
self.consider_pluginarg(opt2)
363
def consider_pluginarg(self, arg):
364
if arg.startswith("no:"):
366
self.set_blocked(name)
367
if not name.startswith("pytest_"):
368
self.set_blocked("pytest_" + name)
370
self.import_plugin(arg)
372
def consider_conftest(self, conftestmodule):
373
if self.register(conftestmodule, name=conftestmodule.__file__):
374
self.consider_module(conftestmodule)
376
def consider_env(self):
377
self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS"))
379
def consider_module(self, mod):
380
plugins = getattr(mod, 'pytest_plugins', [])
381
if isinstance(plugins, str):
383
self.rewrite_hook.mark_rewrite(*plugins)
384
self._import_plugin_specs(plugins)
386
def _import_plugin_specs(self, spec):
388
if isinstance(spec, str):
389
spec = spec.split(",")
390
for import_spec in spec:
391
self.import_plugin(import_spec)
393
def import_plugin(self, modname):
394
# most often modname refers to builtin modules, e.g. "pytester",
395
# "terminal" or "capture". Those plugins are registered under their
396
# basename for historic purposes but must be imported with the
398
assert isinstance(modname, str)
399
if self.get_plugin(modname) is not None:
401
if modname in builtin_plugins:
402
importspec = "_pytest." + modname
406
__import__(importspec)
407
except ImportError as e:
408
new_exc = ImportError('Error importing plugin "%s": %s' % (modname, e))
409
# copy over name and path attributes
410
for attr in ('name', 'path'):
412
setattr(new_exc, attr, getattr(e, attr))
414
except Exception as e:
416
if not hasattr(pytest, 'skip') or not isinstance(e, pytest.skip.Exception):
418
self._warn("skipped plugin %r: %s" %((modname, e.msg)))
420
mod = sys.modules[importspec]
421
self.register(mod, modname)
422
self.consider_module(mod)
426
""" Parser for command line arguments and ini-file values.
428
:ivar extra_info: dict of generic param -> value to display in case
429
there's an error processing the command line arguments.
432
def __init__(self, usage=None, processopt=None):
433
self._anonymous = OptionGroup("custom options", parser=self)
435
self._processopt = processopt
441
def processoption(self, option):
444
self._processopt(option)
446
def getgroup(self, name, description="", after=None):
447
""" get (or create) a named option Group.
449
:name: name of the option group.
450
:description: long description for --help output.
451
:after: name of other group, used for ordering --help output.
453
The returned group object has an ``addoption`` method with the same
454
signature as :py:func:`parser.addoption
455
<_pytest.config.Parser.addoption>` but will be shown in the
456
respective group in the output of ``pytest. --help``.
458
for group in self._groups:
459
if group.name == name:
461
group = OptionGroup(name, description, parser=self)
463
for i, grp in enumerate(self._groups):
464
if grp.name == after:
466
self._groups.insert(i+1, group)
469
def addoption(self, *opts, **attrs):
470
""" register a command line option.
472
:opts: option names, can be short or long options.
473
:attrs: same attributes which the ``add_option()`` function of the
475
<http://docs.python.org/2/library/argparse.html>`_
478
After command line parsing options are available on the pytest config
479
object via ``config.option.NAME`` where ``NAME`` is usually set
480
by passing a ``dest`` attribute, for example
481
``addoption("--long", dest="NAME", ...)``.
483
self._anonymous.addoption(*opts, **attrs)
485
def parse(self, args, namespace=None):
486
from _pytest._argcomplete import try_argcomplete
487
self.optparser = self._getparser()
488
try_argcomplete(self.optparser)
489
return self.optparser.parse_args([str(x) for x in args], namespace=namespace)
491
def _getparser(self):
492
from _pytest._argcomplete import filescompleter
493
optparser = MyOptionParser(self, self.extra_info)
494
groups = self._groups + [self._anonymous]
497
desc = group.description or group.name
498
arggroup = optparser.add_argument_group(desc)
499
for option in group.options:
502
arggroup.add_argument(*n, **a)
503
# bash like autocompletion for dirs (appending '/')
504
optparser.add_argument(FILE_OR_DIR, nargs='*').completer=filescompleter
507
def parse_setoption(self, args, option, namespace=None):
508
parsedoption = self.parse(args, namespace=namespace)
509
for name, value in parsedoption.__dict__.items():
510
setattr(option, name, value)
511
return getattr(parsedoption, FILE_OR_DIR)
513
def parse_known_args(self, args, namespace=None):
514
"""parses and returns a namespace object with known arguments at this
517
return self.parse_known_and_unknown_args(args, namespace=namespace)[0]
519
def parse_known_and_unknown_args(self, args, namespace=None):
520
"""parses and returns a namespace object with known arguments, and
521
the remaining arguments unknown at this point.
523
optparser = self._getparser()
524
args = [str(x) for x in args]
525
return optparser.parse_known_args(args, namespace=namespace)
527
def addini(self, name, help, type=None, default=None):
528
""" register an ini-file option.
530
:name: name of the ini-variable
531
:type: type of the variable, can be ``pathlist``, ``args``, ``linelist``
533
:default: default value if no ini-file option exists but is queried.
535
The value of ini-variables can be retrieved via a call to
536
:py:func:`config.getini(name) <_pytest.config.Config.getini>`.
538
assert type in (None, "pathlist", "args", "linelist", "bool")
539
self._inidict[name] = (help, type, default)
540
self._ininames.append(name)
543
class ArgumentError(Exception):
545
Raised if an Argument instance is created with invalid or
546
inconsistent arguments.
549
def __init__(self, msg, option):
551
self.option_id = str(option)
555
return "option %s: %s" % (self.option_id, self.msg)
561
"""class that mimics the necessary behaviour of optparse.Option
563
its currently a least effort implementation
564
and ignoring choices and integer prefixes
565
https://docs.python.org/3/library/optparse.html#optparse-standard-option-types
574
def __init__(self, *names, **attrs):
575
"""store parms in private vars for use in add_argument"""
577
self._short_opts = []
579
self.dest = attrs.get('dest')
580
if '%default' in (attrs.get('help') or ''):
582
'pytest now uses argparse. "%default" should be'
583
' changed to "%(default)s" ',
591
# this might raise a keyerror as well, don't want to catch that
592
if isinstance(typ, py.builtin._basestring):
595
'type argument to addoption() is a string %r.'
596
' For parsearg this is optional and when supplied '
598
' (options: %s)' % (typ, names),
601
# argparse expects a type here take it from
602
# the type of the first element
603
attrs['type'] = type(attrs['choices'][0])
606
'type argument to addoption() is a string %r.'
607
' For parsearg this should be a type.'
608
' (options: %s)' % (typ, names),
611
attrs['type'] = Argument._typ_map[typ]
612
# used in test_parseopt -> test_parse_defaultgetter
613
self.type = attrs['type']
617
# attribute existence is tested in Config._processopt
618
self.default = attrs['default']
621
self._set_opt_strings(names)
624
self.dest = self._long_opts[0][2:].replace('-', '_')
627
self.dest = self._short_opts[0][1:]
630
'need a long or short option', self)
633
return self._short_opts + self._long_opts
636
# update any attributes set by processopt
637
attrs = 'default dest help'.split()
639
attrs.append(self.dest)
642
self._attrs[attr] = getattr(self, attr)
643
except AttributeError:
645
if self._attrs.get('help'):
646
a = self._attrs['help']
647
a = a.replace('%default', '%(default)s')
648
#a = a.replace('%prog', '%(prog)s')
649
self._attrs['help'] = a
652
def _set_opt_strings(self, opts):
653
"""directly from optparse
655
might not be necessary as this is passed to argparse later on"""
659
"invalid option string %r: "
660
"must be at least two characters long" % opt, self)
662
if not (opt[0] == "-" and opt[1] != "-"):
664
"invalid short option string %r: "
665
"must be of the form -x, (x any non-dash char)" % opt,
667
self._short_opts.append(opt)
669
if not (opt[0:2] == "--" and opt[2] != "-"):
671
"invalid long option string %r: "
672
"must start with --, followed by non-dash" % opt,
674
self._long_opts.append(opt)
679
args += ['_short_opts: ' + repr(self._short_opts)]
681
args += ['_long_opts: ' + repr(self._long_opts)]
682
args += ['dest: ' + repr(self.dest)]
683
if hasattr(self, 'type'):
684
args += ['type: ' + repr(self.type)]
685
if hasattr(self, 'default'):
686
args += ['default: ' + repr(self.default)]
687
return 'Argument({0})'.format(', '.join(args))
691
def __init__(self, name, description="", parser=None):
693
self.description = description
697
def addoption(self, *optnames, **attrs):
698
""" add an option to this group.
700
if a shortened version of a long option is specified it will
701
be suppressed in the help. addoption('--twowords', '--two-words')
702
results in help showing '--two-words' only, but --twowords gets
703
accepted **and** the automatic destination is in args.twowords
705
conflict = set(optnames).intersection(
706
name for opt in self.options for name in opt.names())
708
raise ValueError("option names %s already added" % conflict)
709
option = Argument(*optnames, **attrs)
710
self._addoption_instance(option, shortupper=False)
712
def _addoption(self, *optnames, **attrs):
713
option = Argument(*optnames, **attrs)
714
self._addoption_instance(option, shortupper=True)
716
def _addoption_instance(self, option, shortupper=False):
718
for opt in option._short_opts:
719
if opt[0] == '-' and opt[1].islower():
720
raise ValueError("lowercase shortoptions reserved")
722
self.parser.processoption(option)
723
self.options.append(option)
726
class MyOptionParser(argparse.ArgumentParser):
727
def __init__(self, parser, extra_info=None):
730
self._parser = parser
731
argparse.ArgumentParser.__init__(self, usage=parser._usage,
732
add_help=False, formatter_class=DropShorterLongHelpFormatter)
733
# extra_info is a dict of (param -> value) to display if there's
734
# an usage error to provide more contextual information to the user
735
self.extra_info = extra_info
737
def parse_args(self, args=None, namespace=None):
738
"""allow splitting of positional arguments"""
739
args, argv = self.parse_known_args(args, namespace)
742
if arg and arg[0] == '-':
743
lines = ['unrecognized arguments: %s' % (' '.join(argv))]
744
for k, v in sorted(self.extra_info.items()):
745
lines.append(' %s: %s' % (k, v))
746
self.error('\n'.join(lines))
747
getattr(args, FILE_OR_DIR).extend(argv)
751
class DropShorterLongHelpFormatter(argparse.HelpFormatter):
752
"""shorten help for long options that differ only in extra hyphens
754
- collapse **long** options that are the same except for extra hyphens
755
- special action attribute map_long_option allows surpressing additional
757
- shortcut if there are only two options and one of them is a short one
758
- cache result on action object as this is called at least 2 times
760
def _format_action_invocation(self, action):
761
orgstr = argparse.HelpFormatter._format_action_invocation(self, action)
762
if orgstr and orgstr[0] != '-': # only optional arguments
764
res = getattr(action, '_formatted_action_invocation', None)
767
options = orgstr.split(', ')
768
if len(options) == 2 and (len(options[0]) == 2 or len(options[1]) == 2):
769
# a shortcut for '-h, --help' or '--abc', '-a'
770
action._formatted_action_invocation = orgstr
773
option_map = getattr(action, 'map_long_option', {})
774
if option_map is None:
777
for option in options:
778
if len(option) == 2 or option[2] == ' ':
780
if not option.startswith('--'):
781
raise ArgumentError('long optional argument without "--": [%s]'
783
xxoption = option[2:]
784
if xxoption.split()[0] not in option_map:
785
shortened = xxoption.replace('-', '')
786
if shortened not in short_long or \
787
len(short_long[shortened]) < len(xxoption):
788
short_long[shortened] = xxoption
789
# now short_long has been filled out to the longest with dashes
790
# **and** we keep the right option ordering from add_argument
791
for option in options: #
792
if len(option) == 2 or option[2] == ' ':
793
return_list.append(option)
794
if option[2:] == short_long.get(option.replace('-', '')):
795
return_list.append(option.replace(' ', '='))
796
action._formatted_action_invocation = ', '.join(return_list)
797
return action._formatted_action_invocation
801
def _ensure_removed_sysmodule(modname):
803
del sys.modules[modname]
807
class CmdOptions(object):
808
""" holds cmdline options as attributes."""
809
def __init__(self, values=()):
810
self.__dict__.update(values)
812
return "<CmdOptions %r>" %(self.__dict__,)
814
return CmdOptions(self.__dict__)
821
FILE_OR_DIR = 'file_or_dir'
823
class Config(object):
824
""" access to configuration values, pluginmanager and plugin hooks. """
826
def __init__(self, pluginmanager):
827
#: access to command line option as attributes.
828
#: (deprecated), use :py:func:`getoption() <_pytest.config.Config.getoption>` instead
829
self.option = CmdOptions()
831
self._parser = Parser(
832
usage="%%(prog)s [options] [%s] [%s] [...]" % (_a, _a),
833
processopt=self._processopt,
835
#: a pluginmanager instance
836
self.pluginmanager = pluginmanager
837
self.trace = self.pluginmanager.trace.root.get("config")
838
self.hook = self.pluginmanager.hook
842
self._warn = self.pluginmanager._warn
843
self.pluginmanager.register(self, "pytestconfig")
844
self._configured = False
848
self.hook.pytest_namespace.call_historic(do_setns, {})
849
self.hook.pytest_addoption.call_historic(kwargs=dict(parser=self._parser))
851
def add_cleanup(self, func):
852
""" Add a function to be called when the config object gets out of
853
use (usually coninciding with pytest_unconfigure)."""
854
self._cleanup.append(func)
856
def _do_configure(self):
857
assert not self._configured
858
self._configured = True
859
self.hook.pytest_configure.call_historic(kwargs=dict(config=self))
861
def _ensure_unconfigure(self):
863
self._configured = False
864
self.hook.pytest_unconfigure(config=self)
865
self.hook.pytest_configure._call_history = []
867
fin = self._cleanup.pop()
870
def warn(self, code, message, fslocation=None):
871
""" generate a warning for this test session. """
872
self.hook.pytest_logwarning.call_historic(kwargs=dict(
873
code=code, message=message,
874
fslocation=fslocation, nodeid=None))
876
def get_terminal_writer(self):
877
return self.pluginmanager.get_plugin("terminalreporter")._tw
879
def pytest_cmdline_parse(self, pluginmanager, args):
880
# REF1 assert self == pluginmanager.config, (self, pluginmanager.config)
884
def notify_exception(self, excinfo, option=None):
885
if option and option.fulltrace:
889
excrepr = excinfo.getrepr(funcargs=True,
890
showlocals=getattr(option, 'showlocals', False),
893
res = self.hook.pytest_internalerror(excrepr=excrepr,
895
if not py.builtin.any(res):
896
for line in str(excrepr).split("\n"):
897
sys.stderr.write("INTERNALERROR> %s\n" %line)
900
def cwd_relative_nodeid(self, nodeid):
901
# nodeid's are relative to the rootpath, compute relative to cwd
902
if self.invocation_dir != self.rootdir:
903
fullpath = self.rootdir.join(nodeid)
904
nodeid = self.invocation_dir.bestrelpath(fullpath)
908
def fromdictargs(cls, option_dict, args):
909
""" constructor useable for subprocesses. """
910
config = get_config()
911
config.option.__dict__.update(option_dict)
912
config.parse(args, addopts=False)
913
for x in config.option.plugins:
914
config.pluginmanager.consider_pluginarg(x)
917
def _processopt(self, opt):
918
for name in opt._short_opts + opt._long_opts:
919
self._opt2dest[name] = opt.dest
921
if hasattr(opt, 'default') and opt.dest:
922
if not hasattr(self.option, opt.dest):
923
setattr(self.option, opt.dest, opt.default)
925
@hookimpl(trylast=True)
926
def pytest_load_initial_conftests(self, early_config):
927
self.pluginmanager._set_initial_conftests(early_config.known_args_namespace)
929
def _initini(self, args):
930
ns, unknown_args = self._parser.parse_known_and_unknown_args(args, namespace=self.option.copy())
931
r = determine_setup(ns.inifilename, ns.file_or_dir + unknown_args, warnfunc=self.warn)
932
self.rootdir, self.inifile, self.inicfg = r
933
self._parser.extra_info['rootdir'] = self.rootdir
934
self._parser.extra_info['inifile'] = self.inifile
935
self.invocation_dir = py.path.local()
936
self._parser.addini('addopts', 'extra command line options', 'args')
937
self._parser.addini('minversion', 'minimally required pytest version')
939
def _consider_importhook(self, args, entrypoint_name):
940
"""Install the PEP 302 import hook if using assertion re-writing.
942
Needs to parse the --assert=<mode> option from the commandline
943
and find all the installed plugins to mark them for re-writing
946
ns, unknown_args = self._parser.parse_known_and_unknown_args(args)
948
if mode == 'rewrite':
950
hook = _pytest.assertion.install_importhook(self)
955
self.pluginmanager.rewrite_hook = hook
956
for entrypoint in pkg_resources.iter_entry_points('pytest11'):
957
# 'RECORD' available for plugins installed normally (pip install)
958
# 'SOURCES.txt' available for plugins installed in dev mode (pip install -e)
959
# for installed plugins 'SOURCES.txt' returns an empty list, and vice-versa
960
# so it shouldn't be an issue
961
for metadata in ('RECORD', 'SOURCES.txt'):
962
for entry in entrypoint.dist._get_metadata(metadata):
963
fn = entry.split(',')[0]
964
is_simple_module = os.sep not in fn and fn.endswith('.py')
965
is_package = fn.count(os.sep) == 1 and fn.endswith('__init__.py')
967
module_name, ext = os.path.splitext(fn)
968
hook.mark_rewrite(module_name)
970
package_name = os.path.dirname(fn)
971
hook.mark_rewrite(package_name)
972
self._warn_about_missing_assertion(mode)
974
def _warn_about_missing_assertion(self, mode):
977
except AssertionError:
981
sys.stderr.write("WARNING: ASSERTIONS ARE NOT EXECUTED"
982
" and FAILING TESTS WILL PASS. Are you"
985
sys.stderr.write("WARNING: assertions not in test modules or"
986
" plugins will be ignored"
987
" because assert statements are not executed "
988
"by the underlying Python interpreter "
989
"(are you using python -O?)\n")
991
def _preparse(self, args, addopts=True):
994
args[:] = shlex.split(os.environ.get('PYTEST_ADDOPTS', '')) + args
995
args[:] = self.getini("addopts") + args
997
entrypoint_name = 'pytest11'
998
self._consider_importhook(args, entrypoint_name)
999
self.pluginmanager.consider_preparse(args)
1000
self.pluginmanager.load_setuptools_entrypoints(entrypoint_name)
1001
self.pluginmanager.consider_env()
1002
self.known_args_namespace = ns = self._parser.parse_known_args(args, namespace=self.option.copy())
1003
if self.known_args_namespace.confcutdir is None and self.inifile:
1004
confcutdir = py.path.local(self.inifile).dirname
1005
self.known_args_namespace.confcutdir = confcutdir
1007
self.hook.pytest_load_initial_conftests(early_config=self,
1008
args=args, parser=self._parser)
1009
except ConftestImportFailure:
1010
e = sys.exc_info()[1]
1011
if ns.help or ns.version:
1012
# we don't want to prevent --help/--version to work
1013
# so just let is pass and print a warning at the end
1014
self._warn("could not load initial conftests (%s)\n" % e.path)
1018
def _checkversion(self):
1020
minver = self.inicfg.get('minversion', None)
1022
ver = minver.split(".")
1023
myver = pytest.__version__.split(".")
1025
raise pytest.UsageError(
1026
"%s:%d: requires pytest-%s, actual pytest-%s'" %(
1027
self.inicfg.config.path, self.inicfg.lineof('minversion'),
1028
minver, pytest.__version__))
1030
def parse(self, args, addopts=True):
1031
# parse given cmdline arguments into this config object.
1032
assert not hasattr(self, 'args'), (
1033
"can only parse cmdline args at most once per Config object")
1034
self._origargs = args
1035
self.hook.pytest_addhooks.call_historic(
1036
kwargs=dict(pluginmanager=self.pluginmanager))
1037
self._preparse(args, addopts=addopts)
1038
# XXX deprecated hook:
1039
self.hook.pytest_cmdline_preparse(config=self, args=args)
1040
args = self._parser.parse_setoption(args, self.option, namespace=self.option)
1043
if cwd == self.rootdir:
1044
args = self.getini('testpaths')
1049
def addinivalue_line(self, name, line):
1050
""" add a line to an ini-file option. The option must have been
1051
declared but might not yet be set in which case the line becomes the
1052
the first line in its value. """
1053
x = self.getini(name)
1054
assert isinstance(x, list)
1055
x.append(line) # modifies the cached list inline
1057
def getini(self, name):
1058
""" return configuration value from an :ref:`ini file <inifiles>`. If the
1059
specified name hasn't been registered through a prior
1060
:py:func:`parser.addini <pytest.config.Parser.addini>`
1061
call (usually from a plugin), a ValueError is raised. """
1063
return self._inicache[name]
1065
self._inicache[name] = val = self._getini(name)
1068
def _getini(self, name):
1070
description, type, default = self._parser._inidict[name]
1072
raise ValueError("unknown configuration value: %r" %(name,))
1073
value = self._get_override_ini_value(name)
1076
value = self.inicfg[name]
1078
if default is not None:
1083
if type == "pathlist":
1084
dp = py.path.local(self.inicfg.config.path).dirpath()
1086
for relpath in shlex.split(value):
1087
l.append(dp.join(relpath, abs=True))
1089
elif type == "args":
1090
return shlex.split(value)
1091
elif type == "linelist":
1092
return [t for t in map(lambda x: x.strip(), value.split("\n")) if t]
1093
elif type == "bool":
1094
return bool(_strtobool(value.strip()))
1099
def _getconftest_pathlist(self, name, path):
1101
mod, relroots = self.pluginmanager._rget_with_confmod(name, path)
1104
modpath = py.path.local(mod.__file__).dirpath()
1106
for relroot in relroots:
1107
if not isinstance(relroot, py.path.local):
1108
relroot = relroot.replace("/", py.path.local.sep)
1109
relroot = modpath.join(relroot, abs=True)
1113
def _get_override_ini_value(self, name):
1115
# override_ini is a list of list, to support both -o foo1=bar1 foo2=bar2 and
1116
# and -o foo1=bar1 -o foo2=bar2 options
1117
# always use the last item if multiple value set for same ini-name,
1118
# e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2
1119
if self.getoption("override_ini", None):
1120
for ini_config_list in self.option.override_ini:
1121
for ini_config in ini_config_list:
1122
(key, user_ini_value) = ini_config.split("=", 1)
1124
value = user_ini_value
1127
def getoption(self, name, default=notset, skip=False):
1128
""" return command line option value.
1130
:arg name: name of the option. You may also specify
1131
the literal ``--OPT`` option instead of the "dest" option name.
1132
:arg default: default value if no option of that name exists.
1133
:arg skip: if True raise pytest.skip if option does not exists
1134
or has a None value.
1136
name = self._opt2dest.get(name, name)
1138
val = getattr(self.option, name)
1139
if val is None and skip:
1140
raise AttributeError(name)
1142
except AttributeError:
1143
if default is not notset:
1147
pytest.skip("no %r option found" %(name,))
1148
raise ValueError("no option named %r" % (name,))
1150
def getvalue(self, name, path=None):
1151
""" (deprecated, use getoption()) """
1152
return self.getoption(name)
1154
def getvalueorskip(self, name, path=None):
1155
""" (deprecated, use getoption(skip=True)) """
1156
return self.getoption(name, skip=True)
1158
def exists(path, ignore=EnvironmentError):
1164
def getcfg(args, warnfunc=None):
1166
Search the list of arguments for a valid ini-file for pytest,
1167
and return a tuple of (rootdir, inifile, cfg-dict).
1169
note: warnfunc is an optional function used to warn
1170
about ini-files that use deprecated features.
1171
This parameter should be removed when pytest
1172
adopts standard deprecation warnings (#1804).
1174
from _pytest.deprecated import SETUP_CFG_PYTEST
1175
inibasenames = ["pytest.ini", "tox.ini", "setup.cfg"]
1176
args = [x for x in args if not str(x).startswith("-")]
1178
args = [py.path.local()]
1180
arg = py.path.local(arg)
1181
for base in arg.parts(reverse=True):
1182
for inibasename in inibasenames:
1183
p = base.join(inibasename)
1185
iniconfig = py.iniconfig.IniConfig(p)
1186
if 'pytest' in iniconfig.sections:
1187
if inibasename == 'setup.cfg' and warnfunc:
1188
warnfunc('C1', SETUP_CFG_PYTEST)
1189
return base, p, iniconfig['pytest']
1190
if inibasename == 'setup.cfg' and 'tool:pytest' in iniconfig.sections:
1191
return base, p, iniconfig['tool:pytest']
1192
elif inibasename == "pytest.ini":
1193
# allowed to be empty
1195
return None, None, None
1198
def get_common_ancestor(args):
1199
# args are what we get after early command line parsing (usually
1200
# strings, but can be py.path.local objects as well)
1201
common_ancestor = None
1203
if str(arg)[0] == "-":
1205
p = py.path.local(arg)
1208
if common_ancestor is None:
1211
if p.relto(common_ancestor) or p == common_ancestor:
1213
elif common_ancestor.relto(p):
1216
shared = p.common(common_ancestor)
1217
if shared is not None:
1218
common_ancestor = shared
1219
if common_ancestor is None:
1220
common_ancestor = py.path.local()
1221
elif common_ancestor.isfile():
1222
common_ancestor = common_ancestor.dirpath()
1223
return common_ancestor
1226
def get_dirs_from_args(args):
1227
return [d for d in (py.path.local(x) for x in args
1228
if not str(x).startswith("-"))
1232
def determine_setup(inifile, args, warnfunc=None):
1233
dirs = get_dirs_from_args(args)
1235
iniconfig = py.iniconfig.IniConfig(inifile)
1237
inicfg = iniconfig["pytest"]
1240
rootdir = get_common_ancestor(dirs)
1242
ancestor = get_common_ancestor(dirs)
1243
rootdir, inifile, inicfg = getcfg([ancestor], warnfunc=warnfunc)
1245
for rootdir in ancestor.parts(reverse=True):
1246
if rootdir.join("setup.py").exists():
1249
rootdir, inifile, inicfg = getcfg(dirs, warnfunc=warnfunc)
1251
rootdir = get_common_ancestor([py.path.local(), ancestor])
1252
is_fs_root = os.path.splitdrive(str(rootdir))[1] == os.sep
1255
return rootdir, inifile, inicfg or {}
1258
def setns(obj, dic):
1260
for name, value in dic.items():
1261
if isinstance(value, dict):
1262
mod = getattr(obj, name, None)
1264
modname = "pytest.%s" % name
1265
mod = types.ModuleType(modname)
1266
sys.modules[modname] = mod
1268
setattr(obj, name, mod)
1269
obj.__all__.append(name)
1272
setattr(obj, name, value)
1273
obj.__all__.append(name)
1275
# pytest.__all__.append(name)
1276
setattr(pytest, name, value)
1279
def create_terminal_writer(config, *args, **kwargs):
1280
"""Create a TerminalWriter instance configured according to the options
1281
in the config object. Every code which requires a TerminalWriter object
1282
and has access to a config object should use this function.
1284
tw = py.io.TerminalWriter(*args, **kwargs)
1285
if config.option.color == 'yes':
1287
if config.option.color == 'no':
1288
tw.hasmarkup = False
1292
def _strtobool(val):
1293
"""Convert a string representation of truth to true (1) or false (0).
1295
True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
1296
are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if
1297
'val' is anything else.
1299
.. note:: copied from distutils.util
1302
if val in ('y', 'yes', 't', 'true', 'on', '1'):
1304
elif val in ('n', 'no', 'f', 'false', 'off', '0'):
1307
raise ValueError("invalid truth value %r" % (val,))