1
# -*- test-case-name: twisted.test.test_roots -*-
2
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
3
# See LICENSE for details.
6
Twisted Python Roots: an abstract hierarchy representation for Twisted.
8
Maintainer: Glyph Lefkowitz
13
from twisted.python import reflect
15
class NotSupportedError(NotImplementedError):
17
An exception meaning that the tree-manipulation operation
18
you're attempting to perform is not supported.
23
"""I am an abstract representation of a request for an entity.
25
I also function as the response. The request is responded to by calling
26
self.write(data) until there is no data left and then calling
29
# This attribute should be set to the string name of the protocol being
30
# responded to (e.g. HTTP or FTP)
32
def write(self, data):
33
"""Add some data to the response to this request.
35
raise NotImplementedError("%s.write" % reflect.qual(self.__class__))
38
"""The response to this request is finished; flush all data to the network stream.
40
raise NotImplementedError("%s.finish" % reflect.qual(self.__class__))
44
"""I am a terminal object in a hierarchy, with no children.
46
I represent a null interface; certain non-instance objects (strings and
47
integers, notably) are Entities.
49
Methods on this class are suggested to be implemented, but are not
50
required, and will be emulated on a per-protocol basis for types which do
53
def render(self, request):
55
I produce a stream of bytes for the request, by calling request.write()
58
raise NotImplementedError("%s.render" % reflect.qual(self.__class__))
62
"""I represent a static collection of entities.
64
I contain methods designed to represent collections that can be dynamically
68
def __init__(self, entities=None):
71
if entities is not None:
72
self.entities = entities
76
def getStaticEntity(self, name):
77
"""Get an entity that was added to me using putEntity.
79
This method will return 'None' if it fails.
81
return self.entities.get(name)
83
def getDynamicEntity(self, name, request):
84
"""Subclass this to generate an entity on demand.
86
This method should return 'None' if it fails.
89
def getEntity(self, name, request):
90
"""Retrieve an entity from me.
92
I will first attempt to retrieve an entity statically; static entities
93
will obscure dynamic ones. If that fails, I will retrieve the entity
96
If I cannot retrieve an entity, I will return 'None'.
98
ent = self.getStaticEntity(name)
101
ent = self.getDynamicEntity(name, request)
106
def putEntity(self, name, entity):
107
"""Store a static reference on 'name' for 'entity'.
109
Raises a KeyError if the operation fails.
111
self.entities[name] = entity
113
def delEntity(self, name):
114
"""Remove a static reference for 'name'.
116
Raises a KeyError if the operation fails.
118
del self.entities[name]
120
def storeEntity(self, name, request):
121
"""Store an entity for 'name', based on the content of 'request'.
123
raise NotSupportedError("%s.storeEntity" % reflect.qual(self.__class__))
125
def removeEntity(self, name, request):
126
"""Remove an entity for 'name', based on the content of 'request'.
128
raise NotSupportedError("%s.removeEntity" % reflect.qual(self.__class__))
130
def listStaticEntities(self):
131
"""Retrieve a list of all name, entity pairs that I store references to.
135
return self.entities.items()
137
def listDynamicEntities(self, request):
138
"""A list of all name, entity that I can generate on demand.
140
See getDynamicEntity.
144
def listEntities(self, request):
145
"""Retrieve a list of all name, entity pairs I contain.
149
return self.listStaticEntities() + self.listDynamicEntities(request)
151
def listStaticNames(self):
152
"""Retrieve a list of the names of entities that I store references to.
156
return self.entities.keys()
159
def listDynamicNames(self):
160
"""Retrieve a list of the names of entities that I store references to.
162
See getDynamicEntity.
167
def listNames(self, request):
168
"""Retrieve a list of all names for entities that I contain.
172
return self.listStaticNames()
175
class ConstraintViolation(Exception):
176
"""An exception raised when a constraint is violated.
180
class Constrained(Collection):
181
"""A collection that has constraints on its names and/or entities."""
183
def nameConstraint(self, name):
184
"""A method that determines whether an entity may be added to me with a given name.
186
If the constraint is satisfied, return 1; if the constraint is not
187
satisfied, either return 0 or raise a descriptive ConstraintViolation.
191
def entityConstraint(self, entity):
192
"""A method that determines whether an entity may be added to me.
194
If the constraint is satisfied, return 1; if the constraint is not
195
satisfied, either return 0 or raise a descriptive ConstraintViolation.
199
def reallyPutEntity(self, name, entity):
200
Collection.putEntity(self, name, entity)
202
def putEntity(self, name, entity):
203
"""Store an entity if it meets both constraints.
205
Otherwise raise a ConstraintViolation.
207
if self.nameConstraint(name):
208
if self.entityConstraint(entity):
209
self.reallyPutEntity(name, entity)
211
raise ConstraintViolation("Entity constraint violated.")
213
raise ConstraintViolation("Name constraint violated.")
216
class Locked(Constrained):
217
"""A collection that can be locked from adding entities."""
224
def entityConstraint(self, entity):
225
return not self.locked
228
class Homogenous(Constrained):
229
"""A homogenous collection of entities.
231
I will only contain entities that are an instance of the class or type
232
specified by my 'entityType' attribute.
235
entityType = types.InstanceType
237
def entityConstraint(self, entity):
238
if isinstance(entity, self.entityType):
241
raise ConstraintViolation("%s of incorrect type (%s)" %
242
(entity, self.entityType))
244
def getNameType(self):
247
def getEntityType(self):
248
return self.entityType.__name__