| OLD | NEW |
| (Empty) |
| 1 # -*- test-case-name: twisted.web.test.test_web -*- | |
| 2 # | |
| 3 # Copyright (c) 2001-2004 Twisted Matrix Laboratories. | |
| 4 # See LICENSE for details. | |
| 5 | |
| 6 | |
| 7 """I hold the lowest-level Resource class.""" | |
| 8 | |
| 9 | |
| 10 # System Imports | |
| 11 from twisted.internet import defer | |
| 12 from twisted.python import roots, reflect | |
| 13 from zope.interface import Attribute, implements, Interface | |
| 14 | |
| 15 class IResource(Interface): | |
| 16 """A web resource.""" | |
| 17 | |
| 18 isLeaf = Attribute(\ | |
| 19 """Signal if this IResource implementor is a "leaf node" or not. If True, | |
| 20 getChildWithDefault will not be called on this Resource.""") | |
| 21 | |
| 22 def getChildWithDefault(name, request): | |
| 23 """Return a child with the given name for the given request. | |
| 24 This is the external interface used by the Resource publishing | |
| 25 machinery. If implementing IResource without subclassing | |
| 26 Resource, it must be provided. However, if subclassing Resource, | |
| 27 getChild overridden instead. | |
| 28 """ | |
| 29 | |
| 30 def putChild(path, child): | |
| 31 """Put a child IResource implementor at the given path. | |
| 32 """ | |
| 33 | |
| 34 def render(request): | |
| 35 """Render a request. This is called on the leaf resource for | |
| 36 a request. Render must return either a string, which will | |
| 37 be sent to the browser as the HTML for the request, or | |
| 38 server.NOT_DONE_YET. If NOT_DONE_YET is returned, | |
| 39 at some point later (in a Deferred callback, usually) | |
| 40 call request.write("<html>") to write data to the request, | |
| 41 and request.finish() to send the data to the browser. | |
| 42 """ | |
| 43 | |
| 44 | |
| 45 def getChildForRequest(resource, request): | |
| 46 """Traverse resource tree to find who will handle the request.""" | |
| 47 while request.postpath and not resource.isLeaf: | |
| 48 pathElement = request.postpath.pop(0) | |
| 49 request.prepath.append(pathElement) | |
| 50 resource = resource.getChildWithDefault(pathElement, request) | |
| 51 return resource | |
| 52 | |
| 53 | |
| 54 class Resource: | |
| 55 """I define a web-accessible resource. | |
| 56 | |
| 57 I serve 2 main purposes; one is to provide a standard representation for | |
| 58 what HTTP specification calls an 'entity', and the other is to provide an | |
| 59 abstract directory structure for URL retrieval. | |
| 60 """ | |
| 61 | |
| 62 implements(IResource) | |
| 63 | |
| 64 entityType = IResource | |
| 65 | |
| 66 server = None | |
| 67 | |
| 68 def __init__(self): | |
| 69 """Initialize. | |
| 70 """ | |
| 71 self.children = {} | |
| 72 | |
| 73 isLeaf = 0 | |
| 74 | |
| 75 ### Abstract Collection Interface | |
| 76 | |
| 77 def listStaticNames(self): | |
| 78 return self.children.keys() | |
| 79 | |
| 80 def listStaticEntities(self): | |
| 81 return self.children.items() | |
| 82 | |
| 83 def listNames(self): | |
| 84 return self.listStaticNames() + self.listDynamicNames() | |
| 85 | |
| 86 def listEntities(self): | |
| 87 return self.listStaticEntities() + self.listDynamicEntities() | |
| 88 | |
| 89 def listDynamicNames(self): | |
| 90 return [] | |
| 91 | |
| 92 def listDynamicEntities(self, request=None): | |
| 93 return [] | |
| 94 | |
| 95 def getStaticEntity(self, name): | |
| 96 return self.children.get(name) | |
| 97 | |
| 98 def getDynamicEntity(self, name, request): | |
| 99 if not self.children.has_key(name): | |
| 100 return self.getChild(name, request) | |
| 101 else: | |
| 102 return None | |
| 103 | |
| 104 def delEntity(self, name): | |
| 105 del self.children[name] | |
| 106 | |
| 107 def reallyPutEntity(self, name, entity): | |
| 108 self.children[name] = entity | |
| 109 | |
| 110 # Concrete HTTP interface | |
| 111 | |
| 112 def getChild(self, path, request): | |
| 113 """Retrieve a 'child' resource from me. | |
| 114 | |
| 115 Implement this to create dynamic resource generation -- resources which | |
| 116 are always available may be registered with self.putChild(). | |
| 117 | |
| 118 This will not be called if the class-level variable 'isLeaf' is set in | |
| 119 your subclass; instead, the 'postpath' attribute of the request will be | |
| 120 left as a list of the remaining path elements. | |
| 121 | |
| 122 For example, the URL /foo/bar/baz will normally be:: | |
| 123 | |
| 124 | site.resource.getChild('foo').getChild('bar').getChild('baz'). | |
| 125 | |
| 126 However, if the resource returned by 'bar' has isLeaf set to true, then | |
| 127 the getChild call will never be made on it. | |
| 128 | |
| 129 @param path: a string, describing the child | |
| 130 | |
| 131 @param request: a twisted.web.server.Request specifying meta-information | |
| 132 about the request that is being made for this child. | |
| 133 """ | |
| 134 return error.NoResource("No such child resource.") | |
| 135 | |
| 136 def getChildWithDefault(self, path, request): | |
| 137 """Retrieve a static or dynamically generated child resource from me. | |
| 138 | |
| 139 First checks if a resource was added manually by putChild, and then | |
| 140 call getChild to check for dynamic resources. Only override if you want | |
| 141 to affect behaviour of all child lookups, rather than just dynamic | |
| 142 ones. | |
| 143 | |
| 144 This will check to see if I have a pre-registered child resource of the | |
| 145 given name, and call getChild if I do not. | |
| 146 """ | |
| 147 if self.children.has_key(path): | |
| 148 return self.children[path] | |
| 149 | |
| 150 return self.getChild(path, request) | |
| 151 | |
| 152 def getChildForRequest(self, request): | |
| 153 import warnings | |
| 154 warnings.warn("Please use module level getChildForRequest.", Deprecation
Warning, 2) | |
| 155 return getChildForRequest(self, request) | |
| 156 | |
| 157 def putChild(self, path, child): | |
| 158 """Register a static child. | |
| 159 | |
| 160 You almost certainly don't want '/' in your path. If you | |
| 161 intended to have the root of a folder, e.g. /foo/, you want | |
| 162 path to be ''. | |
| 163 """ | |
| 164 self.children[path] = child | |
| 165 child.server = self.server | |
| 166 | |
| 167 def render(self, request): | |
| 168 """Render a given resource. See L{IResource}'s render method. | |
| 169 | |
| 170 I delegate to methods of self with the form 'render_METHOD' | |
| 171 where METHOD is the HTTP that was used to make the | |
| 172 request. Examples: render_GET, render_HEAD, render_POST, and | |
| 173 so on. Generally you should implement those methods instead of | |
| 174 overriding this one. | |
| 175 | |
| 176 render_METHOD methods are expected to return a string which | |
| 177 will be the rendered page, unless the return value is | |
| 178 twisted.web.server.NOT_DONE_YET, in which case it is this | |
| 179 class's responsibility to write the results to | |
| 180 request.write(data), then call request.finish(). | |
| 181 | |
| 182 Old code that overrides render() directly is likewise expected | |
| 183 to return a string or NOT_DONE_YET. | |
| 184 """ | |
| 185 m = getattr(self, 'render_' + request.method, None) | |
| 186 if not m: | |
| 187 from twisted.web.server import UnsupportedMethod | |
| 188 raise UnsupportedMethod(getattr(self, 'allowedMethods', ())) | |
| 189 return m(request) | |
| 190 | |
| 191 def render_HEAD(self, request): | |
| 192 """Default handling of HEAD method. | |
| 193 | |
| 194 I just return self.render_GET(request). When method is HEAD, | |
| 195 the framework will handle this correctly. | |
| 196 """ | |
| 197 return self.render_GET(request) | |
| 198 | |
| 199 | |
| 200 #t.w imports | |
| 201 #This is ugly, I know, but since error.py directly access resource.Resource | |
| 202 #during import-time (it subclasses it), the Resource class must be defined | |
| 203 #by the time error is imported. | |
| 204 import error | |
| OLD | NEW |