# Twisted, the Framework of Your Internet # Copyright (C) 2001 Matthew W. Lefkowitz # # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General Public # License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """I hold the lowest-level Resource class.""" # System Imports from twisted.spread.refpath import PathReferenceAcquisitionContext from twisted.internet import defer from twisted.python import roots, components, reflect from twisted.coil import coil class IResource(components.Interface): """A web resource.""" class Resource(coil.ConfigCollection): """I define a web-accessible resource. I serve 2 main purposes; one is to provide a standard representation for what HTTP specification calls an 'entity', and the other is to provide an abstract directory structure for URL retrieval. """ __implements__ = (IResource, coil.IConfigCollection) entityType = IResource server = None def __init__(self): """Initialize. """ self.children = {} isLeaf = 0 ### Abstract Collection Interface def listStaticNames(self): return self.children.keys() def listStaticEntities(self): return self.children.items() listNames = listStaticNames listEntities = listStaticEntities def listDynamicNames(self): return [] def listDynamicEntities(self, request): return [] def getStaticEntity(self, name): return self.children.get(name) def getDynamicEntity(self, name, request): if not self.children.has_key(name): return self.getChild(name, request) else: return None def delEntity(self, name): del self.children[name] def reallyPutEntity(self, name, entity): self.children[name] = entity # Concrete HTTP interface def getChild(self, path, request): """Retrieve a 'child' resource from me. Implement this to create dynamic resource generation -- resources which are always available may be registered with self.putChild(). This will not be called if the class-level variable 'isLeaf' is set in your subclass; instead, the 'postpath' attribute of the request will be left as a list of the remaining path elements. For example, the URL /foo/bar/baz will normally be:: | site.resource.getChild('foo').getChild('bar').getChild('baz'). However, if the resource returned by 'bar' has isLeaf set to true, then the getChild call will never be made on it. @param path: a string, describing the child @param request: a twisted.web.server.Request specifying meta-information about the request that is being made for this child. """ return error.NoResource("No such child resource.") def getChildWithDefault(self, path, request): """(internal) Retrieve a static or dynamically generated child resource from me. Arguments are similiar to getChild. This will check to see if I have a pre-registered child resource of the given name, and call getChild if I do not. """ if self.children.has_key(path): return self.children[path] return self.getChild(path, request) def getChildForRequest(self, request): """(internal) Get a child of mine dependant on a particular request. This will be called on me as a top-level resource of a site in order to retrieve my appropriate child or grandchild to display. """ res = self while request.postpath and not res.isLeaf: pathElement = request.postpath.pop(0) request.acqpath.append(pathElement) request.prepath.append(pathElement) res = res.getChildWithDefault(pathElement, request) return res def putChild(self, path, child): """Register a child with me. """ self.children[path] = child child.server = self.server def render(self, request): """Render a given resource. This must be implemented in all subclasses of Resource. The return value of this method will be the rendered page, unless the return value is twisted.web.server.NOT_DONE_YET, in which case it is this class's responsibility to write the results to request.write(data), then call request.finish(). """ raise NotImplementedError("%s.render called" % reflect.qual(self.__class__)) #t.w imports #This is ugly, I know, but since error.py directly access resource.Resource #during import-time (it subclasses it), the Resource class must be defined #by the time error is imported. import error