[pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci
This commit is contained in:
pre-commit-ci[bot] 2024-04-13 00:00:18 +00:00
parent 72ad6dc953
commit f4cd1ba0d6
813 changed files with 66015 additions and 58839 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,10 @@
# mypy: allow-untyped-defs
from __future__ import annotations
import argparse
from gettext import gettext
import os
import sys
from gettext import gettext
from typing import Any
from typing import Callable
from typing import cast
@ -22,12 +24,12 @@ from _pytest.config.exceptions import UsageError
from _pytest.deprecated import check_ispytest
FILE_OR_DIR = "file_or_dir"
FILE_OR_DIR = 'file_or_dir'
class NotSet:
def __repr__(self) -> str:
return "<notset>"
return '<notset>'
NOT_SET = NotSet()
@ -41,32 +43,32 @@ class Parser:
there's an error processing the command line arguments.
"""
prog: Optional[str] = None
prog: str | None = None
def __init__(
self,
usage: Optional[str] = None,
processopt: Optional[Callable[["Argument"], None]] = None,
usage: str | None = None,
processopt: Callable[[Argument], None] | None = None,
*,
_ispytest: bool = False,
) -> None:
check_ispytest(_ispytest)
self._anonymous = OptionGroup("Custom options", parser=self, _ispytest=True)
self._groups: List[OptionGroup] = []
self._anonymous = OptionGroup('Custom options', parser=self, _ispytest=True)
self._groups: list[OptionGroup] = []
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] = {}
self._inidict: dict[str, tuple[str, str | None, Any]] = {}
self._ininames: list[str] = []
self.extra_info: dict[str, Any] = {}
def processoption(self, option: "Argument") -> None:
def processoption(self, option: Argument) -> None:
if self._processopt:
if option.dest:
self._processopt(option)
def getgroup(
self, name: str, description: str = "", after: Optional[str] = None
) -> "OptionGroup":
self, name: str, description: str = '', after: str | None = None,
) -> OptionGroup:
"""Get (or create) a named option Group.
:param name: Name of the option group.
@ -108,8 +110,8 @@ class Parser:
def parse(
self,
args: Sequence[Union[str, "os.PathLike[str]"]],
namespace: Optional[argparse.Namespace] = None,
args: Sequence[str | os.PathLike[str]],
namespace: argparse.Namespace | None = None,
) -> argparse.Namespace:
from _pytest._argcomplete import try_argcomplete
@ -118,7 +120,7 @@ class Parser:
strargs = [os.fspath(x) for x in args]
return self.optparser.parse_args(strargs, namespace=namespace)
def _getparser(self) -> "MyOptionParser":
def _getparser(self) -> MyOptionParser:
from _pytest._argcomplete import filescompleter
optparser = MyOptionParser(self, self.extra_info, prog=self.prog)
@ -131,7 +133,7 @@ class Parser:
n = option.names()
a = option.attrs()
arggroup.add_argument(*n, **a)
file_or_dir_arg = optparser.add_argument(FILE_OR_DIR, nargs="*")
file_or_dir_arg = optparser.add_argument(FILE_OR_DIR, nargs='*')
# bash like autocompletion for dirs (appending '/')
# Type ignored because typeshed doesn't know about argcomplete.
file_or_dir_arg.completer = filescompleter # type: ignore
@ -139,10 +141,10 @@ class Parser:
def parse_setoption(
self,
args: Sequence[Union[str, "os.PathLike[str]"]],
args: Sequence[str | os.PathLike[str]],
option: argparse.Namespace,
namespace: Optional[argparse.Namespace] = None,
) -> List[str]:
namespace: argparse.Namespace | None = None,
) -> list[str]:
parsedoption = self.parse(args, namespace=namespace)
for name, value in parsedoption.__dict__.items():
setattr(option, name, value)
@ -150,8 +152,8 @@ class Parser:
def parse_known_args(
self,
args: Sequence[Union[str, "os.PathLike[str]"]],
namespace: Optional[argparse.Namespace] = None,
args: Sequence[str | os.PathLike[str]],
namespace: argparse.Namespace | None = None,
) -> argparse.Namespace:
"""Parse the known arguments at this point.
@ -161,9 +163,9 @@ class Parser:
def parse_known_and_unknown_args(
self,
args: Sequence[Union[str, "os.PathLike[str]"]],
namespace: Optional[argparse.Namespace] = None,
) -> Tuple[argparse.Namespace, List[str]]:
args: Sequence[str | os.PathLike[str]],
namespace: argparse.Namespace | None = None,
) -> tuple[argparse.Namespace, list[str]]:
"""Parse the known arguments at this point, and also return the
remaining unknown arguments.
@ -179,9 +181,9 @@ class Parser:
self,
name: str,
help: str,
type: Optional[
Literal["string", "paths", "pathlist", "args", "linelist", "bool"]
] = None,
type: None | (
Literal['string', 'paths', 'pathlist', 'args', 'linelist', 'bool']
) = None,
default: Any = NOT_SET,
) -> None:
"""Register an ini-file option.
@ -215,7 +217,7 @@ class Parser:
The value of ini-variables can be retrieved via a call to
:py:func:`config.getini(name) <pytest.Config.getini>`.
"""
assert type in (None, "string", "paths", "pathlist", "args", "linelist", "bool")
assert type in (None, 'string', 'paths', 'pathlist', 'args', 'linelist', 'bool')
if default is NOT_SET:
default = get_ini_default_for_type(type)
@ -224,33 +226,33 @@ class Parser:
def get_ini_default_for_type(
type: Optional[Literal["string", "paths", "pathlist", "args", "linelist", "bool"]],
type: Literal['string', 'paths', 'pathlist', 'args', 'linelist', 'bool'] | None,
) -> Any:
"""
Used by addini to get the default value for a given ini-option type, when
default is not supplied.
"""
if type is None:
return ""
elif type in ("paths", "pathlist", "args", "linelist"):
return ''
elif type in ('paths', 'pathlist', 'args', 'linelist'):
return []
elif type == "bool":
elif type == 'bool':
return False
else:
return ""
return ''
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:
def __init__(self, msg: str, option: Argument | str) -> None:
self.msg = msg
self.option_id = str(option)
def __str__(self) -> str:
if self.option_id:
return f"option {self.option_id}: {self.msg}"
return f'option {self.option_id}: {self.msg}'
else:
return self.msg
@ -267,36 +269,36 @@ class Argument:
def __init__(self, *names: str, **attrs: Any) -> None:
"""Store params in private vars for use in add_argument."""
self._attrs = attrs
self._short_opts: List[str] = []
self._long_opts: List[str] = []
self._short_opts: list[str] = []
self._long_opts: list[str] = []
try:
self.type = attrs["type"]
self.type = attrs['type']
except KeyError:
pass
try:
# Attribute existence is tested in Config._processopt.
self.default = attrs["default"]
self.default = attrs['default']
except KeyError:
pass
self._set_opt_strings(names)
dest: Optional[str] = attrs.get("dest")
dest: str | None = attrs.get('dest')
if dest:
self.dest = dest
elif self._long_opts:
self.dest = self._long_opts[0][2:].replace("-", "_")
self.dest = self._long_opts[0][2:].replace('-', '_')
else:
try:
self.dest = self._short_opts[0][1:]
except IndexError as e:
self.dest = "???" # Needed for the error repr.
raise ArgumentError("need a long or short option", self) from e
self.dest = '???' # Needed for the error repr.
raise ArgumentError('need a long or short option', self) from e
def names(self) -> List[str]:
def names(self) -> list[str]:
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:
@ -313,39 +315,39 @@ class Argument:
for opt in opts:
if len(opt) < 2:
raise ArgumentError(
"invalid option string %r: "
"must be at least two characters long" % opt,
'invalid option string %r: '
'must be at least two characters long' % opt,
self,
)
elif len(opt) == 2:
if not (opt[0] == "-" and opt[1] != "-"):
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,
'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] != "-"):
if not (opt[0:2] == '--' and opt[2] != '-'):
raise ArgumentError(
"invalid long option string %r: "
"must start with --, followed by non-dash" % opt,
'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] = []
args: list[str] = []
if self._short_opts:
args += ["_short_opts: " + repr(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))
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:
@ -354,15 +356,15 @@ class OptionGroup:
def __init__(
self,
name: str,
description: str = "",
parser: Optional[Parser] = None,
description: str = '',
parser: Parser | None = None,
*,
_ispytest: bool = False,
) -> None:
check_ispytest(_ispytest)
self.name = name
self.description = description
self.options: List[Argument] = []
self.options: list[Argument] = []
self.parser = parser
def addoption(self, *opts: str, **attrs: Any) -> None:
@ -383,7 +385,7 @@ class OptionGroup:
name for opt in self.options for name in opt.names()
)
if conflict:
raise ValueError("option names %s already added" % conflict)
raise ValueError('option names %s already added' % conflict)
option = Argument(*opts, **attrs)
self._addoption_instance(option, shortupper=False)
@ -391,11 +393,11 @@ class OptionGroup:
option = Argument(*opts, **attrs)
self._addoption_instance(option, shortupper=True)
def _addoption_instance(self, option: "Argument", shortupper: bool = False) -> None:
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 opt[0] == '-' and opt[1].islower():
raise ValueError('lowercase shortoptions reserved')
if self.parser:
self.parser.processoption(option)
self.options.append(option)
@ -405,8 +407,8 @@ class MyOptionParser(argparse.ArgumentParser):
def __init__(
self,
parser: Parser,
extra_info: Optional[Dict[str, Any]] = None,
prog: Optional[str] = None,
extra_info: dict[str, Any] | None = None,
prog: str | None = None,
) -> None:
self._parser = parser
super().__init__(
@ -422,29 +424,29 @@ class MyOptionParser(argparse.ArgumentParser):
def error(self, message: str) -> NoReturn:
"""Transform argparse error message into UsageError."""
msg = f"{self.prog}: error: {message}"
msg = f'{self.prog}: error: {message}'
if hasattr(self._parser, "_config_source_hint"):
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
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.
def parse_args( # type: ignore
self,
args: Optional[Sequence[str]] = None,
namespace: Optional[argparse.Namespace] = None,
args: Sequence[str] | None = None,
namespace: argparse.Namespace | None = None,
) -> argparse.Namespace:
"""Allow splitting of positional arguments."""
parsed, unrecognized = self.parse_known_args(args, namespace)
if unrecognized:
for arg in unrecognized:
if arg and arg[0] == "-":
lines = ["unrecognized arguments: %s" % (" ".join(unrecognized))]
if arg and arg[0] == '-':
lines = ['unrecognized arguments: %s' % (' '.join(unrecognized))]
for k, v in sorted(self.extra_info.items()):
lines.append(f" {k}: {v}")
self.error("\n".join(lines))
lines.append(f' {k}: {v}')
self.error('\n'.join(lines))
getattr(parsed, FILE_OR_DIR).extend(unrecognized)
return parsed
@ -452,8 +454,8 @@ class MyOptionParser(argparse.ArgumentParser):
# Backport of https://github.com/python/cpython/pull/14316 so we can
# disable long --argument abbreviations without breaking short flags.
def _parse_optional(
self, arg_string: str
) -> Optional[Tuple[Optional[argparse.Action], str, Optional[str]]]:
self, arg_string: str,
) -> tuple[argparse.Action | None, str, str | None] | None:
if not arg_string:
return None
if arg_string[0] not in self.prefix_chars:
@ -463,26 +465,26 @@ class MyOptionParser(argparse.ArgumentParser):
return action, arg_string, None
if len(arg_string) == 1:
return None
if "=" in arg_string:
option_string, explicit_arg = arg_string.split("=", 1)
if '=' in arg_string:
option_string, explicit_arg = arg_string.split('=', 1)
if option_string in self._option_string_actions:
action = self._option_string_actions[option_string]
return action, option_string, explicit_arg
if self.allow_abbrev or not arg_string.startswith("--"):
if self.allow_abbrev or not arg_string.startswith('--'):
option_tuples = self._get_option_tuples(arg_string)
if len(option_tuples) > 1:
msg = gettext(
"ambiguous option: %(option)s could match %(matches)s"
'ambiguous option: %(option)s could match %(matches)s',
)
options = ", ".join(option for _, option, _ in option_tuples)
self.error(msg % {"option": arg_string, "matches": options})
options = ', '.join(option for _, option, _ in option_tuples)
self.error(msg % {'option': arg_string, 'matches': options})
elif len(option_tuples) == 1:
(option_tuple,) = option_tuples
return option_tuple
if self._negative_number_matcher.match(arg_string):
if not self._has_negative_number_optionals:
return None
if " " in arg_string:
if ' ' in arg_string:
return None
return None, arg_string, None
@ -497,45 +499,45 @@ class DropShorterLongHelpFormatter(argparse.HelpFormatter):
def __init__(self, *args: Any, **kwargs: Any) -> None:
# Use more accurate terminal width.
if "width" not in kwargs:
kwargs["width"] = _pytest._io.get_terminal_width()
if 'width' not in kwargs:
kwargs['width'] = _pytest._io.get_terminal_width()
super().__init__(*args, **kwargs)
def _format_action_invocation(self, action: argparse.Action) -> str:
orgstr = super()._format_action_invocation(action)
if orgstr and orgstr[0] != "-": # only optional arguments
if orgstr and orgstr[0] != '-': # only optional arguments
return orgstr
res: Optional[str] = getattr(action, "_formatted_action_invocation", None)
res: str | None = getattr(action, '_formatted_action_invocation', None)
if res:
return res
options = orgstr.split(", ")
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 = []
short_long: Dict[str, str] = {}
short_long: dict[str, str] = {}
for option in options:
if len(option) == 2 or option[2] == " ":
if len(option) == 2 or option[2] == ' ':
continue
if not option.startswith("--"):
if not option.startswith('--'):
raise ArgumentError(
'long optional argument without "--": [%s]' % (option), option
'long optional argument without "--": [%s]' % (option), option,
)
xxoption = option[2:]
shortened = xxoption.replace("-", "")
shortened = xxoption.replace('-', '')
if shortened not in short_long or len(short_long[shortened]) < len(
xxoption
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] == " ":
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)
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

