~certify-web-dev/twisted/certify-trunk

« back to all changes in this revision

Viewing changes to twisted/web/resource.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2007-01-17 14:52:35 UTC
  • mfrom: (1.1.5 upstream) (2.1.2 etch)
  • Revision ID: james.westby@ubuntu.com-20070117145235-btmig6qfmqfen0om
Tags: 2.5.0-0ubuntu1
New upstream version, compatible with python2.5.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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.", DeprecationWarning, 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