201) -> Dict[Optional[str], int]:
202 """Assign weights to each node based on how "deep" they are.
203
204 This implementation may change at any point in the future without prior
205 notice.
206
207 We first simplify the dependency graph by pruning any leaves and giving them
208 the highest weight: a package without any dependencies should be installed
209 first. This is done again and again in the same way, giving ever less weight
210 to the newly found leaves. The loop stops when no leaves are left: all
211 remaining packages have at least one dependency left in the graph.
212
213 Then we continue with the remaining graph, by taking the length for the
214 longest path to any node from root, ignoring any paths that contain a single
215 node twice (i.e. cycles). This is done through a depth-first search through
216 the graph, while keeping track of the path to the node.
217
218 Cycles in the graph result would result in node being revisited while also
219 being on its own path. In this case, take no action. This helps ensure we
220 don't get stuck in a cycle.
221
222 When assigning weight, the longer path (i.e. larger length) is preferred.
223
224 We are only interested in the weights of packages that are in the
225 requirement_keys.
226 """
227 path: Set[Optional[str]] = set()
228 weights: Dict[Optional[str], int] = {}
229
230 def visit(node: Optional[str]) ->
None:
231 if node in path:
232
233 return
234
235
240
241 if node not in requirement_keys:
242 return
243
245 weights[node] = max(last_known_parent_count,
len(path))
246
247
248
249
250
251
252 while True:
253 leaves = set()
254 for key in graph:
255 if key is None:
256 continue
258
259 break
260 else:
261
263 if not leaves:
264
265 break
266
267 weight =
len(graph) - 1
268 for leaf in leaves:
269 if leaf not in requirement_keys:
270 continue
271 weights[leaf] = weight
272
273 for leaf in leaves:
275
276
277
279
280
281
283 assert not difference, difference
284
285 return weights
286
287