View file

@ -1,10 +1,10 @@
from __future__ import annotations
import functools
import warnings
from pathlib import Path
from typing import Any
from typing import Mapping
import warnings
import pluggy
@ -15,19 +15,19 @@ from ..deprecated import HOOK_LEGACY_PATH_ARG
# hookname: (Path, LEGACY_PATH)
imply_paths_hooks: Mapping[str, tuple[str, str]] = {
"pytest_ignore_collect": ("collection_path", "path"),
"pytest_collect_file": ("file_path", "path"),
"pytest_pycollect_makemodule": ("module_path", "path"),
"pytest_report_header": ("start_path", "startdir"),
"pytest_report_collectionfinish": ("start_path", "startdir"),
'pytest_ignore_collect': ('collection_path', 'path'),
'pytest_collect_file': ('file_path', 'path'),
'pytest_pycollect_makemodule': ('module_path', 'path'),
'pytest_report_header': ('start_path', 'startdir'),
'pytest_report_collectionfinish': ('start_path', 'startdir'),
}
def _check_path(path: Path, fspath: LEGACY_PATH) -> None:
if Path(fspath) != path:
raise ValueError(
f"Path({fspath!r}) != {path!r}\n"
"if both path and fspath are given they need to be equal"
f'Path({fspath!r}) != {path!r}\n'
'if both path and fspath are given they need to be equal',
)
@ -61,7 +61,7 @@ class PathAwareHookProxy:
if fspath_value is not None:
warnings.warn(
HOOK_LEGACY_PATH_ARG.format(
pylib_path_arg=fspath_var, pathlib_path_arg=path_var
pylib_path_arg=fspath_var, pathlib_path_arg=path_var,
),
stacklevel=2,
)

