| 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 |