| # -*- test-case-name: twisted.test.test_roots -*- |
| # Copyright (c) 2001-2004 Twisted Matrix Laboratories. |
| # See LICENSE for details. |
| |
| """ |
| Twisted Python Roots: an abstract hierarchy representation for Twisted. |
| |
| Maintainer: Glyph Lefkowitz |
| """ |
| |
| # System imports |
| import types |
| from twisted.python import reflect |
| |
| class NotSupportedError(NotImplementedError): |
| """ |
| An exception meaning that the tree-manipulation operation |
| you're attempting to perform is not supported. |
| """ |
| |
| |
| class Request: |
| """I am an abstract representation of a request for an entity. |
| |
| I also function as the response. The request is responded to by calling |
| self.write(data) until there is no data left and then calling |
| self.finish(). |
| """ |
| # This attribute should be set to the string name of the protocol being |
| # responded to (e.g. HTTP or FTP) |
| wireProtocol = None |
| def write(self, data): |
| """Add some data to the response to this request. |
| """ |
| raise NotImplementedError("%s.write" % reflect.qual(self.__class__)) |
| |
| def finish(self): |
| """The response to this request is finished; flush all data to the network stream. |
| """ |
| raise NotImplementedError("%s.finish" % reflect.qual(self.__class__)) |
| |
| |
| class Entity: |
| """I am a terminal object in a hierarchy, with no children. |
| |
| I represent a null interface; certain non-instance objects (strings and |
| integers, notably) are Entities. |
| |
| Methods on this class are suggested to be implemented, but are not |
| required, and will be emulated on a per-protocol basis for types which do |
| not handle them. |
| """ |
| def render(self, request): |
| """ |
| I produce a stream of bytes for the request, by calling request.write() |
| and request.finish(). |
| """ |
| raise NotImplementedError("%s.render" % reflect.qual(self.__class__)) |
| |
| |
| class Collection: |
| """I represent a static collection of entities. |
| |
| I contain methods designed to represent collections that can be dynamically |
| created. |
| """ |
| |
| def __init__(self, entities=None): |
| """Initialize me. |
| """ |
| if entities is not None: |
| self.entities = entities |
| else: |
| self.entities = {} |
| |
| def getStaticEntity(self, name): |
| """Get an entity that was added to me using putEntity. |
| |
| This method will return 'None' if it fails. |
| """ |
| return self.entities.get(name) |
| |
| def getDynamicEntity(self, name, request): |
| """Subclass this to generate an entity on demand. |
| |
| This method should return 'None' if it fails. |
| """ |
| |
| def getEntity(self, name, request): |
| """Retrieve an entity from me. |
| |
| I will first attempt to retrieve an entity statically; static entities |
| will obscure dynamic ones. If that fails, I will retrieve the entity |
| dynamically. |
| |
| If I cannot retrieve an entity, I will return 'None'. |
| """ |
| ent = self.getStaticEntity(name) |
| if ent is not None: |
| return ent |
| ent = self.getDynamicEntity(name, request) |
| if ent is not None: |
| return ent |
| return None |
| |
| def putEntity(self, name, entity): |
| """Store a static reference on 'name' for 'entity'. |
| |
| Raises a KeyError if the operation fails. |
| """ |
| self.entities[name] = entity |
| |
| def delEntity(self, name): |
| """Remove a static reference for 'name'. |
| |
| Raises a KeyError if the operation fails. |
| """ |
| del self.entities[name] |
| |
| def storeEntity(self, name, request): |
| """Store an entity for 'name', based on the content of 'request'. |
| """ |
| raise NotSupportedError("%s.storeEntity" % reflect.qual(self.__class__)) |
| |
| def removeEntity(self, name, request): |
| """Remove an entity for 'name', based on the content of 'request'. |
| """ |
| raise NotSupportedError("%s.removeEntity" % reflect.qual(self.__class__)) |
| |
| def listStaticEntities(self): |
| """Retrieve a list of all name, entity pairs that I store references to. |
| |
| See getStaticEntity. |
| """ |
| return self.entities.items() |
| |
| def listDynamicEntities(self, request): |
| """A list of all name, entity that I can generate on demand. |
| |
| See getDynamicEntity. |
| """ |
| return [] |
| |
| def listEntities(self, request): |
| """Retrieve a list of all name, entity pairs I contain. |
| |
| See getEntity. |
| """ |
| return self.listStaticEntities() + self.listDynamicEntities(request) |
| |
| def listStaticNames(self): |
| """Retrieve a list of the names of entities that I store references to. |
| |
| See getStaticEntity. |
| """ |
| return self.entities.keys() |
| |
| |
| def listDynamicNames(self): |
| """Retrieve a list of the names of entities that I store references to. |
| |
| See getDynamicEntity. |
| """ |
| return [] |
| |
| |
| def listNames(self, request): |
| """Retrieve a list of all names for entities that I contain. |
| |
| See getEntity. |
| """ |
| return self.listStaticNames() |
| |
| |
| class ConstraintViolation(Exception): |
| """An exception raised when a constraint is violated. |
| """ |
| |
| |
| class Constrained(Collection): |
| """A collection that has constraints on its names and/or entities.""" |
| |
| def nameConstraint(self, name): |
| """A method that determines whether an entity may be added to me with a given name. |
| |
| If the constraint is satisfied, return 1; if the constraint is not |
| satisfied, either return 0 or raise a descriptive ConstraintViolation. |
| """ |
| return 1 |
| |
| def entityConstraint(self, entity): |
| """A method that determines whether an entity may be added to me. |
| |
| If the constraint is satisfied, return 1; if the constraint is not |
| satisfied, either return 0 or raise a descriptive ConstraintViolation. |
| """ |
| return 1 |
| |
| def reallyPutEntity(self, name, entity): |
| Collection.putEntity(self, name, entity) |
| |
| def putEntity(self, name, entity): |
| """Store an entity if it meets both constraints. |
| |
| Otherwise raise a ConstraintViolation. |
| """ |
| if self.nameConstraint(name): |
| if self.entityConstraint(entity): |
| self.reallyPutEntity(name, entity) |
| else: |
| raise ConstraintViolation("Entity constraint violated.") |
| else: |
| raise ConstraintViolation("Name constraint violated.") |
| |
| |
| class Locked(Constrained): |
| """A collection that can be locked from adding entities.""" |
| |
| locked = 0 |
| |
| def lock(self): |
| self.locked = 1 |
| |
| def entityConstraint(self, entity): |
| return not self.locked |
| |
| |
| class Homogenous(Constrained): |
| """A homogenous collection of entities. |
| |
| I will only contain entities that are an instance of the class or type |
| specified by my 'entityType' attribute. |
| """ |
| |
| entityType = types.InstanceType |
| |
| def entityConstraint(self, entity): |
| if isinstance(entity, self.entityType): |
| return 1 |
| else: |
| raise ConstraintViolation("%s of incorrect type (%s)" % |
| (entity, self.entityType)) |
| |
| def getNameType(self): |
| return "Name" |
| |
| def getEntityType(self): |
| return self.entityType.__name__ |