summaryrefslogtreecommitdiff
path: root/lib/git/async/graph.py
diff options
context:
space:
mode:
authorSebastian Thiel <byronimo@gmail.com>2010-06-06 12:48:25 +0200
committerSebastian Thiel <byronimo@gmail.com>2010-06-06 12:48:25 +0200
commitec28ad575ce1d7bb6a616ffc404f32bbb1af67b2 (patch)
tree90003f8f93becbb0b8aacd4c2ff7119842fa8003 /lib/git/async/graph.py
parentb72e2704022d889f116e49abf3e1e5d3e3192d3b (diff)
downloadgitpython-ec28ad575ce1d7bb6a616ffc404f32bbb1af67b2.tar.gz
thread: adjusted worker thread not to provide an output queue anymore - this is handled by the task system
graph: implemented it including test according to the pools requirements pool: implemented set_pool_size
Diffstat (limited to 'lib/git/async/graph.py')
-rw-r--r--lib/git/async/graph.py84
1 files changed, 74 insertions, 10 deletions
diff --git a/lib/git/async/graph.py b/lib/git/async/graph.py
index 0c0a2137..b4d6aa00 100644
--- a/lib/git/async/graph.py
+++ b/lib/git/async/graph.py
@@ -6,31 +6,95 @@ class Node(object):
we need"""
__slots__ = ('in_nodes', 'out_nodes')
+ def __init__(self):
+ self.in_nodes = list()
+ self.out_nodes = list()
+
class Graph(object):
"""A simple graph implementation, keeping nodes and providing basic access and
- editing functions"""
+ editing functions. The performance is only suitable for small graphs of not
+ more than 10 nodes !"""
__slots__ = "nodes"
def __init__(self):
self.nodes = list()
def add_node(self, node):
- """Add a new node to the graph"""
- raise NotImplementedError()
+ """Add a new node to the graph
+ :return: the newly added node"""
+ self.nodes.append(node)
+ return node
def del_node(self, node):
- """Delete a node from the graph"""
- raise NotImplementedError()
+ """Delete a node from the graph
+ :return: self"""
+ # clear connections
+ for outn in node.out_nodes:
+ del(outn.in_nodes[outn.in_nodes.index(node)])
+ for inn in node.in_nodes:
+ del(inn.out_nodes[inn.out_nodes.index(node)])
+ del(self.nodes[self.nodes.index(node)])
+ return self
def add_edge(self, u, v):
"""Add an undirected edge between the given nodes u and v.
+
+ return: self
:raise ValueError: If the new edge would create a cycle"""
- raise NotImplementedError()
+ if u is v:
+ raise ValueError("Cannot connect a node with itself")
+
+ # are they already connected ?
+ if u in v.in_nodes and v in u.out_nodes or \
+ v in u.in_nodes and u in v.out_nodes:
+ return self
+ # END handle connection exists
+
+ # cycle check - if we can reach any of the two by following either ones
+ # history, its a cycle
+ for start, end in ((u, v), (v,u)):
+ if not start.in_nodes:
+ continue
+ nodes = start.in_nodes[:]
+ seen = set()
+ # depth first search - its faster
+ while nodes:
+ n = nodes.pop()
+ if n in seen:
+ continue
+ seen.add(n)
+ if n is end:
+ raise ValueError("Connecting u with v would create a cycle")
+ nodes.extend(n.in_nodes)
+ # END while we are searching
+ # END for each direction to look
+
+ # connection is valid, set it up
+ u.out_nodes.append(v)
+ v.in_nodes.append(u)
+
+ return self
- def visit_input_depth_first(self, node, visitor=lambda n: True ):
+ def visit_input_inclusive_depth_first(self, node, visitor=lambda n: True ):
"""Visit all input nodes of the given node, depth first, calling visitor
for each node on our way. If the function returns False, the traversal
- will not go any deeper, but continue at the next branch"""
- raise NotImplementedError()
-
+ will not go any deeper, but continue at the next branch
+ It will return the actual input node in the end !"""
+ nodes = node.in_nodes[:]
+ seen = set()
+
+ # depth first
+ while nodes:
+ n = nodes.pop()
+ if n in seen:
+ continue
+ seen.add(n)
+
+ # only proceed in that direction if visitor is fine with it
+ if visitor(n):
+ nodes.extend(n.in_nodes)
+ # END call visitor
+ # END while walking
+ visitor(node)
+