Format docstrings in a consistent style
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
""" command line options, ini-file and conftest.py processing. """
|
||||
"""Command line options, ini-file and conftest.py processing."""
|
||||
import argparse
|
||||
import collections.abc
|
||||
import contextlib
|
||||
@@ -34,7 +34,7 @@ from pluggy import PluginManager
|
||||
|
||||
import _pytest._code
|
||||
import _pytest.deprecated
|
||||
import _pytest.hookspec # the extension point definitions
|
||||
import _pytest.hookspec
|
||||
from .exceptions import PrintHelp as PrintHelp
|
||||
from .exceptions import UsageError as UsageError
|
||||
from .findpaths import determine_setup
|
||||
@@ -61,9 +61,12 @@ if TYPE_CHECKING:
|
||||
|
||||
_PluggyPlugin = object
|
||||
"""A type to represent plugin objects.
|
||||
|
||||
Plugins can be any namespace, so we can't narrow it down much, but we use an
|
||||
alias to make the intent clear.
|
||||
Ideally this type would be provided by pluggy itself."""
|
||||
|
||||
Ideally this type would be provided by pluggy itself.
|
||||
"""
|
||||
|
||||
|
||||
hookimpl = HookimplMarker("pytest")
|
||||
@@ -71,25 +74,24 @@ hookspec = HookspecMarker("pytest")
|
||||
|
||||
|
||||
class ExitCode(enum.IntEnum):
|
||||
"""
|
||||
.. versionadded:: 5.0
|
||||
|
||||
Encodes the valid exit codes by pytest.
|
||||
"""Encodes the valid exit codes by pytest.
|
||||
|
||||
Currently users and plugins may supply other exit codes as well.
|
||||
|
||||
.. versionadded:: 5.0
|
||||
"""
|
||||
|
||||
#: tests passed
|
||||
#: Tests passed.
|
||||
OK = 0
|
||||
#: tests failed
|
||||
#: Tests failed.
|
||||
TESTS_FAILED = 1
|
||||
#: pytest was interrupted
|
||||
#: pytest was interrupted.
|
||||
INTERRUPTED = 2
|
||||
#: an internal error got in the way
|
||||
#: An internal error got in the way.
|
||||
INTERNAL_ERROR = 3
|
||||
#: pytest was misused
|
||||
#: pytest was misused.
|
||||
USAGE_ERROR = 4
|
||||
#: pytest couldn't find tests
|
||||
#: pytest couldn't find tests.
|
||||
NO_TESTS_COLLECTED = 5
|
||||
|
||||
|
||||
@@ -112,7 +114,7 @@ class ConftestImportFailure(Exception):
|
||||
def filter_traceback_for_conftest_import_failure(
|
||||
entry: _pytest._code.TracebackEntry,
|
||||
) -> bool:
|
||||
"""filters tracebacks entries which point to pytest internals or importlib.
|
||||
"""Filter tracebacks entries which point to pytest internals or importlib.
|
||||
|
||||
Make a special case for importlib because we use it to import test modules and conftest files
|
||||
in _pytest.pathlib.import_path.
|
||||
@@ -124,12 +126,12 @@ def main(
|
||||
args: Optional[List[str]] = None,
|
||||
plugins: Optional[Sequence[Union[str, _PluggyPlugin]]] = None,
|
||||
) -> Union[int, ExitCode]:
|
||||
""" return exit code, after performing an in-process test run.
|
||||
"""Perform an in-process test run.
|
||||
|
||||
:arg args: list of command line arguments.
|
||||
:param args: List of command line arguments.
|
||||
:param plugins: List of plugin objects to be auto-registered during initialization.
|
||||
|
||||
:arg plugins: list of plugin objects to be auto-registered during
|
||||
initialization.
|
||||
:returns: An exit code.
|
||||
"""
|
||||
try:
|
||||
try:
|
||||
@@ -171,7 +173,7 @@ def main(
|
||||
|
||||
|
||||
def console_main() -> int:
|
||||
"""pytest's CLI entry point.
|
||||
"""The CLI entry point of pytest.
|
||||
|
||||
This function is not meant for programmable use; use `main()` instead.
|
||||
"""
|
||||
@@ -193,10 +195,10 @@ class cmdline: # compatibility namespace
|
||||
|
||||
|
||||
def filename_arg(path: str, optname: str) -> str:
|
||||
""" Argparse type validator for filename arguments.
|
||||
"""Argparse type validator for filename arguments.
|
||||
|
||||
:path: path of filename
|
||||
:optname: name of the option
|
||||
:path: Path of filename.
|
||||
:optname: Name of the option.
|
||||
"""
|
||||
if os.path.isdir(path):
|
||||
raise UsageError("{} must be a filename, given: {}".format(optname, path))
|
||||
@@ -206,8 +208,8 @@ def filename_arg(path: str, optname: str) -> str:
|
||||
def directory_arg(path: str, optname: str) -> str:
|
||||
"""Argparse type validator for directory arguments.
|
||||
|
||||
:path: path of directory
|
||||
:optname: name of the option
|
||||
:path: Path of directory.
|
||||
:optname: Name of the option.
|
||||
"""
|
||||
if not os.path.isdir(path):
|
||||
raise UsageError("{} must be a directory, given: {}".format(optname, path))
|
||||
@@ -278,8 +280,7 @@ def get_config(
|
||||
|
||||
|
||||
def get_plugin_manager() -> "PytestPluginManager":
|
||||
"""
|
||||
Obtain a new instance of the
|
||||
"""Obtain a new instance of the
|
||||
:py:class:`_pytest.config.PytestPluginManager`, with default plugins
|
||||
already loaded.
|
||||
|
||||
@@ -320,13 +321,12 @@ def _prepareconfig(
|
||||
|
||||
|
||||
class PytestPluginManager(PluginManager):
|
||||
"""
|
||||
Overwrites :py:class:`pluggy.PluginManager <pluggy.PluginManager>` to add pytest-specific
|
||||
functionality:
|
||||
"""A :py:class:`pluggy.PluginManager <pluggy.PluginManager>` with
|
||||
additional pytest-specific functionality:
|
||||
|
||||
* loading plugins from the command line, ``PYTEST_PLUGINS`` env variable and
|
||||
``pytest_plugins`` global variables found in plugins being loaded;
|
||||
* ``conftest.py`` loading during start-up;
|
||||
* Loading plugins from the command line, ``PYTEST_PLUGINS`` env variable and
|
||||
``pytest_plugins`` global variables found in plugins being loaded.
|
||||
* ``conftest.py`` loading during start-up.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
@@ -359,27 +359,27 @@ class PytestPluginManager(PluginManager):
|
||||
|
||||
# Config._consider_importhook will set a real object if required.
|
||||
self.rewrite_hook = _pytest.assertion.DummyRewriteHook()
|
||||
# Used to know when we are importing conftests after the pytest_configure stage
|
||||
# Used to know when we are importing conftests after the pytest_configure stage.
|
||||
self._configured = False
|
||||
|
||||
def parse_hookimpl_opts(self, plugin: _PluggyPlugin, name: str):
|
||||
# pytest hooks are always prefixed with pytest_
|
||||
# pytest hooks are always prefixed with "pytest_",
|
||||
# so we avoid accessing possibly non-readable attributes
|
||||
# (see issue #1073)
|
||||
# (see issue #1073).
|
||||
if not name.startswith("pytest_"):
|
||||
return
|
||||
# ignore names which can not be hooks
|
||||
# Ignore names which can not be hooks.
|
||||
if name == "pytest_plugins":
|
||||
return
|
||||
|
||||
method = getattr(plugin, name)
|
||||
opts = super().parse_hookimpl_opts(plugin, name)
|
||||
|
||||
# consider only actual functions for hooks (#3775)
|
||||
# Consider only actual functions for hooks (#3775).
|
||||
if not inspect.isroutine(method):
|
||||
return
|
||||
|
||||
# collect unmarked hooks as long as they have the `pytest_' prefix
|
||||
# Collect unmarked hooks as long as they have the `pytest_' prefix.
|
||||
if opts is None and name.startswith("pytest_"):
|
||||
opts = {}
|
||||
if opts is not None:
|
||||
@@ -432,17 +432,18 @@ class PytestPluginManager(PluginManager):
|
||||
return ret
|
||||
|
||||
def getplugin(self, name: str):
|
||||
# support deprecated naming because plugins (xdist e.g.) use it
|
||||
# Support deprecated naming because plugins (xdist e.g.) use it.
|
||||
plugin = self.get_plugin(name) # type: Optional[_PluggyPlugin]
|
||||
return plugin
|
||||
|
||||
def hasplugin(self, name: str) -> bool:
|
||||
"""Return True if the plugin with the given name is registered."""
|
||||
"""Return whether a plugin with the given name is registered."""
|
||||
return bool(self.get_plugin(name))
|
||||
|
||||
def pytest_configure(self, config: "Config") -> None:
|
||||
""":meta private:"""
|
||||
# XXX now that the pluginmanager exposes hookimpl(tryfirst...)
|
||||
# we should remove tryfirst/trylast as markers
|
||||
# we should remove tryfirst/trylast as markers.
|
||||
config.addinivalue_line(
|
||||
"markers",
|
||||
"tryfirst: mark a hook implementation function such that the "
|
||||
@@ -456,15 +457,15 @@ class PytestPluginManager(PluginManager):
|
||||
self._configured = True
|
||||
|
||||
#
|
||||
# internal API for local conftest plugin handling
|
||||
# Internal API for local conftest plugin handling.
|
||||
#
|
||||
def _set_initial_conftests(self, namespace: argparse.Namespace) -> None:
|
||||
""" load initial conftest files given a preparsed "namespace".
|
||||
As conftest files may add their own command line options
|
||||
which have arguments ('--my-opt somepath') we might get some
|
||||
false positives. All builtin and 3rd party plugins will have
|
||||
been loaded, however, so common options will not confuse our logic
|
||||
here.
|
||||
"""Load initial conftest files given a preparsed "namespace".
|
||||
|
||||
As conftest files may add their own command line options which have
|
||||
arguments ('--my-opt somepath') we might get some false positives.
|
||||
All builtin and 3rd party plugins will have been loaded, however, so
|
||||
common options will not confuse our logic here.
|
||||
"""
|
||||
current = py.path.local()
|
||||
self._confcutdir = (
|
||||
@@ -513,7 +514,7 @@ class PytestPluginManager(PluginManager):
|
||||
|
||||
# XXX these days we may rather want to use config.rootdir
|
||||
# and allow users to opt into looking into the rootdir parent
|
||||
# directories instead of requiring to specify confcutdir
|
||||
# directories instead of requiring to specify confcutdir.
|
||||
clist = []
|
||||
for parent in directory.parts():
|
||||
if self._confcutdir and self._confcutdir.relto(parent):
|
||||
@@ -539,8 +540,8 @@ class PytestPluginManager(PluginManager):
|
||||
def _importconftest(
|
||||
self, conftestpath: py.path.local, importmode: Union[str, ImportMode],
|
||||
) -> types.ModuleType:
|
||||
# Use a resolved Path object as key to avoid loading the same conftest twice
|
||||
# with build systems that create build directories containing
|
||||
# Use a resolved Path object as key to avoid loading the same conftest
|
||||
# twice with build systems that create build directories containing
|
||||
# symlinks to actual files.
|
||||
# Using Path().resolve() is better than py.path.realpath because
|
||||
# it resolves to the correct path/drive in case-insensitive file systems (#5792)
|
||||
@@ -627,7 +628,7 @@ class PytestPluginManager(PluginManager):
|
||||
if name in essential_plugins:
|
||||
raise UsageError("plugin %s cannot be disabled" % name)
|
||||
|
||||
# PR #4304 : remove stepwise if cacheprovider is blocked
|
||||
# PR #4304: remove stepwise if cacheprovider is blocked.
|
||||
if name == "cacheprovider":
|
||||
self.set_blocked("stepwise")
|
||||
self.set_blocked("pytest_stepwise")
|
||||
@@ -663,11 +664,12 @@ class PytestPluginManager(PluginManager):
|
||||
self.import_plugin(import_spec)
|
||||
|
||||
def import_plugin(self, modname: str, consider_entry_points: bool = False) -> None:
|
||||
"""Import a plugin with ``modname``.
|
||||
|
||||
If ``consider_entry_points`` is True, entry point names are also
|
||||
considered to find a plugin.
|
||||
"""
|
||||
Imports a plugin with ``modname``. If ``consider_entry_points`` is True, entry point
|
||||
names are also considered to find a plugin.
|
||||
"""
|
||||
# most often modname refers to builtin modules, e.g. "pytester",
|
||||
# Most often modname refers to builtin modules, e.g. "pytester",
|
||||
# "terminal" or "capture". Those plugins are registered under their
|
||||
# basename for historic purposes but must be imported with the
|
||||
# _pytest prefix.
|
||||
@@ -743,10 +745,11 @@ notset = Notset()
|
||||
|
||||
|
||||
def _iter_rewritable_modules(package_files: Iterable[str]) -> Iterator[str]:
|
||||
"""
|
||||
Given an iterable of file names in a source distribution, return the "names" that should
|
||||
be marked for assertion rewrite (for example the package "pytest_mock/__init__.py" should
|
||||
be added as "pytest_mock" in the assertion rewrite mechanism.
|
||||
"""Given an iterable of file names in a source distribution, return the "names" that should
|
||||
be marked for assertion rewrite.
|
||||
|
||||
For example the package "pytest_mock/__init__.py" should be added as "pytest_mock" in
|
||||
the assertion rewrite mechanism.
|
||||
|
||||
This function has to deal with dist-info based distributions and egg based distributions
|
||||
(which are still very much in use for "editable" installs).
|
||||
@@ -790,11 +793,11 @@ def _iter_rewritable_modules(package_files: Iterable[str]) -> Iterator[str]:
|
||||
yield package_name
|
||||
|
||||
if not seen_some:
|
||||
# at this point we did not find any packages or modules suitable for assertion
|
||||
# At this point we did not find any packages or modules suitable for assertion
|
||||
# rewriting, so we try again by stripping the first path component (to account for
|
||||
# "src" based source trees for example)
|
||||
# this approach lets us have the common case continue to be fast, as egg-distributions
|
||||
# are rarer
|
||||
# "src" based source trees for example).
|
||||
# This approach lets us have the common case continue to be fast, as egg-distributions
|
||||
# are rarer.
|
||||
new_package_files = []
|
||||
for fn in package_files:
|
||||
parts = fn.split("/")
|
||||
@@ -810,8 +813,7 @@ def _args_converter(args: Iterable[str]) -> Tuple[str, ...]:
|
||||
|
||||
|
||||
class Config:
|
||||
"""
|
||||
Access to configuration values, pluginmanager and plugin hooks.
|
||||
"""Access to configuration values, pluginmanager and plugin hooks.
|
||||
|
||||
:param PytestPluginManager pluginmanager:
|
||||
|
||||
@@ -837,11 +839,11 @@ class Config:
|
||||
"""
|
||||
|
||||
args = attr.ib(type=Tuple[str, ...], converter=_args_converter)
|
||||
"""tuple of command-line arguments as passed to ``pytest.main()``."""
|
||||
"""Tuple of command-line arguments as passed to ``pytest.main()``."""
|
||||
plugins = attr.ib(type=Optional[Sequence[Union[str, _PluggyPlugin]]])
|
||||
"""list of extra plugins, might be `None`."""
|
||||
"""List of extra plugins, might be `None`."""
|
||||
dir = attr.ib(type=Path)
|
||||
"""directory where ``pytest.main()`` was invoked from."""
|
||||
"""Directory from which ``pytest.main()`` was invoked."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -857,9 +859,10 @@ class Config:
|
||||
)
|
||||
|
||||
self.option = argparse.Namespace()
|
||||
"""access to command line option as attributes.
|
||||
"""Access to command line option as attributes.
|
||||
|
||||
:type: argparse.Namespace"""
|
||||
:type: argparse.Namespace
|
||||
"""
|
||||
|
||||
self.invocation_params = invocation_params
|
||||
|
||||
@@ -869,9 +872,10 @@ class Config:
|
||||
processopt=self._processopt,
|
||||
)
|
||||
self.pluginmanager = pluginmanager
|
||||
"""the plugin manager handles plugin registration and hook invocation.
|
||||
"""The plugin manager handles plugin registration and hook invocation.
|
||||
|
||||
:type: PytestPluginManager"""
|
||||
:type: PytestPluginManager.
|
||||
"""
|
||||
|
||||
self.trace = self.pluginmanager.trace.root.get("config")
|
||||
self.hook = self.pluginmanager.hook
|
||||
@@ -895,11 +899,11 @@ class Config:
|
||||
|
||||
@property
|
||||
def invocation_dir(self) -> py.path.local:
|
||||
"""Backward compatibility"""
|
||||
"""Backward compatibility."""
|
||||
return py.path.local(str(self.invocation_params.dir))
|
||||
|
||||
def add_cleanup(self, func: Callable[[], None]) -> None:
|
||||
""" Add a function to be called when the config object gets out of
|
||||
"""Add a function to be called when the config object gets out of
|
||||
use (usually coninciding with pytest_unconfigure)."""
|
||||
self._cleanup.append(func)
|
||||
|
||||
@@ -970,7 +974,7 @@ class Config:
|
||||
sys.stderr.flush()
|
||||
|
||||
def cwd_relative_nodeid(self, nodeid: str) -> str:
|
||||
# nodeid's are relative to the rootpath, compute relative to cwd
|
||||
# nodeid's are relative to the rootpath, compute relative to cwd.
|
||||
if self.invocation_dir != self.rootdir:
|
||||
fullpath = self.rootdir.join(nodeid)
|
||||
nodeid = self.invocation_dir.bestrelpath(fullpath)
|
||||
@@ -978,7 +982,7 @@ class Config:
|
||||
|
||||
@classmethod
|
||||
def fromdictargs(cls, option_dict, args) -> "Config":
|
||||
""" constructor usable for subprocesses. """
|
||||
"""Constructor usable for subprocesses."""
|
||||
config = get_config(args)
|
||||
config.option.__dict__.update(option_dict)
|
||||
config.parse(args, addopts=False)
|
||||
@@ -1041,11 +1045,9 @@ class Config:
|
||||
self._warn_about_missing_assertion(mode)
|
||||
|
||||
def _mark_plugins_for_rewrite(self, hook) -> None:
|
||||
"""
|
||||
Given an importhook, mark for rewrite any top-level
|
||||
"""Given an importhook, mark for rewrite any top-level
|
||||
modules or packages in the distribution package for
|
||||
all pytest plugins.
|
||||
"""
|
||||
all pytest plugins."""
|
||||
self.pluginmanager.rewrite_hook = hook
|
||||
|
||||
if os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"):
|
||||
@@ -1194,7 +1196,7 @@ class Config:
|
||||
return [name for name in self.inicfg if name not in parser_inicfg]
|
||||
|
||||
def parse(self, args: List[str], addopts: bool = True) -> None:
|
||||
# parse given cmdline arguments into this config object.
|
||||
# Parse given cmdline arguments into this config object.
|
||||
assert not hasattr(
|
||||
self, "args"
|
||||
), "can only parse cmdline args at most once per Config object"
|
||||
@@ -1219,18 +1221,20 @@ class Config:
|
||||
pass
|
||||
|
||||
def addinivalue_line(self, name: str, line: str) -> None:
|
||||
""" add a line to an ini-file option. The option must have been
|
||||
declared but might not yet be set in which case the line becomes the
|
||||
the first line in its value. """
|
||||
"""Add a line to an ini-file option. The option must have been
|
||||
declared but might not yet be set in which case the line becomes
|
||||
the first line in its value."""
|
||||
x = self.getini(name)
|
||||
assert isinstance(x, list)
|
||||
x.append(line) # modifies the cached list inline
|
||||
|
||||
def getini(self, name: str):
|
||||
""" return configuration value from an :ref:`ini file <configfiles>`. If the
|
||||
specified name hasn't been registered through a prior
|
||||
"""Return configuration value from an :ref:`ini file <configfiles>`.
|
||||
|
||||
If the specified name hasn't been registered through a prior
|
||||
:py:func:`parser.addini <_pytest.config.argparsing.Parser.addini>`
|
||||
call (usually from a plugin), a ValueError is raised. """
|
||||
call (usually from a plugin), a ValueError is raised.
|
||||
"""
|
||||
try:
|
||||
return self._inicache[name]
|
||||
except KeyError:
|
||||
@@ -1254,19 +1258,20 @@ class Config:
|
||||
return []
|
||||
else:
|
||||
value = override_value
|
||||
# coerce the values based on types
|
||||
# note: some coercions are only required if we are reading from .ini files, because
|
||||
# Coerce the values based on types.
|
||||
#
|
||||
# Note: some coercions are only required if we are reading from .ini files, because
|
||||
# the file format doesn't contain type information, but when reading from toml we will
|
||||
# get either str or list of str values (see _parse_ini_config_from_pyproject_toml).
|
||||
# for example:
|
||||
# For example:
|
||||
#
|
||||
# ini:
|
||||
# a_line_list = "tests acceptance"
|
||||
# in this case, we need to split the string to obtain a list of strings
|
||||
# in this case, we need to split the string to obtain a list of strings.
|
||||
#
|
||||
# toml:
|
||||
# a_line_list = ["tests", "acceptance"]
|
||||
# in this case, we already have a list ready to use
|
||||
# in this case, we already have a list ready to use.
|
||||
#
|
||||
if type == "pathlist":
|
||||
# TODO: This assert is probably not valid in all cases.
|
||||
@@ -1307,9 +1312,9 @@ class Config:
|
||||
|
||||
def _get_override_ini_value(self, name: str) -> Optional[str]:
|
||||
value = None
|
||||
# override_ini is a list of "ini=value" options
|
||||
# always use the last item if multiple values are set for same ini-name,
|
||||
# e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2
|
||||
# override_ini is a list of "ini=value" options.
|
||||
# Always use the last item if multiple values are set for same ini-name,
|
||||
# e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2.
|
||||
for ini_config in self._override_ini:
|
||||
try:
|
||||
key, user_ini_value = ini_config.split("=", 1)
|
||||
@@ -1325,12 +1330,12 @@ class Config:
|
||||
return value
|
||||
|
||||
def getoption(self, name: str, default=notset, skip: bool = False):
|
||||
""" return command line option value.
|
||||
"""Return command line option value.
|
||||
|
||||
:arg name: name of the option. You may also specify
|
||||
:param name: Name of the option. You may also specify
|
||||
the literal ``--OPT`` option instead of the "dest" option name.
|
||||
:arg default: default value if no option of that name exists.
|
||||
:arg skip: if True raise pytest.skip if option does not exists
|
||||
:param default: Default value if no option of that name exists.
|
||||
:param skip: If True, raise pytest.skip if option does not exists
|
||||
or has a None value.
|
||||
"""
|
||||
name = self._opt2dest.get(name, name)
|
||||
@@ -1349,11 +1354,11 @@ class Config:
|
||||
raise ValueError("no option named {!r}".format(name)) from e
|
||||
|
||||
def getvalue(self, name: str, path=None):
|
||||
""" (deprecated, use getoption()) """
|
||||
"""Deprecated, use getoption() instead."""
|
||||
return self.getoption(name)
|
||||
|
||||
def getvalueorskip(self, name: str, path=None):
|
||||
""" (deprecated, use getoption(skip=True)) """
|
||||
"""Deprecated, use getoption(skip=True) instead."""
|
||||
return self.getoption(name, skip=True)
|
||||
|
||||
def _warn_about_missing_assertion(self, mode: str) -> None:
|
||||
@@ -1392,10 +1397,13 @@ def create_terminal_writer(
|
||||
config: Config, file: Optional[TextIO] = None
|
||||
) -> TerminalWriter:
|
||||
"""Create a TerminalWriter instance configured according to the options
|
||||
in the config object. Every code which requires a TerminalWriter object
|
||||
and has access to a config object should use this function.
|
||||
in the config object.
|
||||
|
||||
Every code which requires a TerminalWriter object and has access to a
|
||||
config object should use this function.
|
||||
"""
|
||||
tw = TerminalWriter(file=file)
|
||||
|
||||
if config.option.color == "yes":
|
||||
tw.hasmarkup = True
|
||||
elif config.option.color == "no":
|
||||
@@ -1405,6 +1413,7 @@ def create_terminal_writer(
|
||||
tw.code_highlight = True
|
||||
elif config.option.code_highlight == "no":
|
||||
tw.code_highlight = False
|
||||
|
||||
return tw
|
||||
|
||||
|
||||
@@ -1415,7 +1424,7 @@ def _strtobool(val: str) -> bool:
|
||||
are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if
|
||||
'val' is anything else.
|
||||
|
||||
.. note:: copied from distutils.util
|
||||
.. note:: Copied from distutils.util.
|
||||
"""
|
||||
val = val.lower()
|
||||
if val in ("y", "yes", "t", "true", "on", "1"):
|
||||
|
||||
@@ -27,9 +27,9 @@ FILE_OR_DIR = "file_or_dir"
|
||||
|
||||
|
||||
class Parser:
|
||||
""" Parser for command line arguments and ini-file values.
|
||||
"""Parser for command line arguments and ini-file values.
|
||||
|
||||
:ivar extra_info: dict of generic param -> value to display in case
|
||||
:ivar extra_info: Dict of generic param -> value to display in case
|
||||
there's an error processing the command line arguments.
|
||||
"""
|
||||
|
||||
@@ -56,11 +56,11 @@ class Parser:
|
||||
def getgroup(
|
||||
self, name: str, description: str = "", after: Optional[str] = None
|
||||
) -> "OptionGroup":
|
||||
""" get (or create) a named option Group.
|
||||
"""Get (or create) a named option Group.
|
||||
|
||||
:name: name of the option group.
|
||||
:description: long description for --help output.
|
||||
:after: name of other group, used for ordering --help output.
|
||||
:name: Name of the option group.
|
||||
:description: Long description for --help output.
|
||||
:after: Name of another group, used for ordering --help output.
|
||||
|
||||
The returned group object has an ``addoption`` method with the same
|
||||
signature as :py:func:`parser.addoption
|
||||
@@ -79,15 +79,14 @@ class Parser:
|
||||
return group
|
||||
|
||||
def addoption(self, *opts: str, **attrs: Any) -> None:
|
||||
""" register a command line option.
|
||||
"""Register a command line option.
|
||||
|
||||
:opts: option names, can be short or long options.
|
||||
:attrs: same attributes which the ``add_argument()`` function of the
|
||||
`argparse library
|
||||
<https://docs.python.org/library/argparse.html>`_
|
||||
:opts: Option names, can be short or long options.
|
||||
:attrs: Same attributes which the ``add_argument()`` function of the
|
||||
`argparse library <https://docs.python.org/library/argparse.html>`_
|
||||
accepts.
|
||||
|
||||
After command line parsing options are available on the pytest config
|
||||
After command line parsing, options are available on the pytest config
|
||||
object via ``config.option.NAME`` where ``NAME`` is usually set
|
||||
by passing a ``dest`` attribute, for example
|
||||
``addoption("--long", dest="NAME", ...)``.
|
||||
@@ -141,9 +140,7 @@ class Parser:
|
||||
args: Sequence[Union[str, py.path.local]],
|
||||
namespace: Optional[argparse.Namespace] = None,
|
||||
) -> argparse.Namespace:
|
||||
"""parses and returns a namespace object with known arguments at this
|
||||
point.
|
||||
"""
|
||||
"""Parse and return a namespace object with known arguments at this point."""
|
||||
return self.parse_known_and_unknown_args(args, namespace=namespace)[0]
|
||||
|
||||
def parse_known_and_unknown_args(
|
||||
@@ -151,9 +148,8 @@ class Parser:
|
||||
args: Sequence[Union[str, py.path.local]],
|
||||
namespace: Optional[argparse.Namespace] = None,
|
||||
) -> Tuple[argparse.Namespace, List[str]]:
|
||||
"""parses and returns a namespace object with known arguments, and
|
||||
the remaining arguments unknown at this point.
|
||||
"""
|
||||
"""Parse and return a namespace object with known arguments, and
|
||||
the remaining arguments unknown at this point."""
|
||||
optparser = self._getparser()
|
||||
strargs = [str(x) if isinstance(x, py.path.local) else x for x in args]
|
||||
return optparser.parse_known_args(strargs, namespace=namespace)
|
||||
@@ -165,12 +161,12 @@ class Parser:
|
||||
type: Optional["Literal['pathlist', 'args', 'linelist', 'bool']"] = None,
|
||||
default=None,
|
||||
) -> None:
|
||||
""" register an ini-file option.
|
||||
"""Register an ini-file option.
|
||||
|
||||
:name: name of the ini-variable
|
||||
:type: type of the variable, can be ``pathlist``, ``args``, ``linelist``
|
||||
:name: Name of the ini-variable.
|
||||
:type: Type of the variable, can be ``pathlist``, ``args``, ``linelist``
|
||||
or ``bool``.
|
||||
:default: default value if no ini-file option exists but is queried.
|
||||
:default: Default value if no ini-file option exists but is queried.
|
||||
|
||||
The value of ini-variables can be retrieved via a call to
|
||||
:py:func:`config.getini(name) <_pytest.config.Config.getini>`.
|
||||
@@ -181,10 +177,8 @@ class Parser:
|
||||
|
||||
|
||||
class ArgumentError(Exception):
|
||||
"""
|
||||
Raised if an Argument instance is created with invalid or
|
||||
inconsistent arguments.
|
||||
"""
|
||||
"""Raised if an Argument instance is created with invalid or
|
||||
inconsistent arguments."""
|
||||
|
||||
def __init__(self, msg: str, option: Union["Argument", str]) -> None:
|
||||
self.msg = msg
|
||||
@@ -198,17 +192,18 @@ class ArgumentError(Exception):
|
||||
|
||||
|
||||
class Argument:
|
||||
"""class that mimics the necessary behaviour of optparse.Option
|
||||
"""Class that mimics the necessary behaviour of optparse.Option.
|
||||
|
||||
It's currently a least effort implementation and ignoring choices
|
||||
and integer prefixes.
|
||||
|
||||
it's currently a least effort implementation
|
||||
and ignoring choices and integer prefixes
|
||||
https://docs.python.org/3/library/optparse.html#optparse-standard-option-types
|
||||
"""
|
||||
|
||||
_typ_map = {"int": int, "string": str, "float": float, "complex": complex}
|
||||
|
||||
def __init__(self, *names: str, **attrs: Any) -> None:
|
||||
"""store parms in private vars for use in add_argument"""
|
||||
"""Store parms in private vars for use in add_argument."""
|
||||
self._attrs = attrs
|
||||
self._short_opts = [] # type: List[str]
|
||||
self._long_opts = [] # type: List[str]
|
||||
@@ -224,7 +219,7 @@ class Argument:
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
# this might raise a keyerror as well, don't want to catch that
|
||||
# This might raise a keyerror as well, don't want to catch that.
|
||||
if isinstance(typ, str):
|
||||
if typ == "choice":
|
||||
warnings.warn(
|
||||
@@ -247,12 +242,12 @@ class Argument:
|
||||
stacklevel=4,
|
||||
)
|
||||
attrs["type"] = Argument._typ_map[typ]
|
||||
# used in test_parseopt -> test_parse_defaultgetter
|
||||
# Used in test_parseopt -> test_parse_defaultgetter.
|
||||
self.type = attrs["type"]
|
||||
else:
|
||||
self.type = typ
|
||||
try:
|
||||
# attribute existence is tested in Config._processopt
|
||||
# Attribute existence is tested in Config._processopt.
|
||||
self.default = attrs["default"]
|
||||
except KeyError:
|
||||
pass
|
||||
@@ -273,7 +268,7 @@ class Argument:
|
||||
return self._short_opts + self._long_opts
|
||||
|
||||
def attrs(self) -> Mapping[str, Any]:
|
||||
# update any attributes set by processopt
|
||||
# Update any attributes set by processopt.
|
||||
attrs = "default dest help".split()
|
||||
attrs.append(self.dest)
|
||||
for attr in attrs:
|
||||
@@ -289,9 +284,10 @@ class Argument:
|
||||
return self._attrs
|
||||
|
||||
def _set_opt_strings(self, opts: Sequence[str]) -> None:
|
||||
"""directly from optparse
|
||||
"""Directly from optparse.
|
||||
|
||||
might not be necessary as this is passed to argparse later on"""
|
||||
Might not be necessary as this is passed to argparse later on.
|
||||
"""
|
||||
for opt in opts:
|
||||
if len(opt) < 2:
|
||||
raise ArgumentError(
|
||||
@@ -340,12 +336,12 @@ class OptionGroup:
|
||||
self.parser = parser
|
||||
|
||||
def addoption(self, *optnames: str, **attrs: Any) -> None:
|
||||
""" add an option to this group.
|
||||
"""Add an option to this group.
|
||||
|
||||
if a shortened version of a long option is specified it will
|
||||
If a shortened version of a long option is specified, it will
|
||||
be suppressed in the help. addoption('--twowords', '--two-words')
|
||||
results in help showing '--two-words' only, but --twowords gets
|
||||
accepted **and** the automatic destination is in args.twowords
|
||||
accepted **and** the automatic destination is in args.twowords.
|
||||
"""
|
||||
conflict = set(optnames).intersection(
|
||||
name for opt in self.options for name in opt.names()
|
||||
@@ -386,7 +382,7 @@ class MyOptionParser(argparse.ArgumentParser):
|
||||
allow_abbrev=False,
|
||||
)
|
||||
# extra_info is a dict of (param -> value) to display if there's
|
||||
# an usage error to provide more contextual information to the user
|
||||
# an usage error to provide more contextual information to the user.
|
||||
self.extra_info = extra_info if extra_info else {}
|
||||
|
||||
def error(self, message: str) -> "NoReturn":
|
||||
@@ -405,7 +401,7 @@ class MyOptionParser(argparse.ArgumentParser):
|
||||
args: Optional[Sequence[str]] = None,
|
||||
namespace: Optional[argparse.Namespace] = None,
|
||||
) -> argparse.Namespace:
|
||||
"""allow splitting of positional arguments"""
|
||||
"""Allow splitting of positional arguments."""
|
||||
parsed, unrecognized = self.parse_known_args(args, namespace)
|
||||
if unrecognized:
|
||||
for arg in unrecognized:
|
||||
@@ -457,15 +453,15 @@ class MyOptionParser(argparse.ArgumentParser):
|
||||
|
||||
|
||||
class DropShorterLongHelpFormatter(argparse.HelpFormatter):
|
||||
"""shorten help for long options that differ only in extra hyphens
|
||||
"""Shorten help for long options that differ only in extra hyphens.
|
||||
|
||||
- collapse **long** options that are the same except for extra hyphens
|
||||
- shortcut if there are only two options and one of them is a short one
|
||||
- cache result on action object as this is called at least 2 times
|
||||
- Collapse **long** options that are the same except for extra hyphens.
|
||||
- Shortcut if there are only two options and one of them is a short one.
|
||||
- Cache result on the action object as this is called at least 2 times.
|
||||
"""
|
||||
|
||||
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||
"""Use more accurate terminal width via pylib."""
|
||||
# Use more accurate terminal width.
|
||||
if "width" not in kwargs:
|
||||
kwargs["width"] = _pytest._io.get_terminal_width()
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
class UsageError(Exception):
|
||||
""" error in pytest usage or invocation"""
|
||||
"""Error in pytest usage or invocation."""
|
||||
|
||||
|
||||
class PrintHelp(Exception):
|
||||
"""Raised when pytest should print it's help to skip the rest of the
|
||||
"""Raised when pytest should print its help to skip the rest of the
|
||||
argument parsing and validation."""
|
||||
|
||||
pass
|
||||
|
||||
@@ -18,10 +18,10 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
def _parse_ini_config(path: py.path.local) -> iniconfig.IniConfig:
|
||||
"""Parses the given generic '.ini' file using legacy IniConfig parser, returning
|
||||
"""Parse the given generic '.ini' file using legacy IniConfig parser, returning
|
||||
the parsed object.
|
||||
|
||||
Raises UsageError if the file cannot be parsed.
|
||||
Raise UsageError if the file cannot be parsed.
|
||||
"""
|
||||
try:
|
||||
return iniconfig.IniConfig(path)
|
||||
@@ -32,23 +32,23 @@ def _parse_ini_config(path: py.path.local) -> iniconfig.IniConfig:
|
||||
def load_config_dict_from_file(
|
||||
filepath: py.path.local,
|
||||
) -> Optional[Dict[str, Union[str, List[str]]]]:
|
||||
"""Loads pytest configuration from the given file path, if supported.
|
||||
"""Load pytest configuration from the given file path, if supported.
|
||||
|
||||
Return None if the file does not contain valid pytest configuration.
|
||||
"""
|
||||
|
||||
# configuration from ini files are obtained from the [pytest] section, if present.
|
||||
# Configuration from ini files are obtained from the [pytest] section, if present.
|
||||
if filepath.ext == ".ini":
|
||||
iniconfig = _parse_ini_config(filepath)
|
||||
|
||||
if "pytest" in iniconfig:
|
||||
return dict(iniconfig["pytest"].items())
|
||||
else:
|
||||
# "pytest.ini" files are always the source of configuration, even if empty
|
||||
# "pytest.ini" files are always the source of configuration, even if empty.
|
||||
if filepath.basename == "pytest.ini":
|
||||
return {}
|
||||
|
||||
# '.cfg' files are considered if they contain a "[tool:pytest]" section
|
||||
# '.cfg' files are considered if they contain a "[tool:pytest]" section.
|
||||
elif filepath.ext == ".cfg":
|
||||
iniconfig = _parse_ini_config(filepath)
|
||||
|
||||
@@ -59,7 +59,7 @@ def load_config_dict_from_file(
|
||||
# plain "[pytest]" sections in setup.cfg files is no longer supported (#3086).
|
||||
fail(CFG_PYTEST_SECTION.format(filename="setup.cfg"), pytrace=False)
|
||||
|
||||
# '.toml' files are considered if they contain a [tool.pytest.ini_options] table
|
||||
# '.toml' files are considered if they contain a [tool.pytest.ini_options] table.
|
||||
elif filepath.ext == ".toml":
|
||||
import toml
|
||||
|
||||
@@ -83,10 +83,8 @@ def locate_config(
|
||||
) -> Tuple[
|
||||
Optional[py.path.local], Optional[py.path.local], Dict[str, Union[str, List[str]]],
|
||||
]:
|
||||
"""
|
||||
Search in the list of arguments for a valid ini-file for pytest,
|
||||
and return a tuple of (rootdir, inifile, cfg-dict).
|
||||
"""
|
||||
"""Search in the list of arguments for a valid ini-file for pytest,
|
||||
and return a tuple of (rootdir, inifile, cfg-dict)."""
|
||||
config_names = [
|
||||
"pytest.ini",
|
||||
"pyproject.toml",
|
||||
|
||||
Reference in New Issue
Block a user