Skip to content

← Modules, Packaging and Tooling step 11 of 41

Hard Framework

List Every Import Cycle Once

In Find One Import Cycle you stopped at the first cycle. A real check reports every cycle, so you can fix them all in one pass. Write find_import_cycles(graph), which lists every cycle in an import graph exactly once.

def find_import_cycles(graph: Mapping[str, Sequence[str]]) -> list[tuple[str, ...]]: ...

graph maps each module name to the modules it imports.

find_import_cycles({"b": ["c"], "c": ["a"], "a": ["b"]})
# [("a", "b", "c")]
find_import_cycles({"a": ["b", "c"], "b": ["c"], "c": ["a", "b"]})
# [("a", "b", "c"), ("a", "c"), ("b", "c")]
find_import_cycles({"a": ["a", "a", "external"]})
# [("a",)]
find_import_cycles({})
# []

What counts as a cycle

  • Only the graph's keys are modules. Skip any import of a name that is not a key, such as "external" above.
  • Treat a target listed twice as one import.
  • Compare names as plain strings. "Models" and "models" are different, and the dots in a name have no special meaning.
  • A cycle follows imports from a module back to itself without visiting any other module twice.
  • Cycles that share a module are separate findings. Do not join them into one longer path that repeats the shared module.
  • A module that imports itself is a cycle of one.

How to write each cycle

  • Write a cycle as a tuple of its modules in import order, without repeating the first module at the end. This differs from the easy problem, which returned a list that starts and ends on the same module.
  • Rotate the tuple so the smallest name, as min() would pick it, comes first. ("b", "c", "a") and ("c", "a", "b") are both written ("a", "b", "c").
  • Do not sort the names inside a cycle. ("a", "c", "b") is a different cycle from ("a", "b", "c"), and you report it only if those imports exist.
  • Return the cycles sorted with normal tuple ordering.

Search the whole graph, including modules that nothing imports. Do not change graph or its lists. Your solution must pass mypy --strict.