diff options
Diffstat (limited to '.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups')
13 files changed, 417 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/__init__.py b/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/__init__.py new file mode 100644 index 0000000..9fec202 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/__init__.py @@ -0,0 +1,13 @@ +from ._implementation import ( + CyclicDependencyError, + DependencyGroupInclude, + DependencyGroupResolver, + resolve, +) + +__all__ = ( + "CyclicDependencyError", + "DependencyGroupInclude", + "DependencyGroupResolver", + "resolve", +) diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/__main__.py b/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/__main__.py new file mode 100644 index 0000000..48ebb0d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/__main__.py @@ -0,0 +1,65 @@ +import argparse +import sys + +from ._implementation import resolve +from ._toml_compat import tomllib + + +def main() -> None: + if tomllib is None: + print( + "Usage error: dependency-groups CLI requires tomli or Python 3.11+", + file=sys.stderr, + ) + raise SystemExit(2) + + parser = argparse.ArgumentParser( + description=( + "A dependency-groups CLI. Prints out a resolved group, newline-delimited." + ) + ) + parser.add_argument( + "GROUP_NAME", nargs="*", help="The dependency group(s) to resolve." + ) + parser.add_argument( + "-f", + "--pyproject-file", + default="pyproject.toml", + help="The pyproject.toml file. Defaults to trying in the current directory.", + ) + parser.add_argument( + "-o", + "--output", + help="An output file. Defaults to stdout.", + ) + parser.add_argument( + "-l", + "--list", + action="store_true", + help="List the available dependency groups", + ) + args = parser.parse_args() + + with open(args.pyproject_file, "rb") as fp: + pyproject = tomllib.load(fp) + + dependency_groups_raw = pyproject.get("dependency-groups", {}) + + if args.list: + print(*dependency_groups_raw.keys()) + return + if not args.GROUP_NAME: + print("A GROUP_NAME is required", file=sys.stderr) + raise SystemExit(3) + + content = "\n".join(resolve(dependency_groups_raw, *args.GROUP_NAME)) + + if args.output is None or args.output == "-": + print(content) + else: + with open(args.output, "w", encoding="utf-8") as fp: + print(content, file=fp) + + +if __name__ == "__main__": + main() diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/__pycache__/__init__.cpython-312.pyc Binary files differnew file mode 100644 index 0000000..95091da --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/__pycache__/__init__.cpython-312.pyc diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/__pycache__/__main__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/__pycache__/__main__.cpython-312.pyc Binary files differnew file mode 100644 index 0000000..dbe877a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/__pycache__/__main__.cpython-312.pyc diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/__pycache__/_implementation.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/__pycache__/_implementation.cpython-312.pyc Binary files differnew file mode 100644 index 0000000..1c31334 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/__pycache__/_implementation.cpython-312.pyc diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/__pycache__/_lint_dependency_groups.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/__pycache__/_lint_dependency_groups.cpython-312.pyc Binary files differnew file mode 100644 index 0000000..b54783e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/__pycache__/_lint_dependency_groups.cpython-312.pyc diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/__pycache__/_pip_wrapper.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/__pycache__/_pip_wrapper.cpython-312.pyc Binary files differnew file mode 100644 index 0000000..f3a8897 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/__pycache__/_pip_wrapper.cpython-312.pyc diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/__pycache__/_toml_compat.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/__pycache__/_toml_compat.cpython-312.pyc Binary files differnew file mode 100644 index 0000000..528d177 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/__pycache__/_toml_compat.cpython-312.pyc diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/_implementation.py b/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/_implementation.py new file mode 100644 index 0000000..64e314a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/_implementation.py @@ -0,0 +1,209 @@ +from __future__ import annotations + +import dataclasses +import re +from collections.abc import Mapping + +from pip._vendor.packaging.requirements import Requirement + + +def _normalize_name(name: str) -> str: + return re.sub(r"[-_.]+", "-", name).lower() + + +def _normalize_group_names( + dependency_groups: Mapping[str, str | Mapping[str, str]], +) -> Mapping[str, str | Mapping[str, str]]: + original_names: dict[str, list[str]] = {} + normalized_groups = {} + + for group_name, value in dependency_groups.items(): + normed_group_name = _normalize_name(group_name) + original_names.setdefault(normed_group_name, []).append(group_name) + normalized_groups[normed_group_name] = value + + errors = [] + for normed_name, names in original_names.items(): + if len(names) > 1: + errors.append(f"{normed_name} ({', '.join(names)})") + if errors: + raise ValueError(f"Duplicate dependency group names: {', '.join(errors)}") + + return normalized_groups + + +@dataclasses.dataclass +class DependencyGroupInclude: + include_group: str + + +class CyclicDependencyError(ValueError): + """ + An error representing the detection of a cycle. + """ + + def __init__(self, requested_group: str, group: str, include_group: str) -> None: + self.requested_group = requested_group + self.group = group + self.include_group = include_group + + if include_group == group: + reason = f"{group} includes itself" + else: + reason = f"{include_group} -> {group}, {group} -> {include_group}" + super().__init__( + "Cyclic dependency group include while resolving " + f"{requested_group}: {reason}" + ) + + +class DependencyGroupResolver: + """ + A resolver for Dependency Group data. + + This class handles caching, name normalization, cycle detection, and other + parsing requirements. There are only two public methods for exploring the data: + ``lookup()`` and ``resolve()``. + + :param dependency_groups: A mapping, as provided via pyproject + ``[dependency-groups]``. + """ + + def __init__( + self, + dependency_groups: Mapping[str, str | Mapping[str, str]], + ) -> None: + if not isinstance(dependency_groups, Mapping): + raise TypeError("Dependency Groups table is not a mapping") + self.dependency_groups = _normalize_group_names(dependency_groups) + # a map of group names to parsed data + self._parsed_groups: dict[ + str, tuple[Requirement | DependencyGroupInclude, ...] + ] = {} + # a map of group names to their ancestors, used for cycle detection + self._include_graph_ancestors: dict[str, tuple[str, ...]] = {} + # a cache of completed resolutions to Requirement lists + self._resolve_cache: dict[str, tuple[Requirement, ...]] = {} + + def lookup(self, group: str) -> tuple[Requirement | DependencyGroupInclude, ...]: + """ + Lookup a group name, returning the parsed dependency data for that group. + This will not resolve includes. + + :param group: the name of the group to lookup + + :raises ValueError: if the data does not appear to be valid dependency group + data + :raises TypeError: if the data is not a string + :raises LookupError: if group name is absent + :raises packaging.requirements.InvalidRequirement: if a specifier is not valid + """ + if not isinstance(group, str): + raise TypeError("Dependency group name is not a str") + group = _normalize_name(group) + return self._parse_group(group) + + def resolve(self, group: str) -> tuple[Requirement, ...]: + """ + Resolve a dependency group to a list of requirements. + + :param group: the name of the group to resolve + + :raises TypeError: if the inputs appear to be the wrong types + :raises ValueError: if the data does not appear to be valid dependency group + data + :raises LookupError: if group name is absent + :raises packaging.requirements.InvalidRequirement: if a specifier is not valid + """ + if not isinstance(group, str): + raise TypeError("Dependency group name is not a str") + group = _normalize_name(group) + return self._resolve(group, group) + + def _parse_group( + self, group: str + ) -> tuple[Requirement | DependencyGroupInclude, ...]: + # short circuit -- never do the work twice + if group in self._parsed_groups: + return self._parsed_groups[group] + + if group not in self.dependency_groups: + raise LookupError(f"Dependency group '{group}' not found") + + raw_group = self.dependency_groups[group] + if not isinstance(raw_group, list): + raise TypeError(f"Dependency group '{group}' is not a list") + + elements: list[Requirement | DependencyGroupInclude] = [] + for item in raw_group: + if isinstance(item, str): + # packaging.requirements.Requirement parsing ensures that this is a + # valid PEP 508 Dependency Specifier + # raises InvalidRequirement on failure + elements.append(Requirement(item)) + elif isinstance(item, dict): + if tuple(item.keys()) != ("include-group",): + raise ValueError(f"Invalid dependency group item: {item}") + + include_group = next(iter(item.values())) + elements.append(DependencyGroupInclude(include_group=include_group)) + else: + raise ValueError(f"Invalid dependency group item: {item}") + + self._parsed_groups[group] = tuple(elements) + return self._parsed_groups[group] + + def _resolve(self, group: str, requested_group: str) -> tuple[Requirement, ...]: + """ + This is a helper for cached resolution to strings. + + :param group: The name of the group to resolve. + :param requested_group: The group which was used in the original, user-facing + request. + """ + if group in self._resolve_cache: + return self._resolve_cache[group] + + parsed = self._parse_group(group) + + resolved_group = [] + for item in parsed: + if isinstance(item, Requirement): + resolved_group.append(item) + elif isinstance(item, DependencyGroupInclude): + include_group = _normalize_name(item.include_group) + if include_group in self._include_graph_ancestors.get(group, ()): + raise CyclicDependencyError( + requested_group, group, item.include_group + ) + self._include_graph_ancestors[include_group] = ( + *self._include_graph_ancestors.get(group, ()), + group, + ) + resolved_group.extend(self._resolve(include_group, requested_group)) + else: # unreachable + raise NotImplementedError( + f"Invalid dependency group item after parse: {item}" + ) + + self._resolve_cache[group] = tuple(resolved_group) + return self._resolve_cache[group] + + +def resolve( + dependency_groups: Mapping[str, str | Mapping[str, str]], /, *groups: str +) -> tuple[str, ...]: + """ + Resolve a dependency group to a tuple of requirements, as strings. + + :param dependency_groups: the parsed contents of the ``[dependency-groups]`` table + from ``pyproject.toml`` + :param groups: the name of the group(s) to resolve + + :raises TypeError: if the inputs appear to be the wrong types + :raises ValueError: if the data does not appear to be valid dependency group data + :raises LookupError: if group name is absent + :raises packaging.requirements.InvalidRequirement: if a specifier is not valid + """ + resolver = DependencyGroupResolver(dependency_groups) + return tuple(str(r) for group in groups for r in resolver.resolve(group)) diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/_lint_dependency_groups.py b/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/_lint_dependency_groups.py new file mode 100644 index 0000000..09454bd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/_lint_dependency_groups.py @@ -0,0 +1,59 @@ +from __future__ import annotations + +import argparse +import sys + +from ._implementation import DependencyGroupResolver +from ._toml_compat import tomllib + + +def main(*, argv: list[str] | None = None) -> None: + if tomllib is None: + print( + "Usage error: dependency-groups CLI requires tomli or Python 3.11+", + file=sys.stderr, + ) + raise SystemExit(2) + + parser = argparse.ArgumentParser( + description=( + "Lint Dependency Groups for validity. " + "This will eagerly load and check all of your Dependency Groups." + ) + ) + parser.add_argument( + "-f", + "--pyproject-file", + default="pyproject.toml", + help="The pyproject.toml file. Defaults to trying in the current directory.", + ) + args = parser.parse_args(argv if argv is not None else sys.argv[1:]) + + with open(args.pyproject_file, "rb") as fp: + pyproject = tomllib.load(fp) + dependency_groups_raw = pyproject.get("dependency-groups", {}) + + errors: list[str] = [] + try: + resolver = DependencyGroupResolver(dependency_groups_raw) + except (ValueError, TypeError) as e: + errors.append(f"{type(e).__name__}: {e}") + else: + for groupname in resolver.dependency_groups: + try: + resolver.resolve(groupname) + except (LookupError, ValueError, TypeError) as e: + errors.append(f"{type(e).__name__}: {e}") + + if errors: + print("errors encountered while examining dependency groups:") + for msg in errors: + print(f" {msg}") + sys.exit(1) + else: + print("ok") + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/_pip_wrapper.py b/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/_pip_wrapper.py new file mode 100644 index 0000000..f86d896 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/_pip_wrapper.py @@ -0,0 +1,62 @@ +from __future__ import annotations + +import argparse +import subprocess +import sys + +from ._implementation import DependencyGroupResolver +from ._toml_compat import tomllib + + +def _invoke_pip(deps: list[str]) -> None: + subprocess.check_call([sys.executable, "-m", "pip", "install", *deps]) + + +def main(*, argv: list[str] | None = None) -> None: + if tomllib is None: + print( + "Usage error: dependency-groups CLI requires tomli or Python 3.11+", + file=sys.stderr, + ) + raise SystemExit(2) + + parser = argparse.ArgumentParser(description="Install Dependency Groups.") + parser.add_argument( + "DEPENDENCY_GROUP", nargs="+", help="The dependency groups to install." + ) + parser.add_argument( + "-f", + "--pyproject-file", + default="pyproject.toml", + help="The pyproject.toml file. Defaults to trying in the current directory.", + ) + args = parser.parse_args(argv if argv is not None else sys.argv[1:]) + + with open(args.pyproject_file, "rb") as fp: + pyproject = tomllib.load(fp) + dependency_groups_raw = pyproject.get("dependency-groups", {}) + + errors: list[str] = [] + resolved: list[str] = [] + try: + resolver = DependencyGroupResolver(dependency_groups_raw) + except (ValueError, TypeError) as e: + errors.append(f"{type(e).__name__}: {e}") + else: + for groupname in args.DEPENDENCY_GROUP: + try: + resolved.extend(str(r) for r in resolver.resolve(groupname)) + except (LookupError, ValueError, TypeError) as e: + errors.append(f"{type(e).__name__}: {e}") + + if errors: + print("errors encountered while examining dependency groups:") + for msg in errors: + print(f" {msg}") + sys.exit(1) + + _invoke_pip(resolved) + + +if __name__ == "__main__": + main() diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/_toml_compat.py b/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/_toml_compat.py new file mode 100644 index 0000000..8d6f921 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/_toml_compat.py @@ -0,0 +1,9 @@ +try: + import tomllib +except ImportError: + try: + from pip._vendor import tomli as tomllib # type: ignore[no-redef, unused-ignore] + except ModuleNotFoundError: # pragma: no cover + tomllib = None # type: ignore[assignment, unused-ignore] + +__all__ = ("tomllib",) diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/py.typed b/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/py.typed new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/dependency_groups/py.typed |
