Contact: fumanchu@aminus.org

Log in as guest/dejavu to create tickets

I think I've seen this ORM somewhere before...

root/trunk/dejavu/containers.py

Revision 479 (checked in by fumanchu, 6 years ago)

Moved all content down into the new dejavu top-level folder.

  • Property svn:eol-style set to native
Line 
1 """Useful container classes.
2
3 According to Stroustrup:
4 (http://www.research.att.com/~bs/glossary.html#Gcontainer)
5
6 container - (1) object that holds other objects. (2) type of object that
7 holds other objects. (3) template that generates types of objects that
8 hold other objects. (4) standard library template such as vector, list,
9 and map.
10
11 Find herein the containers: warehouse and Graph.
12
13 This work, including the source code, documentation
14 and related data, is placed into the public domain.
15
16 The orginal author is Robert Brewer.
17
18 THIS SOFTWARE IS PROVIDED AS-IS, WITHOUT WARRANTY
19 OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF
20 MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE
21 ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE
22 RESULTING FROM THE USE, MODIFICATION, OR
23 REDISTRIBUTION OF THIS SOFTWARE.
24
25 """
26
27 __all__ = ["Graph", "warehouse"]
28
29
30 def warehouse(stock, factory=None):
31     """warehouse(stock, factory=None) -> iavailable, iremainder.
32     
33     Iterate over stock, extending as needed. Once the 'stock' sequence is
34     exhausted, the factory function is called to produce a new valid object
35     upon each subsequent call to next().
36     
37     If factory is None, the class of the first item in the sequence is used
38     as a constructor. Otherwise, the factory function does not receive any
39     arguments, so if your class has mandatory arguments to __init__,
40     wrap the class in a function which can supply those.
41     
42     The most common use for warehouse is to reuse a set of existing
43     objects, often because object creation and/or destruction is expensive.
44     
45     Example:
46     
47     available, remainder = warehouse(seq)
48     for line in order:
49         line.use(available.next())
50     for item in remainder:
51         item.close()
52     """
53     stock = iter(stock)
54    
55     def pull():
56         for item in stock:
57             yield item
58        
59         if factory is None:
60             try:
61                 local_factory = item.__class__
62             except NameError:
63                 raise ValueError("Empty sequence and no factory supplied.")
64         else:
65             local_factory = factory
66        
67         while True:
68             yield local_factory()
69    
70     return pull(), stock
71
72
73 class Graph(dict):
74     """An unweighted graph. Can be directed or undirected."""
75    
76     def __init__(self, data={}, directed=False):
77         self.update(data)
78         self.directed = directed
79         self._cached_paths = {}
80         self.declared_paths = {}
81    
82     def add(self, node):
83         if node not in self:
84             self[node] = []
85             self._cached_paths = self.declared_paths.copy()
86    
87     def _make_arc(self, node, othernode):
88         bucket = self.setdefault(node, [])
89         if othernode not in bucket:
90             bucket.append(othernode)
91    
92     def connect(self, node, othernodes):
93         """Create an edge between node and each node in othernodes.
94         
95         Examples:
96         Graph().connect('A', ('B', 'C', 'D')) becomes:
97               --B
98              /
99             A---C
100              \
101               --D
102         Graph(directed=True).connect('A', ('B', 'C', 'D')) becomes:
103               ->B
104              /
105             A-->C
106              \
107               ->D
108         """
109         try:
110             othernodes = iter(othernodes)
111         except TypeError:
112             othernodes = (othernodes, )
113         for othernode in othernodes:
114             self._make_arc(node, othernode)
115             if not self.directed:
116                 self._make_arc(othernode, node)
117         self._cached_paths = self.declared_paths.copy()
118    
119     def chain(self, *nodes):
120         """Create an edge between each node in sequence.
121         
122         Examples:
123         Graph().chain('A', 'B', 'C', 'D') becomes:
124             A--B--C--D
125         Graph(directed=True).chain('A', 'B', 'C', 'D') becomes:
126             A-->B-->C-->D
127         """
128         node = nodes[0]
129         for nextnode in nodes[1:]:
130             self._make_arc(node, nextnode)
131             if not self.directed:
132                 self._make_arc(nextnode, node)
133             # Lather, rinse, repeat
134             node = nextnode
135         self._cached_paths = self.declared_paths.copy()
136    
137     def shortest_path(self, start, end):
138         """A list of nodes, or None if start is valid but no path is found."""
139         key = (start, end)
140         try:
141             return self._cached_paths[key][:]
142         except KeyError:
143             shortest = self._shortest_path(start, end)
144             if shortest is not None:
145                 self._cached_paths[key] = shortest
146                 # Return a copy which is separate from our cached copy.
147                 shortest = shortest[:]
148             return shortest
149    
150     def _shortest_path(self, start, end, path=[]):
151         """A list of nodes, or None if start is valid but no path is found."""
152         if path == []:
153             # Test for presence of start in self.
154             # Let the KeyError propagate out.
155             nodes = self[start]
156             path = [start]
157         else:
158             path = path + [start]
159             if start == end:
160                 return path
161             nodes = self[start]
162        
163         shortest = None
164         for node in nodes:
165             if node not in path:
166                 try:
167                     newpath = self._shortest_path(node, end, path)
168                 except KeyError:
169                     pass
170                 else:
171                     if newpath:
172                         if not shortest or len(newpath) < len(shortest):
173                             shortest = newpath
174         return shortest
175    
176     def declare_path(self, path, symmetric=True):
177         start, end = path[0], path[-1]
178         self.declared_paths[(start, end)] = path[:]
179         self._cached_paths[(start, end)] = path[:]
180         if symmetric:
181             revpath = path[:]
182             revpath.reverse()
183             self.declared_paths[(end, start)] = revpath
184             self._cached_paths[(end, start)] = revpath[:]
185
186
Note: See TracBrowser for help on using the browser.