Skip to content

Commit

Permalink
Add a 'pip install' wrapper command
Browse files Browse the repository at this point in the history
pip-install-dependency-groups installs dependency groups by calling
`pip` in a subprocess.

Use this to replace the rendered requirements data in `requirements/`
by having `tox` invoke `pip-install-dependency-groups` as a
pre-command.
  • Loading branch information
sirosen committed Oct 2, 2024
1 parent 34988b4 commit 432b7da
Show file tree
Hide file tree
Showing 11 changed files with 100 additions and 41 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

- Add a new command, `pip-install-dependency-groups`, which is capable of
installing dependency groups by invoking `pip`

## 0.2.2

- The pre-commit hook sets `pass_filenames: false`
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,14 @@ repos:
- id: lint-dependency-groups
```
### Install CLI
`dependency-groups` includes a `pip` wrapper, `pip-install-dependency-groups`.

Usage is simple, just `pip-install-dependency-groups groupname` to install!

Use `pip-install-dependency-groups --help` for more details.

## License

`dependency-groups` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
6 changes: 5 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,14 @@ classifiers = [
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
]
dependencies = ["packaging"]
dependencies = [
"packaging",
"tomli;python_version<'3.11'",
]

[project.scripts]
lint-dependency-groups = "dependency_groups._lint_dependency_groups:main"
pip-install-dependency-groups = "dependency_groups._pip_wrapper:main"

[project.urls]
source = "https://github.com/sirosen/dependency-groups"
Expand Down
4 changes: 0 additions & 4 deletions requirements/README.md

This file was deleted.

2 changes: 0 additions & 2 deletions requirements/build.txt

This file was deleted.

1 change: 0 additions & 1 deletion requirements/coverage.txt

This file was deleted.

1 change: 0 additions & 1 deletion requirements/lint.txt

This file was deleted.

2 changes: 0 additions & 2 deletions requirements/test.txt

This file was deleted.

2 changes: 0 additions & 2 deletions requirements/typing.txt

This file was deleted.

70 changes: 70 additions & 0 deletions src/dependency_groups/_pip_wrapper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/env python
from __future__ import annotations

import argparse
import subprocess
import sys

from dependency_groups import DependencyGroupResolver

try:
import tomllib
except ImportError:
try:
import tomli as tomllib # type: ignore[no-redef]
except ImportError: # pragma: no cover
tomllib = None # type: ignore[assignment]


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 not tomllib:
print(
"Usage error: dependency-groups CLI requires tomli or Python 3.11+",
file=sys.stderr,
)
sys.exit(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()
42 changes: 14 additions & 28 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ labels =
[testenv]
package = wheel
wheel_build_env = build_wheel
deps = -r requirements/test.txt
commands_pre = pip-install-dependency-groups test
commands = coverage run -m pytest -v {posargs}

depends =
Expand All @@ -21,49 +21,35 @@ depends =
covreport: covcombine

[testenv:covclean]
deps = -r requirements/coverage.txt
skip_install = true
commands_pre = pip-install-dependency-groups coverage
commands = coverage erase

[testenv:covcombine]
deps = -r requirements/coverage.txt
skip_install = true
commands_pre = pip-install-dependency-groups coverage
commands = coverage combine

[testenv:covreport]
deps = -r requirements/coverage.txt
skip_install = true
commands_pre = coverage html --fail-under=0
commands_pre =
pip-install-dependency-groups coverage
coverage html --fail-under=0
commands = coverage report

[testenv:render-requirements]
deps =
commands =
python -m dependency_groups 'test' -o requirements/test.txt
python -m dependency_groups 'coverage' -o requirements/coverage.txt
python -m dependency_groups 'lint' -o requirements/lint.txt
python -m dependency_groups 'typing' -o requirements/typing.txt
python -m dependency_groups 'build' -o requirements/build.txt


[testenv:lint]
deps = -r requirements/lint.txt
skip_install = true
commands_pre = pip-install-dependency-groups lint
commands = pre-commit run -a

[testenv:mypy]
commands_pre = pip-install-dependency-groups typing
commands = mypy src/


[testenv:twine-check]
description = "check the metadata on a package build"
skip_install = true
deps = -r requirements/build.txt
allowlist_externals = rm
commands_pre = rm -rf dist/
commands_pre =
pip-install-dependency-groups build
rm -rf dist/
# check that twine validating package data works
commands = python -m build
twine check dist/*


[testenv:mypy]
deps = -r requirements/typing.txt
skip_install = true
commands = mypy src/

0 comments on commit 432b7da

Please sign in to comment.