Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions src/taskgraph/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import functools
from dataclasses import dataclass

from .util.readonlydict import ReadOnlyDict


@dataclass(frozen=True)
class _Graph:
Expand Down Expand Up @@ -81,7 +83,7 @@ def _visit(self, reverse):
dependencies = reverse_links if reverse else forward_links
dependents = forward_links if reverse else reverse_links

indegree = {node: len(dependencies[node]) for node in self.nodes}
indegree = {node: len(dependencies.get(node, ())) for node in self.nodes}

queue = collections.deque(
node for node, degree in indegree.items() if degree == 0
Expand All @@ -91,7 +93,7 @@ def _visit(self, reverse):
node = queue.popleft()
yield node

for dependent in dependents[node]:
for dependent in dependents.get(node, ()):
indegree[dependent] -= 1
if indegree[dependent] == 0:
queue.append(dependent)
Expand Down Expand Up @@ -120,13 +122,18 @@ def links_and_reverse_links_dict(self):
Returns a (forward_links, reverse_links) tuple where forward_links maps
each node to the set of nodes it links to, and reverse_links maps each
node to the set of nodes linking to it.

Because the return value is cached, this function returns a pair of
`ReadOnlyDict` instead of defaultdicts like its `links_dict` and
`reverse_links_dict` counterparts to avoid consumers modifying the
cached value by mistake.
"""
forward = collections.defaultdict(set)
reverse = collections.defaultdict(set)
for left, right, _ in self.edges:
forward[left].add(right)
reverse[right].add(left)
return (forward, reverse)
return (ReadOnlyDict(forward), ReadOnlyDict(reverse))

def links_dict(self):
"""
Expand Down
Loading