123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- """
- altgraph.ObjectGraph - Graph of objects with an identifier
- ==========================================================
- A graph of objects that have a "graphident" attribute.
- graphident is the key for the object in the graph
- """
- from altgraph import GraphError
- from altgraph.Graph import Graph
- from altgraph.GraphUtil import filter_stack
- class ObjectGraph(object):
- """
- A graph of objects that have a "graphident" attribute.
- graphident is the key for the object in the graph
- """
- def __init__(self, graph=None, debug=0):
- if graph is None:
- graph = Graph()
- self.graphident = self
- self.graph = graph
- self.debug = debug
- self.indent = 0
- graph.add_node(self, None)
- def __repr__(self):
- return "<%s>" % (type(self).__name__,)
- def flatten(self, condition=None, start=None):
- """
- Iterate over the subgraph that is entirely reachable by condition
- starting from the given start node or the ObjectGraph root
- """
- if start is None:
- start = self
- start = self.getRawIdent(start)
- return self.graph.iterdata(start=start, condition=condition)
- def nodes(self):
- for ident in self.graph:
- node = self.graph.node_data(ident)
- if node is not None:
- yield self.graph.node_data(ident)
- def get_edges(self, node):
- if node is None:
- node = self
- start = self.getRawIdent(node)
- _, _, outraw, incraw = self.graph.describe_node(start)
- def iter_edges(lst, n):
- seen = set()
- for tpl in (self.graph.describe_edge(e) for e in lst):
- ident = tpl[n]
- if ident not in seen:
- yield self.findNode(ident)
- seen.add(ident)
- return iter_edges(outraw, 3), iter_edges(incraw, 2)
- def edgeData(self, fromNode, toNode):
- if fromNode is None:
- fromNode = self
- start = self.getRawIdent(fromNode)
- stop = self.getRawIdent(toNode)
- edge = self.graph.edge_by_node(start, stop)
- return self.graph.edge_data(edge)
- def updateEdgeData(self, fromNode, toNode, edgeData):
- if fromNode is None:
- fromNode = self
- start = self.getRawIdent(fromNode)
- stop = self.getRawIdent(toNode)
- edge = self.graph.edge_by_node(start, stop)
- self.graph.update_edge_data(edge, edgeData)
- def filterStack(self, filters):
- """
- Filter the ObjectGraph in-place by removing all edges to nodes that
- do not match every filter in the given filter list
- Returns a tuple containing the number of:
- (nodes_visited, nodes_removed, nodes_orphaned)
- """
- visited, removes, orphans = filter_stack(self.graph, self, filters)
- for last_good, tail in orphans:
- self.graph.add_edge(last_good, tail, edge_data="orphan")
- for node in removes:
- self.graph.hide_node(node)
- return len(visited) - 1, len(removes), len(orphans)
- def removeNode(self, node):
- """
- Remove the given node from the graph if it exists
- """
- ident = self.getIdent(node)
- if ident is not None:
- self.graph.hide_node(ident)
- def removeReference(self, fromnode, tonode):
- """
- Remove all edges from fromnode to tonode
- """
- if fromnode is None:
- fromnode = self
- fromident = self.getIdent(fromnode)
- toident = self.getIdent(tonode)
- if fromident is not None and toident is not None:
- while True:
- edge = self.graph.edge_by_node(fromident, toident)
- if edge is None:
- break
- self.graph.hide_edge(edge)
- def getIdent(self, node):
- """
- Get the graph identifier for a node
- """
- ident = self.getRawIdent(node)
- if ident is not None:
- return ident
- node = self.findNode(node)
- if node is None:
- return None
- return node.graphident
- def getRawIdent(self, node):
- """
- Get the identifier for a node object
- """
- if node is self:
- return node
- ident = getattr(node, "graphident", None)
- return ident
- def __contains__(self, node):
- return self.findNode(node) is not None
- def findNode(self, node):
- """
- Find the node on the graph
- """
- ident = self.getRawIdent(node)
- if ident is None:
- ident = node
- try:
- return self.graph.node_data(ident)
- except KeyError:
- return None
- def addNode(self, node):
- """
- Add a node to the graph referenced by the root
- """
- self.msg(4, "addNode", node)
- try:
- self.graph.restore_node(node.graphident)
- except GraphError:
- self.graph.add_node(node.graphident, node)
- def createReference(self, fromnode, tonode, edge_data=None):
- """
- Create a reference from fromnode to tonode
- """
- if fromnode is None:
- fromnode = self
- fromident, toident = self.getIdent(fromnode), self.getIdent(tonode)
- if fromident is None or toident is None:
- return
- self.msg(4, "createReference", fromnode, tonode, edge_data)
- self.graph.add_edge(fromident, toident, edge_data=edge_data)
- def createNode(self, cls, name, *args, **kw):
- """
- Add a node of type cls to the graph if it does not already exist
- by the given name
- """
- m = self.findNode(name)
- if m is None:
- m = cls(name, *args, **kw)
- self.addNode(m)
- return m
- def msg(self, level, s, *args):
- """
- Print a debug message with the given level
- """
- if s and level <= self.debug:
- print("%s%s %s" % (" " * self.indent, s, " ".join(map(repr, args))))
- def msgin(self, level, s, *args):
- """
- Print a debug message and indent
- """
- if level <= self.debug:
- self.msg(level, s, *args)
- self.indent = self.indent + 1
- def msgout(self, level, s, *args):
- """
- Dedent and print a debug message
- """
- if level <= self.debug:
- self.indent = self.indent - 1
- self.msg(level, s, *args)
|