1
# -*- test-case-name: twisted.web.test.test_web -*-
3
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
4
# See LICENSE for details.
7
"""I hold the lowest-level Resource class."""
11
from twisted.internet import defer
12
from twisted.python import roots, reflect
13
from zope.interface import Attribute, implements, Interface
15
class IResource(Interface):
19
"""Signal if this IResource implementor is a "leaf node" or not. If True,
20
getChildWithDefault will not be called on this Resource.""")
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.
30
def putChild(path, child):
31
"""Put a child IResource implementor at the given path.
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.
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)
55
"""I define a web-accessible resource.
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.
64
entityType = IResource
75
### Abstract Collection Interface
77
def listStaticNames(self):
78
return self.children.keys()
80
def listStaticEntities(self):
81
return self.children.items()
84
return self.listStaticNames() + self.listDynamicNames()
86
def listEntities(self):
87
return self.listStaticEntities() + self.listDynamicEntities()
89
def listDynamicNames(self):
92
def listDynamicEntities(self, request=None):
95
def getStaticEntity(self, name):
96
return self.children.get(name)
98
def getDynamicEntity(self, name, request):
99
if not self.children.has_key(name):
100
return self.getChild(name, request)
104
def delEntity(self, name):
105
del self.children[name]
107
def reallyPutEntity(self, name, entity):
108
self.children[name] = entity
110
# Concrete HTTP interface
112
def getChild(self, path, request):
113
"""Retrieve a 'child' resource from me.
115
Implement this to create dynamic resource generation -- resources which
116
are always available may be registered with self.putChild().
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.
122
For example, the URL /foo/bar/baz will normally be::
124
| site.resource.getChild('foo').getChild('bar').getChild('baz').
126
However, if the resource returned by 'bar' has isLeaf set to true, then
127
the getChild call will never be made on it.
129
@param path: a string, describing the child
131
@param request: a twisted.web.server.Request specifying meta-information
132
about the request that is being made for this child.
134
return error.NoResource("No such child resource.")
136
def getChildWithDefault(self, path, request):
137
"""Retrieve a static or dynamically generated child resource from me.
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
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.
147
if self.children.has_key(path):
148
return self.children[path]
150
return self.getChild(path, request)
152
def getChildForRequest(self, request):
154
warnings.warn("Please use module level getChildForRequest.", DeprecationWarning, 2)
155
return getChildForRequest(self, request)
157
def putChild(self, path, child):
158
"""Register a static child.
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
164
self.children[path] = child
165
child.server = self.server
167
def render(self, request):
168
"""Render a given resource. See L{IResource}'s render method.
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
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().
182
Old code that overrides render() directly is likewise expected
183
to return a string or NOT_DONE_YET.
185
m = getattr(self, 'render_' + request.method, None)
187
from twisted.web.server import UnsupportedMethod
188
raise UnsupportedMethod(getattr(self, 'allowedMethods', ()))
191
def render_HEAD(self, request):
192
"""Default handling of HEAD method.
194
I just return self.render_GET(request). When method is HEAD,
195
the framework will handle this correctly.
197
return self.render_GET(request)
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.