1""" version info, help messages, tracing configuration.  """
2from __future__ import absolute_import, division, print_function
3
4import py
5import pytest
6from _pytest.config import PrintHelp
7import os
8import sys
9from argparse import Action
10
11
12class HelpAction(Action):
13    """This is an argparse Action that will raise an exception in
14    order to skip the rest of the argument parsing when --help is passed.
15    This prevents argparse from quitting due to missing required arguments
16    when any are defined, for example by ``pytest_addoption``.
17    This is similar to the way that the builtin argparse --help option is
18    implemented by raising SystemExit.
19    """
20
21    def __init__(self,
22                 option_strings,
23                 dest=None,
24                 default=False,
25                 help=None):
26        super(HelpAction, self).__init__(
27            option_strings=option_strings,
28            dest=dest,
29            const=True,
30            default=default,
31            nargs=0,
32            help=help)
33
34    def __call__(self, parser, namespace, values, option_string=None):
35        setattr(namespace, self.dest, self.const)
36
37        # We should only skip the rest of the parsing after preparse is done
38        if getattr(parser._parser, 'after_preparse', False):
39            raise PrintHelp
40
41
42def pytest_addoption(parser):
43    group = parser.getgroup('debugconfig')
44    group.addoption('--version', action="store_true",
45                    help="display pytest lib version and import information.")
46    group._addoption("-h", "--help", action=HelpAction, dest="help",
47                     help="show help message and configuration info")
48    group._addoption('-p', action="append", dest="plugins", default=[],
49                     metavar="name",
50                     help="early-load given plugin (multi-allowed). "
51                     "To avoid loading of plugins, use the `no:` prefix, e.g. "
52                     "`no:doctest`.")
53    group.addoption('--traceconfig', '--trace-config',
54                    action="store_true", default=False,
55                    help="trace considerations of conftest.py files."),
56    group.addoption('--debug',
57                    action="store_true", dest="debug", default=False,
58                    help="store internal tracing debug information in 'pytestdebug.log'.")
59    group._addoption(
60        '-o', '--override-ini', nargs='*', dest="override_ini",
61        action="append",
62        help="override config option with option=value style, e.g. `-o xfail_strict=True`.")
63
64
65@pytest.hookimpl(hookwrapper=True)
66def pytest_cmdline_parse():
67    outcome = yield
68    config = outcome.get_result()
69    if config.option.debug:
70        path = os.path.abspath("pytestdebug.log")
71        debugfile = open(path, 'w')
72        debugfile.write("versions pytest-%s, py-%s, "
73                        "python-%s\ncwd=%s\nargs=%s\n\n" % (
74                            pytest.__version__, py.__version__,
75                            ".".join(map(str, sys.version_info)),
76                            os.getcwd(), config._origargs))
77        config.trace.root.setwriter(debugfile.write)
78        undo_tracing = config.pluginmanager.enable_tracing()
79        sys.stderr.write("writing pytestdebug information to %s\n" % path)
80
81        def unset_tracing():
82            debugfile.close()
83            sys.stderr.write("wrote pytestdebug information to %s\n" %
84                             debugfile.name)
85            config.trace.root.setwriter(None)
86            undo_tracing()
87
88        config.add_cleanup(unset_tracing)
89
90
91def pytest_cmdline_main(config):
92    if config.option.version:
93        p = py.path.local(pytest.__file__)
94        sys.stderr.write("This is pytest version %s, imported from %s\n" %
95                         (pytest.__version__, p))
96        plugininfo = getpluginversioninfo(config)
97        if plugininfo:
98            for line in plugininfo:
99                sys.stderr.write(line + "\n")
100        return 0
101    elif config.option.help:
102        config._do_configure()
103        showhelp(config)
104        config._ensure_unconfigure()
105        return 0
106
107
108def showhelp(config):
109    reporter = config.pluginmanager.get_plugin('terminalreporter')
110    tw = reporter._tw
111    tw.write(config._parser.optparser.format_help())
112    tw.line()
113    tw.line()
114    tw.line("[pytest] ini-options in the first "
115            "pytest.ini|tox.ini|setup.cfg file found:")
116    tw.line()
117
118    for name in config._parser._ininames:
119        help, type, default = config._parser._inidict[name]
120        if type is None:
121            type = "string"
122        spec = "%s (%s)" % (name, type)
123        line = "  %-24s %s" % (spec, help)
124        tw.line(line[:tw.fullwidth])
125
126    tw.line()
127    tw.line("environment variables:")
128    vars = [
129        ("PYTEST_ADDOPTS", "extra command line options"),
130        ("PYTEST_PLUGINS", "comma-separated plugins to load during startup"),
131        ("PYTEST_DEBUG", "set to enable debug tracing of pytest's internals")
132    ]
133    for name, help in vars:
134        tw.line("  %-24s %s" % (name, help))
135    tw.line()
136    tw.line()
137
138    tw.line("to see available markers type: pytest --markers")
139    tw.line("to see available fixtures type: pytest --fixtures")
140    tw.line("(shown according to specified file_or_dir or current dir "
141            "if not specified)")
142
143    for warningreport in reporter.stats.get('warnings', []):
144        tw.line("warning : " + warningreport.message, red=True)
145    return
146
147
148conftest_options = [
149    ('pytest_plugins', 'list of plugin names to load'),
150]
151
152
153def getpluginversioninfo(config):
154    lines = []
155    plugininfo = config.pluginmanager.list_plugin_distinfo()
156    if plugininfo:
157        lines.append("setuptools registered plugins:")
158        for plugin, dist in plugininfo:
159            loc = getattr(plugin, '__file__', repr(plugin))
160            content = "%s-%s at %s" % (dist.project_name, dist.version, loc)
161            lines.append("  " + content)
162    return lines
163
164
165def pytest_report_header(config):
166    lines = []
167    if config.option.debug or config.option.traceconfig:
168        lines.append("using: pytest-%s pylib-%s" %
169                     (pytest.__version__, py.__version__))
170
171        verinfo = getpluginversioninfo(config)
172        if verinfo:
173            lines.extend(verinfo)
174
175    if config.option.traceconfig:
176        lines.append("active plugins:")
177        items = config.pluginmanager.list_name_plugin()
178        for name, plugin in items:
179            if hasattr(plugin, '__file__'):
180                r = plugin.__file__
181            else:
182                r = repr(plugin)
183            lines.append("    %-20s: %s" % (name, r))
184    return lines
185