We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Seams, Modules, Packaging and Tooling step 24 of 36
Flattening a dependency group
Implement the resolution step every PEP 735 aware installer performs before it can install anything.
GroupTable = Mapping[str, Sequence[str | Mapping[str, str]]]
class CyclicGroupError(Exception):
path: tuple[str, ...]
class UnknownGroupError(KeyError):
name: str
def resolve_group(groups: GroupTable, name: str) -> list[str]:
Each entry in a group is either a requirement string ("pytest>=8") or
an include ({"include-group": "test"}). Flatten the named group into a
flat list of requirement strings:
- First-seen order is preserved. An include contributes its requirements at the position the include appears.
-
Duplicates collapse. A diamond —
devincludestestandlint, both of which includebase— yieldsbase‘s requirements exactly once, at their first position. -
An include of a group that does not exist raises
UnknownGroupErrornaming the missing group, which is not necessarily the one you were asked to resolve. -
A cycle raises
CyclicGroupErrorwhosepathis the cycle itself, starting and ending at the repeated group:("a", "b", "a"). A group that includes itself gives("a", "a").
Because the harness compares return values rather than exceptions, the graded entrypoint is a thin driver that reports what happened:
def solve(groups: GroupTable, name: str) -> tuple[Literal["ok", "cycle", "unknown"], list[str]]:
-
("ok", requirements)on success, -
("cycle", list(err.path))forCyclicGroupError, -
("unknown", [err.name])forUnknownGroupError.
Write the exceptions properly anyway: resolve_group must raise, and solve
must catch. An error type that carries structured data — the cycle path,
the missing name — rather than only a formatted message is the difference
between a caller that can print something useful and one that has to parse
your string.
Why the cycle path, not just “there is a cycle”. dev includes test
includes integration includes dev. Told only that a cycle exists, a
maintainer stares at a twelve-group table. Handed ("dev", "test", "integration", "dev"), they delete one line. Note also that the reported
path must be the cycle, not the whole traversal stack: resolving top,
which includes a cycle it is not part of, reports the cycle without top.
Your submission must pass mypy --strict. Entries are str | Mapping[str, str],
so narrow with isinstance before calling .get, and remember that
Mapping.get returns str | None.
Loading visualization…
Stuck?
Python reference solution
Sign in to attempt this problem and reveal the reference solution.