1""" command line options, ini-file and conftest.py processing. """
2from __future__ import absolute_import, division, print_function
3import argparse
4import shlex
5import traceback
6import types
7import warnings
8import copy
9import six
10import py
11
12# DON't import pytest here because it causes import cycle troubles
13import sys
14import os
15from _pytest.outcomes import Skipped
16
17import _pytest._code
18import _pytest.hookspec  # the extension point definitions
19import _pytest.assertion
20from pluggy import PluginManager, HookimplMarker, HookspecMarker
21from _pytest.compat import safe_str
22from .exceptions import UsageError, PrintHelp
23from .findpaths import determine_setup, exists
24
25hookimpl = HookimplMarker("pytest")
26hookspec = HookspecMarker("pytest")
27
28# pytest startup
29#
30
31
32class ConftestImportFailure(Exception):
33
34    def __init__(self, path, excinfo):
35        Exception.__init__(self, path, excinfo)
36        self.path = path
37        self.excinfo = excinfo
38
39    def __str__(self):
40        etype, evalue, etb = self.excinfo
41        formatted = traceback.format_tb(etb)
42        # The level of the tracebacks we want to print is hand crafted :(
43        return repr(evalue) + "\n" + "".join(formatted[2:])
44
45
46def main(args=None, plugins=None):
47    """ return exit code, after performing an in-process test run.
48
49    :arg args: list of command line arguments.
50
51    :arg plugins: list of plugin objects to be auto-registered during
52                  initialization.
53    """
54    try:
55        try:
56            config = _prepareconfig(args, plugins)
57        except ConftestImportFailure as e:
58            tw = py.io.TerminalWriter(sys.stderr)
59            for line in traceback.format_exception(*e.excinfo):
60                tw.line(line.rstrip(), red=True)
61            tw.line("ERROR: could not load %s\n" % (e.path,), red=True)
62            return 4
63        else:
64            try:
65                return config.hook.pytest_cmdline_main(config=config)
66            finally:
67                config._ensure_unconfigure()
68    except UsageError as e:
69        tw = py.io.TerminalWriter(sys.stderr)
70        for msg in e.args:
71            tw.line("ERROR: {}\n".format(msg), red=True)
72        return 4
73
74
75class cmdline(object):  # NOQA compatibility namespace
76    main = staticmethod(main)
77
78
79def filename_arg(path, optname):
80    """ Argparse type validator for filename arguments.
81
82    :path: path of filename
83    :optname: name of the option
84    """
85    if os.path.isdir(path):
86        raise UsageError("{} must be a filename, given: {}".format(optname, path))
87    return path
88
89
90def directory_arg(path, optname):
91    """Argparse type validator for directory arguments.
92
93    :path: path of directory
94    :optname: name of the option
95    """
96    if not os.path.isdir(path):
97        raise UsageError("{} must be a directory, given: {}".format(optname, path))
98    return path
99
100
101default_plugins = (
102    "mark",
103    "main",
104    "terminal",
105    "runner",
106    "python",
107    "fixtures",
108    "debugging",
109    "unittest",
110    "capture",
111    "skipping",
112    "tmpdir",
113    "monkeypatch",
114    "recwarn",
115    "pastebin",
116    "helpconfig",
117    "nose",
118    "assertion",
119    "junitxml",
120    "resultlog",
121    "doctest",
122    "cacheprovider",
123    "freeze_support",
124    "setuponly",
125    "setupplan",
126    "warnings",
127    "logging",
128)
129
130
131builtin_plugins = set(default_plugins)
132builtin_plugins.add("pytester")
133
134
135def get_config():
136    # subsequent calls to main will create a fresh instance
137    pluginmanager = PytestPluginManager()
138    config = Config(pluginmanager)
139    for spec in default_plugins:
140        pluginmanager.import_plugin(spec)
141    return config
142
143
144def get_plugin_manager():
145    """
146    Obtain a new instance of the
147    :py:class:`_pytest.config.PytestPluginManager`, with default plugins
148    already loaded.
149
150    This function can be used by integration with other tools, like hooking
151    into pytest to run tests into an IDE.
152    """
153    return get_config().pluginmanager
154
155
156def _prepareconfig(args=None, plugins=None):
157    warning = None
158    if args is None:
159        args = sys.argv[1:]
160    elif isinstance(args, py.path.local):
161        args = [str(args)]
162    elif not isinstance(args, (tuple, list)):
163        if not isinstance(args, str):
164            raise ValueError("not a string or argument list: %r" % (args,))
165        args = shlex.split(args, posix=sys.platform != "win32")
166        from _pytest import deprecated
167
168        warning = deprecated.MAIN_STR_ARGS
169    config = get_config()
170    pluginmanager = config.pluginmanager
171    try:
172        if plugins:
173            for plugin in plugins:
174                if isinstance(plugin, six.string_types):
175                    pluginmanager.consider_pluginarg(plugin)
176                else:
177                    pluginmanager.register(plugin)
178        if warning:
179            config.warn("C1", warning)
180        return pluginmanager.hook.pytest_cmdline_parse(
181            pluginmanager=pluginmanager, args=args
182        )
183    except BaseException:
184        config._ensure_unconfigure()
185        raise
186
187
188class PytestPluginManager(PluginManager):
189    """
190    Overwrites :py:class:`pluggy.PluginManager <pluggy.PluginManager>` to add pytest-specific
191    functionality:
192
193    * loading plugins from the command line, ``PYTEST_PLUGINS`` env variable and
194      ``pytest_plugins`` global variables found in plugins being loaded;
195    * ``conftest.py`` loading during start-up;
196    """
197
198    def __init__(self):
199        super(PytestPluginManager, self).__init__("pytest")
200        self._conftest_plugins = set()
201
202        # state related to local conftest plugins
203        self._path2confmods = {}
204        self._conftestpath2mod = {}
205        self._confcutdir = None
206        self._noconftest = False
207        self._duplicatepaths = set()
208
209        self.add_hookspecs(_pytest.hookspec)
210        self.register(self)
211        if os.environ.get("PYTEST_DEBUG"):
212            err = sys.stderr
213            encoding = getattr(err, "encoding", "utf8")
214            try:
215                err = py.io.dupfile(err, encoding=encoding)
216            except Exception:
217                pass
218            self.trace.root.setwriter(err.write)
219            self.enable_tracing()
220
221        # Config._consider_importhook will set a real object if required.
222        self.rewrite_hook = _pytest.assertion.DummyRewriteHook()
223        # Used to know when we are importing conftests after the pytest_configure stage
224        self._configured = False
225
226    def addhooks(self, module_or_class):
227        """
228        .. deprecated:: 2.8
229
230        Use :py:meth:`pluggy.PluginManager.add_hookspecs <PluginManager.add_hookspecs>`
231        instead.
232        """
233        warning = dict(
234            code="I2",
235            fslocation=_pytest._code.getfslineno(sys._getframe(1)),
236            nodeid=None,
237            message="use pluginmanager.add_hookspecs instead of "
238            "deprecated addhooks() method.",
239        )
240        self._warn(warning)
241        return self.add_hookspecs(module_or_class)
242
243    def parse_hookimpl_opts(self, plugin, name):
244        # pytest hooks are always prefixed with pytest_
245        # so we avoid accessing possibly non-readable attributes
246        # (see issue #1073)
247        if not name.startswith("pytest_"):
248            return
249        # ignore some historic special names which can not be hooks anyway
250        if name == "pytest_plugins" or name.startswith("pytest_funcarg__"):
251            return
252
253        method = getattr(plugin, name)
254        opts = super(PytestPluginManager, self).parse_hookimpl_opts(plugin, name)
255
256        # collect unmarked hooks as long as they have the `pytest_' prefix
257        if opts is None and name.startswith("pytest_"):
258            opts = {}
259
260        if opts is not None:
261            for name in ("tryfirst", "trylast", "optionalhook", "hookwrapper"):
262                opts.setdefault(name, hasattr(method, name))
263        return opts
264
265    def parse_hookspec_opts(self, module_or_class, name):
266        opts = super(PytestPluginManager, self).parse_hookspec_opts(
267            module_or_class, name
268        )
269        if opts is None:
270            method = getattr(module_or_class, name)
271            if name.startswith("pytest_"):
272                opts = {
273                    "firstresult": hasattr(method, "firstresult"),
274                    "historic": hasattr(method, "historic"),
275                }
276        return opts
277
278    def register(self, plugin, name=None):
279        if name in ["pytest_catchlog", "pytest_capturelog"]:
280            self._warn(
281                "{} plugin has been merged into the core, "
282                "please remove it from your requirements.".format(
283                    name.replace("_", "-")
284                )
285            )
286            return
287        ret = super(PytestPluginManager, self).register(plugin, name)
288        if ret:
289            self.hook.pytest_plugin_registered.call_historic(
290                kwargs=dict(plugin=plugin, manager=self)
291            )
292
293            if isinstance(plugin, types.ModuleType):
294                self.consider_module(plugin)
295        return ret
296
297    def getplugin(self, name):
298        # support deprecated naming because plugins (xdist e.g.) use it
299        return self.get_plugin(name)
300
301    def hasplugin(self, name):
302        """Return True if the plugin with the given name is registered."""
303        return bool(self.get_plugin(name))
304
305    def pytest_configure(self, config):
306        # XXX now that the pluginmanager exposes hookimpl(tryfirst...)
307        # we should remove tryfirst/trylast as markers
308        config.addinivalue_line(
309            "markers",
310            "tryfirst: mark a hook implementation function such that the "
311            "plugin machinery will try to call it first/as early as possible.",
312        )
313        config.addinivalue_line(
314            "markers",
315            "trylast: mark a hook implementation function such that the "
316            "plugin machinery will try to call it last/as late as possible.",
317        )
318        self._configured = True
319
320    def _warn(self, message):
321        kwargs = message if isinstance(message, dict) else {
322            "code": "I1", "message": message, "fslocation": None, "nodeid": None
323        }
324        self.hook.pytest_logwarning.call_historic(kwargs=kwargs)
325
326    #
327    # internal API for local conftest plugin handling
328    #
329    def _set_initial_conftests(self, namespace):
330        """ load initial conftest files given a preparsed "namespace".
331            As conftest files may add their own command line options
332            which have arguments ('--my-opt somepath') we might get some
333            false positives.  All builtin and 3rd party plugins will have
334            been loaded, however, so common options will not confuse our logic
335            here.
336        """
337        current = py.path.local()
338        self._confcutdir = current.join(
339            namespace.confcutdir, abs=True
340        ) if namespace.confcutdir else None
341        self._noconftest = namespace.noconftest
342        testpaths = namespace.file_or_dir
343        foundanchor = False
344        for path in testpaths:
345            path = str(path)
346            # remove node-id syntax
347            i = path.find("::")
348            if i != -1:
349                path = path[:i]
350            anchor = current.join(path, abs=1)
351            if exists(anchor):  # we found some file object
352                self._try_load_conftest(anchor)
353                foundanchor = True
354        if not foundanchor:
355            self._try_load_conftest(current)
356
357    def _try_load_conftest(self, anchor):
358        self._getconftestmodules(anchor)
359        # let's also consider test* subdirs
360        if anchor.check(dir=1):
361            for x in anchor.listdir("test*"):
362                if x.check(dir=1):
363                    self._getconftestmodules(x)
364
365    def _getconftestmodules(self, path):
366        if self._noconftest:
367            return []
368        try:
369            return self._path2confmods[path]
370        except KeyError:
371            if path.isfile():
372                clist = self._getconftestmodules(path.dirpath())
373            else:
374                # XXX these days we may rather want to use config.rootdir
375                # and allow users to opt into looking into the rootdir parent
376                # directories instead of requiring to specify confcutdir
377                clist = []
378                for parent in path.parts():
379                    if self._confcutdir and self._confcutdir.relto(parent):
380                        continue
381                    conftestpath = parent.join("conftest.py")
382                    if conftestpath.isfile():
383                        mod = self._importconftest(conftestpath)
384                        clist.append(mod)
385
386            self._path2confmods[path] = clist
387            return clist
388
389    def _rget_with_confmod(self, name, path):
390        modules = self._getconftestmodules(path)
391        for mod in reversed(modules):
392            try:
393                return mod, getattr(mod, name)
394            except AttributeError:
395                continue
396        raise KeyError(name)
397
398    def _importconftest(self, conftestpath):
399        try:
400            return self._conftestpath2mod[conftestpath]
401        except KeyError:
402            pkgpath = conftestpath.pypkgpath()
403            if pkgpath is None:
404                _ensure_removed_sysmodule(conftestpath.purebasename)
405            try:
406                mod = conftestpath.pyimport()
407                if hasattr(mod, "pytest_plugins") and self._configured:
408                    from _pytest.deprecated import PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST
409
410                    warnings.warn(PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST)
411            except Exception:
412                raise ConftestImportFailure(conftestpath, sys.exc_info())
413
414            self._conftest_plugins.add(mod)
415            self._conftestpath2mod[conftestpath] = mod
416            dirpath = conftestpath.dirpath()
417            if dirpath in self._path2confmods:
418                for path, mods in self._path2confmods.items():
419                    if path and path.relto(dirpath) or path == dirpath:
420                        assert mod not in mods
421                        mods.append(mod)
422            self.trace("loaded conftestmodule %r" % (mod))
423            self.consider_conftest(mod)
424            return mod
425
426    #
427    # API for bootstrapping plugin loading
428    #
429    #
430
431    def consider_preparse(self, args):
432        for opt1, opt2 in zip(args, args[1:]):
433            if opt1 == "-p":
434                self.consider_pluginarg(opt2)
435
436    def consider_pluginarg(self, arg):
437        if arg.startswith("no:"):
438            name = arg[3:]
439            self.set_blocked(name)
440            if not name.startswith("pytest_"):
441                self.set_blocked("pytest_" + name)
442        else:
443            self.import_plugin(arg)
444
445    def consider_conftest(self, conftestmodule):
446        self.register(conftestmodule, name=conftestmodule.__file__)
447
448    def consider_env(self):
449        self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS"))
450
451    def consider_module(self, mod):
452        self._import_plugin_specs(getattr(mod, "pytest_plugins", []))
453
454    def _import_plugin_specs(self, spec):
455        plugins = _get_plugin_specs_as_list(spec)
456        for import_spec in plugins:
457            self.import_plugin(import_spec)
458
459    def import_plugin(self, modname):
460        # most often modname refers to builtin modules, e.g. "pytester",
461        # "terminal" or "capture".  Those plugins are registered under their
462        # basename for historic purposes but must be imported with the
463        # _pytest prefix.
464        assert isinstance(modname, (six.text_type, str)), (
465            "module name as text required, got %r" % modname
466        )
467        modname = str(modname)
468        if self.is_blocked(modname) or self.get_plugin(modname) is not None:
469            return
470        if modname in builtin_plugins:
471            importspec = "_pytest." + modname
472        else:
473            importspec = modname
474        self.rewrite_hook.mark_rewrite(importspec)
475        try:
476            __import__(importspec)
477        except ImportError as e:
478            new_exc_type = ImportError
479            new_exc_message = 'Error importing plugin "%s": %s' % (
480                modname, safe_str(e.args[0])
481            )
482            new_exc = new_exc_type(new_exc_message)
483
484            six.reraise(new_exc_type, new_exc, sys.exc_info()[2])
485
486        except Skipped as e:
487            self._warn("skipped plugin %r: %s" % ((modname, e.msg)))
488        else:
489            mod = sys.modules[importspec]
490            self.register(mod, modname)
491
492
493def _get_plugin_specs_as_list(specs):
494    """
495    Parses a list of "plugin specs" and returns a list of plugin names.
496
497    Plugin specs can be given as a list of strings separated by "," or already as a list/tuple in
498    which case it is returned as a list. Specs can also be `None` in which case an
499    empty list is returned.
500    """
501    if specs is not None:
502        if isinstance(specs, str):
503            specs = specs.split(",") if specs else []
504        if not isinstance(specs, (list, tuple)):
505            raise UsageError(
506                "Plugin specs must be a ','-separated string or a "
507                "list/tuple of strings for plugin names. Given: %r" % specs
508            )
509        return list(specs)
510    return []
511
512
513def _ensure_removed_sysmodule(modname):
514    try:
515        del sys.modules[modname]
516    except KeyError:
517        pass
518
519
520class Notset(object):
521
522    def __repr__(self):
523        return "<NOTSET>"
524
525
526notset = Notset()
527
528
529def _iter_rewritable_modules(package_files):
530    for fn in package_files:
531        is_simple_module = "/" not in fn and fn.endswith(".py")
532        is_package = fn.count("/") == 1 and fn.endswith("__init__.py")
533        if is_simple_module:
534            module_name, _ = os.path.splitext(fn)
535            yield module_name
536        elif is_package:
537            package_name = os.path.dirname(fn)
538            yield package_name
539
540
541class Config(object):
542    """ access to configuration values, pluginmanager and plugin hooks.  """
543
544    def __init__(self, pluginmanager):
545        #: access to command line option as attributes.
546        #: (deprecated), use :py:func:`getoption() <_pytest.config.Config.getoption>` instead
547        self.option = argparse.Namespace()
548        from .argparsing import Parser, FILE_OR_DIR
549
550        _a = FILE_OR_DIR
551        self._parser = Parser(
552            usage="%%(prog)s [options] [%s] [%s] [...]" % (_a, _a),
553            processopt=self._processopt,
554        )
555        #: a pluginmanager instance
556        self.pluginmanager = pluginmanager
557        self.trace = self.pluginmanager.trace.root.get("config")
558        self.hook = self.pluginmanager.hook
559        self._inicache = {}
560        self._override_ini = ()
561        self._opt2dest = {}
562        self._cleanup = []
563        self._warn = self.pluginmanager._warn
564        self.pluginmanager.register(self, "pytestconfig")
565        self._configured = False
566
567        def do_setns(dic):
568            import pytest
569
570            setns(pytest, dic)
571
572        self.hook.pytest_namespace.call_historic(do_setns, {})
573        self.hook.pytest_addoption.call_historic(kwargs=dict(parser=self._parser))
574
575    def add_cleanup(self, func):
576        """ Add a function to be called when the config object gets out of
577        use (usually coninciding with pytest_unconfigure)."""
578        self._cleanup.append(func)
579
580    def _do_configure(self):
581        assert not self._configured
582        self._configured = True
583        self.hook.pytest_configure.call_historic(kwargs=dict(config=self))
584
585    def _ensure_unconfigure(self):
586        if self._configured:
587            self._configured = False
588            self.hook.pytest_unconfigure(config=self)
589            self.hook.pytest_configure._call_history = []
590        while self._cleanup:
591            fin = self._cleanup.pop()
592            fin()
593
594    def warn(self, code, message, fslocation=None, nodeid=None):
595        """ generate a warning for this test session. """
596        self.hook.pytest_logwarning.call_historic(
597            kwargs=dict(
598                code=code, message=message, fslocation=fslocation, nodeid=nodeid
599            )
600        )
601
602    def get_terminal_writer(self):
603        return self.pluginmanager.get_plugin("terminalreporter")._tw
604
605    def pytest_cmdline_parse(self, pluginmanager, args):
606        # REF1 assert self == pluginmanager.config, (self, pluginmanager.config)
607        self.parse(args)
608        return self
609
610    def notify_exception(self, excinfo, option=None):
611        if option and option.fulltrace:
612            style = "long"
613        else:
614            style = "native"
615        excrepr = excinfo.getrepr(
616            funcargs=True, showlocals=getattr(option, "showlocals", False), style=style
617        )
618        res = self.hook.pytest_internalerror(excrepr=excrepr, excinfo=excinfo)
619        if not any(res):
620            for line in str(excrepr).split("\n"):
621                sys.stderr.write("INTERNALERROR> %s\n" % line)
622                sys.stderr.flush()
623
624    def cwd_relative_nodeid(self, nodeid):
625        # nodeid's are relative to the rootpath, compute relative to cwd
626        if self.invocation_dir != self.rootdir:
627            fullpath = self.rootdir.join(nodeid)
628            nodeid = self.invocation_dir.bestrelpath(fullpath)
629        return nodeid
630
631    @classmethod
632    def fromdictargs(cls, option_dict, args):
633        """ constructor useable for subprocesses. """
634        config = get_config()
635        config.option.__dict__.update(option_dict)
636        config.parse(args, addopts=False)
637        for x in config.option.plugins:
638            config.pluginmanager.consider_pluginarg(x)
639        return config
640
641    def _processopt(self, opt):
642        for name in opt._short_opts + opt._long_opts:
643            self._opt2dest[name] = opt.dest
644
645        if hasattr(opt, "default") and opt.dest:
646            if not hasattr(self.option, opt.dest):
647                setattr(self.option, opt.dest, opt.default)
648
649    @hookimpl(trylast=True)
650    def pytest_load_initial_conftests(self, early_config):
651        self.pluginmanager._set_initial_conftests(early_config.known_args_namespace)
652
653    def _initini(self, args):
654        ns, unknown_args = self._parser.parse_known_and_unknown_args(
655            args, namespace=copy.copy(self.option)
656        )
657        r = determine_setup(
658            ns.inifilename,
659            ns.file_or_dir + unknown_args,
660            warnfunc=self.warn,
661            rootdir_cmd_arg=ns.rootdir or None,
662        )
663        self.rootdir, self.inifile, self.inicfg = r
664        self._parser.extra_info["rootdir"] = self.rootdir
665        self._parser.extra_info["inifile"] = self.inifile
666        self.invocation_dir = py.path.local()
667        self._parser.addini("addopts", "extra command line options", "args")
668        self._parser.addini("minversion", "minimally required pytest version")
669        self._override_ini = ns.override_ini or ()
670
671    def _consider_importhook(self, args):
672        """Install the PEP 302 import hook if using assertion rewriting.
673
674        Needs to parse the --assert=<mode> option from the commandline
675        and find all the installed plugins to mark them for rewriting
676        by the importhook.
677        """
678        ns, unknown_args = self._parser.parse_known_and_unknown_args(args)
679        mode = ns.assertmode
680        if mode == "rewrite":
681            try:
682                hook = _pytest.assertion.install_importhook(self)
683            except SystemError:
684                mode = "plain"
685            else:
686                self._mark_plugins_for_rewrite(hook)
687        _warn_about_missing_assertion(mode)
688
689    def _mark_plugins_for_rewrite(self, hook):
690        """
691        Given an importhook, mark for rewrite any top-level
692        modules or packages in the distribution package for
693        all pytest plugins.
694        """
695        import pkg_resources
696
697        self.pluginmanager.rewrite_hook = hook
698
699        # 'RECORD' available for plugins installed normally (pip install)
700        # 'SOURCES.txt' available for plugins installed in dev mode (pip install -e)
701        # for installed plugins 'SOURCES.txt' returns an empty list, and vice-versa
702        # so it shouldn't be an issue
703        metadata_files = "RECORD", "SOURCES.txt"
704
705        package_files = (
706            entry.split(",")[0]
707            for entrypoint in pkg_resources.iter_entry_points("pytest11")
708            for metadata in metadata_files
709            for entry in entrypoint.dist._get_metadata(metadata)
710        )
711
712        for name in _iter_rewritable_modules(package_files):
713            hook.mark_rewrite(name)
714
715    def _preparse(self, args, addopts=True):
716        if addopts:
717            args[:] = shlex.split(os.environ.get("PYTEST_ADDOPTS", "")) + args
718        self._initini(args)
719        if addopts:
720            args[:] = self.getini("addopts") + args
721        self._checkversion()
722        self._consider_importhook(args)
723        self.pluginmanager.consider_preparse(args)
724        self.pluginmanager.load_setuptools_entrypoints("pytest11")
725        self.pluginmanager.consider_env()
726        self.known_args_namespace = ns = self._parser.parse_known_args(
727            args, namespace=copy.copy(self.option)
728        )
729        if self.known_args_namespace.confcutdir is None and self.inifile:
730            confcutdir = py.path.local(self.inifile).dirname
731            self.known_args_namespace.confcutdir = confcutdir
732        try:
733            self.hook.pytest_load_initial_conftests(
734                early_config=self, args=args, parser=self._parser
735            )
736        except ConftestImportFailure:
737            e = sys.exc_info()[1]
738            if ns.help or ns.version:
739                # we don't want to prevent --help/--version to work
740                # so just let is pass and print a warning at the end
741                self._warn("could not load initial conftests (%s)\n" % e.path)
742            else:
743                raise
744
745    def _checkversion(self):
746        import pytest
747
748        minver = self.inicfg.get("minversion", None)
749        if minver:
750            ver = minver.split(".")
751            myver = pytest.__version__.split(".")
752            if myver < ver:
753                raise pytest.UsageError(
754                    "%s:%d: requires pytest-%s, actual pytest-%s'"
755                    % (
756                        self.inicfg.config.path,
757                        self.inicfg.lineof("minversion"),
758                        minver,
759                        pytest.__version__,
760                    )
761                )
762
763    def parse(self, args, addopts=True):
764        # parse given cmdline arguments into this config object.
765        assert not hasattr(
766            self, "args"
767        ), "can only parse cmdline args at most once per Config object"
768        self._origargs = args
769        self.hook.pytest_addhooks.call_historic(
770            kwargs=dict(pluginmanager=self.pluginmanager)
771        )
772        self._preparse(args, addopts=addopts)
773        # XXX deprecated hook:
774        self.hook.pytest_cmdline_preparse(config=self, args=args)
775        self._parser.after_preparse = True
776        try:
777            args = self._parser.parse_setoption(
778                args, self.option, namespace=self.option
779            )
780            if not args:
781                cwd = os.getcwd()
782                if cwd == self.rootdir:
783                    args = self.getini("testpaths")
784                if not args:
785                    args = [cwd]
786            self.args = args
787        except PrintHelp:
788            pass
789
790    def addinivalue_line(self, name, line):
791        """ add a line to an ini-file option. The option must have been
792        declared but might not yet be set in which case the line becomes the
793        the first line in its value. """
794        x = self.getini(name)
795        assert isinstance(x, list)
796        x.append(line)  # modifies the cached list inline
797
798    def getini(self, name):
799        """ return configuration value from an :ref:`ini file <inifiles>`. If the
800        specified name hasn't been registered through a prior
801        :py:func:`parser.addini <_pytest.config.Parser.addini>`
802        call (usually from a plugin), a ValueError is raised. """
803        try:
804            return self._inicache[name]
805        except KeyError:
806            self._inicache[name] = val = self._getini(name)
807            return val
808
809    def _getini(self, name):
810        try:
811            description, type, default = self._parser._inidict[name]
812        except KeyError:
813            raise ValueError("unknown configuration value: %r" % (name,))
814        value = self._get_override_ini_value(name)
815        if value is None:
816            try:
817                value = self.inicfg[name]
818            except KeyError:
819                if default is not None:
820                    return default
821                if type is None:
822                    return ""
823                return []
824        if type == "pathlist":
825            dp = py.path.local(self.inicfg.config.path).dirpath()
826            values = []
827            for relpath in shlex.split(value):
828                values.append(dp.join(relpath, abs=True))
829            return values
830        elif type == "args":
831            return shlex.split(value)
832        elif type == "linelist":
833            return [t for t in map(lambda x: x.strip(), value.split("\n")) if t]
834        elif type == "bool":
835            return bool(_strtobool(value.strip()))
836        else:
837            assert type is None
838            return value
839
840    def _getconftest_pathlist(self, name, path):
841        try:
842            mod, relroots = self.pluginmanager._rget_with_confmod(name, path)
843        except KeyError:
844            return None
845        modpath = py.path.local(mod.__file__).dirpath()
846        values = []
847        for relroot in relroots:
848            if not isinstance(relroot, py.path.local):
849                relroot = relroot.replace("/", py.path.local.sep)
850                relroot = modpath.join(relroot, abs=True)
851            values.append(relroot)
852        return values
853
854    def _get_override_ini_value(self, name):
855        value = None
856        # override_ini is a list of "ini=value" options
857        # always use the last item if multiple values are set for same ini-name,
858        # e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2
859        for ini_config in self._override_ini:
860            try:
861                key, user_ini_value = ini_config.split("=", 1)
862            except ValueError:
863                raise UsageError("-o/--override-ini expects option=value style.")
864            else:
865                if key == name:
866                    value = user_ini_value
867        return value
868
869    def getoption(self, name, default=notset, skip=False):
870        """ return command line option value.
871
872        :arg name: name of the option.  You may also specify
873            the literal ``--OPT`` option instead of the "dest" option name.
874        :arg default: default value if no option of that name exists.
875        :arg skip: if True raise pytest.skip if option does not exists
876            or has a None value.
877        """
878        name = self._opt2dest.get(name, name)
879        try:
880            val = getattr(self.option, name)
881            if val is None and skip:
882                raise AttributeError(name)
883            return val
884        except AttributeError:
885            if default is not notset:
886                return default
887            if skip:
888                import pytest
889
890                pytest.skip("no %r option found" % (name,))
891            raise ValueError("no option named %r" % (name,))
892
893    def getvalue(self, name, path=None):
894        """ (deprecated, use getoption()) """
895        return self.getoption(name)
896
897    def getvalueorskip(self, name, path=None):
898        """ (deprecated, use getoption(skip=True)) """
899        return self.getoption(name, skip=True)
900
901
902def _assertion_supported():
903    try:
904        assert False
905    except AssertionError:
906        return True
907    else:
908        return False
909
910
911def _warn_about_missing_assertion(mode):
912    if not _assertion_supported():
913        if mode == "plain":
914            sys.stderr.write(
915                "WARNING: ASSERTIONS ARE NOT EXECUTED"
916                " and FAILING TESTS WILL PASS.  Are you"
917                " using python -O?"
918            )
919        else:
920            sys.stderr.write(
921                "WARNING: assertions not in test modules or"
922                " plugins will be ignored"
923                " because assert statements are not executed "
924                "by the underlying Python interpreter "
925                "(are you using python -O?)\n"
926            )
927
928
929def setns(obj, dic):
930    import pytest
931
932    for name, value in dic.items():
933        if isinstance(value, dict):
934            mod = getattr(obj, name, None)
935            if mod is None:
936                modname = "pytest.%s" % name
937                mod = types.ModuleType(modname)
938                sys.modules[modname] = mod
939                mod.__all__ = []
940                setattr(obj, name, mod)
941            obj.__all__.append(name)
942            setns(mod, value)
943        else:
944            setattr(obj, name, value)
945            obj.__all__.append(name)
946            # if obj != pytest:
947            #    pytest.__all__.append(name)
948            setattr(pytest, name, value)
949
950
951def create_terminal_writer(config, *args, **kwargs):
952    """Create a TerminalWriter instance configured according to the options
953    in the config object. Every code which requires a TerminalWriter object
954    and has access to a config object should use this function.
955    """
956    tw = py.io.TerminalWriter(*args, **kwargs)
957    if config.option.color == "yes":
958        tw.hasmarkup = True
959    if config.option.color == "no":
960        tw.hasmarkup = False
961    return tw
962
963
964def _strtobool(val):
965    """Convert a string representation of truth to true (1) or false (0).
966
967    True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
968    are 'n', 'no', 'f', 'false', 'off', and '0'.  Raises ValueError if
969    'val' is anything else.
970
971    .. note:: copied from distutils.util
972    """
973    val = val.lower()
974    if val in ("y", "yes", "t", "true", "on", "1"):
975        return 1
976    elif val in ("n", "no", "f", "false", "off", "0"):
977        return 0
978    else:
979        raise ValueError("invalid truth value %r" % (val,))
980