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 |