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
15
from _pytest.compat import safe_str
17
hookimpl = HookimplMarker("pytest")
18
hookspec = HookspecMarker("pytest")
24
class ConftestImportFailure(Exception):
25
def __init__(self, path, excinfo):
26
Exception.__init__(self, path, excinfo)
28
self.excinfo = excinfo
31
etype, evalue, etb = self.excinfo
32
formatted = traceback.format_tb(etb)
33
# The level of the tracebacks we want to print is hand crafted :(
34
return repr(evalue) + '\n' + ''.join(formatted[2:])
37
def main(args=None, plugins=None):
38
""" return exit code, after performing an in-process test run.
40
:arg args: list of command line arguments.
42
:arg plugins: list of plugin objects to be auto-registered during
47
config = _prepareconfig(args, plugins)
48
except ConftestImportFailure as e:
49
tw = py.io.TerminalWriter(sys.stderr)
50
for line in traceback.format_exception(*e.excinfo):
51
tw.line(line.rstrip(), red=True)
52
tw.line("ERROR: could not load %s\n" % (e.path), red=True)
56
config.pluginmanager.check_pending()
57
return config.hook.pytest_cmdline_main(config=config)
59
config._ensure_unconfigure()
60
except UsageError as e:
62
sys.stderr.write("ERROR: %s\n" %(msg,))
65
class cmdline: # compatibility namespace
66
main = staticmethod(main)
69
class UsageError(Exception):
70
""" error in pytest usage or invocation"""
73
def filename_arg(path, optname):
74
""" Argparse type validator for filename arguments.
76
:path: path of filename
77
:optname: name of the option
79
if os.path.isdir(path):
80
raise UsageError("{0} must be a filename, given: {1}".format(optname, path))
84
def directory_arg(path, optname):
85
"""Argparse type validator for directory arguments.
87
:path: path of directory
88
:optname: name of the option
90
if not os.path.isdir(path):
91
raise UsageError("{0} must be a directory, given: {1}".format(optname, path))
98
"mark main terminal runner python fixtures debugging unittest capture skipping "
99
"tmpdir monkeypatch recwarn pastebin helpconfig nose assertion "
100
"junitxml resultlog doctest cacheprovider freeze_support "
101
"setuponly setupplan").split()
103
builtin_plugins = set(default_plugins)
104
builtin_plugins.add("pytester")
107
def _preloadplugins():
109
_preinit.append(get_config())
113
return _preinit.pop(0)
114
# subsequent calls to main will create a fresh instance
115
pluginmanager = PytestPluginManager()
116
config = Config(pluginmanager)
117
for spec in default_plugins:
118
pluginmanager.import_plugin(spec)
121
def get_plugin_manager():
123
Obtain a new instance of the
124
:py:class:`_pytest.config.PytestPluginManager`, with default plugins
127
This function can be used by integration with other tools, like hooking
128
into pytest to run tests into an IDE.
130
return get_config().pluginmanager
132
def _prepareconfig(args=None, plugins=None):
136
elif isinstance(args, py.path.local):
138
elif not isinstance(args, (tuple, list)):
139
if not isinstance(args, str):
140
raise ValueError("not a string or argument list: %r" % (args,))
141
args = shlex.split(args, posix=sys.platform != "win32")
142
from _pytest import deprecated
143
warning = deprecated.MAIN_STR_ARGS
144
config = get_config()
145
pluginmanager = config.pluginmanager
148
for plugin in plugins:
149
if isinstance(plugin, py.builtin._basestring):
150
pluginmanager.consider_pluginarg(plugin)
152
pluginmanager.register(plugin)
154
config.warn('C1', warning)
155
return pluginmanager.hook.pytest_cmdline_parse(
156
pluginmanager=pluginmanager, args=args)
157
except BaseException:
158
config._ensure_unconfigure()
162
class PytestPluginManager(PluginManager):
164
Overwrites :py:class:`pluggy.PluginManager` to add pytest-specific
167
* loading plugins from the command line, ``PYTEST_PLUGIN`` env variable and
168
``pytest_plugins`` global variables found in plugins being loaded;
169
* ``conftest.py`` loading during start-up;
172
super(PytestPluginManager, self).__init__("pytest", implprefix="pytest_")
173
self._conftest_plugins = set()
175
# state related to local conftest plugins
176
self._path2confmods = {}
177
self._conftestpath2mod = {}
178
self._confcutdir = None
179
self._noconftest = False
180
self._duplicatepaths = set()
182
self.add_hookspecs(_pytest.hookspec)
184
if os.environ.get('PYTEST_DEBUG'):
186
encoding = getattr(err, 'encoding', 'utf8')
188
err = py.io.dupfile(err, encoding=encoding)
191
self.trace.root.setwriter(err.write)
192
self.enable_tracing()
194
# Config._consider_importhook will set a real object if required.
195
self.rewrite_hook = _pytest.assertion.DummyRewriteHook()
197
def addhooks(self, module_or_class):
201
Use :py:meth:`pluggy.PluginManager.add_hookspecs` instead.
203
warning = dict(code="I2",
204
fslocation=_pytest._code.getfslineno(sys._getframe(1)),
206
message="use pluginmanager.add_hookspecs instead of "
207
"deprecated addhooks() method.")
209
return self.add_hookspecs(module_or_class)
211
def parse_hookimpl_opts(self, plugin, name):
212
# pytest hooks are always prefixed with pytest_
213
# so we avoid accessing possibly non-readable attributes
215
if not name.startswith("pytest_"):
217
# ignore some historic special names which can not be hooks anyway
218
if name == "pytest_plugins" or name.startswith("pytest_funcarg__"):
221
method = getattr(plugin, name)
222
opts = super(PytestPluginManager, self).parse_hookimpl_opts(plugin, name)
224
for name in ("tryfirst", "trylast", "optionalhook", "hookwrapper"):
225
opts.setdefault(name, hasattr(method, name))
228
def parse_hookspec_opts(self, module_or_class, name):
229
opts = super(PytestPluginManager, self).parse_hookspec_opts(
230
module_or_class, name)
232
method = getattr(module_or_class, name)
233
if name.startswith("pytest_"):
234
opts = {"firstresult": hasattr(method, "firstresult"),
235
"historic": hasattr(method, "historic")}
238
def _verify_hook(self, hook, hookmethod):
239
super(PytestPluginManager, self)._verify_hook(hook, hookmethod)
240
if "__multicall__" in hookmethod.argnames:
241
fslineno = _pytest._code.getfslineno(hookmethod.function)
242
warning = dict(code="I1",
245
message="%r hook uses deprecated __multicall__ "
246
"argument" % (hook.name))
249
def register(self, plugin, name=None):
250
ret = super(PytestPluginManager, self).register(plugin, name)
252
self.hook.pytest_plugin_registered.call_historic(
253
kwargs=dict(plugin=plugin, manager=self))
256
def getplugin(self, name):
257
# support deprecated naming because plugins (xdist e.g.) use it
258
return self.get_plugin(name)
260
def hasplugin(self, name):
261
"""Return True if the plugin with the given name is registered."""
262
return bool(self.get_plugin(name))
264
def pytest_configure(self, config):
265
# XXX now that the pluginmanager exposes hookimpl(tryfirst...)
266
# we should remove tryfirst/trylast as markers
267
config.addinivalue_line("markers",
268
"tryfirst: mark a hook implementation function such that the "
269
"plugin machinery will try to call it first/as early as possible.")
270
config.addinivalue_line("markers",
271
"trylast: mark a hook implementation function such that the "
272
"plugin machinery will try to call it last/as late as possible.")
274
def _warn(self, message):
275
kwargs = message if isinstance(message, dict) else {
281
self.hook.pytest_logwarning.call_historic(kwargs=kwargs)
284
# internal API for local conftest plugin handling
286
def _set_initial_conftests(self, namespace):
287
""" load initial conftest files given a preparsed "namespace".
288
As conftest files may add their own command line options
289
which have arguments ('--my-opt somepath') we might get some
290
false positives. All builtin and 3rd party plugins will have
291
been loaded, however, so common options will not confuse our logic
294
current = py.path.local()
295
self._confcutdir = current.join(namespace.confcutdir, abs=True) \
296
if namespace.confcutdir else None
297
self._noconftest = namespace.noconftest
298
testpaths = namespace.file_or_dir
300
for path in testpaths:
302
# remove node-id syntax
306
anchor = current.join(path, abs=1)
307
if exists(anchor): # we found some file object
308
self._try_load_conftest(anchor)
311
self._try_load_conftest(current)
313
def _try_load_conftest(self, anchor):
314
self._getconftestmodules(anchor)
315
# let's also consider test* subdirs
316
if anchor.check(dir=1):
317
for x in anchor.listdir("test*"):
319
self._getconftestmodules(x)
321
def _getconftestmodules(self, path):
325
return self._path2confmods[path]
328
clist = self._getconftestmodules(path.dirpath())
330
# XXX these days we may rather want to use config.rootdir
331
# and allow users to opt into looking into the rootdir parent
332
# directories instead of requiring to specify confcutdir
334
for parent in path.parts():
335
if self._confcutdir and self._confcutdir.relto(parent):
337
conftestpath = parent.join("conftest.py")
338
if conftestpath.isfile():
339
mod = self._importconftest(conftestpath)
342
self._path2confmods[path] = clist
345
def _rget_with_confmod(self, name, path):
346
modules = self._getconftestmodules(path)
347
for mod in reversed(modules):
349
return mod, getattr(mod, name)
350
except AttributeError:
354
def _importconftest(self, conftestpath):
356
return self._conftestpath2mod[conftestpath]
358
pkgpath = conftestpath.pypkgpath()
360
_ensure_removed_sysmodule(conftestpath.purebasename)
362
mod = conftestpath.pyimport()
364
raise ConftestImportFailure(conftestpath, sys.exc_info())
366
self._conftest_plugins.add(mod)
367
self._conftestpath2mod[conftestpath] = mod
368
dirpath = conftestpath.dirpath()
369
if dirpath in self._path2confmods:
370
for path, mods in self._path2confmods.items():
371
if path and path.relto(dirpath) or path == dirpath:
372
assert mod not in mods
374
self.trace("loaded conftestmodule %r" %(mod))
375
self.consider_conftest(mod)
379
# API for bootstrapping plugin loading
383
def consider_preparse(self, args):
384
for opt1,opt2 in zip(args, args[1:]):
386
self.consider_pluginarg(opt2)
388
def consider_pluginarg(self, arg):
389
if arg.startswith("no:"):
391
self.set_blocked(name)
392
if not name.startswith("pytest_"):
393
self.set_blocked("pytest_" + name)
395
self.import_plugin(arg)
397
def consider_conftest(self, conftestmodule):
398
if self.register(conftestmodule, name=conftestmodule.__file__):
399
self.consider_module(conftestmodule)
401
def consider_env(self):
402
self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS"))
404
def consider_module(self, mod):
405
self._import_plugin_specs(getattr(mod, 'pytest_plugins', []))
407
def _import_plugin_specs(self, spec):
408
plugins = _get_plugin_specs_as_list(spec)
409
for import_spec in plugins:
410
self.import_plugin(import_spec)
412
def import_plugin(self, modname):
413
# most often modname refers to builtin modules, e.g. "pytester",
414
# "terminal" or "capture". Those plugins are registered under their
415
# basename for historic purposes but must be imported with the
417
assert isinstance(modname, str), "module name as string required, got %r" % modname
418
if self.get_plugin(modname) is not None:
420
if modname in builtin_plugins:
421
importspec = "_pytest." + modname
424
self.rewrite_hook.mark_rewrite(importspec)
426
__import__(importspec)
427
except ImportError as e:
428
new_exc = ImportError('Error importing plugin "%s": %s' % (modname, safe_str(e.args[0])))
429
# copy over name and path attributes
430
for attr in ('name', 'path'):
432
setattr(new_exc, attr, getattr(e, attr))
434
except Exception as e:
436
if not hasattr(pytest, 'skip') or not isinstance(e, pytest.skip.Exception):
438
self._warn("skipped plugin %r: %s" %((modname, e.msg)))
440
mod = sys.modules[importspec]
441
self.register(mod, modname)
442
self.consider_module(mod)
445
def _get_plugin_specs_as_list(specs):
447
Parses a list of "plugin specs" and returns a list of plugin names.
449
Plugin specs can be given as a list of strings separated by "," or already as a list/tuple in
450
which case it is returned as a list. Specs can also be `None` in which case an
451
empty list is returned.
453
if specs is not None:
454
if isinstance(specs, str):
455
specs = specs.split(',') if specs else []
456
if not isinstance(specs, (list, tuple)):
457
raise UsageError("Plugin specs must be a ','-separated string or a "
458
"list/tuple of strings for plugin names. Given: %r" % specs)
464
""" Parser for command line arguments and ini-file values.
466
:ivar extra_info: dict of generic param -> value to display in case
467
there's an error processing the command line arguments.
470
def __init__(self, usage=None, processopt=None):
471
self._anonymous = OptionGroup("custom options", parser=self)
473
self._processopt = processopt
479
def processoption(self, option):
482
self._processopt(option)
484
def getgroup(self, name, description="", after=None):
485
""" get (or create) a named option Group.
487
:name: name of the option group.
488
:description: long description for --help output.
489
:after: name of other group, used for ordering --help output.
491
The returned group object has an ``addoption`` method with the same
492
signature as :py:func:`parser.addoption
493
<_pytest.config.Parser.addoption>` but will be shown in the
494
respective group in the output of ``pytest. --help``.
496
for group in self._groups:
497
if group.name == name:
499
group = OptionGroup(name, description, parser=self)
501
for i, grp in enumerate(self._groups):
502
if grp.name == after:
504
self._groups.insert(i+1, group)
507
def addoption(self, *opts, **attrs):
508
""" register a command line option.
510
:opts: option names, can be short or long options.
511
:attrs: same attributes which the ``add_option()`` function of the
513
<http://docs.python.org/2/library/argparse.html>`_
516
After command line parsing options are available on the pytest config
517
object via ``config.option.NAME`` where ``NAME`` is usually set
518
by passing a ``dest`` attribute, for example
519
``addoption("--long", dest="NAME", ...)``.
521
self._anonymous.addoption(*opts, **attrs)
523
def parse(self, args, namespace=None):
524
from _pytest._argcomplete import try_argcomplete
525
self.optparser = self._getparser()
526
try_argcomplete(self.optparser)
527
return self.optparser.parse_args([str(x) for x in args], namespace=namespace)
529
def _getparser(self):
530
from _pytest._argcomplete import filescompleter
531
optparser = MyOptionParser(self, self.extra_info)
532
groups = self._groups + [self._anonymous]
535
desc = group.description or group.name
536
arggroup = optparser.add_argument_group(desc)
537
for option in group.options:
540
arggroup.add_argument(*n, **a)
541
# bash like autocompletion for dirs (appending '/')
542
optparser.add_argument(FILE_OR_DIR, nargs='*').completer=filescompleter
545
def parse_setoption(self, args, option, namespace=None):
546
parsedoption = self.parse(args, namespace=namespace)
547
for name, value in parsedoption.__dict__.items():
548
setattr(option, name, value)
549
return getattr(parsedoption, FILE_OR_DIR)
551
def parse_known_args(self, args, namespace=None):
552
"""parses and returns a namespace object with known arguments at this
555
return self.parse_known_and_unknown_args(args, namespace=namespace)[0]
557
def parse_known_and_unknown_args(self, args, namespace=None):
558
"""parses and returns a namespace object with known arguments, and
559
the remaining arguments unknown at this point.
561
optparser = self._getparser()
562
args = [str(x) for x in args]
563
return optparser.parse_known_args(args, namespace=namespace)
565
def addini(self, name, help, type=None, default=None):
566
""" register an ini-file option.
568
:name: name of the ini-variable
569
:type: type of the variable, can be ``pathlist``, ``args``, ``linelist``
571
:default: default value if no ini-file option exists but is queried.
573
The value of ini-variables can be retrieved via a call to
574
:py:func:`config.getini(name) <_pytest.config.Config.getini>`.
576
assert type in (None, "pathlist", "args", "linelist", "bool")
577
self._inidict[name] = (help, type, default)
578
self._ininames.append(name)
581
class ArgumentError(Exception):
583
Raised if an Argument instance is created with invalid or
584
inconsistent arguments.
587
def __init__(self, msg, option):
589
self.option_id = str(option)
593
return "option %s: %s" % (self.option_id, self.msg)
599
"""class that mimics the necessary behaviour of optparse.Option
601
its currently a least effort implementation
602
and ignoring choices and integer prefixes
603
https://docs.python.org/3/library/optparse.html#optparse-standard-option-types
612
def __init__(self, *names, **attrs):
613
"""store parms in private vars for use in add_argument"""
615
self._short_opts = []
617
self.dest = attrs.get('dest')
618
if '%default' in (attrs.get('help') or ''):
620
'pytest now uses argparse. "%default" should be'
621
' changed to "%(default)s" ',
629
# this might raise a keyerror as well, don't want to catch that
630
if isinstance(typ, py.builtin._basestring):
633
'type argument to addoption() is a string %r.'
634
' For parsearg this is optional and when supplied'
636
' (options: %s)' % (typ, names),
639
# argparse expects a type here take it from
640
# the type of the first element
641
attrs['type'] = type(attrs['choices'][0])
644
'type argument to addoption() is a string %r.'
645
' For parsearg this should be a type.'
646
' (options: %s)' % (typ, names),
649
attrs['type'] = Argument._typ_map[typ]
650
# used in test_parseopt -> test_parse_defaultgetter
651
self.type = attrs['type']
655
# attribute existence is tested in Config._processopt
656
self.default = attrs['default']
659
self._set_opt_strings(names)
662
self.dest = self._long_opts[0][2:].replace('-', '_')
665
self.dest = self._short_opts[0][1:]
668
'need a long or short option', self)
671
return self._short_opts + self._long_opts
674
# update any attributes set by processopt
675
attrs = 'default dest help'.split()
677
attrs.append(self.dest)
680
self._attrs[attr] = getattr(self, attr)
681
except AttributeError:
683
if self._attrs.get('help'):
684
a = self._attrs['help']
685
a = a.replace('%default', '%(default)s')
686
#a = a.replace('%prog', '%(prog)s')
687
self._attrs['help'] = a
690
def _set_opt_strings(self, opts):
691
"""directly from optparse
693
might not be necessary as this is passed to argparse later on"""
697
"invalid option string %r: "
698
"must be at least two characters long" % opt, self)
700
if not (opt[0] == "-" and opt[1] != "-"):
702
"invalid short option string %r: "
703
"must be of the form -x, (x any non-dash char)" % opt,
705
self._short_opts.append(opt)
707
if not (opt[0:2] == "--" and opt[2] != "-"):
709
"invalid long option string %r: "
710
"must start with --, followed by non-dash" % opt,
712
self._long_opts.append(opt)
717
args += ['_short_opts: ' + repr(self._short_opts)]
719
args += ['_long_opts: ' + repr(self._long_opts)]
720
args += ['dest: ' + repr(self.dest)]
721
if hasattr(self, 'type'):
722
args += ['type: ' + repr(self.type)]
723
if hasattr(self, 'default'):
724
args += ['default: ' + repr(self.default)]
725
return 'Argument({0})'.format(', '.join(args))
729
def __init__(self, name, description="", parser=None):
731
self.description = description
735
def addoption(self, *optnames, **attrs):
736
""" add an option to this group.
738
if a shortened version of a long option is specified it will
739
be suppressed in the help. addoption('--twowords', '--two-words')
740
results in help showing '--two-words' only, but --twowords gets
741
accepted **and** the automatic destination is in args.twowords
743
conflict = set(optnames).intersection(
744
name for opt in self.options for name in opt.names())
746
raise ValueError("option names %s already added" % conflict)
747
option = Argument(*optnames, **attrs)
748
self._addoption_instance(option, shortupper=False)
750
def _addoption(self, *optnames, **attrs):
751
option = Argument(*optnames, **attrs)
752
self._addoption_instance(option, shortupper=True)
754
def _addoption_instance(self, option, shortupper=False):
756
for opt in option._short_opts:
757
if opt[0] == '-' and opt[1].islower():
758
raise ValueError("lowercase shortoptions reserved")
760
self.parser.processoption(option)
761
self.options.append(option)
764
class MyOptionParser(argparse.ArgumentParser):
765
def __init__(self, parser, extra_info=None):
768
self._parser = parser
769
argparse.ArgumentParser.__init__(self, usage=parser._usage,
770
add_help=False, formatter_class=DropShorterLongHelpFormatter)
771
# extra_info is a dict of (param -> value) to display if there's
772
# an usage error to provide more contextual information to the user
773
self.extra_info = extra_info
775
def parse_args(self, args=None, namespace=None):
776
"""allow splitting of positional arguments"""
777
args, argv = self.parse_known_args(args, namespace)
780
if arg and arg[0] == '-':
781
lines = ['unrecognized arguments: %s' % (' '.join(argv))]
782
for k, v in sorted(self.extra_info.items()):
783
lines.append(' %s: %s' % (k, v))
784
self.error('\n'.join(lines))
785
getattr(args, FILE_OR_DIR).extend(argv)
789
class DropShorterLongHelpFormatter(argparse.HelpFormatter):
790
"""shorten help for long options that differ only in extra hyphens
792
- collapse **long** options that are the same except for extra hyphens
793
- special action attribute map_long_option allows surpressing additional
795
- shortcut if there are only two options and one of them is a short one
796
- cache result on action object as this is called at least 2 times
798
def _format_action_invocation(self, action):
799
orgstr = argparse.HelpFormatter._format_action_invocation(self, action)
800
if orgstr and orgstr[0] != '-': # only optional arguments
802
res = getattr(action, '_formatted_action_invocation', None)
805
options = orgstr.split(', ')
806
if len(options) == 2 and (len(options[0]) == 2 or len(options[1]) == 2):
807
# a shortcut for '-h, --help' or '--abc', '-a'
808
action._formatted_action_invocation = orgstr
811
option_map = getattr(action, 'map_long_option', {})
812
if option_map is None:
815
for option in options:
816
if len(option) == 2 or option[2] == ' ':
818
if not option.startswith('--'):
819
raise ArgumentError('long optional argument without "--": [%s]'
821
xxoption = option[2:]
822
if xxoption.split()[0] not in option_map:
823
shortened = xxoption.replace('-', '')
824
if shortened not in short_long or \
825
len(short_long[shortened]) < len(xxoption):
826
short_long[shortened] = xxoption
827
# now short_long has been filled out to the longest with dashes
828
# **and** we keep the right option ordering from add_argument
829
for option in options: #
830
if len(option) == 2 or option[2] == ' ':
831
return_list.append(option)
832
if option[2:] == short_long.get(option.replace('-', '')):
833
return_list.append(option.replace(' ', '=', 1))
834
action._formatted_action_invocation = ', '.join(return_list)
835
return action._formatted_action_invocation
839
def _ensure_removed_sysmodule(modname):
841
del sys.modules[modname]
845
class CmdOptions(object):
846
""" holds cmdline options as attributes."""
847
def __init__(self, values=()):
848
self.__dict__.update(values)
850
return "<CmdOptions %r>" %(self.__dict__,)
852
return CmdOptions(self.__dict__)
860
FILE_OR_DIR = 'file_or_dir'
863
class Config(object):
864
""" access to configuration values, pluginmanager and plugin hooks. """
866
def __init__(self, pluginmanager):
867
#: access to command line option as attributes.
868
#: (deprecated), use :py:func:`getoption() <_pytest.config.Config.getoption>` instead
869
self.option = CmdOptions()
871
self._parser = Parser(
872
usage="%%(prog)s [options] [%s] [%s] [...]" % (_a, _a),
873
processopt=self._processopt,
875
#: a pluginmanager instance
876
self.pluginmanager = pluginmanager
877
self.trace = self.pluginmanager.trace.root.get("config")
878
self.hook = self.pluginmanager.hook
882
self._warn = self.pluginmanager._warn
883
self.pluginmanager.register(self, "pytestconfig")
884
self._configured = False
890
self.hook.pytest_namespace.call_historic(do_setns, {})
891
self.hook.pytest_addoption.call_historic(kwargs=dict(parser=self._parser))
893
def add_cleanup(self, func):
894
""" Add a function to be called when the config object gets out of
895
use (usually coninciding with pytest_unconfigure)."""
896
self._cleanup.append(func)
898
def _do_configure(self):
899
assert not self._configured
900
self._configured = True
901
self.hook.pytest_configure.call_historic(kwargs=dict(config=self))
903
def _ensure_unconfigure(self):
905
self._configured = False
906
self.hook.pytest_unconfigure(config=self)
907
self.hook.pytest_configure._call_history = []
909
fin = self._cleanup.pop()
912
def warn(self, code, message, fslocation=None):
913
""" generate a warning for this test session. """
914
self.hook.pytest_logwarning.call_historic(kwargs=dict(
915
code=code, message=message,
916
fslocation=fslocation, nodeid=None))
918
def get_terminal_writer(self):
919
return self.pluginmanager.get_plugin("terminalreporter")._tw
921
def pytest_cmdline_parse(self, pluginmanager, args):
922
# REF1 assert self == pluginmanager.config, (self, pluginmanager.config)
926
def notify_exception(self, excinfo, option=None):
927
if option and option.fulltrace:
931
excrepr = excinfo.getrepr(funcargs=True,
932
showlocals=getattr(option, 'showlocals', False),
935
res = self.hook.pytest_internalerror(excrepr=excrepr,
937
if not py.builtin.any(res):
938
for line in str(excrepr).split("\n"):
939
sys.stderr.write("INTERNALERROR> %s\n" %line)
942
def cwd_relative_nodeid(self, nodeid):
943
# nodeid's are relative to the rootpath, compute relative to cwd
944
if self.invocation_dir != self.rootdir:
945
fullpath = self.rootdir.join(nodeid)
946
nodeid = self.invocation_dir.bestrelpath(fullpath)
950
def fromdictargs(cls, option_dict, args):
951
""" constructor useable for subprocesses. """
952
config = get_config()
953
config.option.__dict__.update(option_dict)
954
config.parse(args, addopts=False)
955
for x in config.option.plugins:
956
config.pluginmanager.consider_pluginarg(x)
959
def _processopt(self, opt):
960
for name in opt._short_opts + opt._long_opts:
961
self._opt2dest[name] = opt.dest
963
if hasattr(opt, 'default') and opt.dest:
964
if not hasattr(self.option, opt.dest):
965
setattr(self.option, opt.dest, opt.default)
967
@hookimpl(trylast=True)
968
def pytest_load_initial_conftests(self, early_config):
969
self.pluginmanager._set_initial_conftests(early_config.known_args_namespace)
971
def _initini(self, args):
972
ns, unknown_args = self._parser.parse_known_and_unknown_args(args, namespace=self.option.copy())
973
r = determine_setup(ns.inifilename, ns.file_or_dir + unknown_args, warnfunc=self.warn)
974
self.rootdir, self.inifile, self.inicfg = r
975
self._parser.extra_info['rootdir'] = self.rootdir
976
self._parser.extra_info['inifile'] = self.inifile
977
self.invocation_dir = py.path.local()
978
self._parser.addini('addopts', 'extra command line options', 'args')
979
self._parser.addini('minversion', 'minimally required pytest version')
981
def _consider_importhook(self, args, entrypoint_name):
982
"""Install the PEP 302 import hook if using assertion re-writing.
984
Needs to parse the --assert=<mode> option from the commandline
985
and find all the installed plugins to mark them for re-writing
988
ns, unknown_args = self._parser.parse_known_and_unknown_args(args)
990
if mode == 'rewrite':
992
hook = _pytest.assertion.install_importhook(self)
997
self.pluginmanager.rewrite_hook = hook
998
for entrypoint in pkg_resources.iter_entry_points('pytest11'):
999
# 'RECORD' available for plugins installed normally (pip install)
1000
# 'SOURCES.txt' available for plugins installed in dev mode (pip install -e)
1001
# for installed plugins 'SOURCES.txt' returns an empty list, and vice-versa
1002
# so it shouldn't be an issue
1003
for metadata in ('RECORD', 'SOURCES.txt'):
1004
for entry in entrypoint.dist._get_metadata(metadata):
1005
fn = entry.split(',')[0]
1006
is_simple_module = os.sep not in fn and fn.endswith('.py')
1007
is_package = fn.count(os.sep) == 1 and fn.endswith('__init__.py')
1008
if is_simple_module:
1009
module_name, ext = os.path.splitext(fn)
1010
hook.mark_rewrite(module_name)
1012
package_name = os.path.dirname(fn)
1013
hook.mark_rewrite(package_name)
1014
self._warn_about_missing_assertion(mode)
1016
def _warn_about_missing_assertion(self, mode):
1019
except AssertionError:
1023
sys.stderr.write("WARNING: ASSERTIONS ARE NOT EXECUTED"
1024
" and FAILING TESTS WILL PASS. Are you"
1025
" using python -O?")
1027
sys.stderr.write("WARNING: assertions not in test modules or"
1028
" plugins will be ignored"
1029
" because assert statements are not executed "
1030
"by the underlying Python interpreter "
1031
"(are you using python -O?)\n")
1033
def _preparse(self, args, addopts=True):
1036
args[:] = shlex.split(os.environ.get('PYTEST_ADDOPTS', '')) + args
1037
args[:] = self.getini("addopts") + args
1038
self._checkversion()
1039
entrypoint_name = 'pytest11'
1040
self._consider_importhook(args, entrypoint_name)
1041
self.pluginmanager.consider_preparse(args)
1042
self.pluginmanager.load_setuptools_entrypoints(entrypoint_name)
1043
self.pluginmanager.consider_env()
1044
self.known_args_namespace = ns = self._parser.parse_known_args(args, namespace=self.option.copy())
1045
confcutdir = self.known_args_namespace.confcutdir
1046
if self.known_args_namespace.confcutdir is None and self.inifile:
1047
confcutdir = py.path.local(self.inifile).dirname
1048
self.known_args_namespace.confcutdir = confcutdir
1050
self.hook.pytest_load_initial_conftests(early_config=self,
1051
args=args, parser=self._parser)
1052
except ConftestImportFailure:
1053
e = sys.exc_info()[1]
1054
if ns.help or ns.version:
1055
# we don't want to prevent --help/--version to work
1056
# so just let is pass and print a warning at the end
1057
self._warn("could not load initial conftests (%s)\n" % e.path)
1061
def _checkversion(self):
1063
minver = self.inicfg.get('minversion', None)
1065
ver = minver.split(".")
1066
myver = pytest.__version__.split(".")
1068
raise pytest.UsageError(
1069
"%s:%d: requires pytest-%s, actual pytest-%s'" %(
1070
self.inicfg.config.path, self.inicfg.lineof('minversion'),
1071
minver, pytest.__version__))
1073
def parse(self, args, addopts=True):
1074
# parse given cmdline arguments into this config object.
1075
assert not hasattr(self, 'args'), (
1076
"can only parse cmdline args at most once per Config object")
1077
self._origargs = args
1078
self.hook.pytest_addhooks.call_historic(
1079
kwargs=dict(pluginmanager=self.pluginmanager))
1080
self._preparse(args, addopts=addopts)
1081
# XXX deprecated hook:
1082
self.hook.pytest_cmdline_preparse(config=self, args=args)
1083
args = self._parser.parse_setoption(args, self.option, namespace=self.option)
1086
if cwd == self.rootdir:
1087
args = self.getini('testpaths')
1092
def addinivalue_line(self, name, line):
1093
""" add a line to an ini-file option. The option must have been
1094
declared but might not yet be set in which case the line becomes the
1095
the first line in its value. """
1096
x = self.getini(name)
1097
assert isinstance(x, list)
1098
x.append(line) # modifies the cached list inline
1100
def getini(self, name):
1101
""" return configuration value from an :ref:`ini file <inifiles>`. If the
1102
specified name hasn't been registered through a prior
1103
:py:func:`parser.addini <pytest.config.Parser.addini>`
1104
call (usually from a plugin), a ValueError is raised. """
1106
return self._inicache[name]
1108
self._inicache[name] = val = self._getini(name)
1111
def _getini(self, name):
1113
description, type, default = self._parser._inidict[name]
1115
raise ValueError("unknown configuration value: %r" %(name,))
1116
value = self._get_override_ini_value(name)
1119
value = self.inicfg[name]
1121
if default is not None:
1126
if type == "pathlist":
1127
dp = py.path.local(self.inicfg.config.path).dirpath()
1129
for relpath in shlex.split(value):
1130
l.append(dp.join(relpath, abs=True))
1132
elif type == "args":
1133
return shlex.split(value)
1134
elif type == "linelist":
1135
return [t for t in map(lambda x: x.strip(), value.split("\n")) if t]
1136
elif type == "bool":
1137
return bool(_strtobool(value.strip()))
1142
def _getconftest_pathlist(self, name, path):
1144
mod, relroots = self.pluginmanager._rget_with_confmod(name, path)
1147
modpath = py.path.local(mod.__file__).dirpath()
1149
for relroot in relroots:
1150
if not isinstance(relroot, py.path.local):
1151
relroot = relroot.replace("/", py.path.local.sep)
1152
relroot = modpath.join(relroot, abs=True)
1156
def _get_override_ini_value(self, name):
1158
# override_ini is a list of list, to support both -o foo1=bar1 foo2=bar2 and
1159
# and -o foo1=bar1 -o foo2=bar2 options
1160
# always use the last item if multiple value set for same ini-name,
1161
# e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2
1162
if self.getoption("override_ini", None):
1163
for ini_config_list in self.option.override_ini:
1164
for ini_config in ini_config_list:
1166
(key, user_ini_value) = ini_config.split("=", 1)
1168
raise UsageError("-o/--override-ini expects option=value style.")
1170
value = user_ini_value
1173
def getoption(self, name, default=notset, skip=False):
1174
""" return command line option value.
1176
:arg name: name of the option. You may also specify
1177
the literal ``--OPT`` option instead of the "dest" option name.
1178
:arg default: default value if no option of that name exists.
1179
:arg skip: if True raise pytest.skip if option does not exists
1180
or has a None value.
1182
name = self._opt2dest.get(name, name)
1184
val = getattr(self.option, name)
1185
if val is None and skip:
1186
raise AttributeError(name)
1188
except AttributeError:
1189
if default is not notset:
1193
pytest.skip("no %r option found" %(name,))
1194
raise ValueError("no option named %r" % (name,))
1196
def getvalue(self, name, path=None):
1197
""" (deprecated, use getoption()) """
1198
return self.getoption(name)
1200
def getvalueorskip(self, name, path=None):
1201
""" (deprecated, use getoption(skip=True)) """
1202
return self.getoption(name, skip=True)
1204
def exists(path, ignore=EnvironmentError):
1210
def getcfg(args, warnfunc=None):
1212
Search the list of arguments for a valid ini-file for pytest,
1213
and return a tuple of (rootdir, inifile, cfg-dict).
1215
note: warnfunc is an optional function used to warn
1216
about ini-files that use deprecated features.
1217
This parameter should be removed when pytest
1218
adopts standard deprecation warnings (#1804).
1220
from _pytest.deprecated import SETUP_CFG_PYTEST
1221
inibasenames = ["pytest.ini", "tox.ini", "setup.cfg"]
1222
args = [x for x in args if not str(x).startswith("-")]
1224
args = [py.path.local()]
1226
arg = py.path.local(arg)
1227
for base in arg.parts(reverse=True):
1228
for inibasename in inibasenames:
1229
p = base.join(inibasename)
1231
iniconfig = py.iniconfig.IniConfig(p)
1232
if 'pytest' in iniconfig.sections:
1233
if inibasename == 'setup.cfg' and warnfunc:
1234
warnfunc('C1', SETUP_CFG_PYTEST)
1235
return base, p, iniconfig['pytest']
1236
if inibasename == 'setup.cfg' and 'tool:pytest' in iniconfig.sections:
1237
return base, p, iniconfig['tool:pytest']
1238
elif inibasename == "pytest.ini":
1239
# allowed to be empty
1241
return None, None, None
1244
def get_common_ancestor(paths):
1245
common_ancestor = None
1247
if not path.exists():
1249
if common_ancestor is None:
1250
common_ancestor = path
1252
if path.relto(common_ancestor) or path == common_ancestor:
1254
elif common_ancestor.relto(path):
1255
common_ancestor = path
1257
shared = path.common(common_ancestor)
1258
if shared is not None:
1259
common_ancestor = shared
1260
if common_ancestor is None:
1261
common_ancestor = py.path.local()
1262
elif common_ancestor.isfile():
1263
common_ancestor = common_ancestor.dirpath()
1264
return common_ancestor
1267
def get_dirs_from_args(args):
1269
return str(x).startswith('-')
1271
def get_file_part_from_node_id(x):
1272
return str(x).split('::')[0]
1274
def get_dir_from_path(path):
1277
return py.path.local(path.dirname)
1279
# These look like paths but may not exist
1281
py.path.local(get_file_part_from_node_id(arg))
1283
if not is_option(arg)
1287
get_dir_from_path(path)
1288
for path in possible_paths
1293
def determine_setup(inifile, args, warnfunc=None):
1294
dirs = get_dirs_from_args(args)
1296
iniconfig = py.iniconfig.IniConfig(inifile)
1298
inicfg = iniconfig["pytest"]
1301
rootdir = get_common_ancestor(dirs)
1303
ancestor = get_common_ancestor(dirs)
1304
rootdir, inifile, inicfg = getcfg([ancestor], warnfunc=warnfunc)
1306
for rootdir in ancestor.parts(reverse=True):
1307
if rootdir.join("setup.py").exists():
1310
rootdir, inifile, inicfg = getcfg(dirs, warnfunc=warnfunc)
1312
rootdir = get_common_ancestor([py.path.local(), ancestor])
1313
is_fs_root = os.path.splitdrive(str(rootdir))[1] == os.sep
1316
return rootdir, inifile, inicfg or {}
1319
def setns(obj, dic):
1321
for name, value in dic.items():
1322
if isinstance(value, dict):
1323
mod = getattr(obj, name, None)
1325
modname = "pytest.%s" % name
1326
mod = types.ModuleType(modname)
1327
sys.modules[modname] = mod
1329
setattr(obj, name, mod)
1330
obj.__all__.append(name)
1333
setattr(obj, name, value)
1334
obj.__all__.append(name)
1336
# pytest.__all__.append(name)
1337
setattr(pytest, name, value)
1340
def create_terminal_writer(config, *args, **kwargs):
1341
"""Create a TerminalWriter instance configured according to the options
1342
in the config object. Every code which requires a TerminalWriter object
1343
and has access to a config object should use this function.
1345
tw = py.io.TerminalWriter(*args, **kwargs)
1346
if config.option.color == 'yes':
1348
if config.option.color == 'no':
1349
tw.hasmarkup = False
1353
def _strtobool(val):
1354
"""Convert a string representation of truth to true (1) or false (0).
1356
True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
1357
are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if
1358
'val' is anything else.
1360
.. note:: copied from distutils.util
1363
if val in ('y', 'yes', 't', 'true', 'on', '1'):
1365
elif val in ('n', 'no', 'f', 'false', 'off', '0'):
1368
raise ValueError("invalid truth value %r" % (val,))