updates
This commit is contained in:
@@ -1,58 +1,44 @@
|
||||
# mypy: allow-untyped-defs
|
||||
"""Version info, help messages, tracing configuration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
from collections.abc import Generator
|
||||
from collections.abc import Sequence
|
||||
import os
|
||||
import sys
|
||||
from typing import Any
|
||||
from argparse import Action
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Union
|
||||
|
||||
import pytest
|
||||
from _pytest.config import Config
|
||||
from _pytest.config import ExitCode
|
||||
from _pytest.config import PrintHelp
|
||||
from _pytest.config.argparsing import Parser
|
||||
from _pytest.terminal import TerminalReporter
|
||||
import pytest
|
||||
|
||||
|
||||
class HelpAction(argparse.Action):
|
||||
"""An argparse Action that will raise a PrintHelp exception in order to skip
|
||||
the rest of the argument parsing when --help is passed.
|
||||
class HelpAction(Action):
|
||||
"""An argparse Action that will raise an exception in order to skip the
|
||||
rest of the argument parsing when --help is passed.
|
||||
|
||||
This prevents argparse from raising UsageError when `--help` is used along
|
||||
with missing required arguments when any are defined, for example by
|
||||
``pytest_addoption``. This is similar to the way that the builtin argparse
|
||||
--help option is implemented by raising SystemExit.
|
||||
|
||||
To opt in to this behavior, the parse caller must set
|
||||
`namespace._raise_print_help = True`. Otherwise it just sets the option.
|
||||
This prevents argparse from quitting due to missing required arguments
|
||||
when any are defined, for example by ``pytest_addoption``.
|
||||
This is similar to the way that the builtin argparse --help option is
|
||||
implemented by raising SystemExit.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, option_strings: Sequence[str], dest: str, *, help: str | None = None
|
||||
) -> None:
|
||||
def __init__(self, option_strings, dest=None, default=False, help=None):
|
||||
super().__init__(
|
||||
option_strings=option_strings,
|
||||
dest=dest,
|
||||
nargs=0,
|
||||
const=True,
|
||||
default=False,
|
||||
default=default,
|
||||
nargs=0,
|
||||
help=help,
|
||||
)
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
parser: argparse.ArgumentParser,
|
||||
namespace: argparse.Namespace,
|
||||
values: str | Sequence[Any] | None,
|
||||
option_string: str | None = None,
|
||||
) -> None:
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
setattr(namespace, self.dest, self.const)
|
||||
|
||||
if getattr(namespace, "_raise_print_help", False):
|
||||
# We should only skip the rest of the parsing after preparse is done.
|
||||
if getattr(parser._parser, "after_preparse", False):
|
||||
raise PrintHelp
|
||||
|
||||
|
||||
@@ -67,14 +53,14 @@ def pytest_addoption(parser: Parser) -> None:
|
||||
help="Display pytest version and information about plugins. "
|
||||
"When given twice, also display information about plugins.",
|
||||
)
|
||||
group._addoption( # private to use reserved lower-case short option
|
||||
group._addoption(
|
||||
"-h",
|
||||
"--help",
|
||||
action=HelpAction,
|
||||
dest="help",
|
||||
help="Show help message and configuration info",
|
||||
)
|
||||
group._addoption( # private to use reserved lower-case short option
|
||||
group._addoption(
|
||||
"-p",
|
||||
action="append",
|
||||
dest="plugins",
|
||||
@@ -82,14 +68,7 @@ def pytest_addoption(parser: Parser) -> None:
|
||||
metavar="name",
|
||||
help="Early-load given plugin module name or entry point (multi-allowed). "
|
||||
"To avoid loading of plugins, use the `no:` prefix, e.g. "
|
||||
"`no:doctest`. See also --disable-plugin-autoload.",
|
||||
)
|
||||
group.addoption(
|
||||
"--disable-plugin-autoload",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Disable plugin auto-loading through entry point packaging metadata. "
|
||||
"Only plugins explicitly specified in -p or env var PYTEST_PLUGINS will be loaded.",
|
||||
"`no:doctest`.",
|
||||
)
|
||||
group.addoption(
|
||||
"--traceconfig",
|
||||
@@ -109,78 +88,79 @@ def pytest_addoption(parser: Parser) -> None:
|
||||
"This file is opened with 'w' and truncated as a result, care advised. "
|
||||
"Default: pytestdebug.log.",
|
||||
)
|
||||
group._addoption( # private to use reserved lower-case short option
|
||||
group._addoption(
|
||||
"-o",
|
||||
"--override-ini",
|
||||
dest="override_ini",
|
||||
action="append",
|
||||
help='Override configuration option with "option=value" style, '
|
||||
"e.g. `-o strict_xfail=True -o cache_dir=cache`.",
|
||||
help='Override ini option with "option=value" style, '
|
||||
"e.g. `-o xfail_strict=True -o cache_dir=cache`.",
|
||||
)
|
||||
|
||||
|
||||
@pytest.hookimpl(wrapper=True)
|
||||
def pytest_cmdline_parse() -> Generator[None, Config, Config]:
|
||||
config = yield
|
||||
@pytest.hookimpl(hookwrapper=True)
|
||||
def pytest_cmdline_parse():
|
||||
outcome = yield
|
||||
config: Config = outcome.get_result()
|
||||
|
||||
if config.option.debug:
|
||||
# --debug | --debug <file.log> was provided.
|
||||
path = config.option.debug
|
||||
debugfile = open(path, "w", encoding="utf-8")
|
||||
debugfile.write(
|
||||
"versions pytest-{}, "
|
||||
"python-{}\ninvocation_dir={}\ncwd={}\nargs={}\n\n".format(
|
||||
"versions pytest-%s, "
|
||||
"python-%s\ncwd=%s\nargs=%s\n\n"
|
||||
% (
|
||||
pytest.__version__,
|
||||
".".join(map(str, sys.version_info)),
|
||||
config.invocation_params.dir,
|
||||
os.getcwd(),
|
||||
config.invocation_params.args,
|
||||
)
|
||||
)
|
||||
config.trace.root.setwriter(debugfile.write)
|
||||
undo_tracing = config.pluginmanager.enable_tracing()
|
||||
sys.stderr.write(f"writing pytest debug information to {path}\n")
|
||||
sys.stderr.write("writing pytest debug information to %s\n" % path)
|
||||
|
||||
def unset_tracing() -> None:
|
||||
debugfile.close()
|
||||
sys.stderr.write(f"wrote pytest debug information to {debugfile.name}\n")
|
||||
sys.stderr.write("wrote pytest debug information to %s\n" % debugfile.name)
|
||||
config.trace.root.setwriter(None)
|
||||
undo_tracing()
|
||||
|
||||
config.add_cleanup(unset_tracing)
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def show_version_verbose(config: Config) -> None:
|
||||
"""Show verbose pytest version installation, including plugins."""
|
||||
sys.stdout.write(
|
||||
f"This is pytest version {pytest.__version__}, imported from {pytest.__file__}\n"
|
||||
)
|
||||
plugininfo = getpluginversioninfo(config)
|
||||
if plugininfo:
|
||||
for line in plugininfo:
|
||||
sys.stdout.write(line + "\n")
|
||||
|
||||
|
||||
def pytest_cmdline_main(config: Config) -> int | ExitCode | None:
|
||||
# Note: a single `--version` argument is handled directly by `Config.main()` to avoid starting up the entire
|
||||
# pytest infrastructure just to display the version (#13574).
|
||||
def showversion(config: Config) -> None:
|
||||
if config.option.version > 1:
|
||||
show_version_verbose(config)
|
||||
return ExitCode.OK
|
||||
sys.stdout.write(
|
||||
"This is pytest version {}, imported from {}\n".format(
|
||||
pytest.__version__, pytest.__file__
|
||||
)
|
||||
)
|
||||
plugininfo = getpluginversioninfo(config)
|
||||
if plugininfo:
|
||||
for line in plugininfo:
|
||||
sys.stdout.write(line + "\n")
|
||||
else:
|
||||
sys.stdout.write(f"pytest {pytest.__version__}\n")
|
||||
|
||||
|
||||
def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]:
|
||||
if config.option.version > 0:
|
||||
showversion(config)
|
||||
return 0
|
||||
elif config.option.help:
|
||||
config._do_configure()
|
||||
showhelp(config)
|
||||
config._ensure_unconfigure()
|
||||
return ExitCode.OK
|
||||
return 0
|
||||
return None
|
||||
|
||||
|
||||
def showhelp(config: Config) -> None:
|
||||
import textwrap
|
||||
|
||||
reporter: TerminalReporter | None = config.pluginmanager.get_plugin(
|
||||
reporter: Optional[TerminalReporter] = config.pluginmanager.get_plugin(
|
||||
"terminalreporter"
|
||||
)
|
||||
assert reporter is not None
|
||||
@@ -188,20 +168,22 @@ def showhelp(config: Config) -> None:
|
||||
tw.write(config._parser.optparser.format_help())
|
||||
tw.line()
|
||||
tw.line(
|
||||
"[pytest] configuration options in the first "
|
||||
"pytest.toml|pytest.ini|tox.ini|setup.cfg|pyproject.toml file found:"
|
||||
"[pytest] ini-options in the first "
|
||||
"pytest.ini|tox.ini|setup.cfg|pyproject.toml file found:"
|
||||
)
|
||||
tw.line()
|
||||
|
||||
columns = tw.fullwidth # costly call
|
||||
indent_len = 24 # based on argparse's max_help_position=24
|
||||
indent = " " * indent_len
|
||||
for name in config._parser._inidict:
|
||||
help, type, _default = config._parser._inidict[name]
|
||||
for name in config._parser._ininames:
|
||||
help, type, default = config._parser._inidict[name]
|
||||
if type is None:
|
||||
type = "string"
|
||||
if help is None:
|
||||
raise TypeError(f"help argument cannot be None for {name}")
|
||||
spec = f"{name} ({type}):"
|
||||
tw.write(f" {spec}")
|
||||
tw.write(" %s" % spec)
|
||||
spec_len = len(spec)
|
||||
if spec_len > (indent_len - 3):
|
||||
# Display help starting at a new line.
|
||||
@@ -229,19 +211,10 @@ def showhelp(config: Config) -> None:
|
||||
tw.line()
|
||||
tw.line("Environment variables:")
|
||||
vars = [
|
||||
(
|
||||
"CI",
|
||||
"When set to a non-empty value, pytest knows it is running in a "
|
||||
"CI process and does not truncate summary info",
|
||||
),
|
||||
("BUILD_NUMBER", "Equivalent to CI"),
|
||||
("PYTEST_ADDOPTS", "Extra command line options"),
|
||||
("PYTEST_PLUGINS", "Comma-separated plugins to load during startup"),
|
||||
("PYTEST_DISABLE_PLUGIN_AUTOLOAD", "Set to disable plugin auto-loading"),
|
||||
("PYTEST_DEBUG", "Set to enable debug tracing of pytest's internals"),
|
||||
("PYTEST_DEBUG_TEMPROOT", "Override the system temporary directory"),
|
||||
("PYTEST_THEME", "The Pygments style to use for code output"),
|
||||
("PYTEST_THEME_MODE", "Set the PYTEST_THEME to be either 'dark' or 'light'"),
|
||||
]
|
||||
for name, help in vars:
|
||||
tw.line(f" {name:<24} {help}")
|
||||
@@ -258,13 +231,17 @@ def showhelp(config: Config) -> None:
|
||||
|
||||
for warningreport in reporter.stats.get("warnings", []):
|
||||
tw.line("warning : " + warningreport.message, red=True)
|
||||
return
|
||||
|
||||
|
||||
def getpluginversioninfo(config: Config) -> list[str]:
|
||||
conftest_options = [("pytest_plugins", "list of plugin names to load")]
|
||||
|
||||
|
||||
def getpluginversioninfo(config: Config) -> List[str]:
|
||||
lines = []
|
||||
plugininfo = config.pluginmanager.list_plugin_distinfo()
|
||||
if plugininfo:
|
||||
lines.append("registered third-party plugins:")
|
||||
lines.append("setuptools registered plugins:")
|
||||
for plugin, dist in plugininfo:
|
||||
loc = getattr(plugin, "__file__", repr(plugin))
|
||||
content = f"{dist.project_name}-{dist.version} at {loc}"
|
||||
@@ -272,7 +249,7 @@ def getpluginversioninfo(config: Config) -> list[str]:
|
||||
return lines
|
||||
|
||||
|
||||
def pytest_report_header(config: Config) -> list[str]:
|
||||
def pytest_report_header(config: Config) -> List[str]:
|
||||
lines = []
|
||||
if config.option.debug or config.option.traceconfig:
|
||||
lines.append(f"using: pytest-{pytest.__version__}")
|
||||
|
||||
Reference in New Issue
Block a user