View file

@ -1,3 +1,5 @@
from __future__ import annotations
from typing import final

View file

@ -1,6 +1,8 @@
from __future__ import annotations
import os
from pathlib import Path
import sys
from pathlib import Path
from typing import Dict
from typing import Iterable
from typing import List
@ -10,13 +12,13 @@ from typing import Tuple
from typing import Union
import iniconfig
from .exceptions import UsageError
from _pytest.outcomes import fail
from _pytest.pathlib import absolutepath
from _pytest.pathlib import commonpath
from _pytest.pathlib import safe_exists
from .exceptions import UsageError
def _parse_ini_config(path: Path) -> iniconfig.IniConfig:
"""Parse the given generic '.ini' file using legacy IniConfig parser, returning
@ -32,52 +34,52 @@ def _parse_ini_config(path: Path) -> iniconfig.IniConfig:
def load_config_dict_from_file(
filepath: Path,
) -> Optional[Dict[str, Union[str, List[str]]]]:
) -> dict[str, str | list[str]] | None:
"""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":
if filepath.suffix == '.ini':
iniconfig = _parse_ini_config(filepath)
if "pytest" in iniconfig:
return dict(iniconfig["pytest"].items())
if 'pytest' in iniconfig:
return dict(iniconfig['pytest'].items())
else:
# "pytest.ini" files are always the source of configuration, even if empty.
if filepath.name == "pytest.ini":
if filepath.name == 'pytest.ini':
return {}
# '.cfg' files are considered if they contain a "[tool:pytest]" section.
elif filepath.suffix == ".cfg":
elif filepath.suffix == '.cfg':
iniconfig = _parse_ini_config(filepath)
if "tool:pytest" in iniconfig.sections:
return dict(iniconfig["tool:pytest"].items())
elif "pytest" in iniconfig.sections:
if 'tool:pytest' in iniconfig.sections:
return dict(iniconfig['tool:pytest'].items())
elif 'pytest' in iniconfig.sections:
# If a setup.cfg contains a "[pytest]" section, we raise a failure to indicate users that
# plain "[pytest]" sections in setup.cfg files is no longer supported (#3086).
fail(CFG_PYTEST_SECTION.format(filename="setup.cfg"), pytrace=False)
fail(CFG_PYTEST_SECTION.format(filename='setup.cfg'), pytrace=False)
# '.toml' files are considered if they contain a [tool.pytest.ini_options] table.
elif filepath.suffix == ".toml":
elif filepath.suffix == '.toml':
if sys.version_info >= (3, 11):
import tomllib
else:
import tomli as tomllib
toml_text = filepath.read_text(encoding="utf-8")
toml_text = filepath.read_text(encoding='utf-8')
try:
config = tomllib.loads(toml_text)
except tomllib.TOMLDecodeError as exc:
raise UsageError(f"{filepath}: {exc}") from exc
raise UsageError(f'{filepath}: {exc}') from exc
result = config.get("tool", {}).get("pytest", {}).get("ini_options", None)
result = config.get('tool', {}).get('pytest', {}).get('ini_options', None)
if result is not None:
# TOML supports richer data types than ini files (strings, arrays, floats, ints, etc),
# however we need to convert all scalar values to str for compatibility with the rest
# of the configuration system, which expects strings only.
def make_scalar(v: object) -> Union[str, List[str]]:
def make_scalar(v: object) -> str | list[str]:
return v if isinstance(v, list) else str(v)
return {k: make_scalar(v) for k, v in result.items()}
@ -88,27 +90,27 @@ def load_config_dict_from_file(
def locate_config(
invocation_dir: Path,
args: Iterable[Path],
) -> Tuple[Optional[Path], Optional[Path], Dict[str, Union[str, List[str]]]]:
) -> tuple[Path | None, Path | None, dict[str, 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)."""
config_names = [
"pytest.ini",
".pytest.ini",
"pyproject.toml",
"tox.ini",
"setup.cfg",
'pytest.ini',
'.pytest.ini',
'pyproject.toml',
'tox.ini',
'setup.cfg',
]
args = [x for x in args if not str(x).startswith("-")]
args = [x for x in args if not str(x).startswith('-')]
if not args:
args = [invocation_dir]
found_pyproject_toml: Optional[Path] = None
found_pyproject_toml: Path | None = None
for arg in args:
argpath = absolutepath(arg)
for base in (argpath, *argpath.parents):
for config_name in config_names:
p = base / config_name
if p.is_file():
if p.name == "pyproject.toml" and found_pyproject_toml is None:
if p.name == 'pyproject.toml' and found_pyproject_toml is None:
found_pyproject_toml = p
ini_config = load_config_dict_from_file(p)
if ini_config is not None:
@ -122,7 +124,7 @@ def get_common_ancestor(
invocation_dir: Path,
paths: Iterable[Path],
) -> Path:
common_ancestor: Optional[Path] = None
common_ancestor: Path | None = None
for path in paths:
if not path.exists():
continue
@ -144,12 +146,12 @@ def get_common_ancestor(
return common_ancestor
def get_dirs_from_args(args: Iterable[str]) -> List[Path]:
def get_dirs_from_args(args: Iterable[str]) -> list[Path]:
def is_option(x: str) -> bool:
return x.startswith("-")
return x.startswith('-')
def get_file_part_from_node_id(x: str) -> str:
return x.split("::")[0]
return x.split('::')[0]
def get_dir_from_path(path: Path) -> Path:
if path.is_dir():
@ -166,16 +168,16 @@ def get_dirs_from_args(args: Iterable[str]) -> List[Path]:
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."
CFG_PYTEST_SECTION = '[pytest] section in {filename} files is no longer supported, change to [tool:pytest] instead.'
def determine_setup(
*,
inifile: Optional[str],
inifile: str | None,
args: Sequence[str],
rootdir_cmd_arg: Optional[str],
rootdir_cmd_arg: str | None,
invocation_dir: Path,
) -> Tuple[Path, Optional[Path], Dict[str, Union[str, List[str]]]]:
) -> tuple[Path, Path | None, dict[str, str | list[str]]]:
"""Determine the rootdir, inifile and ini configuration values from the
command line arguments.
@ -192,7 +194,7 @@ def determine_setup(
dirs = get_dirs_from_args(args)
if inifile:
inipath_ = absolutepath(inifile)
inipath: Optional[Path] = inipath_
inipath: Path | None = inipath_
inicfg = load_config_dict_from_file(inipath_) or {}
if rootdir_cmd_arg is None:
rootdir = inipath_.parent
@ -201,7 +203,7 @@ def determine_setup(
rootdir, inipath, inicfg = locate_config(invocation_dir, [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():
if (possible_rootdir / 'setup.py').is_file():
rootdir = possible_rootdir
break
else:
@ -209,7 +211,7 @@ def determine_setup(
rootdir, inipath, inicfg = locate_config(invocation_dir, dirs)
if rootdir is None:
rootdir = get_common_ancestor(
invocation_dir, [invocation_dir, ancestor]
invocation_dir, [invocation_dir, ancestor],
)
if is_fs_root(rootdir):
rootdir = ancestor
@ -217,7 +219,7 @@ def determine_setup(
rootdir = absolutepath(os.path.expandvars(rootdir_cmd_arg))
if not rootdir.is_dir():
raise UsageError(
f"Directory '{rootdir}' not found. Check your '--rootdir' option."
f"Directory '{rootdir}' not found. Check your '--rootdir' option.",
)
assert rootdir is not None
return rootdir, inipath, inicfg or {}