summaryrefslogtreecommitdiffstats
path: root/contrib/python/pytest/py3/_pytest/config
diff options
context:
space:
mode:
authordeshevoy <[email protected]>2022-02-10 16:46:57 +0300
committerDaniil Cherednik <[email protected]>2022-02-10 16:46:57 +0300
commit28148f76dbfcc644d96427d41c92f36cbf2fdc6e (patch)
treeb83306b6e37edeea782e9eed673d89286c4fef35 /contrib/python/pytest/py3/_pytest/config
parente988f30484abe5fdeedcc7a5d3c226c01a21800c (diff)
Restoring authorship annotation for <[email protected]>. Commit 2 of 2.
Diffstat (limited to 'contrib/python/pytest/py3/_pytest/config')
-rw-r--r--contrib/python/pytest/py3/_pytest/config/__init__.py1140
-rw-r--r--contrib/python/pytest/py3/_pytest/config/argparsing.py534
-rw-r--r--contrib/python/pytest/py3/_pytest/config/exceptions.py10
-rw-r--r--contrib/python/pytest/py3/_pytest/config/findpaths.py114
4 files changed, 899 insertions, 899 deletions
diff --git a/contrib/python/pytest/py3/_pytest/config/__init__.py b/contrib/python/pytest/py3/_pytest/config/__init__.py
index fb07b6bfb9e..bd9e2883f9f 100644
--- a/contrib/python/pytest/py3/_pytest/config/__init__.py
+++ b/contrib/python/pytest/py3/_pytest/config/__init__.py
@@ -1,16 +1,16 @@
"""Command line options, ini-file and conftest.py processing."""
-import argparse
+import argparse
import collections.abc
import contextlib
-import copy
+import copy
import enum
-import inspect
-import os
+import inspect
+import os
import re
-import shlex
-import sys
-import types
-import warnings
+import shlex
+import sys
+import types
+import warnings
from functools import lru_cache
from pathlib import Path
from types import TracebackType
@@ -30,32 +30,32 @@ from typing import Tuple
from typing import Type
from typing import TYPE_CHECKING
from typing import Union
-
+
import attr
-import py
-from pluggy import HookimplMarker
-from pluggy import HookspecMarker
-from pluggy import PluginManager
-
-import _pytest._code
+import py
+from pluggy import HookimplMarker
+from pluggy import HookspecMarker
+from pluggy import PluginManager
+
+import _pytest._code
import _pytest.deprecated
import _pytest.hookspec
from .exceptions import PrintHelp as PrintHelp
from .exceptions import UsageError as UsageError
-from .findpaths import determine_setup
-from _pytest._code import ExceptionInfo
-from _pytest._code import filter_traceback
+from .findpaths import determine_setup
+from _pytest._code import ExceptionInfo
+from _pytest._code import filter_traceback
from _pytest._io import TerminalWriter
from _pytest.compat import final
from _pytest.compat import importlib_metadata
from _pytest.outcomes import fail
-from _pytest.outcomes import Skipped
+from _pytest.outcomes import Skipped
from _pytest.pathlib import bestrelpath
from _pytest.pathlib import import_path
from _pytest.pathlib import ImportMode
from _pytest.store import Store
from _pytest.warning_types import PytestConfigWarning
-
+
if TYPE_CHECKING:
from _pytest._code.code import _TracebackStyle
@@ -73,10 +73,10 @@ Ideally this type would be provided by pluggy itself.
"""
-hookimpl = HookimplMarker("pytest")
-hookspec = HookspecMarker("pytest")
-
-
+hookimpl = HookimplMarker("pytest")
+hookspec = HookspecMarker("pytest")
+
+
@final
class ExitCode(enum.IntEnum):
"""Encodes the valid exit codes by pytest.
@@ -100,30 +100,30 @@ class ExitCode(enum.IntEnum):
NO_TESTS_COLLECTED = 5
-class ConftestImportFailure(Exception):
+class ConftestImportFailure(Exception):
def __init__(
self,
path: py.path.local,
excinfo: Tuple[Type[Exception], Exception, TracebackType],
) -> None:
super().__init__(path, excinfo)
- self.path = path
+ self.path = path
self.excinfo = excinfo
-
+
def __str__(self) -> str:
return "{}: {} (from {})".format(
self.excinfo[0].__name__, self.excinfo[1], self.path
)
-
-
+
+
def filter_traceback_for_conftest_import_failure(
entry: _pytest._code.TracebackEntry,
) -> bool:
"""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.
- """
+ """
return filter_traceback(entry) and "importlib" not in str(entry.path).split(os.sep)
@@ -138,27 +138,27 @@ def main(
:returns: An exit code.
"""
- try:
- try:
- config = _prepareconfig(args, plugins)
- except ConftestImportFailure as e:
- exc_info = ExceptionInfo(e.excinfo)
+ try:
+ try:
+ config = _prepareconfig(args, plugins)
+ except ConftestImportFailure as e:
+ exc_info = ExceptionInfo(e.excinfo)
tw = TerminalWriter(sys.stderr)
tw.line(f"ImportError while loading conftest '{e.path}'.", red=True)
exc_info.traceback = exc_info.traceback.filter(
filter_traceback_for_conftest_import_failure
- )
- exc_repr = (
- exc_info.getrepr(style="short", chain=False)
- if exc_info.traceback
- else exc_info.exconly()
- )
+ )
+ exc_repr = (
+ exc_info.getrepr(style="short", chain=False)
+ if exc_info.traceback
+ else exc_info.exconly()
+ )
formatted_tb = str(exc_repr)
- for line in formatted_tb.splitlines():
- tw.line(line.rstrip(), red=True)
+ for line in formatted_tb.splitlines():
+ tw.line(line.rstrip(), red=True)
return ExitCode.USAGE_ERROR
- else:
- try:
+ else:
+ try:
ret: Union[ExitCode, int] = config.hook.pytest_cmdline_main(
config=config
)
@@ -166,15 +166,15 @@ def main(
return ExitCode(ret)
except ValueError:
return ret
- finally:
- config._ensure_unconfigure()
- except UsageError as e:
+ finally:
+ config._ensure_unconfigure()
+ except UsageError as e:
tw = TerminalWriter(sys.stderr)
- for msg in e.args:
+ for msg in e.args:
tw.line(f"ERROR: {msg}\n", red=True)
return ExitCode.USAGE_ERROR
-
-
+
+
def console_main() -> int:
"""The CLI entry point of pytest.
@@ -194,78 +194,78 @@ def console_main() -> int:
class cmdline: # compatibility namespace
- main = staticmethod(main)
-
-
+ main = staticmethod(main)
+
+
def filename_arg(path: str, optname: str) -> str:
"""Argparse type validator for filename arguments.
-
+
:path: Path of filename.
:optname: Name of the option.
- """
- if os.path.isdir(path):
+ """
+ if os.path.isdir(path):
raise UsageError(f"{optname} must be a filename, given: {path}")
- return path
-
-
+ return path
+
+
def directory_arg(path: str, optname: str) -> str:
- """Argparse type validator for directory arguments.
-
+ """Argparse type validator for directory arguments.
+
:path: Path of directory.
:optname: Name of the option.
- """
- if not os.path.isdir(path):
+ """
+ if not os.path.isdir(path):
raise UsageError(f"{optname} must be a directory, given: {path}")
- return path
-
-
+ return path
+
+
# Plugins that cannot be disabled via "-p no:X" currently.
essential_plugins = (
- "mark",
- "main",
- "runner",
+ "mark",
+ "main",
+ "runner",
"fixtures",
"helpconfig", # Provides -p.
)
default_plugins = essential_plugins + (
- "python",
+ "python",
"terminal",
- "debugging",
- "unittest",
- "capture",
- "skipping",
- "tmpdir",
- "monkeypatch",
- "recwarn",
- "pastebin",
- "nose",
- "assertion",
- "junitxml",
- "doctest",
- "cacheprovider",
- "freeze_support",
- "setuponly",
- "setupplan",
- "stepwise",
- "warnings",
- "logging",
+ "debugging",
+ "unittest",
+ "capture",
+ "skipping",
+ "tmpdir",
+ "monkeypatch",
+ "recwarn",
+ "pastebin",
+ "nose",
+ "assertion",
+ "junitxml",
+ "doctest",
+ "cacheprovider",
+ "freeze_support",
+ "setuponly",
+ "setupplan",
+ "stepwise",
+ "warnings",
+ "logging",
"reports",
*(["unraisableexception", "threadexception"] if sys.version_info >= (3, 8) else []),
"faulthandler",
-)
-
-builtin_plugins = set(default_plugins)
-builtin_plugins.add("pytester")
+)
+
+builtin_plugins = set(default_plugins)
+builtin_plugins.add("pytester")
builtin_plugins.add("pytester_assertions")
-
-
+
+
def get_config(
args: Optional[List[str]] = None,
plugins: Optional[Sequence[Union[str, _PluggyPlugin]]] = None,
) -> "Config":
- # subsequent calls to main will create a fresh instance
- pluginmanager = PytestPluginManager()
+ # subsequent calls to main will create a fresh instance
+ pluginmanager = PytestPluginManager()
config = Config(
pluginmanager,
invocation_params=Config.InvocationParams(
@@ -277,66 +277,66 @@ def get_config(
# Handle any "-p no:plugin" args.
pluginmanager.consider_preparse(args, exclude_only=True)
- for spec in default_plugins:
- pluginmanager.import_plugin(spec)
+ for spec in default_plugins:
+ pluginmanager.import_plugin(spec)
+
+ return config
+
- return config
-
-
def get_plugin_manager() -> "PytestPluginManager":
"""Obtain a new instance of the
- :py:class:`_pytest.config.PytestPluginManager`, with default plugins
- already loaded.
-
- This function can be used by integration with other tools, like hooking
- into pytest to run tests into an IDE.
- """
- return get_config().pluginmanager
-
-
+ :py:class:`_pytest.config.PytestPluginManager`, with default plugins
+ already loaded.
+
+ This function can be used by integration with other tools, like hooking
+ into pytest to run tests into an IDE.
+ """
+ return get_config().pluginmanager
+
+
def _prepareconfig(
args: Optional[Union[py.path.local, List[str]]] = None,
plugins: Optional[Sequence[Union[str, _PluggyPlugin]]] = None,
) -> "Config":
- if args is None:
- args = sys.argv[1:]
- elif isinstance(args, py.path.local):
- args = [str(args)]
+ if args is None:
+ args = sys.argv[1:]
+ elif isinstance(args, py.path.local):
+ args = [str(args)]
elif not isinstance(args, list):
msg = "`args` parameter expected to be a list of strings, got: {!r} (type: {})"
raise TypeError(msg.format(args, type(args)))
-
+
config = get_config(args, plugins)
- pluginmanager = config.pluginmanager
- try:
- if plugins:
- for plugin in plugins:
+ pluginmanager = config.pluginmanager
+ try:
+ if plugins:
+ for plugin in plugins:
if isinstance(plugin, str):
- pluginmanager.consider_pluginarg(plugin)
- else:
- pluginmanager.register(plugin)
+ pluginmanager.consider_pluginarg(plugin)
+ else:
+ pluginmanager.register(plugin)
config = pluginmanager.hook.pytest_cmdline_parse(
- pluginmanager=pluginmanager, args=args
- )
+ pluginmanager=pluginmanager, args=args
+ )
return config
- except BaseException:
- config._ensure_unconfigure()
- raise
-
-
+ except BaseException:
+ config._ensure_unconfigure()
+ raise
+
+
@final
-class PytestPluginManager(PluginManager):
+class PytestPluginManager(PluginManager):
"""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.
- """
-
+ """
+
def __init__(self) -> None:
import _pytest.assertion
-
+
super().__init__("pytest")
# The objects are module objects, only used generically.
self._conftest_plugins: Set[types.ModuleType] = set()
@@ -345,9 +345,9 @@ class PytestPluginManager(PluginManager):
self._dirpath2confmods: Dict[py.path.local, List[types.ModuleType]] = {}
self._conftestpath2mod: Dict[Path, types.ModuleType] = {}
self._confcutdir: Optional[py.path.local] = None
- self._noconftest = False
+ self._noconftest = False
self._duplicatepaths: Set[py.path.local] = set()
-
+
# plugins that were explicitly skipped with pytest.skip
# list of (module name, skip reason)
# previously we would issue a warning when a plugin was skipped, but
@@ -355,71 +355,71 @@ class PytestPluginManager(PluginManager):
# just stored here to be used later.
self.skipped_plugins: List[Tuple[str, str]] = []
- self.add_hookspecs(_pytest.hookspec)
- self.register(self)
- if os.environ.get("PYTEST_DEBUG"):
+ self.add_hookspecs(_pytest.hookspec)
+ self.register(self)
+ if os.environ.get("PYTEST_DEBUG"):
err: IO[str] = sys.stderr
encoding: str = getattr(err, "encoding", "utf8")
- try:
+ try:
err = open(
os.dup(err.fileno()), mode=err.mode, buffering=1, encoding=encoding,
)
- except Exception:
- pass
- self.trace.root.setwriter(err.write)
- self.enable_tracing()
-
- # Config._consider_importhook will set a real object if required.
- self.rewrite_hook = _pytest.assertion.DummyRewriteHook()
+ except Exception:
+ pass
+ self.trace.root.setwriter(err.write)
+ self.enable_tracing()
+
+ # 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.
- self._configured = False
-
+ self._configured = False
+
def parse_hookimpl_opts(self, plugin: _PluggyPlugin, name: str):
# pytest hooks are always prefixed with "pytest_",
- # so we avoid accessing possibly non-readable attributes
+ # so we avoid accessing possibly non-readable attributes
# (see issue #1073).
- if not name.startswith("pytest_"):
- return
+ if not name.startswith("pytest_"):
+ return
# Ignore names which can not be hooks.
if name == "pytest_plugins":
- return
-
- method = getattr(plugin, name)
+ return
+
+ method = getattr(plugin, name)
opts = super().parse_hookimpl_opts(plugin, name)
-
+
# Consider only actual functions for hooks (#3775).
- if not inspect.isroutine(method):
- return
-
+ if not inspect.isroutine(method):
+ return
+
# Collect unmarked hooks as long as they have the `pytest_' prefix.
- if opts is None and name.startswith("pytest_"):
- opts = {}
+ if opts is None and name.startswith("pytest_"):
+ opts = {}
if opts is not None:
# TODO: DeprecationWarning, people should use hookimpl
# https://github.com/pytest-dev/pytest/issues/4562
known_marks = {m.name for m in getattr(method, "pytestmark", [])}
-
- for name in ("tryfirst", "trylast", "optionalhook", "hookwrapper"):
+
+ for name in ("tryfirst", "trylast", "optionalhook", "hookwrapper"):
opts.setdefault(name, hasattr(method, name) or name in known_marks)
- return opts
-
+ return opts
+
def parse_hookspec_opts(self, module_or_class, name: str):
opts = super().parse_hookspec_opts(module_or_class, name)
- if opts is None:
- method = getattr(module_or_class, name)
+ if opts is None:
+ method = getattr(module_or_class, name)
- if name.startswith("pytest_"):
+ if name.startswith("pytest_"):
# todo: deprecate hookspec hacks
# https://github.com/pytest-dev/pytest/issues/4562
known_marks = {m.name for m in getattr(method, "pytestmark", [])}
- opts = {
+ opts = {
"firstresult": hasattr(method, "firstresult")
or "firstresult" in known_marks,
"historic": hasattr(method, "historic")
or "historic" in known_marks,
- }
- return opts
-
+ }
+ return opts
+
def register(
self, plugin: _PluggyPlugin, name: Optional[str] = None
) -> Optional[str]:
@@ -430,47 +430,47 @@ class PytestPluginManager(PluginManager):
"please remove it from your requirements.".format(
name.replace("_", "-")
)
- )
- )
+ )
+ )
return None
ret: Optional[str] = super().register(plugin, name)
- if ret:
- self.hook.pytest_plugin_registered.call_historic(
- kwargs=dict(plugin=plugin, manager=self)
- )
-
- if isinstance(plugin, types.ModuleType):
- self.consider_module(plugin)
- return ret
-
+ if ret:
+ self.hook.pytest_plugin_registered.call_historic(
+ kwargs=dict(plugin=plugin, manager=self)
+ )
+
+ if isinstance(plugin, types.ModuleType):
+ self.consider_module(plugin)
+ return ret
+
def getplugin(self, name: str):
# Support deprecated naming because plugins (xdist e.g.) use it.
plugin: Optional[_PluggyPlugin] = self.get_plugin(name)
return plugin
-
+
def hasplugin(self, name: str) -> bool:
"""Return whether a plugin with the given name is registered."""
- return bool(self.get_plugin(name))
-
+ return bool(self.get_plugin(name))
+
def pytest_configure(self, config: "Config") -> None:
""":meta private:"""
- # XXX now that the pluginmanager exposes hookimpl(tryfirst...)
+ # XXX now that the pluginmanager exposes hookimpl(tryfirst...)
# we should remove tryfirst/trylast as markers.
- config.addinivalue_line(
- "markers",
- "tryfirst: mark a hook implementation function such that the "
- "plugin machinery will try to call it first/as early as possible.",
- )
- config.addinivalue_line(
- "markers",
- "trylast: mark a hook implementation function such that the "
- "plugin machinery will try to call it last/as late as possible.",
- )
- self._configured = True
-
- #
+ config.addinivalue_line(
+ "markers",
+ "tryfirst: mark a hook implementation function such that the "
+ "plugin machinery will try to call it first/as early as possible.",
+ )
+ config.addinivalue_line(
+ "markers",
+ "trylast: mark a hook implementation function such that the "
+ "plugin machinery will try to call it last/as late as possible.",
+ )
+ self._configured = True
+
+ #
# Internal API for local conftest plugin handling.
- #
+ #
def _set_initial_conftests(self, namespace: argparse.Namespace) -> None:
"""Load initial conftest files given a preparsed "namespace".
@@ -478,77 +478,77 @@ class PytestPluginManager(PluginManager):
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 = (
- current.join(namespace.confcutdir, abs=True)
- if namespace.confcutdir
- else None
- )
- self._noconftest = namespace.noconftest
- self._using_pyargs = namespace.pyargs
- testpaths = namespace.file_or_dir
- foundanchor = False
+ """
+ current = py.path.local()
+ self._confcutdir = (
+ current.join(namespace.confcutdir, abs=True)
+ if namespace.confcutdir
+ else None
+ )
+ self._noconftest = namespace.noconftest
+ self._using_pyargs = namespace.pyargs
+ testpaths = namespace.file_or_dir
+ foundanchor = False
for testpath in testpaths:
path = str(testpath)
- # remove node-id syntax
- i = path.find("::")
- if i != -1:
- path = path[:i]
- anchor = current.join(path, abs=1)
+ # remove node-id syntax
+ i = path.find("::")
+ if i != -1:
+ path = path[:i]
+ anchor = current.join(path, abs=1)
if anchor.exists(): # we found some file object
self._try_load_conftest(anchor, namespace.importmode)
- foundanchor = True
- if not foundanchor:
+ foundanchor = True
+ if not foundanchor:
self._try_load_conftest(current, namespace.importmode)
-
+
def _try_load_conftest(
self, anchor: py.path.local, importmode: Union[str, ImportMode]
) -> None:
self._getconftestmodules(anchor, importmode)
- # let's also consider test* subdirs
- if anchor.check(dir=1):
- for x in anchor.listdir("test*"):
- if x.check(dir=1):
+ # let's also consider test* subdirs
+ if anchor.check(dir=1):
+ for x in anchor.listdir("test*"):
+ if x.check(dir=1):
self._getconftestmodules(x, importmode)
-
- @lru_cache(maxsize=128)
+
+ @lru_cache(maxsize=128)
def _getconftestmodules(
self, path: py.path.local, importmode: Union[str, ImportMode],
) -> List[types.ModuleType]:
- if self._noconftest:
- return []
-
- if path.isfile():
- directory = path.dirpath()
- else:
- directory = path
-
+ if self._noconftest:
+ return []
+
+ if path.isfile():
+ directory = path.dirpath()
+ else:
+ directory = path
+
# XXX these days we may rather want to use config.rootpath
- # and allow users to opt into looking into the rootdir parent
+ # and allow users to opt into looking into the rootdir parent
# directories instead of requiring to specify confcutdir.
- clist = []
+ clist = []
for parent in directory.parts():
- if self._confcutdir and self._confcutdir.relto(parent):
- continue
- conftestpath = parent.join("conftest.py")
- if conftestpath.isfile():
+ if self._confcutdir and self._confcutdir.relto(parent):
+ continue
+ conftestpath = parent.join("conftest.py")
+ if conftestpath.isfile():
mod = self._importconftest(conftestpath, importmode)
- clist.append(mod)
- self._dirpath2confmods[directory] = clist
- return clist
-
+ clist.append(mod)
+ self._dirpath2confmods[directory] = clist
+ return clist
+
def _rget_with_confmod(
self, name: str, path: py.path.local, importmode: Union[str, ImportMode],
) -> Tuple[types.ModuleType, Any]:
modules = self._getconftestmodules(path, importmode)
- for mod in reversed(modules):
- try:
- return mod, getattr(mod, name)
- except AttributeError:
- continue
- raise KeyError(name)
-
+ for mod in reversed(modules):
+ try:
+ return mod, getattr(mod, name)
+ except AttributeError:
+ continue
+ raise KeyError(name)
+
def _importconftest(
self, conftestpath: py.path.local, importmode: Union[str, ImportMode],
) -> types.ModuleType:
@@ -561,11 +561,11 @@ class PytestPluginManager(PluginManager):
with contextlib.suppress(KeyError):
return self._conftestpath2mod[key]
-
+
pkgpath = conftestpath.pypkgpath()
if pkgpath is None:
_ensure_removed_sysmodule(conftestpath.purebasename)
-
+
try:
mod = import_path(conftestpath, mode=importmode)
except Exception as e:
@@ -606,11 +606,11 @@ class PytestPluginManager(PluginManager):
)
fail(msg.format(conftestpath, self._confcutdir), pytrace=False)
- #
- # API for bootstrapping plugin loading
- #
- #
-
+ #
+ # API for bootstrapping plugin loading
+ #
+ #
+
def consider_preparse(
self, args: Sequence[str], *, exclude_only: bool = False
) -> None:
@@ -633,22 +633,22 @@ class PytestPluginManager(PluginManager):
if exclude_only and not parg.startswith("no:"):
continue
self.consider_pluginarg(parg)
-
+
def consider_pluginarg(self, arg: str) -> None:
- if arg.startswith("no:"):
- name = arg[3:]
+ if arg.startswith("no:"):
+ name = arg[3:]
if name in essential_plugins:
raise UsageError("plugin %s cannot be disabled" % name)
# PR #4304: remove stepwise if cacheprovider is blocked.
- if name == "cacheprovider":
- self.set_blocked("stepwise")
- self.set_blocked("pytest_stepwise")
-
- self.set_blocked(name)
- if not name.startswith("pytest_"):
- self.set_blocked("pytest_" + name)
- else:
+ if name == "cacheprovider":
+ self.set_blocked("stepwise")
+ self.set_blocked("pytest_stepwise")
+
+ self.set_blocked(name)
+ if not name.startswith("pytest_"):
+ self.set_blocked("pytest_" + name)
+ else:
name = arg
# Unblock the plugin. None indicates that it has been blocked.
# There is no interface with pluggy for this.
@@ -658,23 +658,23 @@ class PytestPluginManager(PluginManager):
if self._name2plugin.get("pytest_" + name, -1) is None:
del self._name2plugin["pytest_" + name]
self.import_plugin(arg, consider_entry_points=True)
-
+
def consider_conftest(self, conftestmodule: types.ModuleType) -> None:
- self.register(conftestmodule, name=conftestmodule.__file__)
-
+ self.register(conftestmodule, name=conftestmodule.__file__)
+
def consider_env(self) -> None:
- self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS"))
-
+ self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS"))
+
def consider_module(self, mod: types.ModuleType) -> None:
- self._import_plugin_specs(getattr(mod, "pytest_plugins", []))
-
+ self._import_plugin_specs(getattr(mod, "pytest_plugins", []))
+
def _import_plugin_specs(
self, spec: Union[None, types.ModuleType, str, Sequence[str]]
) -> None:
- plugins = _get_plugin_specs_as_list(spec)
- for import_spec in plugins:
- self.import_plugin(import_spec)
-
+ plugins = _get_plugin_specs_as_list(spec)
+ for import_spec in plugins:
+ self.import_plugin(import_spec)
+
def import_plugin(self, modname: str, consider_entry_points: bool = False) -> None:
"""Import a plugin with ``modname``.
@@ -682,37 +682,37 @@ class PytestPluginManager(PluginManager):
considered to find a plugin.
"""
# 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.
+ # "terminal" or "capture". Those plugins are registered under their
+ # basename for historic purposes but must be imported with the
+ # _pytest prefix.
assert isinstance(modname, str), (
- "module name as text required, got %r" % modname
- )
- if self.is_blocked(modname) or self.get_plugin(modname) is not None:
- return
+ "module name as text required, got %r" % modname
+ )
+ if self.is_blocked(modname) or self.get_plugin(modname) is not None:
+ return
importspec = "_pytest." + modname if modname in builtin_plugins else modname
- self.rewrite_hook.mark_rewrite(importspec)
+ self.rewrite_hook.mark_rewrite(importspec)
if consider_entry_points:
loaded = self.load_setuptools_entrypoints("pytest11", name=modname)
if loaded:
return
- try:
- __import__(importspec)
- except ImportError as e:
+ try:
+ __import__(importspec)
+ except ImportError as e:
raise ImportError(
'Error importing plugin "{}": {}'.format(modname, str(e.args[0]))
).with_traceback(e.__traceback__) from e
-
- except Skipped as e:
+
+ except Skipped as e:
self.skipped_plugins.append((modname, e.msg or ""))
- else:
- mod = sys.modules[importspec]
- self.register(mod, modname)
-
-
+ else:
+ mod = sys.modules[importspec]
+ self.register(mod, modname)
+
+
def _get_plugin_specs_as_list(
specs: Union[None, types.ModuleType, str, Sequence[str]]
) -> List[str]:
@@ -728,28 +728,28 @@ def _get_plugin_specs_as_list(
return specs.split(",") if specs else []
# Direct specification.
if isinstance(specs, collections.abc.Sequence):
- return list(specs)
+ return list(specs)
raise UsageError(
"Plugins may be specified as a sequence or a ','-separated string of plugin names. Got: %r"
% specs
)
-
-
+
+
def _ensure_removed_sysmodule(modname: str) -> None:
- try:
- del sys.modules[modname]
- except KeyError:
- pass
-
-
+ try:
+ del sys.modules[modname]
+ except KeyError:
+ pass
+
+
class Notset:
- def __repr__(self):
- return "<NOTSET>"
-
-
-notset = Notset()
-
-
+ def __repr__(self):
+ return "<NOTSET>"
+
+
+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.
@@ -784,20 +784,20 @@ def _iter_rewritable_modules(package_files: Iterable[str]) -> Iterator[str]:
"""
package_files = list(package_files)
seen_some = False
- for fn in package_files:
- is_simple_module = "/" not in fn and fn.endswith(".py")
- is_package = fn.count("/") == 1 and fn.endswith("__init__.py")
- if is_simple_module:
- module_name, _ = os.path.splitext(fn)
+ for fn in package_files:
+ is_simple_module = "/" not in fn and fn.endswith(".py")
+ is_package = fn.count("/") == 1 and fn.endswith("__init__.py")
+ if is_simple_module:
+ module_name, _ = os.path.splitext(fn)
# we ignore "setup.py" at the root of the distribution
if module_name != "setup":
seen_some = True
yield module_name
- elif is_package:
- package_name = os.path.dirname(fn)
+ elif is_package:
+ package_name = os.path.dirname(fn)
seen_some = True
- yield package_name
-
+ yield package_name
+
if not seen_some:
# 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
@@ -812,11 +812,11 @@ def _iter_rewritable_modules(package_files: Iterable[str]) -> Iterator[str]:
new_package_files.append(new_fn)
if new_package_files:
yield from _iter_rewritable_modules(new_package_files)
-
+
def _args_converter(args: Iterable[str]) -> Tuple[str, ...]:
return tuple(args)
-
+
@final
class Config:
@@ -875,7 +875,7 @@ class Config:
args=(), plugins=None, dir=Path.cwd()
)
- self.option = argparse.Namespace()
+ self.option = argparse.Namespace()
"""Access to command line option as attributes.
:type: argparse.Namespace
@@ -887,19 +887,19 @@ class Config:
:type: InvocationParams
"""
- _a = FILE_OR_DIR
- self._parser = Parser(
+ _a = FILE_OR_DIR
+ self._parser = Parser(
usage=f"%(prog)s [options] [{_a}] [{_a}] [...]",
- processopt=self._processopt,
- )
- self.pluginmanager = pluginmanager
+ processopt=self._processopt,
+ )
+ self.pluginmanager = pluginmanager
"""The plugin manager handles plugin registration and hook invocation.
:type: PytestPluginManager
"""
- self.trace = self.pluginmanager.trace.root.get("config")
- self.hook = self.pluginmanager.hook
+ self.trace = self.pluginmanager.trace.root.get("config")
+ self.hook = self.pluginmanager.hook
self._inicache: Dict[str, Any] = {}
self._override_ini: Sequence[str] = ()
self._opt2dest: Dict[str, str] = {}
@@ -907,12 +907,12 @@ class Config:
# A place where plugins can store information on the config for their
# own use. Currently only intended for internal plugins.
self._store = Store()
- self.pluginmanager.register(self, "pytestconfig")
- self._configured = False
+ self.pluginmanager.register(self, "pytestconfig")
+ self._configured = False
self.hook.pytest_addoption.call_historic(
kwargs=dict(parser=self._parser, pluginmanager=self.pluginmanager)
)
-
+
if TYPE_CHECKING:
from _pytest.cacheprovider import Cache
@@ -928,7 +928,7 @@ class Config:
:type: py.path.local
"""
return py.path.local(str(self.invocation_params.dir))
-
+
@property
def rootpath(self) -> Path:
"""The path to the :ref:`rootdir <rootdir>`.
@@ -971,44 +971,44 @@ class Config:
def add_cleanup(self, func: Callable[[], None]) -> None:
"""Add a function to be called when the config object gets out of
- use (usually coninciding with pytest_unconfigure)."""
- self._cleanup.append(func)
-
+ use (usually coninciding with pytest_unconfigure)."""
+ self._cleanup.append(func)
+
def _do_configure(self) -> None:
- assert not self._configured
- self._configured = True
+ assert not self._configured
+ self._configured = True
with warnings.catch_warnings():
warnings.simplefilter("default")
self.hook.pytest_configure.call_historic(kwargs=dict(config=self))
-
+
def _ensure_unconfigure(self) -> None:
- if self._configured:
- self._configured = False
- self.hook.pytest_unconfigure(config=self)
- self.hook.pytest_configure._call_history = []
- while self._cleanup:
- fin = self._cleanup.pop()
- fin()
-
+ if self._configured:
+ self._configured = False
+ self.hook.pytest_unconfigure(config=self)
+ self.hook.pytest_configure._call_history = []
+ while self._cleanup:
+ fin = self._cleanup.pop()
+ fin()
+
def get_terminal_writer(self) -> TerminalWriter:
terminalreporter: TerminalReporter = self.pluginmanager.get_plugin(
"terminalreporter"
)
return terminalreporter._tw
-
+
def pytest_cmdline_parse(
self, pluginmanager: PytestPluginManager, args: List[str]
) -> "Config":
try:
self.parse(args)
except UsageError:
-
+
# Handle --version and --help here in a minimal fashion.
# This gets done via helpconfig normally, but its
# pytest_cmdline_main is not called in case of errors.
if getattr(self.option, "version", False) or "--version" in args:
from _pytest.helpconfig import showversion
-
+
showversion(self)
elif (
getattr(self.option, "help", False) or "--help" in args or "-h" in args
@@ -1017,11 +1017,11 @@ class Config:
sys.stdout.write(
"\nNOTE: displaying only minimal help due to UsageError.\n\n"
)
-
+
raise
-
- return self
-
+
+ return self
+
def notify_exception(
self,
excinfo: ExceptionInfo[BaseException],
@@ -1029,111 +1029,111 @@ class Config:
) -> None:
if option and getattr(option, "fulltrace", False):
style: _TracebackStyle = "long"
- else:
- style = "native"
- excrepr = excinfo.getrepr(
- funcargs=True, showlocals=getattr(option, "showlocals", False), style=style
- )
- res = self.hook.pytest_internalerror(excrepr=excrepr, excinfo=excinfo)
- if not any(res):
- for line in str(excrepr).split("\n"):
- sys.stderr.write("INTERNALERROR> %s\n" % line)
- sys.stderr.flush()
-
+ else:
+ style = "native"
+ excrepr = excinfo.getrepr(
+ funcargs=True, showlocals=getattr(option, "showlocals", False), style=style
+ )
+ res = self.hook.pytest_internalerror(excrepr=excrepr, excinfo=excinfo)
+ if not any(res):
+ for line in str(excrepr).split("\n"):
+ sys.stderr.write("INTERNALERROR> %s\n" % line)
+ sys.stderr.flush()
+
def cwd_relative_nodeid(self, nodeid: str) -> str:
# nodeid's are relative to the rootpath, compute relative to cwd.
if self.invocation_params.dir != self.rootpath:
fullpath = self.rootpath / nodeid
nodeid = bestrelpath(self.invocation_params.dir, fullpath)
- return nodeid
-
- @classmethod
+ return nodeid
+
+ @classmethod
def fromdictargs(cls, option_dict, args) -> "Config":
"""Constructor usable for subprocesses."""
config = get_config(args)
- config.option.__dict__.update(option_dict)
- config.parse(args, addopts=False)
- for x in config.option.plugins:
- config.pluginmanager.consider_pluginarg(x)
- return config
-
+ config.option.__dict__.update(option_dict)
+ config.parse(args, addopts=False)
+ for x in config.option.plugins:
+ config.pluginmanager.consider_pluginarg(x)
+ return config
+
def _processopt(self, opt: "Argument") -> None:
- for name in opt._short_opts + opt._long_opts:
- self._opt2dest[name] = opt.dest
-
+ for name in opt._short_opts + opt._long_opts:
+ self._opt2dest[name] = opt.dest
+
if hasattr(opt, "default"):
- if not hasattr(self.option, opt.dest):
- setattr(self.option, opt.dest, opt.default)
-
- @hookimpl(trylast=True)
+ if not hasattr(self.option, opt.dest):
+ setattr(self.option, opt.dest, opt.default)
+
+ @hookimpl(trylast=True)
def pytest_load_initial_conftests(self, early_config: "Config") -> None:
- self.pluginmanager._set_initial_conftests(early_config.known_args_namespace)
-
+ self.pluginmanager._set_initial_conftests(early_config.known_args_namespace)
+
def _initini(self, args: Sequence[str]) -> None:
- ns, unknown_args = self._parser.parse_known_and_unknown_args(
- args, namespace=copy.copy(self.option)
- )
+ ns, unknown_args = self._parser.parse_known_and_unknown_args(
+ args, namespace=copy.copy(self.option)
+ )
rootpath, inipath, inicfg = determine_setup(
- ns.inifilename,
- ns.file_or_dir + unknown_args,
- rootdir_cmd_arg=ns.rootdir or None,
- config=self,
- )
+ ns.inifilename,
+ ns.file_or_dir + unknown_args,
+ rootdir_cmd_arg=ns.rootdir or None,
+ config=self,
+ )
self._rootpath = rootpath
self._inipath = inipath
self.inicfg = inicfg
self._parser.extra_info["rootdir"] = str(self.rootpath)
self._parser.extra_info["inifile"] = str(self.inipath)
- self._parser.addini("addopts", "extra command line options", "args")
- self._parser.addini("minversion", "minimally required pytest version")
+ self._parser.addini("addopts", "extra command line options", "args")
+ self._parser.addini("minversion", "minimally required pytest version")
self._parser.addini(
"required_plugins",
"plugins that must be present for pytest to run",
type="args",
default=[],
)
- self._override_ini = ns.override_ini or ()
-
+ self._override_ini = ns.override_ini or ()
+
def _consider_importhook(self, args: Sequence[str]) -> None:
- """Install the PEP 302 import hook if using assertion rewriting.
-
- Needs to parse the --assert=<mode> option from the commandline
- and find all the installed plugins to mark them for rewriting
- by the importhook.
- """
- ns, unknown_args = self._parser.parse_known_and_unknown_args(args)
+ """Install the PEP 302 import hook if using assertion rewriting.
+
+ Needs to parse the --assert=<mode> option from the commandline
+ and find all the installed plugins to mark them for rewriting
+ by the importhook.
+ """
+ ns, unknown_args = self._parser.parse_known_and_unknown_args(args)
mode = getattr(ns, "assertmode", "plain")
- if mode == "rewrite":
+ if mode == "rewrite":
import _pytest.assertion
- try:
- hook = _pytest.assertion.install_importhook(self)
- except SystemError:
- mode = "plain"
- else:
- self._mark_plugins_for_rewrite(hook)
+ try:
+ hook = _pytest.assertion.install_importhook(self)
+ except SystemError:
+ mode = "plain"
+ else:
+ self._mark_plugins_for_rewrite(hook)
self._warn_about_missing_assertion(mode)
-
+
def _mark_plugins_for_rewrite(self, hook) -> None:
"""Given an importhook, mark for rewrite any top-level
- modules or packages in the distribution package for
+ modules or packages in the distribution package for
all pytest plugins."""
- self.pluginmanager.rewrite_hook = hook
-
- if os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"):
- # We don't autoload from setuptools entry points, no need to continue.
- return
-
- package_files = (
+ self.pluginmanager.rewrite_hook = hook
+
+ if os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"):
+ # We don't autoload from setuptools entry points, no need to continue.
+ return
+
+ package_files = (
str(file)
for dist in importlib_metadata.distributions()
if any(ep.group == "pytest11" for ep in dist.entry_points)
for file in dist.files or []
- )
-
- for name in _iter_rewritable_modules(package_files):
- hook.mark_rewrite(name)
-
+ )
+
+ for name in _iter_rewritable_modules(package_files):
+ hook.mark_rewrite(name)
+
def _validate_args(self, args: List[str], via: str) -> List[str]:
"""Validate known args."""
self._parser._config_source_hint = via # type: ignore
@@ -1147,15 +1147,15 @@ class Config:
return args
def _preparse(self, args: List[str], addopts: bool = True) -> None:
- if addopts:
+ if addopts:
env_addopts = os.environ.get("PYTEST_ADDOPTS", "")
if len(env_addopts):
args[:] = (
self._validate_args(shlex.split(env_addopts), "via PYTEST_ADDOPTS")
+ args
)
- self._initini(args)
- if addopts:
+ self._initini(args)
+ if addopts:
args[:] = (
self._validate_args(self.getini("addopts"), "via addopts config") + args
)
@@ -1163,18 +1163,18 @@ class Config:
self.known_args_namespace = self._parser.parse_known_args(
args, namespace=copy.copy(self.option)
)
- self._checkversion()
- self._consider_importhook(args)
+ self._checkversion()
+ self._consider_importhook(args)
self.pluginmanager.consider_preparse(args, exclude_only=False)
- if not os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"):
- # Don't autoload from setuptools entry point. Only explicitly specified
- # plugins are going to be loaded.
- self.pluginmanager.load_setuptools_entrypoints("pytest11")
- self.pluginmanager.consider_env()
+ if not os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"):
+ # Don't autoload from setuptools entry point. Only explicitly specified
+ # plugins are going to be loaded.
+ self.pluginmanager.load_setuptools_entrypoints("pytest11")
+ self.pluginmanager.consider_env()
self.known_args_namespace = self._parser.parse_known_args(
args, namespace=copy.copy(self.known_args_namespace)
- )
+ )
self._validate_plugins()
self._warn_about_skipped_plugins()
@@ -1186,22 +1186,22 @@ class Config:
if self.known_args_namespace.confcutdir is None and self.inipath is not None:
confcutdir = str(self.inipath.parent)
- self.known_args_namespace.confcutdir = confcutdir
- try:
- self.hook.pytest_load_initial_conftests(
- early_config=self, args=args, parser=self._parser
- )
+ self.known_args_namespace.confcutdir = confcutdir
+ try:
+ self.hook.pytest_load_initial_conftests(
+ early_config=self, args=args, parser=self._parser
+ )
except ConftestImportFailure as e:
if self.known_args_namespace.help or self.known_args_namespace.version:
- # we don't want to prevent --help/--version to work
- # so just let is pass and print a warning at the end
+ # we don't want to prevent --help/--version to work
+ # so just let is pass and print a warning at the end
self.issue_config_time_warning(
PytestConfigWarning(f"could not load initial conftests: {e.path}"),
stacklevel=2,
)
- else:
- raise
-
+ else:
+ raise
+
@hookimpl(hookwrapper=True)
def pytest_collection(self) -> Generator[None, None, None]:
"""Validate invalid ini keys after collection is done so we take in account
@@ -1210,10 +1210,10 @@ class Config:
self._validate_config_options()
def _checkversion(self) -> None:
- import pytest
-
- minver = self.inicfg.get("minversion", None)
- if minver:
+ import pytest
+
+ minver = self.inicfg.get("minversion", None)
+ if minver:
# Imported lazily to improve start-up time.
from packaging.version import Version
@@ -1223,11 +1223,11 @@ class Config:
)
if Version(minver) > Version(pytest.__version__):
- raise pytest.UsageError(
+ raise pytest.UsageError(
"%s: 'minversion' requires pytest-%s, actual pytest-%s'"
% (self.inipath, minver, pytest.__version__,)
- )
-
+ )
+
def _validate_config_options(self) -> None:
for key in sorted(self._get_unknown_ini_keys()):
self._warn_or_fail_if_strict(f"Unknown config option: {key}\n")
@@ -1274,29 +1274,29 @@ class Config:
def parse(self, args: List[str], addopts: bool = True) -> None:
# Parse given cmdline arguments into this config object.
- assert not hasattr(
- self, "args"
- ), "can only parse cmdline args at most once per Config object"
- self.hook.pytest_addhooks.call_historic(
- kwargs=dict(pluginmanager=self.pluginmanager)
- )
- self._preparse(args, addopts=addopts)
- # XXX deprecated hook:
- self.hook.pytest_cmdline_preparse(config=self, args=args)
+ assert not hasattr(
+ self, "args"
+ ), "can only parse cmdline args at most once per Config object"
+ self.hook.pytest_addhooks.call_historic(
+ kwargs=dict(pluginmanager=self.pluginmanager)
+ )
+ self._preparse(args, addopts=addopts)
+ # XXX deprecated hook:
+ self.hook.pytest_cmdline_preparse(config=self, args=args)
self._parser.after_preparse = True # type: ignore
- try:
- args = self._parser.parse_setoption(
- args, self.option, namespace=self.option
- )
- if not args:
+ try:
+ args = self._parser.parse_setoption(
+ args, self.option, namespace=self.option
+ )
+ if not args:
if self.invocation_params.dir == self.rootpath:
- args = self.getini("testpaths")
- if not args:
+ args = self.getini("testpaths")
+ if not args:
args = [str(self.invocation_params.dir)]
- self.args = args
- except PrintHelp:
- pass
-
+ self.args = args
+ except PrintHelp:
+ pass
+
def issue_config_time_warning(self, warning: Warning, stacklevel: int) -> None:
"""Issue and handle a warning during the "configure" stage.
@@ -1344,10 +1344,10 @@ class Config:
"""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
-
+ 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>`.
@@ -1355,27 +1355,27 @@ class Config:
:py:func:`parser.addini <_pytest.config.argparsing.Parser.addini>`
call (usually from a plugin), a ValueError is raised.
"""
- try:
- return self._inicache[name]
- except KeyError:
- self._inicache[name] = val = self._getini(name)
- return val
-
+ try:
+ return self._inicache[name]
+ except KeyError:
+ self._inicache[name] = val = self._getini(name)
+ return val
+
def _getini(self, name: str):
- try:
- description, type, default = self._parser._inidict[name]
+ try:
+ description, type, default = self._parser._inidict[name]
except KeyError as e:
raise ValueError(f"unknown configuration value: {name!r}") from e
override_value = self._get_override_ini_value(name)
if override_value is None:
- try:
- value = self.inicfg[name]
- except KeyError:
- if default is not None:
- return default
- if type is None:
- return ""
- return []
+ try:
+ value = self.inicfg[name]
+ except KeyError:
+ if default is not None:
+ return default
+ if type is None:
+ return ""
+ return []
else:
value = override_value
# Coerce the values based on types.
@@ -1393,94 +1393,94 @@ class Config:
# a_line_list = ["tests", "acceptance"]
# in this case, we already have a list ready to use.
#
- if type == "pathlist":
+ if type == "pathlist":
# TODO: This assert is probably not valid in all cases.
assert self.inipath is not None
dp = self.inipath.parent
input_values = shlex.split(value) if isinstance(value, str) else value
return [py.path.local(str(dp / x)) for x in input_values]
- elif type == "args":
+ elif type == "args":
return shlex.split(value) if isinstance(value, str) else value
- elif type == "linelist":
+ elif type == "linelist":
if isinstance(value, str):
return [t for t in map(lambda x: x.strip(), value.split("\n")) if t]
else:
return value
- elif type == "bool":
+ elif type == "bool":
return _strtobool(str(value).strip())
- else:
+ else:
assert type in [None, "string"]
- return value
-
+ return value
+
def _getconftest_pathlist(
self, name: str, path: py.path.local
) -> Optional[List[py.path.local]]:
- try:
+ try:
mod, relroots = self.pluginmanager._rget_with_confmod(
name, path, self.getoption("importmode")
)
- except KeyError:
- return None
- modpath = py.path.local(mod.__file__).dirpath()
+ except KeyError:
+ return None
+ modpath = py.path.local(mod.__file__).dirpath()
values: List[py.path.local] = []
- for relroot in relroots:
- if not isinstance(relroot, py.path.local):
+ for relroot in relroots:
+ if not isinstance(relroot, py.path.local):
relroot = relroot.replace("/", os.sep)
- relroot = modpath.join(relroot, abs=True)
- values.append(relroot)
- return values
-
+ relroot = modpath.join(relroot, abs=True)
+ values.append(relroot)
+ return values
+
def _get_override_ini_value(self, name: str) -> Optional[str]:
- value = None
+ 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.
- for ini_config in self._override_ini:
- try:
- key, user_ini_value = ini_config.split("=", 1)
+ for ini_config in self._override_ini:
+ try:
+ key, user_ini_value = ini_config.split("=", 1)
except ValueError as e:
raise UsageError(
"-o/--override-ini expects option=value style (got: {!r}).".format(
ini_config
)
) from e
- else:
- if key == name:
- value = user_ini_value
- return value
-
+ else:
+ if key == name:
+ value = user_ini_value
+ return value
+
def getoption(self, name: str, default=notset, skip: bool = False):
"""Return command line option value.
-
+
:param name: Name of the option. You may also specify
- the literal ``--OPT`` option instead of the "dest" option name.
+ the literal ``--OPT`` option instead of the "dest" option name.
: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)
- try:
- val = getattr(self.option, name)
- if val is None and skip:
- raise AttributeError(name)
- return val
+ or has a None value.
+ """
+ name = self._opt2dest.get(name, name)
+ try:
+ val = getattr(self.option, name)
+ if val is None and skip:
+ raise AttributeError(name)
+ return val
except AttributeError as e:
- if default is not notset:
- return default
- if skip:
- import pytest
-
+ if default is not notset:
+ return default
+ if skip:
+ import pytest
+
pytest.skip(f"no {name!r} option found")
raise ValueError(f"no option named {name!r}") from e
-
+
def getvalue(self, name: str, path=None):
"""Deprecated, use getoption() instead."""
- return self.getoption(name)
-
+ return self.getoption(name)
+
def getvalueorskip(self, name: str, path=None):
"""Deprecated, use getoption(skip=True) instead."""
- return self.getoption(name, skip=True)
-
+ return self.getoption(name, skip=True)
+
def _warn_about_missing_assertion(self, mode: str) -> None:
if not _assertion_supported():
if mode == "plain":
@@ -1500,7 +1500,7 @@ class Config:
self.issue_config_time_warning(
PytestConfigWarning(warning_text), stacklevel=3,
)
-
+
def _warn_about_skipped_plugins(self) -> None:
for module_name, msg in self.pluginmanager.skipped_plugins:
self.issue_config_time_warning(
@@ -1510,53 +1510,53 @@ class Config:
def _assertion_supported() -> bool:
- try:
- assert False
- except AssertionError:
- return True
- else:
+ try:
+ assert False
+ except AssertionError:
+ return True
+ else:
return False # type: ignore[unreachable]
-
-
+
+
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.
"""
tw = TerminalWriter(file=file)
-
- if config.option.color == "yes":
- tw.hasmarkup = True
+
+ if config.option.color == "yes":
+ tw.hasmarkup = True
elif config.option.color == "no":
- tw.hasmarkup = False
+ tw.hasmarkup = False
if config.option.code_highlight == "yes":
tw.code_highlight = True
elif config.option.code_highlight == "no":
tw.code_highlight = False
- return tw
-
-
+ return tw
+
+
def _strtobool(val: str) -> bool:
"""Convert a string representation of truth to True or False.
-
- True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
- are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if
- 'val' is anything else.
-
+
+ True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
+ are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if
+ 'val' is anything else.
+
.. note:: Copied from distutils.util.
- """
- val = val.lower()
- if val in ("y", "yes", "t", "true", "on", "1"):
+ """
+ val = val.lower()
+ if val in ("y", "yes", "t", "true", "on", "1"):
return True
- elif val in ("n", "no", "f", "false", "off", "0"):
+ elif val in ("n", "no", "f", "false", "off", "0"):
return False
- else:
+ else:
raise ValueError(f"invalid truth value {val!r}")
diff --git a/contrib/python/pytest/py3/_pytest/config/argparsing.py b/contrib/python/pytest/py3/_pytest/config/argparsing.py
index 20414f2b6e8..9a481965526 100644
--- a/contrib/python/pytest/py3/_pytest/config/argparsing.py
+++ b/contrib/python/pytest/py3/_pytest/config/argparsing.py
@@ -1,6 +1,6 @@
-import argparse
+import argparse
import sys
-import warnings
+import warnings
from gettext import gettext
from typing import Any
from typing import Callable
@@ -13,28 +13,28 @@ from typing import Sequence
from typing import Tuple
from typing import TYPE_CHECKING
from typing import Union
-
-import py
-
+
+import py
+
import _pytest._io
from _pytest.compat import final
from _pytest.config.exceptions import UsageError
-
+
if TYPE_CHECKING:
from typing import NoReturn
from typing_extensions import Literal
-FILE_OR_DIR = "file_or_dir"
-
-
+FILE_OR_DIR = "file_or_dir"
+
+
@final
class Parser:
"""Parser for command line arguments and ini-file values.
-
+
:ivar extra_info: Dict of generic param -> value to display in case
- there's an error processing the command line arguments.
- """
-
+ there's an error processing the command line arguments.
+ """
+
prog: Optional[str] = None
def __init__(
@@ -42,109 +42,109 @@ class Parser:
usage: Optional[str] = None,
processopt: Optional[Callable[["Argument"], None]] = None,
) -> None:
- self._anonymous = OptionGroup("custom options", parser=self)
+ self._anonymous = OptionGroup("custom options", parser=self)
self._groups: List[OptionGroup] = []
- self._processopt = processopt
- self._usage = usage
+ self._processopt = processopt
+ self._usage = usage
self._inidict: Dict[str, Tuple[str, Optional[str], Any]] = {}
self._ininames: List[str] = []
self.extra_info: Dict[str, Any] = {}
-
+
def processoption(self, option: "Argument") -> None:
- if self._processopt:
- if option.dest:
- self._processopt(option)
-
+ if self._processopt:
+ if option.dest:
+ self._processopt(option)
+
def getgroup(
self, name: str, description: str = "", after: Optional[str] = None
) -> "OptionGroup":
"""Get (or create) a named option Group.
-
+
: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
+
+ The returned group object has an ``addoption`` method with the same
+ signature as :py:func:`parser.addoption
<_pytest.config.argparsing.Parser.addoption>` but will be shown in the
- respective group in the output of ``pytest. --help``.
- """
- for group in self._groups:
- if group.name == name:
- return group
- group = OptionGroup(name, description, parser=self)
- i = 0
- for i, grp in enumerate(self._groups):
- if grp.name == after:
- break
- self._groups.insert(i + 1, group)
- return group
-
+ respective group in the output of ``pytest. --help``.
+ """
+ for group in self._groups:
+ if group.name == name:
+ return group
+ group = OptionGroup(name, description, parser=self)
+ i = 0
+ for i, grp in enumerate(self._groups):
+ if grp.name == after:
+ break
+ self._groups.insert(i + 1, group)
+ return group
+
def addoption(self, *opts: str, **attrs: Any) -> None:
"""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>`_
- accepts.
-
+ accepts.
+
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", ...)``.
- """
- self._anonymous.addoption(*opts, **attrs)
-
+ object via ``config.option.NAME`` where ``NAME`` is usually set
+ by passing a ``dest`` attribute, for example
+ ``addoption("--long", dest="NAME", ...)``.
+ """
+ self._anonymous.addoption(*opts, **attrs)
+
def parse(
self,
args: Sequence[Union[str, py.path.local]],
namespace: Optional[argparse.Namespace] = None,
) -> argparse.Namespace:
- from _pytest._argcomplete import try_argcomplete
-
- self.optparser = self._getparser()
- try_argcomplete(self.optparser)
+ from _pytest._argcomplete import try_argcomplete
+
+ self.optparser = self._getparser()
+ try_argcomplete(self.optparser)
strargs = [str(x) if isinstance(x, py.path.local) else x for x in args]
return self.optparser.parse_args(strargs, namespace=namespace)
-
+
def _getparser(self) -> "MyOptionParser":
- from _pytest._argcomplete import filescompleter
-
+ from _pytest._argcomplete import filescompleter
+
optparser = MyOptionParser(self, self.extra_info, prog=self.prog)
- groups = self._groups + [self._anonymous]
- for group in groups:
- if group.options:
- desc = group.description or group.name
- arggroup = optparser.add_argument_group(desc)
- for option in group.options:
- n = option.names()
- a = option.attrs()
- arggroup.add_argument(*n, **a)
+ groups = self._groups + [self._anonymous]
+ for group in groups:
+ if group.options:
+ desc = group.description or group.name
+ arggroup = optparser.add_argument_group(desc)
+ for option in group.options:
+ n = option.names()
+ a = option.attrs()
+ arggroup.add_argument(*n, **a)
file_or_dir_arg = optparser.add_argument(FILE_OR_DIR, nargs="*")
- # bash like autocompletion for dirs (appending '/')
+ # bash like autocompletion for dirs (appending '/')
# Type ignored because typeshed doesn't know about argcomplete.
file_or_dir_arg.completer = filescompleter # type: ignore
- return optparser
-
+ return optparser
+
def parse_setoption(
self,
args: Sequence[Union[str, py.path.local]],
option: argparse.Namespace,
namespace: Optional[argparse.Namespace] = None,
) -> List[str]:
- parsedoption = self.parse(args, namespace=namespace)
- for name, value in parsedoption.__dict__.items():
- setattr(option, name, value)
+ parsedoption = self.parse(args, namespace=namespace)
+ for name, value in parsedoption.__dict__.items():
+ setattr(option, name, value)
return cast(List[str], getattr(parsedoption, FILE_OR_DIR))
-
+
def parse_known_args(
self,
args: Sequence[Union[str, py.path.local]],
namespace: Optional[argparse.Namespace] = None,
) -> argparse.Namespace:
"""Parse and return a namespace object with known arguments at this point."""
- return self.parse_known_and_unknown_args(args, namespace=namespace)[0]
-
+ return self.parse_known_and_unknown_args(args, namespace=namespace)[0]
+
def parse_known_and_unknown_args(
self,
args: Sequence[Union[str, py.path.local]],
@@ -152,10 +152,10 @@ class Parser:
) -> Tuple[argparse.Namespace, List[str]]:
"""Parse and return a namespace object with known arguments, and
the remaining arguments unknown at this point."""
- optparser = self._getparser()
+ 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)
-
+
def addini(
self,
name: str,
@@ -166,97 +166,97 @@ class Parser:
default=None,
) -> None:
"""Register an ini-file option.
-
+
:name: Name of the ini-variable.
:type: Type of the variable, can be ``string``, ``pathlist``, ``args``,
``linelist`` or ``bool``. Defaults to ``string`` if ``None`` or
not passed.
: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>`.
- """
+
+ The value of ini-variables can be retrieved via a call to
+ :py:func:`config.getini(name) <_pytest.config.Config.getini>`.
+ """
assert type in (None, "string", "pathlist", "args", "linelist", "bool")
- self._inidict[name] = (help, type, default)
- self._ininames.append(name)
-
-
-class ArgumentError(Exception):
+ self._inidict[name] = (help, type, default)
+ self._ininames.append(name)
+
+
+class ArgumentError(Exception):
"""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
- self.option_id = str(option)
-
+ self.msg = msg
+ self.option_id = str(option)
+
def __str__(self) -> str:
- if self.option_id:
+ if self.option_id:
return f"option {self.option_id}: {self.msg}"
- else:
- return self.msg
-
-
+ else:
+ return self.msg
+
+
class Argument:
"""Class that mimics the necessary behaviour of optparse.Option.
-
+
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}
-
+ 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."""
- self._attrs = attrs
+ self._attrs = attrs
self._short_opts: List[str] = []
self._long_opts: List[str] = []
- if "%default" in (attrs.get("help") or ""):
- warnings.warn(
- 'pytest now uses argparse. "%default" should be'
- ' changed to "%(default)s" ',
- DeprecationWarning,
- stacklevel=3,
- )
- try:
- typ = attrs["type"]
- except KeyError:
- pass
- else:
+ if "%default" in (attrs.get("help") or ""):
+ warnings.warn(
+ 'pytest now uses argparse. "%default" should be'
+ ' changed to "%(default)s" ',
+ DeprecationWarning,
+ stacklevel=3,
+ )
+ try:
+ typ = attrs["type"]
+ except KeyError:
+ pass
+ else:
# This might raise a keyerror as well, don't want to catch that.
if isinstance(typ, str):
- if typ == "choice":
- warnings.warn(
- "`type` argument to addoption() is the string %r."
- " For choices this is optional and can be omitted, "
- " but when supplied should be a type (for example `str` or `int`)."
- " (options: %s)" % (typ, names),
- DeprecationWarning,
- stacklevel=4,
- )
- # argparse expects a type here take it from
- # the type of the first element
- attrs["type"] = type(attrs["choices"][0])
- else:
- warnings.warn(
- "`type` argument to addoption() is the string %r, "
- " but when supplied should be a type (for example `str` or `int`)."
- " (options: %s)" % (typ, names),
- DeprecationWarning,
- stacklevel=4,
- )
- attrs["type"] = Argument._typ_map[typ]
+ if typ == "choice":
+ warnings.warn(
+ "`type` argument to addoption() is the string %r."
+ " For choices this is optional and can be omitted, "
+ " but when supplied should be a type (for example `str` or `int`)."
+ " (options: %s)" % (typ, names),
+ DeprecationWarning,
+ stacklevel=4,
+ )
+ # argparse expects a type here take it from
+ # the type of the first element
+ attrs["type"] = type(attrs["choices"][0])
+ else:
+ warnings.warn(
+ "`type` argument to addoption() is the string %r, "
+ " but when supplied should be a type (for example `str` or `int`)."
+ " (options: %s)" % (typ, names),
+ DeprecationWarning,
+ stacklevel=4,
+ )
+ attrs["type"] = Argument._typ_map[typ]
# Used in test_parseopt -> test_parse_defaultgetter.
- self.type = attrs["type"]
- else:
- self.type = typ
- try:
+ self.type = attrs["type"]
+ else:
+ self.type = typ
+ try:
# Attribute existence is tested in Config._processopt.
- self.default = attrs["default"]
- except KeyError:
- pass
- self._set_opt_strings(names)
+ self.default = attrs["default"]
+ except KeyError:
+ pass
+ self._set_opt_strings(names)
dest: Optional[str] = attrs.get("dest")
if dest:
self.dest = dest
@@ -268,136 +268,136 @@ class Argument:
except IndexError as e:
self.dest = "???" # Needed for the error repr.
raise ArgumentError("need a long or short option", self) from e
-
+
def names(self) -> List[str]:
- return self._short_opts + self._long_opts
-
+ return self._short_opts + self._long_opts
+
def attrs(self) -> Mapping[str, Any]:
# Update any attributes set by processopt.
- attrs = "default dest help".split()
+ attrs = "default dest help".split()
attrs.append(self.dest)
- for attr in attrs:
- try:
- self._attrs[attr] = getattr(self, attr)
- except AttributeError:
- pass
- if self._attrs.get("help"):
- a = self._attrs["help"]
- a = a.replace("%default", "%(default)s")
- # a = a.replace('%prog', '%(prog)s')
- self._attrs["help"] = a
- return self._attrs
-
+ for attr in attrs:
+ try:
+ self._attrs[attr] = getattr(self, attr)
+ except AttributeError:
+ pass
+ if self._attrs.get("help"):
+ a = self._attrs["help"]
+ a = a.replace("%default", "%(default)s")
+ # a = a.replace('%prog', '%(prog)s')
+ self._attrs["help"] = a
+ return self._attrs
+
def _set_opt_strings(self, opts: Sequence[str]) -> None:
"""Directly from optparse.
-
+
Might not be necessary as this is passed to argparse later on.
"""
- for opt in opts:
- if len(opt) < 2:
- raise ArgumentError(
- "invalid option string %r: "
- "must be at least two characters long" % opt,
- self,
- )
- elif len(opt) == 2:
- if not (opt[0] == "-" and opt[1] != "-"):
- raise ArgumentError(
- "invalid short option string %r: "
- "must be of the form -x, (x any non-dash char)" % opt,
- self,
- )
- self._short_opts.append(opt)
- else:
- if not (opt[0:2] == "--" and opt[2] != "-"):
- raise ArgumentError(
- "invalid long option string %r: "
- "must start with --, followed by non-dash" % opt,
- self,
- )
- self._long_opts.append(opt)
-
+ for opt in opts:
+ if len(opt) < 2:
+ raise ArgumentError(
+ "invalid option string %r: "
+ "must be at least two characters long" % opt,
+ self,
+ )
+ elif len(opt) == 2:
+ if not (opt[0] == "-" and opt[1] != "-"):
+ raise ArgumentError(
+ "invalid short option string %r: "
+ "must be of the form -x, (x any non-dash char)" % opt,
+ self,
+ )
+ self._short_opts.append(opt)
+ else:
+ if not (opt[0:2] == "--" and opt[2] != "-"):
+ raise ArgumentError(
+ "invalid long option string %r: "
+ "must start with --, followed by non-dash" % opt,
+ self,
+ )
+ self._long_opts.append(opt)
+
def __repr__(self) -> str:
args: List[str] = []
- if self._short_opts:
- args += ["_short_opts: " + repr(self._short_opts)]
- if self._long_opts:
- args += ["_long_opts: " + repr(self._long_opts)]
- args += ["dest: " + repr(self.dest)]
- if hasattr(self, "type"):
- args += ["type: " + repr(self.type)]
- if hasattr(self, "default"):
- args += ["default: " + repr(self.default)]
- return "Argument({})".format(", ".join(args))
-
-
+ if self._short_opts:
+ args += ["_short_opts: " + repr(self._short_opts)]
+ if self._long_opts:
+ args += ["_long_opts: " + repr(self._long_opts)]
+ args += ["dest: " + repr(self.dest)]
+ if hasattr(self, "type"):
+ args += ["type: " + repr(self.type)]
+ if hasattr(self, "default"):
+ args += ["default: " + repr(self.default)]
+ return "Argument({})".format(", ".join(args))
+
+
class OptionGroup:
def __init__(
self, name: str, description: str = "", parser: Optional[Parser] = None
) -> None:
- self.name = name
- self.description = description
+ self.name = name
+ self.description = description
self.options: List[Argument] = []
- self.parser = parser
-
+ self.parser = parser
+
def addoption(self, *optnames: str, **attrs: Any) -> None:
"""Add an option to this group.
-
+
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
+ 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.
- """
- conflict = set(optnames).intersection(
- name for opt in self.options for name in opt.names()
- )
- if conflict:
- raise ValueError("option names %s already added" % conflict)
- option = Argument(*optnames, **attrs)
- self._addoption_instance(option, shortupper=False)
-
+ """
+ conflict = set(optnames).intersection(
+ name for opt in self.options for name in opt.names()
+ )
+ if conflict:
+ raise ValueError("option names %s already added" % conflict)
+ option = Argument(*optnames, **attrs)
+ self._addoption_instance(option, shortupper=False)
+
def _addoption(self, *optnames: str, **attrs: Any) -> None:
- option = Argument(*optnames, **attrs)
- self._addoption_instance(option, shortupper=True)
-
+ option = Argument(*optnames, **attrs)
+ self._addoption_instance(option, shortupper=True)
+
def _addoption_instance(self, option: "Argument", shortupper: bool = False) -> None:
- if not shortupper:
- for opt in option._short_opts:
- if opt[0] == "-" and opt[1].islower():
- raise ValueError("lowercase shortoptions reserved")
- if self.parser:
- self.parser.processoption(option)
- self.options.append(option)
-
-
-class MyOptionParser(argparse.ArgumentParser):
+ if not shortupper:
+ for opt in option._short_opts:
+ if opt[0] == "-" and opt[1].islower():
+ raise ValueError("lowercase shortoptions reserved")
+ if self.parser:
+ self.parser.processoption(option)
+ self.options.append(option)
+
+
+class MyOptionParser(argparse.ArgumentParser):
def __init__(
self,
parser: Parser,
extra_info: Optional[Dict[str, Any]] = None,
prog: Optional[str] = None,
) -> None:
- self._parser = parser
- argparse.ArgumentParser.__init__(
- self,
+ self._parser = parser
+ argparse.ArgumentParser.__init__(
+ self,
prog=prog,
- usage=parser._usage,
- add_help=False,
- formatter_class=DropShorterLongHelpFormatter,
+ usage=parser._usage,
+ add_help=False,
+ formatter_class=DropShorterLongHelpFormatter,
allow_abbrev=False,
- )
- # extra_info is a dict of (param -> value) to display if there's
+ )
+ # extra_info is a dict of (param -> value) to display if there's
# 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":
"""Transform argparse error message into UsageError."""
msg = f"{self.prog}: error: {message}"
-
+
if hasattr(self._parser, "_config_source_hint"):
# Type ignored because the attribute is set dynamically.
msg = f"{msg} ({self._parser._config_source_hint})" # type: ignore
-
+
raise UsageError(self.format_usage() + msg)
# Type ignored because typeshed has a very complex type in the superclass.
@@ -410,14 +410,14 @@ class MyOptionParser(argparse.ArgumentParser):
parsed, unrecognized = self.parse_known_args(args, namespace)
if unrecognized:
for arg in unrecognized:
- if arg and arg[0] == "-":
+ if arg and arg[0] == "-":
lines = ["unrecognized arguments: %s" % (" ".join(unrecognized))]
- for k, v in sorted(self.extra_info.items()):
+ for k, v in sorted(self.extra_info.items()):
lines.append(f" {k}: {v}")
- self.error("\n".join(lines))
+ self.error("\n".join(lines))
getattr(parsed, FILE_OR_DIR).extend(unrecognized)
return parsed
-
+
if sys.version_info[:2] < (3, 9): # pragma: no cover
# Backport of https://github.com/python/cpython/pull/14316 so we can
# disable long --argument abbreviations without breaking short flags.
@@ -455,16 +455,16 @@ class MyOptionParser(argparse.ArgumentParser):
if " " in arg_string:
return None
return None, arg_string, None
-
-class DropShorterLongHelpFormatter(argparse.HelpFormatter):
+
+class DropShorterLongHelpFormatter(argparse.HelpFormatter):
"""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 the action object as this is called at least 2 times.
- """
-
+ """
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
# Use more accurate terminal width.
if "width" not in kwargs:
@@ -472,39 +472,39 @@ class DropShorterLongHelpFormatter(argparse.HelpFormatter):
super().__init__(*args, **kwargs)
def _format_action_invocation(self, action: argparse.Action) -> str:
- orgstr = argparse.HelpFormatter._format_action_invocation(self, action)
- if orgstr and orgstr[0] != "-": # only optional arguments
- return orgstr
+ orgstr = argparse.HelpFormatter._format_action_invocation(self, action)
+ if orgstr and orgstr[0] != "-": # only optional arguments
+ return orgstr
res: Optional[str] = getattr(action, "_formatted_action_invocation", None)
- if res:
- return res
- options = orgstr.split(", ")
- if len(options) == 2 and (len(options[0]) == 2 or len(options[1]) == 2):
- # a shortcut for '-h, --help' or '--abc', '-a'
+ if res:
+ return res
+ options = orgstr.split(", ")
+ if len(options) == 2 and (len(options[0]) == 2 or len(options[1]) == 2):
+ # a shortcut for '-h, --help' or '--abc', '-a'
action._formatted_action_invocation = orgstr # type: ignore
- return orgstr
- return_list = []
+ return orgstr
+ return_list = []
short_long: Dict[str, str] = {}
- for option in options:
- if len(option) == 2 or option[2] == " ":
- continue
- if not option.startswith("--"):
- raise ArgumentError(
+ for option in options:
+ if len(option) == 2 or option[2] == " ":
+ continue
+ if not option.startswith("--"):
+ raise ArgumentError(
'long optional argument without "--": [%s]' % (option), option
- )
- xxoption = option[2:]
+ )
+ xxoption = option[2:]
shortened = xxoption.replace("-", "")
if shortened not in short_long or len(short_long[shortened]) < len(
xxoption
):
short_long[shortened] = xxoption
- # now short_long has been filled out to the longest with dashes
- # **and** we keep the right option ordering from add_argument
- for option in options:
- if len(option) == 2 or option[2] == " ":
- return_list.append(option)
- if option[2:] == short_long.get(option.replace("-", "")):
- return_list.append(option.replace(" ", "=", 1))
+ # now short_long has been filled out to the longest with dashes
+ # **and** we keep the right option ordering from add_argument
+ for option in options:
+ if len(option) == 2 or option[2] == " ":
+ return_list.append(option)
+ if option[2:] == short_long.get(option.replace("-", "")):
+ return_list.append(option.replace(" ", "=", 1))
formatted_action_invocation = ", ".join(return_list)
action._formatted_action_invocation = formatted_action_invocation # type: ignore
return formatted_action_invocation
diff --git a/contrib/python/pytest/py3/_pytest/config/exceptions.py b/contrib/python/pytest/py3/_pytest/config/exceptions.py
index a6441629b2c..4f1320e758d 100644
--- a/contrib/python/pytest/py3/_pytest/config/exceptions.py
+++ b/contrib/python/pytest/py3/_pytest/config/exceptions.py
@@ -2,10 +2,10 @@ from _pytest.compat import final
@final
-class UsageError(Exception):
+class UsageError(Exception):
"""Error in pytest usage or invocation."""
-
-
-class PrintHelp(Exception):
+
+
+class PrintHelp(Exception):
"""Raised when pytest should print its help to skip the rest of the
- argument parsing and validation."""
+ argument parsing and validation."""
diff --git a/contrib/python/pytest/py3/_pytest/config/findpaths.py b/contrib/python/pytest/py3/_pytest/config/findpaths.py
index 7eaa4e5c35f..2edf54536ba 100644
--- a/contrib/python/pytest/py3/_pytest/config/findpaths.py
+++ b/contrib/python/pytest/py3/_pytest/config/findpaths.py
@@ -1,4 +1,4 @@
-import os
+import os
from pathlib import Path
from typing import Dict
from typing import Iterable
@@ -8,17 +8,17 @@ from typing import Sequence
from typing import Tuple
from typing import TYPE_CHECKING
from typing import Union
-
+
import iniconfig
-
-from .exceptions import UsageError
+
+from .exceptions import UsageError
from _pytest.outcomes import fail
from _pytest.pathlib import absolutepath
from _pytest.pathlib import commonpath
-
+
if TYPE_CHECKING:
from . import Config
-
+
def _parse_ini_config(path: Path) -> iniconfig.IniConfig:
"""Parse the given generic '.ini' file using legacy IniConfig parser, returning
@@ -26,20 +26,20 @@ def _parse_ini_config(path: Path) -> iniconfig.IniConfig:
Raise UsageError if the file cannot be parsed.
"""
- try:
+ try:
return iniconfig.IniConfig(str(path))
except iniconfig.ParseError as exc:
raise UsageError(str(exc)) from exc
-
-
+
+
def load_config_dict_from_file(
filepath: Path,
) -> Optional[Dict[str, Union[str, List[str]]]]:
"""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.
if filepath.suffix == ".ini":
iniconfig = _parse_ini_config(filepath)
@@ -94,10 +94,10 @@ def locate_config(
"tox.ini",
"setup.cfg",
]
- args = [x for x in args if not str(x).startswith("-")]
- if not args:
+ args = [x for x in args if not str(x).startswith("-")]
+ if not args:
args = [Path.cwd()]
- for arg in args:
+ for arg in args:
argpath = absolutepath(arg)
for base in (argpath, *argpath.parents):
for config_name in config_names:
@@ -107,43 +107,43 @@ def locate_config(
if ini_config is not None:
return base, p, ini_config
return None, None, {}
-
+
def get_common_ancestor(paths: Iterable[Path]) -> Path:
common_ancestor: Optional[Path] = None
- for path in paths:
- if not path.exists():
- continue
- if common_ancestor is None:
- common_ancestor = path
- else:
+ for path in paths:
+ if not path.exists():
+ continue
+ if common_ancestor is None:
+ common_ancestor = path
+ else:
if common_ancestor in path.parents or path == common_ancestor:
- continue
+ continue
elif path in common_ancestor.parents:
- common_ancestor = path
- else:
+ common_ancestor = path
+ else:
shared = commonpath(path, common_ancestor)
- if shared is not None:
- common_ancestor = shared
- if common_ancestor is None:
+ if shared is not None:
+ common_ancestor = shared
+ if common_ancestor is None:
common_ancestor = Path.cwd()
elif common_ancestor.is_file():
common_ancestor = common_ancestor.parent
- return common_ancestor
-
-
+ return common_ancestor
+
+
def get_dirs_from_args(args: Iterable[str]) -> List[Path]:
def is_option(x: str) -> bool:
return x.startswith("-")
-
+
def get_file_part_from_node_id(x: str) -> str:
return x.split("::")[0]
-
+
def get_dir_from_path(path: Path) -> Path:
if path.is_dir():
- return path
+ return path
return path.parent
-
+
def safe_exists(path: Path) -> bool:
# This can throw on paths that contain characters unrepresentable at the OS level,
# or with invalid syntax on Windows (https://bugs.python.org/issue35306)
@@ -152,16 +152,16 @@ def get_dirs_from_args(args: Iterable[str]) -> List[Path]:
except OSError:
return False
- # These look like paths but may not exist
- possible_paths = (
+ # These look like paths but may not exist
+ possible_paths = (
absolutepath(get_file_part_from_node_id(arg))
- for arg in args
- if not is_option(arg)
- )
-
+ for arg in args
+ if not is_option(arg)
+ )
+
return [get_dir_from_path(path) for path in possible_paths if safe_exists(path)]
-
-
+
+
CFG_PYTEST_SECTION = "[pytest] section in {filename} files is no longer supported, change to [tool:pytest] instead."
@@ -172,40 +172,40 @@ def determine_setup(
config: Optional["Config"] = None,
) -> Tuple[Path, Optional[Path], Dict[str, Union[str, List[str]]]]:
rootdir = None
- dirs = get_dirs_from_args(args)
- if inifile:
+ dirs = get_dirs_from_args(args)
+ if inifile:
inipath_ = absolutepath(inifile)
inipath: Optional[Path] = inipath_
inicfg = load_config_dict_from_file(inipath_) or {}
if rootdir_cmd_arg is None:
rootdir = get_common_ancestor(dirs)
- else:
- ancestor = get_common_ancestor(dirs)
+ else:
+ ancestor = get_common_ancestor(dirs)
rootdir, inipath, inicfg = locate_config([ancestor])
if rootdir is None and rootdir_cmd_arg is None:
for possible_rootdir in (ancestor, *ancestor.parents):
if (possible_rootdir / "setup.py").is_file():
rootdir = possible_rootdir
- break
- else:
+ break
+ else:
if dirs != [ancestor]:
rootdir, inipath, inicfg = locate_config(dirs)
- if rootdir is None:
+ if rootdir is None:
if config is not None:
cwd = config.invocation_params.dir
else:
cwd = Path.cwd()
rootdir = get_common_ancestor([cwd, ancestor])
- is_fs_root = os.path.splitdrive(str(rootdir))[1] == "/"
- if is_fs_root:
- rootdir = ancestor
- if rootdir_cmd_arg:
+ is_fs_root = os.path.splitdrive(str(rootdir))[1] == "/"
+ if is_fs_root:
+ rootdir = ancestor
+ if rootdir_cmd_arg:
rootdir = absolutepath(os.path.expandvars(rootdir_cmd_arg))
if not rootdir.is_dir():
- raise UsageError(
- "Directory '{}' not found. Check your '--rootdir' option.".format(
+ raise UsageError(
+ "Directory '{}' not found. Check your '--rootdir' option.".format(
rootdir
- )
- )
+ )
+ )
assert rootdir is not None
return rootdir, inipath, inicfg or {}