Come up with the ideal processing order within an SCC. Using the priorities assigned by all_imported_modules_in_file(), try to reduce the cycle to a DAG, by omitting arcs representing dependencies of lower priority. In the simplest case, if we have A <--> B where A has a top-level
(graph: Graph, ascc: AbstractSet[str], pri_max: int = PRI_INDIRECT)
| 4643 | |
| 4644 | |
| 4645 | def order_ascc(graph: Graph, ascc: AbstractSet[str], pri_max: int = PRI_INDIRECT) -> list[str]: |
| 4646 | """Come up with the ideal processing order within an SCC. |
| 4647 | |
| 4648 | Using the priorities assigned by all_imported_modules_in_file(), |
| 4649 | try to reduce the cycle to a DAG, by omitting arcs representing |
| 4650 | dependencies of lower priority. |
| 4651 | |
| 4652 | In the simplest case, if we have A <--> B where A has a top-level |
| 4653 | "import B" (medium priority) but B only has the reverse "import A" |
| 4654 | inside a function (low priority), we turn the cycle into a DAG by |
| 4655 | dropping the B --> A arc, which leaves only A --> B. |
| 4656 | |
| 4657 | If all arcs have the same priority, we fall back to sorting by |
| 4658 | reverse global order (the order in which modules were first |
| 4659 | encountered). |
| 4660 | |
| 4661 | The algorithm is recursive, as follows: when as arcs of different |
| 4662 | priorities are present, drop all arcs of the lowest priority, |
| 4663 | identify SCCs in the resulting graph, and apply the algorithm to |
| 4664 | each SCC thus found. The recursion is bounded because at each |
| 4665 | recursion the spread in priorities is (at least) one less. |
| 4666 | |
| 4667 | In practice there are only a few priority levels (less than a |
| 4668 | dozen) and in the worst case we just carry out the same algorithm |
| 4669 | for finding SCCs N times. Thus, the complexity is no worse than |
| 4670 | the complexity of the original SCC-finding algorithm -- see |
| 4671 | strongly_connected_components() below for a reference. |
| 4672 | """ |
| 4673 | if len(ascc) == 1: |
| 4674 | return list(ascc) |
| 4675 | pri_spread = set() |
| 4676 | for id in ascc: |
| 4677 | state = graph[id] |
| 4678 | for dep in state.dependencies: |
| 4679 | if dep in ascc: |
| 4680 | pri = state.priorities.get(dep, PRI_HIGH) |
| 4681 | if pri < pri_max: |
| 4682 | pri_spread.add(pri) |
| 4683 | if len(pri_spread) == 1: |
| 4684 | # Filtered dependencies are uniform -- order by global order. |
| 4685 | return sorted(ascc, key=lambda id: -graph[id].order) |
| 4686 | pri_max = max(pri_spread) |
| 4687 | sccs = sorted_components_inner(graph, ascc, pri_max) |
| 4688 | # The recursion is bounded by the len(pri_spread) check above. |
| 4689 | return [s for ss in sccs for s in order_ascc(graph, ss, pri_max)] |
| 4690 | |
| 4691 | |
| 4692 | def process_fresh_modules(graph: Graph, modules: list[str], manager: BuildManager) -> None: |
searching dependent graphs…