OLD | NEW |
| (Empty) |
1 # -*- test-case-name: twisted.test.test_roots -*- | |
2 # | |
3 # Copyright (c) 2001-2004 Twisted Matrix Laboratories. | |
4 # See LICENSE for details. | |
5 | |
6 | |
7 """ | |
8 Twisted Python Roots: an abstract hierarchy representation for Twisted. | |
9 | |
10 Maintainer: Glyph Lefkowitz | |
11 | |
12 Future Plans: Removing distinction between 'static' and 'dynamic' entities, | |
13 which never made much sense anyway and bloats the API horribly. This probably | |
14 involves updating all of Coil to have tree widgets that can be dynamically | |
15 expanded, which probably involves porting all of Coil to Woven, so it might not | |
16 happen for a while. | |
17 | |
18 """ | |
19 | |
20 # System imports | |
21 import types | |
22 from twisted.python import reflect | |
23 | |
24 class NotSupportedError(NotImplementedError): | |
25 """ | |
26 An exception meaning that the tree-manipulation operation | |
27 you're attempting to perform is not supported. | |
28 """ | |
29 | |
30 | |
31 class Request: | |
32 """I am an abstract representation of a request for an entity. | |
33 | |
34 I also function as the response. The request is responded to by calling | |
35 self.write(data) until there is no data left and then calling | |
36 self.finish(). | |
37 """ | |
38 # This attribute should be set to the string name of the protocol being | |
39 # responded to (e.g. HTTP or FTP) | |
40 wireProtocol = None | |
41 def write(self, data): | |
42 """Add some data to the response to this request. | |
43 """ | |
44 raise NotImplementedError("%s.write" % reflect.qual(self.__class__)) | |
45 | |
46 def finish(self): | |
47 """The response to this request is finished; flush all data to the netwo
rk stream. | |
48 """ | |
49 raise NotImplementedError("%s.finish" % reflect.qual(self.__class__)) | |
50 | |
51 | |
52 class Entity: | |
53 """I am a terminal object in a hierarchy, with no children. | |
54 | |
55 I represent a null interface; certain non-instance objects (strings and | |
56 integers, notably) are Entities. | |
57 | |
58 Methods on this class are suggested to be implemented, but are not | |
59 required, and will be emulated on a per-protocol basis for types which do | |
60 not handle them. | |
61 """ | |
62 def render(self, request): | |
63 """ | |
64 I produce a stream of bytes for the request, by calling request.write() | |
65 and request.finish(). | |
66 """ | |
67 raise NotImplementedError("%s.render" % reflect.qual(self.__class__)) | |
68 | |
69 | |
70 class Collection: | |
71 """I represent a static collection of entities. | |
72 | |
73 I contain methods designed to represent collections that can be dynamically | |
74 created. | |
75 """ | |
76 | |
77 def __init__(self, entities=None): | |
78 """Initialize me. | |
79 """ | |
80 if entities is not None: | |
81 self.entities = entities | |
82 else: | |
83 self.entities = {} | |
84 | |
85 def getStaticEntity(self, name): | |
86 """Get an entity that was added to me using putEntity. | |
87 | |
88 This method will return 'None' if it fails. | |
89 """ | |
90 return self.entities.get(name) | |
91 | |
92 def getDynamicEntity(self, name, request): | |
93 """Subclass this to generate an entity on demand. | |
94 | |
95 This method should return 'None' if it fails. | |
96 """ | |
97 | |
98 def getEntity(self, name, request): | |
99 """Retrieve an entity from me. | |
100 | |
101 I will first attempt to retrieve an entity statically; static entities | |
102 will obscure dynamic ones. If that fails, I will retrieve the entity | |
103 dynamically. | |
104 | |
105 If I cannot retrieve an entity, I will return 'None'. | |
106 """ | |
107 ent = self.getStaticEntity(name) | |
108 if ent is not None: | |
109 return ent | |
110 ent = self.getDynamicEntity(name, request) | |
111 if ent is not None: | |
112 return ent | |
113 return None | |
114 | |
115 def putEntity(self, name, entity): | |
116 """Store a static reference on 'name' for 'entity'. | |
117 | |
118 Raises a KeyError if the operation fails. | |
119 """ | |
120 self.entities[name] = entity | |
121 | |
122 def delEntity(self, name): | |
123 """Remove a static reference for 'name'. | |
124 | |
125 Raises a KeyError if the operation fails. | |
126 """ | |
127 del self.entities[name] | |
128 | |
129 def storeEntity(self, name, request): | |
130 """Store an entity for 'name', based on the content of 'request'. | |
131 """ | |
132 raise NotSupportedError("%s.storeEntity" % reflect.qual(self.__class__)) | |
133 | |
134 def removeEntity(self, name, request): | |
135 """Remove an entity for 'name', based on the content of 'request'. | |
136 """ | |
137 raise NotSupportedError("%s.removeEntity" % reflect.qual(self.__class__)
) | |
138 | |
139 def listStaticEntities(self): | |
140 """Retrieve a list of all name, entity pairs that I store references to. | |
141 | |
142 See getStaticEntity. | |
143 """ | |
144 return self.entities.items() | |
145 | |
146 def listDynamicEntities(self, request): | |
147 """A list of all name, entity that I can generate on demand. | |
148 | |
149 See getDynamicEntity. | |
150 """ | |
151 return [] | |
152 | |
153 def listEntities(self, request): | |
154 """Retrieve a list of all name, entity pairs I contain. | |
155 | |
156 See getEntity. | |
157 """ | |
158 return self.listStaticEntities() + self.listDynamicEntities(request) | |
159 | |
160 def listStaticNames(self): | |
161 """Retrieve a list of the names of entities that I store references to. | |
162 | |
163 See getStaticEntity. | |
164 """ | |
165 return self.entities.keys() | |
166 | |
167 | |
168 def listDynamicNames(self): | |
169 """Retrieve a list of the names of entities that I store references to. | |
170 | |
171 See getDynamicEntity. | |
172 """ | |
173 return [] | |
174 | |
175 | |
176 def listNames(self, request): | |
177 """Retrieve a list of all names for entities that I contain. | |
178 | |
179 See getEntity. | |
180 """ | |
181 return self.listStaticNames() | |
182 | |
183 | |
184 class ConstraintViolation(Exception): | |
185 """An exception raised when a constraint is violated. | |
186 """ | |
187 | |
188 | |
189 class Constrained(Collection): | |
190 """A collection that has constraints on its names and/or entities.""" | |
191 | |
192 def nameConstraint(self, name): | |
193 """A method that determines whether an entity may be added to me with a
given name. | |
194 | |
195 If the constraint is satisfied, return 1; if the constraint is not | |
196 satisfied, either return 0 or raise a descriptive ConstraintViolation. | |
197 """ | |
198 return 1 | |
199 | |
200 def entityConstraint(self, entity): | |
201 """A method that determines whether an entity may be added to me. | |
202 | |
203 If the constraint is satisfied, return 1; if the constraint is not | |
204 satisfied, either return 0 or raise a descriptive ConstraintViolation. | |
205 """ | |
206 return 1 | |
207 | |
208 def reallyPutEntity(self, name, entity): | |
209 Collection.putEntity(self, name, entity) | |
210 | |
211 def putEntity(self, name, entity): | |
212 """Store an entity if it meets both constraints. | |
213 | |
214 Otherwise raise a ConstraintViolation. | |
215 """ | |
216 if self.nameConstraint(name): | |
217 if self.entityConstraint(entity): | |
218 self.reallyPutEntity(name, entity) | |
219 else: | |
220 raise ConstraintViolation("Entity constraint violated.") | |
221 else: | |
222 raise ConstraintViolation("Name constraint violated.") | |
223 | |
224 | |
225 class Locked(Constrained): | |
226 """A collection that can be locked from adding entities.""" | |
227 | |
228 locked = 0 | |
229 | |
230 def lock(self): | |
231 self.locked = 1 | |
232 | |
233 def entityConstraint(self, entity): | |
234 return not self.locked | |
235 | |
236 | |
237 class Homogenous(Constrained): | |
238 """A homogenous collection of entities. | |
239 | |
240 I will only contain entities that are an instance of the class or type | |
241 specified by my 'entityType' attribute. | |
242 """ | |
243 | |
244 entityType = types.InstanceType | |
245 | |
246 def entityConstraint(self, entity): | |
247 if isinstance(entity, self.entityType): | |
248 return 1 | |
249 else: | |
250 raise ConstraintViolation("%s of incorrect type (%s)" % | |
251 (entity, self.entityType)) | |
252 | |
253 def getNameType(self): | |
254 return "Name" | |
255 | |
256 def getEntityType(self): | |
257 return self.entityType.__name__ | |
OLD | NEW |