We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Modules, Packaging and Tooling step 30 of 41
Expand a Dependency Group into Its Requirements
A dependency group can include other groups. Before an installer resolves any versions, it expands those includes into one flat list of requirement strings. Write that expansion step. It does not pick versions or install anything.
groups = {
"base": ["coverage"],
"check": [{"include-group": "base"}, "pytest", {"include-group": "base"}],
}
resolve_group(groups, "check") # ["coverage", "pytest", "coverage"]
GroupTable = Mapping[str, Sequence[str | Mapping[str, str]]]
def resolve_group(groups: GroupTable, name: str) -> list[str]:
The table is already checked. Group names and references are normalized.
Each string is a valid requirement, and each mapping has exactly one key,
"include-group", with a string value. A referenced group can still be
missing, and includes can still form a cycle. Do not change the input.
Expansion rules
- Read the group's entries in order, and append each requirement string unchanged.
- At an include, expand that group fully before you read the next entry.
- Keep every occurrence, including the same string reached through two includes. Do not normalize, deduplicate or sort the strings, and do not check versions.
- An empty group expands to an empty list.
- Visit only the groups you reach from the requested one. A cycle or a missing group elsewhere in the table does not matter.
- You may include a group again after its expansion has finished. It adds its requirements again.
- Includes can nest thousands of levels down, and a recursive function fails once it passes Python's recursion limit of about 1,000 calls. Keep your own stack instead. The expanded list can also be large when includes repeat.
Errors
Define two exception classes. Each carries an attribute, so a caller can read the details without parsing the message.
UnknownGroupError(KeyError)hasname: str. Raise it when the requested group or an included group is missing, and setnameto the missing one.CyclicGroupError(Exception)haspath: tuple[str, ...]. Raise it when an include names a group whose expansion has not finished. The path starts at that group and ends with it again, and it leaves out the groups before it. Fortop -> a -> b -> a, the path is("a", "b", "a"). A group that includes itself gives("a", "a").
If the table has more than one problem, raise the first error you meet in depth-first, left-to-right order.
The driver
resolve_group raises the exceptions. Complete solve(groups, name) so
that it catches them and returns:
("ok", requirements)on success,("cycle", list(error.path))for a cycle,("unknown", [error.name])for a missing group.
Your code must pass mypy --strict. Check isinstance(entry, str) before
you treat an entry as an include mapping.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Python reference solution
Sign in to track your attempts toward unlocking the reference solution.