pypa / pypa/dependency-groups

AI review of dependency-groups

Open
#45 6 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
14
Forks
4
Avg merge
7h 13m
Merged PRs (30d)
1

Description

@sirosen, I ran a review (see https://iscinumpy.dev/post/claude-code-reviews/), below. Would you like me to fix any of these? We should add 3.15 to CI/classifiers since it's in RC phase, for sure.

🤖 AI text below 🤖

Review complete. I read the whole implementation, verified suspicious behaviors by running them, and checked the CLIs. The core resolver (cycle detection, caching, normalization) is sound — I traced the ancestor-tracking logic and could not construct a false-positive or false-negative cycle, including diamonds, self-includes, and resolver reuse after errors. The findings are all at the edges:

Bugs

1. Non-string include-group values produce a cryptic error_implementation.py:161
The value of {include-group = ...} is never validated as a string. A TOML file with {include-group = 5} fails deep inside _normalize_name with TypeError: expected string or bytes-like object, got 'int', and the lint CLI dutifully reports that unhelpful message. A str check at parse time with a clear message ("include-group value must be a string") would fix both the API and lint output.

2. pip-install-dependency-groups crashes on an empty resolution_pip_wrapper.py:58
If the requested groups resolve to zero requirements (e.g. empty = []), it runs pip install with no arguments, which fails with a CalledProcessError traceback. Verified. It should skip the pip call (arguably print "nothing to install") when resolved is empty.

3. The dependency-groups CLI shows raw tracebacks for user errors__main__.py:55
dependency-groups nonexistent prints a full traceback ending in LookupError. Same for invalid TOML or a missing pyproject file (this last one applies to all three CLIs). The lint and pip-install CLIs catch LookupError/ValueError/TypeError and print tidy messages; the main CLI should too.

4. Type hints accept Mapping items but the implementation requires dict_implementation.py:157
The signatures say Sequence[str | Mapping[str, str]], but _parse_group checks isinstance(item, dict), so a MappingProxyType({"include-group": "b"}) is rejected as "Invalid dependency group item" (verified). Either check Mapping or tighten the hints to dict.

Smaller issues

  • __main__.main() takes no argv parameter while _lint_dependency_groups.main and _pip_wrapper.main both accept argv for testability. Worth aligning.
  • --list ignores -o/--output and prints group names space-delimited (print(*keys)), which is ambiguous if a name contains a space. Newline-delimited output routed through the same output logic would be more consistent with the rest of the command.
  • _toml_compat.py catches ImportError on the first import but ModuleNotFoundError on the second — harmless, but inconsistent for no reason.

Simplifications

  • _normalize_group_names maintains both original_names (normalized → list of originals) and normalized_to_original (normalized → last original); the second is derivable from the first (names[0] after the duplicate check, since duplicates raise). One structure suffices.
  • _parse_group and _resolve both end with "assign to cache dict, then read it back" (self._parsed_groups[group] = tuple(elements); return self._parsed_groups[group]); assigning a local and returning it is simpler.

Modernization

  • Python 3.9 is past end-of-life (October 2025). requires-python = ">=3.9" plus the 3.9 classifier, tox env, and mypy-py39 could be dropped; that would also unlock dataclass(slots=True) and match-free but cleaner typing. This is a maintainer-policy call for a PyPA library, so I'd raise it with sirosen rather than just doing it.
  • DependencyGroupInclude could be @dataclasses.dataclass(frozen=True) — it's a value type that ends up in cached tuples, so immutability fits (and it becomes hashable).
  • Python 3.15 is in beta; CI/tox could add it to the test matrix alongside the existing 3.14 coverage.

No performance concerns: the resolver caches parses and resolutions, the 100-deep include chain in the test suite resolves fine, and data sizes here are trivially small.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read the referenced areas in _implementation.py, _pip_wrapper.py, and main.py, then confirm with the maintainer which finding is in scope. Check the relevant CLI entry points and existing behavior described in the issue. Done means the selected user-facing error or empty-resolution case behaves clearly without affecting the other resolver behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
cli, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.