Mercurial > hg > WSGraph
annotate wsgraph/model.py @ 37:f17a6577cc0d
make default type configurable
author | Jeff Hammel <jhammel@mozilla.com> |
---|---|
date | Sat, 15 Dec 2012 23:22:19 -0800 |
parents | 16673636dcb6 |
children | df2a719a9b6e |
rev | line source |
---|---|
28 | 1 |
2 import sys | |
0 | 3 from abc import abstractmethod |
9
0affca1f4dc0
start using deepcopy since lord knows we cant trust users
Jeff Hammel <jhammel@mozilla.com>
parents:
8
diff
changeset
|
4 from copy import deepcopy |
15 | 5 from utils import isiterable |
0 | 6 |
25 | 7 class Graph(object): |
28 | 8 """ |
9 abstract base class for WSGraph model of a graph | |
10 | |
11 WSGraph is interacted with by implementing | |
12 wsgraph.model.Graph object for a desired interface | |
13 """ | |
0 | 14 |
15 @abstractmethod | |
32 | 16 def node(self, name, value=None): |
10
81d68388ec97
some nonsense about how this model is full of holes and how it should be better
Jeff Hammel <jhammel@mozilla.com>
parents:
9
diff
changeset
|
17 """ |
81d68388ec97
some nonsense about how this model is full of holes and how it should be better
Jeff Hammel <jhammel@mozilla.com>
parents:
9
diff
changeset
|
18 get or set a node |
81d68388ec97
some nonsense about how this model is full of holes and how it should be better
Jeff Hammel <jhammel@mozilla.com>
parents:
9
diff
changeset
|
19 |
81d68388ec97
some nonsense about how this model is full of holes and how it should be better
Jeff Hammel <jhammel@mozilla.com>
parents:
9
diff
changeset
|
20 When setting a node, a value of `None` will pop the value from |
81d68388ec97
some nonsense about how this model is full of holes and how it should be better
Jeff Hammel <jhammel@mozilla.com>
parents:
9
diff
changeset
|
21 the nodal values |
81d68388ec97
some nonsense about how this model is full of holes and how it should be better
Jeff Hammel <jhammel@mozilla.com>
parents:
9
diff
changeset
|
22 """ |
11 | 23 # TODO: values should not be **kwargs as you could conceivably want |
24 # to set a node or edge to an empty dict | |
25 # Instead, should be (self, name, values=None) | |
0 | 26 |
27 @abstractmethod | |
28 def nodes(self): | |
29 """returns a list of all nodes""" | |
30 | |
31 @abstractmethod | |
32 | 32 def edge(self, node1, node2, value=None): |
10
81d68388ec97
some nonsense about how this model is full of holes and how it should be better
Jeff Hammel <jhammel@mozilla.com>
parents:
9
diff
changeset
|
33 """ |
81d68388ec97
some nonsense about how this model is full of holes and how it should be better
Jeff Hammel <jhammel@mozilla.com>
parents:
9
diff
changeset
|
34 get or set edge from node1 to node2 |
81d68388ec97
some nonsense about how this model is full of holes and how it should be better
Jeff Hammel <jhammel@mozilla.com>
parents:
9
diff
changeset
|
35 """ |
81d68388ec97
some nonsense about how this model is full of holes and how it should be better
Jeff Hammel <jhammel@mozilla.com>
parents:
9
diff
changeset
|
36 |
81d68388ec97
some nonsense about how this model is full of holes and how it should be better
Jeff Hammel <jhammel@mozilla.com>
parents:
9
diff
changeset
|
37 @abstractmethod |
0 | 38 def edges(self): |
39 """returns a list of all edges""" | |
40 | |
23
24d57daaca21
well, now the request dispatches
Jeff Hammel <jhammel@mozilla.com>
parents:
15
diff
changeset
|
41 def __call__(self): |
24d57daaca21
well, now the request dispatches
Jeff Hammel <jhammel@mozilla.com>
parents:
15
diff
changeset
|
42 """ |
24d57daaca21
well, now the request dispatches
Jeff Hammel <jhammel@mozilla.com>
parents:
15
diff
changeset
|
43 returns JSGN format of graph: |
24d57daaca21
well, now the request dispatches
Jeff Hammel <jhammel@mozilla.com>
parents:
15
diff
changeset
|
44 {'nodes': {'node1': {}, |
24d57daaca21
well, now the request dispatches
Jeff Hammel <jhammel@mozilla.com>
parents:
15
diff
changeset
|
45 'node2': {}, ...}, |
24d57daaca21
well, now the request dispatches
Jeff Hammel <jhammel@mozilla.com>
parents:
15
diff
changeset
|
46 'edges': {'node1': {'node2': {}, |
24d57daaca21
well, now the request dispatches
Jeff Hammel <jhammel@mozilla.com>
parents:
15
diff
changeset
|
47 'node3': {}, ...}, |
24d57daaca21
well, now the request dispatches
Jeff Hammel <jhammel@mozilla.com>
parents:
15
diff
changeset
|
48 'node2': {'node1': {}, ...}} |
24d57daaca21
well, now the request dispatches
Jeff Hammel <jhammel@mozilla.com>
parents:
15
diff
changeset
|
49 } |
24d57daaca21
well, now the request dispatches
Jeff Hammel <jhammel@mozilla.com>
parents:
15
diff
changeset
|
50 """ |
24d57daaca21
well, now the request dispatches
Jeff Hammel <jhammel@mozilla.com>
parents:
15
diff
changeset
|
51 retval = {'nodes': {}, 'edges': {}} |
24d57daaca21
well, now the request dispatches
Jeff Hammel <jhammel@mozilla.com>
parents:
15
diff
changeset
|
52 for node in self.nodes(): |
24d57daaca21
well, now the request dispatches
Jeff Hammel <jhammel@mozilla.com>
parents:
15
diff
changeset
|
53 retval['nodes'][node] = self.node(node) |
24d57daaca21
well, now the request dispatches
Jeff Hammel <jhammel@mozilla.com>
parents:
15
diff
changeset
|
54 for node1, node2 in self.edges(): |
24d57daaca21
well, now the request dispatches
Jeff Hammel <jhammel@mozilla.com>
parents:
15
diff
changeset
|
55 retval['edges'].setdefault(node1, {})[node2] = self.edge(node1, node2) |
24d57daaca21
well, now the request dispatches
Jeff Hammel <jhammel@mozilla.com>
parents:
15
diff
changeset
|
56 return retval |
24d57daaca21
well, now the request dispatches
Jeff Hammel <jhammel@mozilla.com>
parents:
15
diff
changeset
|
57 |
0 | 58 def __getitem__(self, key): |
59 """ | |
60 if key is a basestring, return the node of that name; | |
61 if key is a 2-tuple/list, return the edge of that name | |
62 """ | |
63 | |
15 | 64 if isinstance(key, basestring) or (not isiterable(key)): |
8 | 65 return self.node(key) |
66 else: | |
67 return self.edge(*key) | |
68 | |
31 | 69 def __setitem__(self, key, value): |
70 if isinstance(key, basestring) or (not isiterable(key)): | |
71 self.node(key, value) | |
72 else: | |
73 key1, key2 = key | |
74 self.edge(key1, key2, value) | |
75 | |
8 | 76 def __contains__(self, key): |
77 """ | |
78 if key is ..., returns if that node is in the graph | |
79 if key is a 2-tuple/list, returns if the edge is in the graph | |
80 """ | |
81 # XXX not necessarily the best implementation! | |
15 | 82 if isinstance(key, basestring) or (not isiterable(key)): |
8 | 83 return key in self.nodes() |
84 else: | |
85 return tuple(key) in self.edges() | |
0 | 86 |
15 | 87 |
37
f17a6577cc0d
make default type configurable
Jeff Hammel <jhammel@mozilla.com>
parents:
34
diff
changeset
|
88 class DirectedGraph(Graph): |
f17a6577cc0d
make default type configurable
Jeff Hammel <jhammel@mozilla.com>
parents:
34
diff
changeset
|
89 """mix-in class for directed graphs""" |
f17a6577cc0d
make default type configurable
Jeff Hammel <jhammel@mozilla.com>
parents:
34
diff
changeset
|
90 # TODO: is this possible without super or other black magicks? |
f17a6577cc0d
make default type configurable
Jeff Hammel <jhammel@mozilla.com>
parents:
34
diff
changeset
|
91 |
f17a6577cc0d
make default type configurable
Jeff Hammel <jhammel@mozilla.com>
parents:
34
diff
changeset
|
92 |
25 | 93 class MemoryCache(Graph): |
15 | 94 """volatile in-memory representation of a graph""" |
0 | 95 |
37
f17a6577cc0d
make default type configurable
Jeff Hammel <jhammel@mozilla.com>
parents:
34
diff
changeset
|
96 def __init__(self, node_type=dict): |
0 | 97 self._edges = {} |
98 self._nodes = {} | |
37
f17a6577cc0d
make default type configurable
Jeff Hammel <jhammel@mozilla.com>
parents:
34
diff
changeset
|
99 self.node_type = node_type |
0 | 100 |
32 | 101 def node(self, name, value=None): |
102 if value is not None: | |
0 | 103 # setter |
32 | 104 self._nodes[name] = deepcopy(value) |
0 | 105 else: |
106 # getter | |
9
0affca1f4dc0
start using deepcopy since lord knows we cant trust users
Jeff Hammel <jhammel@mozilla.com>
parents:
8
diff
changeset
|
107 # TODO: deepcopy |
34 | 108 return deepcopy(self._nodes.get(name, None)) |
0 | 109 |
110 def nodes(self): | |
111 return self._nodes.keys() | |
112 | |
32 | 113 def edge(self, node1, node2, value=None): |
114 if value is not None: | |
0 | 115 # setter |
32 | 116 self._edges[(node1, node2)] = deepcopy(value) |
117 for node in node1, node2: | |
37
f17a6577cc0d
make default type configurable
Jeff Hammel <jhammel@mozilla.com>
parents:
34
diff
changeset
|
118 self._nodes.setdefault(node, self.node_type()) |
0 | 119 else: |
120 # getter | |
9
0affca1f4dc0
start using deepcopy since lord knows we cant trust users
Jeff Hammel <jhammel@mozilla.com>
parents:
8
diff
changeset
|
121 # TODO: deepcopy |
34 | 122 return deepcopy(self._edges.get((node1, node2), None)) |
0 | 123 |
124 def edges(self): | |
125 return self._edges.keys() | |
25 | 126 |
37
f17a6577cc0d
make default type configurable
Jeff Hammel <jhammel@mozilla.com>
parents:
34
diff
changeset
|
127 |
25 | 128 class FileCache(MemoryCache): |
129 """on-disk JSON file cache""" | |
130 | |
131 def __init__(self, filename): | |
132 self.filename = filename | |
133 raise NotImplementedError | |
134 | |
135 # TODO: CLI entry point to convert from one model to another | |
28 | 136 # def main(args=sys.argv[1:]): |