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
8
9import six
10import py
11# DON't import pytest here because it causes import cycle troubles
12import sys
13import os
14import _pytest._code
15import _pytest.hookspec  # the extension point definitions
16import _pytest.assertion
17from pluggy import PluginManager, HookimplMarker, HookspecMarker
18from _pytest.compat import safe_str
19
20hookimpl = HookimplMarker("pytest")
21hookspec = HookspecMarker("pytest")
22
23# pytest startup
24#
25
26
27class ConftestImportFailure(Exception):
28    def __init__(self, path, excinfo):
29        Exception.__init__(self, path, excinfo)
30        self.path = path
31        self.excinfo = excinfo
32
33    def __str__(self):
34        etype, evalue, etb = self.excinfo
35        formatted = traceback.format_tb(etb)
36        # The level of the tracebacks we want to print is hand crafted :(
37        return repr(evalue) + '\n' + ''.join(formatted[2:])
38
39
40def main(args=None, plugins=None):
41    """ return exit code, after performing an in-process test run.
42
43    :arg args: list of command line arguments.
44
45    :arg plugins: list of plugin objects to be auto-registered during
46                  initialization.
47    """
48    try:
49        try:
50            config = _prepareconfig(args, plugins)
51        except ConftestImportFailure as e:
52            tw = py.io.TerminalWriter(sys.stderr)
53            for line in traceback.format_exception(*e.excinfo):
54                tw.line(line.rstrip(), red=True)
55            tw.line("ERROR: could not load %s\n" % (e.path), red=True)
56            return 4
57        else:
58            try:
59                return config.hook.pytest_cmdline_main(config=config)
60            finally:
61                config._ensure_unconfigure()
62    except UsageError as e:
63        for msg in e.args:
64            sys.stderr.write("ERROR: %s\n" % (msg,))
65        return 4
66
67
68class cmdline:  # compatibility namespace
69    main = staticmethod(main)
70
71
72class UsageError(Exception):
73    """ error in pytest usage or invocation"""
74
75
76class PrintHelp(Exception):
77    """Raised when pytest should print it's help to skip the rest of the
78    argument parsing and validation."""
79    pass
80
81
82def filename_arg(path, optname):
83    """ Argparse type validator for filename arguments.
84
85    :path: path of filename
86    :optname: name of the option
87    """
88    if os.path.isdir(path):
89        raise UsageError("{0} must be a filename, given: {1}".format(optname, path))
90    return path
91
92
93def directory_arg(path, optname):
94    """Argparse type validator for directory arguments.
95
96    :path: path of directory
97    :optname: name of the option
98    """
99    if not os.path.isdir(path):
100        raise UsageError("{0} must be a directory, given: {1}".format(optname, path))
101    return path
102
103
104default_plugins = (
105    "mark main terminal runner python fixtures debugging unittest capture skipping "
106    "tmpdir monkeypatch recwarn pastebin helpconfig nose assertion "
107    "junitxml resultlog doctest cacheprovider freeze_support "
108    "setuponly setupplan warnings logging").split()
109
110
111builtin_plugins = set(default_plugins)
112builtin_plugins.add("pytester")
113
114
115def get_config():
116    # subsequent calls to main will create a fresh instance
117    pluginmanager = PytestPluginManager()
118    config = Config(pluginmanager)
119    for spec in default_plugins:
120        pluginmanager.import_plugin(spec)
121    return config
122
123
124def get_plugin_manager():
125    """
126    Obtain a new instance of the
127    :py:class:`_pytest.config.PytestPluginManager`, with default plugins
128    already loaded.
129
130    This function can be used by integration with other tools, like hooking
131    into pytest to run tests into an IDE.
132    """
133    return get_config().pluginmanager
134
135
136def _prepareconfig(args=None, plugins=None):
137    warning = None
138    if args is None:
139        args = sys.argv[1:]
140    elif isinstance(args, py.path.local):
141        args = [str(args)]
142    elif not isinstance(args, (tuple, list)):
143        if not isinstance(args, str):
144            raise ValueError("not a string or argument list: %r" % (args,))
145        args = shlex.split(args, posix=sys.platform != "win32")
146        from _pytest import deprecated
147        warning = deprecated.MAIN_STR_ARGS
148    config = get_config()
149    pluginmanager = config.pluginmanager
150    try:
151        if plugins:
152            for plugin in plugins:
153                if isinstance(plugin, six.string_types):
154                    pluginmanager.consider_pluginarg(plugin)
155                else:
156                    pluginmanager.register(plugin)
157        if warning:
158            config.warn('C1', warning)
159        return pluginmanager.hook.pytest_cmdline_parse(
160            pluginmanager=pluginmanager, args=args)
161    except BaseException:
162        config._ensure_unconfigure()
163        raise
164
165
166class PytestPluginManager(PluginManager):
167    """
168    Overwrites :py:class:`pluggy.PluginManager <pluggy.PluginManager>` to add pytest-specific
169    functionality:
170
171    * loading plugins from the command line, ``PYTEST_PLUGIN`` env variable and
172      ``pytest_plugins`` global variables found in plugins being loaded;
173    * ``conftest.py`` loading during start-up;
174    """
175
176    def __init__(self):
177        super(PytestPluginManager, self).__init__("pytest", implprefix="pytest_")
178        self._conftest_plugins = set()
179
180        # state related to local conftest plugins
181        self._path2confmods = {}
182        self._conftestpath2mod = {}
183        self._confcutdir = None
184        self._noconftest = False
185        self._duplicatepaths = set()
186
187        self.add_hookspecs(_pytest.hookspec)
188        self.register(self)
189        if os.environ.get('PYTEST_DEBUG'):
190            err = sys.stderr
191            encoding = getattr(err, 'encoding', 'utf8')
192            try:
193                err = py.io.dupfile(err, encoding=encoding)
194            except Exception:
195                pass
196            self.trace.root.setwriter(err.write)
197            self.enable_tracing()
198
199        # Config._consider_importhook will set a real object if required.
200        self.rewrite_hook = _pytest.assertion.DummyRewriteHook()
201
202    def addhooks(self, module_or_class):
203        """
204        .. deprecated:: 2.8
205
206        Use :py:meth:`pluggy.PluginManager.add_hookspecs <PluginManager.add_hookspecs>`
207        instead.
208        """
209        warning = dict(code="I2",
210                       fslocation=_pytest._code.getfslineno(sys._getframe(1)),
211                       nodeid=None,
212                       message="use pluginmanager.add_hookspecs instead of "
213                               "deprecated addhooks() method.")
214        self._warn(warning)
215        return self.add_hookspecs(module_or_class)
216
217    def parse_hookimpl_opts(self, plugin, name):
218        # pytest hooks are always prefixed with pytest_
219        # so we avoid accessing possibly non-readable attributes
220        # (see issue #1073)
221        if not name.startswith("pytest_"):
222            return
223        # ignore some historic special names which can not be hooks anyway
224        if name == "pytest_plugins" or name.startswith("pytest_funcarg__"):
225            return
226
227        method = getattr(plugin, name)
228        opts = super(PytestPluginManager, self).parse_hookimpl_opts(plugin, name)
229        if opts is not None:
230            for name in ("tryfirst", "trylast", "optionalhook", "hookwrapper"):
231                opts.setdefault(name, hasattr(method, name))
232        return opts
233
234    def parse_hookspec_opts(self, module_or_class, name):
235        opts = super(PytestPluginManager, self).parse_hookspec_opts(
236            module_or_class, name)
237        if opts is None:
238            method = getattr(module_or_class, name)
239            if name.startswith("pytest_"):
240                opts = {"firstresult": hasattr(method, "firstresult"),
241                        "historic": hasattr(method, "historic")}
242        return opts
243
244    def register(self, plugin, name=None):
245        if name == 'pytest_catchlog':
246            self._warn('pytest-catchlog plugin has been merged into the core, '
247                       'please remove it from your requirements.')
248            return
249        ret = super(PytestPluginManager, self).register(plugin, name)
250        if ret:
251            self.hook.pytest_plugin_registered.call_historic(
252                kwargs=dict(plugin=plugin, manager=self))
253
254            if isinstance(plugin, types.ModuleType):
255                self.consider_module(plugin)
256        return ret
257
258    def getplugin(self, name):
259        # support deprecated naming because plugins (xdist e.g.) use it
260        return self.get_plugin(name)
261
262    def hasplugin(self, name):
263        """Return True if the plugin with the given name is registered."""
264        return bool(self.get_plugin(name))
265
266    def pytest_configure(self, config):
267        # XXX now that the pluginmanager exposes hookimpl(tryfirst...)
268        # we should remove tryfirst/trylast as markers
269        config.addinivalue_line("markers",
270                                "tryfirst: mark a hook implementation function such that the "
271                                "plugin machinery will try to call it first/as early as possible.")
272        config.addinivalue_line("markers",
273                                "trylast: mark a hook implementation function such that the "
274                                "plugin machinery will try to call it last/as late as possible.")
275
276    def _warn(self, message):
277        kwargs = message if isinstance(message, dict) else {
278            'code': 'I1',
279            'message': message,
280            'fslocation': None,
281            'nodeid': None,
282        }
283        self.hook.pytest_logwarning.call_historic(kwargs=kwargs)
284
285    #
286    # internal API for local conftest plugin handling
287    #
288    def _set_initial_conftests(self, namespace):
289        """ load initial conftest files given a preparsed "namespace".
290            As conftest files may add their own command line options
291            which have arguments ('--my-opt somepath') we might get some
292            false positives.  All builtin and 3rd party plugins will have
293            been loaded, however, so common options will not confuse our logic
294            here.
295        """
296        current = py.path.local()
297        self._confcutdir = current.join(namespace.confcutdir, abs=True) \
298            if namespace.confcutdir else None
299        self._noconftest = namespace.noconftest
300        testpaths = namespace.file_or_dir
301        foundanchor = False
302        for path in testpaths:
303            path = str(path)
304            # remove node-id syntax
305            i = path.find("::")
306            if i != -1:
307                path = path[:i]
308            anchor = current.join(path, abs=1)
309            if exists(anchor):  # we found some file object
310                self._try_load_conftest(anchor)
311                foundanchor = True
312        if not foundanchor:
313            self._try_load_conftest(current)
314
315    def _try_load_conftest(self, anchor):
316        self._getconftestmodules(anchor)
317        # let's also consider test* subdirs
318        if anchor.check(dir=1):
319            for x in anchor.listdir("test*"):
320                if x.check(dir=1):
321                    self._getconftestmodules(x)
322
323    def _getconftestmodules(self, path):
324        if self._noconftest:
325            return []
326        try:
327            return self._path2confmods[path]
328        except KeyError:
329            if path.isfile():
330                clist = self._getconftestmodules(path.dirpath())
331            else:
332                # XXX these days we may rather want to use config.rootdir
333                # and allow users to opt into looking into the rootdir parent
334                # directories instead of requiring to specify confcutdir
335                clist = []
336                for parent in path.parts():
337                    if self._confcutdir and self._confcutdir.relto(parent):
338                        continue
339                    conftestpath = parent.join("conftest.py")
340                    if conftestpath.isfile():
341                        mod = self._importconftest(conftestpath)
342                        clist.append(mod)
343
344            self._path2confmods[path] = clist
345            return clist
346
347    def _rget_with_confmod(self, name, path):
348        modules = self._getconftestmodules(path)
349        for mod in reversed(modules):
350            try:
351                return mod, getattr(mod, name)
352            except AttributeError:
353                continue
354        raise KeyError(name)
355
356    def _importconftest(self, conftestpath):
357        try:
358            return self._conftestpath2mod[conftestpath]
359        except KeyError:
360            pkgpath = conftestpath.pypkgpath()
361            if pkgpath is None:
362                _ensure_removed_sysmodule(conftestpath.purebasename)
363            try:
364                mod = conftestpath.pyimport()
365            except Exception:
366                raise ConftestImportFailure(conftestpath, sys.exc_info())
367
368            self._conftest_plugins.add(mod)
369            self._conftestpath2mod[conftestpath] = mod
370            dirpath = conftestpath.dirpath()
371            if dirpath in self._path2confmods:
372                for path, mods in self._path2confmods.items():
373                    if path and path.relto(dirpath) or path == dirpath:
374                        assert mod not in mods
375                        mods.append(mod)
376            self.trace("loaded conftestmodule %r" % (mod))
377            self.consider_conftest(mod)
378            return mod
379
380    #
381    # API for bootstrapping plugin loading
382    #
383    #
384
385    def consider_preparse(self, args):
386        for opt1, opt2 in zip(args, args[1:]):
387            if opt1 == "-p":
388                self.consider_pluginarg(opt2)
389
390    def consider_pluginarg(self, arg):
391        if arg.startswith("no:"):
392            name = arg[3:]
393            self.set_blocked(name)
394            if not name.startswith("pytest_"):
395                self.set_blocked("pytest_" + name)
396        else:
397            self.import_plugin(arg)
398
399    def consider_conftest(self, conftestmodule):
400        self.register(conftestmodule, name=conftestmodule.__file__)
401
402    def consider_env(self):
403        self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS"))
404
405    def consider_module(self, mod):
406        self._import_plugin_specs(getattr(mod, 'pytest_plugins', []))
407
408    def _import_plugin_specs(self, spec):
409        plugins = _get_plugin_specs_as_list(spec)
410        for import_spec in plugins:
411            self.import_plugin(import_spec)
412
413    def import_plugin(self, modname):
414        # most often modname refers to builtin modules, e.g. "pytester",
415        # "terminal" or "capture".  Those plugins are registered under their
416        # basename for historic purposes but must be imported with the
417        # _pytest prefix.
418        assert isinstance(modname, (six.text_type, str)), "module name as text required, got %r" % modname
419        modname = str(modname)
420        if self.is_blocked(modname) or self.get_plugin(modname) is not None:
421            return
422        if modname in builtin_plugins:
423            importspec = "_pytest." + modname
424        else:
425            importspec = modname
426        self.rewrite_hook.mark_rewrite(importspec)
427        try:
428            __import__(importspec)
429        except ImportError as e:
430            new_exc_type = ImportError
431            new_exc_message = 'Error importing plugin "%s": %s' % (modname, safe_str(e.args[0]))
432            new_exc = new_exc_type(new_exc_message)
433
434            six.reraise(new_exc_type, new_exc, sys.exc_info()[2])
435
436        except Exception as e:
437            import pytest
438            if not hasattr(pytest, 'skip') or not isinstance(e, pytest.skip.Exception):
439                raise
440            self._warn("skipped plugin %r: %s" % ((modname, e.msg)))
441        else:
442            mod = sys.modules[importspec]
443            self.register(mod, modname)
444
445
446def _get_plugin_specs_as_list(specs):
447    """
448    Parses a list of "plugin specs" and returns a list of plugin names.
449
450    Plugin specs can be given as a list of strings separated by "," or already as a list/tuple in
451    which case it is returned as a list. Specs can also be `None` in which case an
452    empty list is returned.
453    """
454    if specs is not None:
455        if isinstance(specs, str):
456            specs = specs.split(',') if specs else []
457        if not isinstance(specs, (list, tuple)):
458            raise UsageError("Plugin specs must be a ','-separated string or a "
459                             "list/tuple of strings for plugin names. Given: %r" % specs)
460        return list(specs)
461    return []
462
463
464class Parser:
465    """ Parser for command line arguments and ini-file values.
466
467    :ivar extra_info: dict of generic param -> value to display in case
468        there's an error processing the command line arguments.
469    """
470
471    def __init__(self, usage=None, processopt=None):
472        self._anonymous = OptionGroup("custom options", parser=self)
473        self._groups = []
474        self._processopt = processopt
475        self._usage = usage
476        self._inidict = {}
477        self._ininames = []
478        self.extra_info = {}
479
480    def processoption(self, option):
481        if self._processopt:
482            if option.dest:
483                self._processopt(option)
484
485    def getgroup(self, name, description="", after=None):
486        """ get (or create) a named option Group.
487
488        :name: name of the option group.
489        :description: long description for --help output.
490        :after: name of other group, used for ordering --help output.
491
492        The returned group object has an ``addoption`` method with the same
493        signature as :py:func:`parser.addoption
494        <_pytest.config.Parser.addoption>` but will be shown in the
495        respective group in the output of ``pytest. --help``.
496        """
497        for group in self._groups:
498            if group.name == name:
499                return group
500        group = OptionGroup(name, description, parser=self)
501        i = 0
502        for i, grp in enumerate(self._groups):
503            if grp.name == after:
504                break
505        self._groups.insert(i + 1, group)
506        return group
507
508    def addoption(self, *opts, **attrs):
509        """ register a command line option.
510
511        :opts: option names, can be short or long options.
512        :attrs: same attributes which the ``add_option()`` function of the
513           `argparse library
514           <http://docs.python.org/2/library/argparse.html>`_
515           accepts.
516
517        After command line parsing options are available on the pytest config
518        object via ``config.option.NAME`` where ``NAME`` is usually set
519        by passing a ``dest`` attribute, for example
520        ``addoption("--long", dest="NAME", ...)``.
521        """
522        self._anonymous.addoption(*opts, **attrs)
523
524    def parse(self, args, namespace=None):
525        from _pytest._argcomplete import try_argcomplete
526        self.optparser = self._getparser()
527        try_argcomplete(self.optparser)
528        return self.optparser.parse_args([str(x) for x in args], namespace=namespace)
529
530    def _getparser(self):
531        from _pytest._argcomplete import filescompleter
532        optparser = MyOptionParser(self, self.extra_info)
533        groups = self._groups + [self._anonymous]
534        for group in groups:
535            if group.options:
536                desc = group.description or group.name
537                arggroup = optparser.add_argument_group(desc)
538                for option in group.options:
539                    n = option.names()
540                    a = option.attrs()
541                    arggroup.add_argument(*n, **a)
542        # bash like autocompletion for dirs (appending '/')
543        optparser.add_argument(FILE_OR_DIR, nargs='*').completer = filescompleter
544        return optparser
545
546    def parse_setoption(self, args, option, namespace=None):
547        parsedoption = self.parse(args, namespace=namespace)
548        for name, value in parsedoption.__dict__.items():
549            setattr(option, name, value)
550        return getattr(parsedoption, FILE_OR_DIR)
551
552    def parse_known_args(self, args, namespace=None):
553        """parses and returns a namespace object with known arguments at this
554        point.
555        """
556        return self.parse_known_and_unknown_args(args, namespace=namespace)[0]
557
558    def parse_known_and_unknown_args(self, args, namespace=None):
559        """parses and returns a namespace object with known arguments, and
560        the remaining arguments unknown at this point.
561        """
562        optparser = self._getparser()
563        args = [str(x) for x in args]
564        return optparser.parse_known_args(args, namespace=namespace)
565
566    def addini(self, name, help, type=None, default=None):
567        """ register an ini-file option.
568
569        :name: name of the ini-variable
570        :type: type of the variable, can be ``pathlist``, ``args``, ``linelist``
571               or ``bool``.
572        :default: default value if no ini-file option exists but is queried.
573
574        The value of ini-variables can be retrieved via a call to
575        :py:func:`config.getini(name) <_pytest.config.Config.getini>`.
576        """
577        assert type in (None, "pathlist", "args", "linelist", "bool")
578        self._inidict[name] = (help, type, default)
579        self._ininames.append(name)
580
581
582class ArgumentError(Exception):
583    """
584    Raised if an Argument instance is created with invalid or
585    inconsistent arguments.
586    """
587
588    def __init__(self, msg, option):
589        self.msg = msg
590        self.option_id = str(option)
591
592    def __str__(self):
593        if self.option_id:
594            return "option %s: %s" % (self.option_id, self.msg)
595        else:
596            return self.msg
597
598
599class Argument:
600    """class that mimics the necessary behaviour of optparse.Option
601
602    its currently a least effort implementation
603    and ignoring choices and integer prefixes
604    https://docs.python.org/3/library/optparse.html#optparse-standard-option-types
605    """
606    _typ_map = {
607        'int': int,
608        'string': str,
609        'float': float,
610        'complex': complex,
611    }
612
613    def __init__(self, *names, **attrs):
614        """store parms in private vars for use in add_argument"""
615        self._attrs = attrs
616        self._short_opts = []
617        self._long_opts = []
618        self.dest = attrs.get('dest')
619        if '%default' in (attrs.get('help') or ''):
620            warnings.warn(
621                'pytest now uses argparse. "%default" should be'
622                ' changed to "%(default)s" ',
623                DeprecationWarning,
624                stacklevel=3)
625        try:
626            typ = attrs['type']
627        except KeyError:
628            pass
629        else:
630            # this might raise a keyerror as well, don't want to catch that
631            if isinstance(typ, six.string_types):
632                if typ == 'choice':
633                    warnings.warn(
634                        'type argument to addoption() is a string %r.'
635                        ' For parsearg this is optional and when supplied'
636                        ' should be a type.'
637                        ' (options: %s)' % (typ, names),
638                        DeprecationWarning,
639                        stacklevel=3)
640                    # argparse expects a type here take it from
641                    # the type of the first element
642                    attrs['type'] = type(attrs['choices'][0])
643                else:
644                    warnings.warn(
645                        'type argument to addoption() is a string %r.'
646                        ' For parsearg this should be a type.'
647                        ' (options: %s)' % (typ, names),
648                        DeprecationWarning,
649                        stacklevel=3)
650                    attrs['type'] = Argument._typ_map[typ]
651                # used in test_parseopt -> test_parse_defaultgetter
652                self.type = attrs['type']
653            else:
654                self.type = typ
655        try:
656            # attribute existence is tested in Config._processopt
657            self.default = attrs['default']
658        except KeyError:
659            pass
660        self._set_opt_strings(names)
661        if not self.dest:
662            if self._long_opts:
663                self.dest = self._long_opts[0][2:].replace('-', '_')
664            else:
665                try:
666                    self.dest = self._short_opts[0][1:]
667                except IndexError:
668                    raise ArgumentError(
669                        'need a long or short option', self)
670
671    def names(self):
672        return self._short_opts + self._long_opts
673
674    def attrs(self):
675        # update any attributes set by processopt
676        attrs = 'default dest help'.split()
677        if self.dest:
678            attrs.append(self.dest)
679        for attr in attrs:
680            try:
681                self._attrs[attr] = getattr(self, attr)
682            except AttributeError:
683                pass
684        if self._attrs.get('help'):
685            a = self._attrs['help']
686            a = a.replace('%default', '%(default)s')
687            # a = a.replace('%prog', '%(prog)s')
688            self._attrs['help'] = a
689        return self._attrs
690
691    def _set_opt_strings(self, opts):
692        """directly from optparse
693
694        might not be necessary as this is passed to argparse later on"""
695        for opt in opts:
696            if len(opt) < 2:
697                raise ArgumentError(
698                    "invalid option string %r: "
699                    "must be at least two characters long" % opt, self)
700            elif len(opt) == 2:
701                if not (opt[0] == "-" and opt[1] != "-"):
702                    raise ArgumentError(
703                        "invalid short option string %r: "
704                        "must be of the form -x, (x any non-dash char)" % opt,
705                        self)
706                self._short_opts.append(opt)
707            else:
708                if not (opt[0:2] == "--" and opt[2] != "-"):
709                    raise ArgumentError(
710                        "invalid long option string %r: "
711                        "must start with --, followed by non-dash" % opt,
712                        self)
713                self._long_opts.append(opt)
714
715    def __repr__(self):
716        args = []
717        if self._short_opts:
718            args += ['_short_opts: ' + repr(self._short_opts)]
719        if self._long_opts:
720            args += ['_long_opts: ' + repr(self._long_opts)]
721        args += ['dest: ' + repr(self.dest)]
722        if hasattr(self, 'type'):
723            args += ['type: ' + repr(self.type)]
724        if hasattr(self, 'default'):
725            args += ['default: ' + repr(self.default)]
726        return 'Argument({0})'.format(', '.join(args))
727
728
729class OptionGroup:
730    def __init__(self, name, description="", parser=None):
731        self.name = name
732        self.description = description
733        self.options = []
734        self.parser = parser
735
736    def addoption(self, *optnames, **attrs):
737        """ add an option to this group.
738
739        if a shortened version of a long option is specified it will
740        be suppressed in the help. addoption('--twowords', '--two-words')
741        results in help showing '--two-words' only, but --twowords gets
742        accepted **and** the automatic destination is in args.twowords
743        """
744        conflict = set(optnames).intersection(
745            name for opt in self.options for name in opt.names())
746        if conflict:
747            raise ValueError("option names %s already added" % conflict)
748        option = Argument(*optnames, **attrs)
749        self._addoption_instance(option, shortupper=False)
750
751    def _addoption(self, *optnames, **attrs):
752        option = Argument(*optnames, **attrs)
753        self._addoption_instance(option, shortupper=True)
754
755    def _addoption_instance(self, option, shortupper=False):
756        if not shortupper:
757            for opt in option._short_opts:
758                if opt[0] == '-' and opt[1].islower():
759                    raise ValueError("lowercase shortoptions reserved")
760        if self.parser:
761            self.parser.processoption(option)
762        self.options.append(option)
763
764
765class MyOptionParser(argparse.ArgumentParser):
766    def __init__(self, parser, extra_info=None):
767        if not extra_info:
768            extra_info = {}
769        self._parser = parser
770        argparse.ArgumentParser.__init__(self, usage=parser._usage,
771                                         add_help=False, formatter_class=DropShorterLongHelpFormatter)
772        # extra_info is a dict of (param -> value) to display if there's
773        # an usage error to provide more contextual information to the user
774        self.extra_info = extra_info
775
776    def parse_args(self, args=None, namespace=None):
777        """allow splitting of positional arguments"""
778        args, argv = self.parse_known_args(args, namespace)
779        if argv:
780            for arg in argv:
781                if arg and arg[0] == '-':
782                    lines = ['unrecognized arguments: %s' % (' '.join(argv))]
783                    for k, v in sorted(self.extra_info.items()):
784                        lines.append('  %s: %s' % (k, v))
785                    self.error('\n'.join(lines))
786            getattr(args, FILE_OR_DIR).extend(argv)
787        return args
788
789
790class DropShorterLongHelpFormatter(argparse.HelpFormatter):
791    """shorten help for long options that differ only in extra hyphens
792
793    - collapse **long** options that are the same except for extra hyphens
794    - special action attribute map_long_option allows surpressing additional
795      long options
796    - shortcut if there are only two options and one of them is a short one
797    - cache result on action object as this is called at least 2 times
798    """
799
800    def _format_action_invocation(self, action):
801        orgstr = argparse.HelpFormatter._format_action_invocation(self, action)
802        if orgstr and orgstr[0] != '-':  # only optional arguments
803            return orgstr
804        res = getattr(action, '_formatted_action_invocation', None)
805        if res:
806            return res
807        options = orgstr.split(', ')
808        if len(options) == 2 and (len(options[0]) == 2 or len(options[1]) == 2):
809            # a shortcut for '-h, --help' or '--abc', '-a'
810            action._formatted_action_invocation = orgstr
811            return orgstr
812        return_list = []
813        option_map = getattr(action, 'map_long_option', {})
814        if option_map is None:
815            option_map = {}
816        short_long = {}
817        for option in options:
818            if len(option) == 2 or option[2] == ' ':
819                continue
820            if not option.startswith('--'):
821                raise ArgumentError('long optional argument without "--": [%s]'
822                                    % (option), self)
823            xxoption = option[2:]
824            if xxoption.split()[0] not in option_map:
825                shortened = xxoption.replace('-', '')
826                if shortened not in short_long or \
827                   len(short_long[shortened]) < len(xxoption):
828                    short_long[shortened] = xxoption
829        # now short_long has been filled out to the longest with dashes
830        # **and** we keep the right option ordering from add_argument
831        for option in options:
832            if len(option) == 2 or option[2] == ' ':
833                return_list.append(option)
834            if option[2:] == short_long.get(option.replace('-', '')):
835                return_list.append(option.replace(' ', '=', 1))
836        action._formatted_action_invocation = ', '.join(return_list)
837        return action._formatted_action_invocation
838
839
840def _ensure_removed_sysmodule(modname):
841    try:
842        del sys.modules[modname]
843    except KeyError:
844        pass
845
846
847class CmdOptions(object):
848    """ holds cmdline options as attributes."""
849
850    def __init__(self, values=()):
851        self.__dict__.update(values)
852
853    def __repr__(self):
854        return "<CmdOptions %r>" % (self.__dict__,)
855
856    def copy(self):
857        return CmdOptions(self.__dict__)
858
859
860class Notset:
861    def __repr__(self):
862        return "<NOTSET>"
863
864
865notset = Notset()
866FILE_OR_DIR = 'file_or_dir'
867
868
869def _iter_rewritable_modules(package_files):
870    for fn in package_files:
871        is_simple_module = '/' not in fn and fn.endswith('.py')
872        is_package = fn.count('/') == 1 and fn.endswith('__init__.py')
873        if is_simple_module:
874            module_name, _ = os.path.splitext(fn)
875            yield module_name
876        elif is_package:
877            package_name = os.path.dirname(fn)
878            yield package_name
879
880
881class Config(object):
882    """ access to configuration values, pluginmanager and plugin hooks.  """
883
884    def __init__(self, pluginmanager):
885        #: access to command line option as attributes.
886        #: (deprecated), use :py:func:`getoption() <_pytest.config.Config.getoption>` instead
887        self.option = CmdOptions()
888        _a = FILE_OR_DIR
889        self._parser = Parser(
890            usage="%%(prog)s [options] [%s] [%s] [...]" % (_a, _a),
891            processopt=self._processopt,
892        )
893        #: a pluginmanager instance
894        self.pluginmanager = pluginmanager
895        self.trace = self.pluginmanager.trace.root.get("config")
896        self.hook = self.pluginmanager.hook
897        self._inicache = {}
898        self._override_ini = ()
899        self._opt2dest = {}
900        self._cleanup = []
901        self._warn = self.pluginmanager._warn
902        self.pluginmanager.register(self, "pytestconfig")
903        self._configured = False
904
905        def do_setns(dic):
906            import pytest
907            setns(pytest, dic)
908
909        self.hook.pytest_namespace.call_historic(do_setns, {})
910        self.hook.pytest_addoption.call_historic(kwargs=dict(parser=self._parser))
911
912    def add_cleanup(self, func):
913        """ Add a function to be called when the config object gets out of
914        use (usually coninciding with pytest_unconfigure)."""
915        self._cleanup.append(func)
916
917    def _do_configure(self):
918        assert not self._configured
919        self._configured = True
920        self.hook.pytest_configure.call_historic(kwargs=dict(config=self))
921
922    def _ensure_unconfigure(self):
923        if self._configured:
924            self._configured = False
925            self.hook.pytest_unconfigure(config=self)
926            self.hook.pytest_configure._call_history = []
927        while self._cleanup:
928            fin = self._cleanup.pop()
929            fin()
930
931    def warn(self, code, message, fslocation=None, nodeid=None):
932        """ generate a warning for this test session. """
933        self.hook.pytest_logwarning.call_historic(kwargs=dict(
934            code=code, message=message,
935            fslocation=fslocation, nodeid=nodeid))
936
937    def get_terminal_writer(self):
938        return self.pluginmanager.get_plugin("terminalreporter")._tw
939
940    def pytest_cmdline_parse(self, pluginmanager, args):
941        # REF1 assert self == pluginmanager.config, (self, pluginmanager.config)
942        self.parse(args)
943        return self
944
945    def notify_exception(self, excinfo, option=None):
946        if option and option.fulltrace:
947            style = "long"
948        else:
949            style = "native"
950        excrepr = excinfo.getrepr(funcargs=True,
951                                  showlocals=getattr(option, 'showlocals', False),
952                                  style=style,
953                                  )
954        res = self.hook.pytest_internalerror(excrepr=excrepr,
955                                             excinfo=excinfo)
956        if not any(res):
957            for line in str(excrepr).split("\n"):
958                sys.stderr.write("INTERNALERROR> %s\n" % line)
959                sys.stderr.flush()
960
961    def cwd_relative_nodeid(self, nodeid):
962        # nodeid's are relative to the rootpath, compute relative to cwd
963        if self.invocation_dir != self.rootdir:
964            fullpath = self.rootdir.join(nodeid)
965            nodeid = self.invocation_dir.bestrelpath(fullpath)
966        return nodeid
967
968    @classmethod
969    def fromdictargs(cls, option_dict, args):
970        """ constructor useable for subprocesses. """
971        config = get_config()
972        config.option.__dict__.update(option_dict)
973        config.parse(args, addopts=False)
974        for x in config.option.plugins:
975            config.pluginmanager.consider_pluginarg(x)
976        return config
977
978    def _processopt(self, opt):
979        for name in opt._short_opts + opt._long_opts:
980            self._opt2dest[name] = opt.dest
981
982        if hasattr(opt, 'default') and opt.dest:
983            if not hasattr(self.option, opt.dest):
984                setattr(self.option, opt.dest, opt.default)
985
986    @hookimpl(trylast=True)
987    def pytest_load_initial_conftests(self, early_config):
988        self.pluginmanager._set_initial_conftests(early_config.known_args_namespace)
989
990    def _initini(self, args):
991        ns, unknown_args = self._parser.parse_known_and_unknown_args(args, namespace=self.option.copy())
992        r = determine_setup(ns.inifilename, ns.file_or_dir + unknown_args, warnfunc=self.warn)
993        self.rootdir, self.inifile, self.inicfg = r
994        self._parser.extra_info['rootdir'] = self.rootdir
995        self._parser.extra_info['inifile'] = self.inifile
996        self.invocation_dir = py.path.local()
997        self._parser.addini('addopts', 'extra command line options', 'args')
998        self._parser.addini('minversion', 'minimally required pytest version')
999        self._override_ini = ns.override_ini or ()
1000
1001    def _consider_importhook(self, args):
1002        """Install the PEP 302 import hook if using assertion rewriting.
1003
1004        Needs to parse the --assert=<mode> option from the commandline
1005        and find all the installed plugins to mark them for rewriting
1006        by the importhook.
1007        """
1008        ns, unknown_args = self._parser.parse_known_and_unknown_args(args)
1009        mode = ns.assertmode
1010        if mode == 'rewrite':
1011            try:
1012                hook = _pytest.assertion.install_importhook(self)
1013            except SystemError:
1014                mode = 'plain'
1015            else:
1016                self._mark_plugins_for_rewrite(hook)
1017        self._warn_about_missing_assertion(mode)
1018
1019    def _mark_plugins_for_rewrite(self, hook):
1020        """
1021        Given an importhook, mark for rewrite any top-level
1022        modules or packages in the distribution package for
1023        all pytest plugins.
1024        """
1025        import pkg_resources
1026        self.pluginmanager.rewrite_hook = hook
1027
1028        # 'RECORD' available for plugins installed normally (pip install)
1029        # 'SOURCES.txt' available for plugins installed in dev mode (pip install -e)
1030        # for installed plugins 'SOURCES.txt' returns an empty list, and vice-versa
1031        # so it shouldn't be an issue
1032        metadata_files = 'RECORD', 'SOURCES.txt'
1033
1034        package_files = (
1035            entry.split(',')[0]
1036            for entrypoint in pkg_resources.iter_entry_points('pytest11')
1037            for metadata in metadata_files
1038            for entry in entrypoint.dist._get_metadata(metadata)
1039        )
1040
1041        for name in _iter_rewritable_modules(package_files):
1042            hook.mark_rewrite(name)
1043
1044    def _warn_about_missing_assertion(self, mode):
1045        try:
1046            assert False
1047        except AssertionError:
1048            pass
1049        else:
1050            if mode == 'plain':
1051                sys.stderr.write("WARNING: ASSERTIONS ARE NOT EXECUTED"
1052                                 " and FAILING TESTS WILL PASS.  Are you"
1053                                 " using python -O?")
1054            else:
1055                sys.stderr.write("WARNING: assertions not in test modules or"
1056                                 " plugins will be ignored"
1057                                 " because assert statements are not executed "
1058                                 "by the underlying Python interpreter "
1059                                 "(are you using python -O?)\n")
1060
1061    def _preparse(self, args, addopts=True):
1062        if addopts:
1063            args[:] = shlex.split(os.environ.get('PYTEST_ADDOPTS', '')) + args
1064        self._initini(args)
1065        if addopts:
1066            args[:] = self.getini("addopts") + args
1067        self._checkversion()
1068        self._consider_importhook(args)
1069        self.pluginmanager.consider_preparse(args)
1070        self.pluginmanager.load_setuptools_entrypoints('pytest11')
1071        self.pluginmanager.consider_env()
1072        self.known_args_namespace = ns = self._parser.parse_known_args(args, namespace=self.option.copy())
1073        if self.known_args_namespace.confcutdir is None and self.inifile:
1074            confcutdir = py.path.local(self.inifile).dirname
1075            self.known_args_namespace.confcutdir = confcutdir
1076        try:
1077            self.hook.pytest_load_initial_conftests(early_config=self,
1078                                                    args=args, parser=self._parser)
1079        except ConftestImportFailure:
1080            e = sys.exc_info()[1]
1081            if ns.help or ns.version:
1082                # we don't want to prevent --help/--version to work
1083                # so just let is pass and print a warning at the end
1084                self._warn("could not load initial conftests (%s)\n" % e.path)
1085            else:
1086                raise
1087
1088    def _checkversion(self):
1089        import pytest
1090        minver = self.inicfg.get('minversion', None)
1091        if minver:
1092            ver = minver.split(".")
1093            myver = pytest.__version__.split(".")
1094            if myver < ver:
1095                raise pytest.UsageError(
1096                    "%s:%d: requires pytest-%s, actual pytest-%s'" % (
1097                        self.inicfg.config.path, self.inicfg.lineof('minversion'),
1098                        minver, pytest.__version__))
1099
1100    def parse(self, args, addopts=True):
1101        # parse given cmdline arguments into this config object.
1102        assert not hasattr(self, 'args'), (
1103            "can only parse cmdline args at most once per Config object")
1104        self._origargs = args
1105        self.hook.pytest_addhooks.call_historic(
1106            kwargs=dict(pluginmanager=self.pluginmanager))
1107        self._preparse(args, addopts=addopts)
1108        # XXX deprecated hook:
1109        self.hook.pytest_cmdline_preparse(config=self, args=args)
1110        self._parser.after_preparse = True
1111        try:
1112            args = self._parser.parse_setoption(args, self.option, namespace=self.option)
1113            if not args:
1114                cwd = os.getcwd()
1115                if cwd == self.rootdir:
1116                    args = self.getini('testpaths')
1117                if not args:
1118                    args = [cwd]
1119            self.args = args
1120        except PrintHelp:
1121            pass
1122
1123    def addinivalue_line(self, name, line):
1124        """ add a line to an ini-file option. The option must have been
1125        declared but might not yet be set in which case the line becomes the
1126        the first line in its value. """
1127        x = self.getini(name)
1128        assert isinstance(x, list)
1129        x.append(line)  # modifies the cached list inline
1130
1131    def getini(self, name):
1132        """ return configuration value from an :ref:`ini file <inifiles>`. If the
1133        specified name hasn't been registered through a prior
1134        :py:func:`parser.addini <_pytest.config.Parser.addini>`
1135        call (usually from a plugin), a ValueError is raised. """
1136        try:
1137            return self._inicache[name]
1138        except KeyError:
1139            self._inicache[name] = val = self._getini(name)
1140            return val
1141
1142    def _getini(self, name):
1143        try:
1144            description, type, default = self._parser._inidict[name]
1145        except KeyError:
1146            raise ValueError("unknown configuration value: %r" % (name,))
1147        value = self._get_override_ini_value(name)
1148        if value is None:
1149            try:
1150                value = self.inicfg[name]
1151            except KeyError:
1152                if default is not None:
1153                    return default
1154                if type is None:
1155                    return ''
1156                return []
1157        if type == "pathlist":
1158            dp = py.path.local(self.inicfg.config.path).dirpath()
1159            values = []
1160            for relpath in shlex.split(value):
1161                values.append(dp.join(relpath, abs=True))
1162            return values
1163        elif type == "args":
1164            return shlex.split(value)
1165        elif type == "linelist":
1166            return [t for t in map(lambda x: x.strip(), value.split("\n")) if t]
1167        elif type == "bool":
1168            return bool(_strtobool(value.strip()))
1169        else:
1170            assert type is None
1171            return value
1172
1173    def _getconftest_pathlist(self, name, path):
1174        try:
1175            mod, relroots = self.pluginmanager._rget_with_confmod(name, path)
1176        except KeyError:
1177            return None
1178        modpath = py.path.local(mod.__file__).dirpath()
1179        values = []
1180        for relroot in relroots:
1181            if not isinstance(relroot, py.path.local):
1182                relroot = relroot.replace("/", py.path.local.sep)
1183                relroot = modpath.join(relroot, abs=True)
1184            values.append(relroot)
1185        return values
1186
1187    def _get_override_ini_value(self, name):
1188        value = None
1189        # override_ini is a list of list, to support both -o foo1=bar1 foo2=bar2 and
1190        # and -o foo1=bar1 -o foo2=bar2 options
1191        # always use the last item if multiple value set for same ini-name,
1192        # e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2
1193        for ini_config_list in self._override_ini:
1194            for ini_config in ini_config_list:
1195                try:
1196                    (key, user_ini_value) = ini_config.split("=", 1)
1197                except ValueError:
1198                    raise UsageError("-o/--override-ini expects option=value style.")
1199                if key == name:
1200                    value = user_ini_value
1201        return value
1202
1203    def getoption(self, name, default=notset, skip=False):
1204        """ return command line option value.
1205
1206        :arg name: name of the option.  You may also specify
1207            the literal ``--OPT`` option instead of the "dest" option name.
1208        :arg default: default value if no option of that name exists.
1209        :arg skip: if True raise pytest.skip if option does not exists
1210            or has a None value.
1211        """
1212        name = self._opt2dest.get(name, name)
1213        try:
1214            val = getattr(self.option, name)
1215            if val is None and skip:
1216                raise AttributeError(name)
1217            return val
1218        except AttributeError:
1219            if default is not notset:
1220                return default
1221            if skip:
1222                import pytest
1223                pytest.skip("no %r option found" % (name,))
1224            raise ValueError("no option named %r" % (name,))
1225
1226    def getvalue(self, name, path=None):
1227        """ (deprecated, use getoption()) """
1228        return self.getoption(name)
1229
1230    def getvalueorskip(self, name, path=None):
1231        """ (deprecated, use getoption(skip=True)) """
1232        return self.getoption(name, skip=True)
1233
1234
1235def exists(path, ignore=EnvironmentError):
1236    try:
1237        return path.check()
1238    except ignore:
1239        return False
1240
1241
1242def getcfg(args, warnfunc=None):
1243    """
1244    Search the list of arguments for a valid ini-file for pytest,
1245    and return a tuple of (rootdir, inifile, cfg-dict).
1246
1247    note: warnfunc is an optional function used to warn
1248        about ini-files that use deprecated features.
1249        This parameter should be removed when pytest
1250        adopts standard deprecation warnings (#1804).
1251    """
1252    from _pytest.deprecated import SETUP_CFG_PYTEST
1253    inibasenames = ["pytest.ini", "tox.ini", "setup.cfg"]
1254    args = [x for x in args if not str(x).startswith("-")]
1255    if not args:
1256        args = [py.path.local()]
1257    for arg in args:
1258        arg = py.path.local(arg)
1259        for base in arg.parts(reverse=True):
1260            for inibasename in inibasenames:
1261                p = base.join(inibasename)
1262                if exists(p):
1263                    iniconfig = py.iniconfig.IniConfig(p)
1264                    if 'pytest' in iniconfig.sections:
1265                        if inibasename == 'setup.cfg' and warnfunc:
1266                            warnfunc('C1', SETUP_CFG_PYTEST)
1267                        return base, p, iniconfig['pytest']
1268                    if inibasename == 'setup.cfg' and 'tool:pytest' in iniconfig.sections:
1269                        return base, p, iniconfig['tool:pytest']
1270                    elif inibasename == "pytest.ini":
1271                        # allowed to be empty
1272                        return base, p, {}
1273    return None, None, None
1274
1275
1276def get_common_ancestor(paths):
1277    common_ancestor = None
1278    for path in paths:
1279        if not path.exists():
1280            continue
1281        if common_ancestor is None:
1282            common_ancestor = path
1283        else:
1284            if path.relto(common_ancestor) or path == common_ancestor:
1285                continue
1286            elif common_ancestor.relto(path):
1287                common_ancestor = path
1288            else:
1289                shared = path.common(common_ancestor)
1290                if shared is not None:
1291                    common_ancestor = shared
1292    if common_ancestor is None:
1293        common_ancestor = py.path.local()
1294    elif common_ancestor.isfile():
1295        common_ancestor = common_ancestor.dirpath()
1296    return common_ancestor
1297
1298
1299def get_dirs_from_args(args):
1300    def is_option(x):
1301        return str(x).startswith('-')
1302
1303    def get_file_part_from_node_id(x):
1304        return str(x).split('::')[0]
1305
1306    def get_dir_from_path(path):
1307        if path.isdir():
1308            return path
1309        return py.path.local(path.dirname)
1310
1311    # These look like paths but may not exist
1312    possible_paths = (
1313        py.path.local(get_file_part_from_node_id(arg))
1314        for arg in args
1315        if not is_option(arg)
1316    )
1317
1318    return [
1319        get_dir_from_path(path)
1320        for path in possible_paths
1321        if path.exists()
1322    ]
1323
1324
1325def determine_setup(inifile, args, warnfunc=None):
1326    dirs = get_dirs_from_args(args)
1327    if inifile:
1328        iniconfig = py.iniconfig.IniConfig(inifile)
1329        try:
1330            inicfg = iniconfig["pytest"]
1331        except KeyError:
1332            inicfg = None
1333        rootdir = get_common_ancestor(dirs)
1334    else:
1335        ancestor = get_common_ancestor(dirs)
1336        rootdir, inifile, inicfg = getcfg([ancestor], warnfunc=warnfunc)
1337        if rootdir is None:
1338            for rootdir in ancestor.parts(reverse=True):
1339                if rootdir.join("setup.py").exists():
1340                    break
1341            else:
1342                rootdir, inifile, inicfg = getcfg(dirs, warnfunc=warnfunc)
1343                if rootdir is None:
1344                    rootdir = get_common_ancestor([py.path.local(), ancestor])
1345                    is_fs_root = os.path.splitdrive(str(rootdir))[1] == '/'
1346                    if is_fs_root:
1347                        rootdir = ancestor
1348    return rootdir, inifile, inicfg or {}
1349
1350
1351def setns(obj, dic):
1352    import pytest
1353    for name, value in dic.items():
1354        if isinstance(value, dict):
1355            mod = getattr(obj, name, None)
1356            if mod is None:
1357                modname = "pytest.%s" % name
1358                mod = types.ModuleType(modname)
1359                sys.modules[modname] = mod
1360                mod.__all__ = []
1361                setattr(obj, name, mod)
1362            obj.__all__.append(name)
1363            setns(mod, value)
1364        else:
1365            setattr(obj, name, value)
1366            obj.__all__.append(name)
1367            # if obj != pytest:
1368            #    pytest.__all__.append(name)
1369            setattr(pytest, name, value)
1370
1371
1372def create_terminal_writer(config, *args, **kwargs):
1373    """Create a TerminalWriter instance configured according to the options
1374    in the config object. Every code which requires a TerminalWriter object
1375    and has access to a config object should use this function.
1376    """
1377    tw = py.io.TerminalWriter(*args, **kwargs)
1378    if config.option.color == 'yes':
1379        tw.hasmarkup = True
1380    if config.option.color == 'no':
1381        tw.hasmarkup = False
1382    return tw
1383
1384
1385def _strtobool(val):
1386    """Convert a string representation of truth to true (1) or false (0).
1387
1388    True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
1389    are 'n', 'no', 'f', 'false', 'off', and '0'.  Raises ValueError if
1390    'val' is anything else.
1391
1392    .. note:: copied from distutils.util
1393    """
1394    val = val.lower()
1395    if val in ('y', 'yes', 't', 'true', 'on', '1'):
1396        return 1
1397    elif val in ('n', 'no', 'f', 'false', 'off', '0'):
1398        return 0
1399    else:
1400        raise ValueError("invalid truth value %r" % (val,))
1401