Source code for programslice.graph
"""
Note: The reason for a self implemented graph and edge is not to be
smarter, since there are better modules available for this. It is an
educational project for me.
"""
from collections import OrderedDict
from collections import deque
[docs]class Edge(object):
"""
.. option:: synopsis
Representing the edge of a :class:`graph`.
"""
def __init__(self, name, ln, column):
assert isinstance(ln, int), "line number needs to be an integer"
assert isinstance(name, str), "name needs to be str"
self.name = name
self.lineno = ln
self.column = column
def __key(self):
return (self.lineno, self.name, self.column)
def __eq__(self, other):
return type(self) == type(other) and self.__key() == other.__key()
def __ne__(self, other):
return not self.__eq__(other)
def __hash__(self):
return hash(self.__key())
def __repr__(self):
return ('<{self.__class__.__name__} {self.name} at '
'#{self.lineno}@{self.column}>'.format(self=self))
@classmethod
def create_from_astnode(klass, node):
return Edge(node.id, node.lineno, node.col_offset)
[docs]class Graph(object):
"""
.. option:: synopsis
A graph which represents a function visited by a
programslice.visitor. The edges of this graph are the line numbers
of the parsed function.
:param name: A name identifying this graph (e.g. function X)
:type name: string
"""
def __init__(self, name=''):
self.name = name
self.graph = OrderedDict()
def __repr__(self):
return '<{} {} {}>'.format(
self.__class__.__name__,
self.name,
self.graph.items())
[docs] def add(self, edge):
""" Adds a new edge with the given parameters to the graph.
:rtype: Edge
"""
self.graph.setdefault(edge, [])
return edge
@property
def edges(self):
return self.graph.keys()
def connect(self, e1, e2):
assert isinstance(e1, type(e1)) and isinstance(e2, type(e2))
edges = self.graph.setdefault(e1, [])
if e2 not in edges:
self.graph[e1].append(e2)
self.add(e2)
[docs] def get(self, edge, default=[]):
""" Returns all referenced edges from the given edge an empty
list otherwise.
"""
return self.graph.get(edge, default)
def __len__(self):
return len(self.graph)
def __getitem__(self, key):
return self.graph[key]
def __contains__(self, edge):
for e in self.edges:
if e == edge:
return True
[docs]class Slice(object):
""" A simple search over a graph, starting from an edge.
:param graph: The graph to slice
:type graph: Graph
"""
def __init__(self, graph):
self.graph = graph
def __call__(self, edge):
assert isinstance(edge, Edge)
result = self.slice_forward(edge)
return result
[docs] def slice_forward(self, edge):
"""
A forward slice starting at the given edge. A match is when
``Edge.lineno == lineno`` and ``Edge.name == name``.
:param edge: An edge.
:type edge: Edge
:rtype: list of edges
"""
visited = [edge]
children = deque(self.graph.get(edge))
if not children:
return []
while children:
edge = children.popleft()
if isinstance(edge, Graph) and edge not in visited:
slice = Slice(edge)
visited.extend(slice(edge.first))
elif edge not in visited:
children.extend(deque(self.graph.get(edge)))
visited.append(edge)
return visited