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