[Twisted-Python] Dynamic web application using twisted.web.resource
![](https://secure.gravatar.com/avatar/c38527aaf80d384eb75b75005d02d960.jpg?s=120&d=mm&r=g)
Hello fellow twisters, This is a question (albeit a long one) directed at twisted.web and resource. The project: I would like to serve a dynamic website using the twisted web server. I understand that the webserver's 'root' directory can be set at a file? I may have a misunderstanding of the code and docs but if so, feel free to set me straight. The website (not real): http://www.mywebsite.com/ This is the root of the site, which is in reality a erm..rpy? program? Which means that there are _no_ static pages at all. And no other 'file' - everything goes through that .rpy file. (or whatever else I should be using) In a browser, entering this would get a dynamically 'constructed' page which is in fact: myobject.index() From here on, something like: http://www.mywebsite.com/projects/project_ako would actually be (in the code) something like: myobject.projects["project_ako"].index(myvar=None) which returns an html page suitable for the browser's consumption. On the other hand this: http://www.mywebsite.com/projects/project_ako?myvar=bold (for http GET. POST should be handled as well...how? I am guessing the request object?) Would call: myobject.projects["project_ako"].index(myvar='bold') Any other subobject of project_ako would 'call' a method on that object. So for: http://www.mywebsite.com/projects/project_ako/getSize Would call: myobject.projects["project_ako"].getSize() The return value being sent back to the browser. Calls to non-existing methods get a "No such method or object" error returned - formatted, of course. The question is of course, how do you differentiate between an object and a method? This is done by the application itself. It would check to see if that object has a method with that name, if not check to see if it has a child object with that name. Else return error. The thing is, I know I need to use twisted.web.resource but I'm not sure _how_. How does this fit in with the .rpy file? I think I need to create my modules and classes and then create an .rpy which acts as the erm..'interface' to the modules and classes. Could someone give me some hints and tips? Conceptually, it's quite simple but I can't seem to figure out how to do it with twisted...Anyway, thanks for any help, in advance. -- Regards, Mukhsein Johari
![](https://secure.gravatar.com/avatar/433365de0f787faa3ed3e6dd1da5884f.jpg?s=120&d=mm&r=g)
Mukhsein Johari wrote:
The thing is, I know I need to use twisted.web.resource but I'm not sure _how_. How does this fit in with the .rpy file? I think I need to create my modules and classes and then create an .rpy which acts as the erm..'interface' to the modules and classes. Could someone give me some hints and tips?
The model you're describing is just what Zope's ZPublisher and Quixote do. I would suggest using Zope, but if you feel it's too heavy then Quixote will likely be a perfect fit. Another alternative, if you intend to use Twisted's other capabilities and thus want to use twisted.web, is something like this: class PublisherResource(resource.Resource): isLeaf = 1 def __init__(self, root): self.root = root def render(self, request): obj = self.root for p in request.postpath: # convert "/foo/bar" to self.root.foo.bar obj = getattr(obj, p) if not callable(obj): return "arg" else: return obj(request) Of course, this is probably insecure, buggy, and may not even work, but that's the general idea of how to do it.
![](https://secure.gravatar.com/avatar/74bde9dd414c74dc244c6a448e5decc6.jpg?s=120&d=mm&r=g)
On Tuesday, July 30, 2002, at 09:19 AM, Mukhsein Johari wrote:
The project: I would like to serve a dynamic website using the twisted web server. I understand that the webserver's 'root' directory can be set at a file? I may have a misunderstanding of the code and docs but if so, feel free to set me straight.
When you create a tap file using "bin/mktap web --path /foo/bar/baz", mktap creates a File instance, which is a Resource subclass, which knows to look in "/foo/bar/baz" as the root directory of the site. However, mktap is just a "helper" script that instanciates, configures, and persists various common application types. If you want the root object of your web site to be a custom Resource subclass that only serves dynamic pages, you're free to do that.
The website (not real): http://www.mywebsite.com/
This is the root of the site, which is in reality a erm..rpy? program? Which means that there are _no_ static pages at all. And no other 'file' - everything goes through that .rpy file. (or whatever else I should be using)
So, as I just said, what you should do is create your own Application instance, create an instance of twisted.web.server.Site, and create your own custom Resource subclass to act as the root of that site. Your Resource subclass should override getChild(name, request) and render(request), as I will describe below. Here's how you would go about starting your custom server: # MyCoolWebServer.py from twisted.web import server from twisted.internet import reactor import myGreatResourceSubclass root = myGreatResourceSubclass.MyRoot() site = server.Site(root) reactor.listenTCP(8080, site) reactor.run()
In a browser, entering this would get a dynamically 'constructed' page which is in fact:
myobject.index()
In the example above, visiting the root page would end up invoking root.render(request).
From here on, something like: http://www.mywebsite.com/projects/project_ako
would actually be (in the code) something like:
myobject.projects["project_ako"].index(myvar=None)
If these semantics are what you *really* want (i.e. transparency, getitem, or getattr being used for traversal), I suggest looking at Zope or Quixote, as Itamar suggested. However, here's how the following would look in twisted: root.getChild("projects", request).getChild("project_ako", request).render(request) In your root Resource subclass, you can define getChild to do whatever you wish such as dynamically generate a new Resource subclass to handle the "projects" portion, which in turn has a getChild that knows how to create an object to handle the "project_ako" portion, which in turn has a render() method that returns a string, which will be sent to the browser.
which returns an html page suitable for the browser's consumption. On the other hand this:
http://www.mywebsite.com/projects/project_ako?myvar=bold
(for http GET. POST should be handled as well...how? I am guessing the request object?)
Would call: myobject.projects["project_ako"].index(myvar='bold')
It would be exactly the same call as above, except request.args would be a dictionary that looked like this: {'myvar': ['bold']} Note that every value in the args dictionary is always a list, to make handling lists and non lists more uniform.
Any other subobject of project_ako would 'call' a method on that object.
So for: http://www.mywebsite.com/projects/project_ako/getSize
Would call: myobject.projects["project_ako"].getSize()
The return value being sent back to the browser. Calls to non-existing methods get a "No such method or object" error returned - formatted, of course.
If you want automatic getChild traversal to stop at the object representing "project_ako", set isLeaf = 1 on the object representing "project_ako". This will cause render(request) to be called on "project_ako" with the remaining elements of the path in request.postpath. You can then implement the semantics described above in your render() method. However, I find it much easier to divide functionality into separate classes and always use render() to do the html generation.
The question is of course, how do you differentiate between an object and a method? This is done by the application itself. It would check to see if that object has a method with that name, if not check to see if it has a child object with that name. Else return error.
If this is *really* the functionality that you want, again, this is far more similar to the traversal semantics of Zope or Quixote. Twisted has taken a far more explicit tack, where there is a specific api that your objects must implement to provide object-publishing functionality.
The thing is, I know I need to use twisted.web.resource but I'm not sure _how_. How does this fit in with the .rpy file? I think I need to create my modules and classes and then create an .rpy which acts as the erm..'interface' to the modules and classes. Could someone give me some hints and tips?
Conceptually, it's quite simple but I can't seem to figure out how to do it with twisted...Anyway, thanks for any help, in advance.
Hope this helps! Donovan
![](https://secure.gravatar.com/avatar/c38527aaf80d384eb75b75005d02d960.jpg?s=120&d=mm&r=g)
If this is *really* the functionality that you want, again, this is far more similar to the traversal semantics of Zope or Quixote. Twisted has taken a far more explicit tack, where there is a specific api that your objects must implement to provide object-publishing functionality.
Well, to tell the truth, I've decided that I don't really want this Zope-like functionality after all. What I do want to understand, though is how stuff works. The docs are too sparse and I cannot understand how things all work together. Specifically, I'm interested to know how to build a custom webserver using twisted that does not serve any files at all. And does not publish objects like zope. The build your own server with twisted doc is not enough somehow. It'd be nice if a tutorial used http as an example. How do you use all those classes and modules? (twisted.web.server, twisted.internet.reactor)? The example Donovan gave (for my earlier post) did not give me much insight into how to use these modules and classes. I just know it's some really easy answer but...I'm still lost. How does twisted implement http? wrt the Basic/Simple/CGIHTTPServer modules in the python disrto. I remember there was some docs about this but I cannot remember where! It's not in the Twisted distro (that I could find anyway). -- Regards, Mukhsein Johari
![](https://secure.gravatar.com/avatar/b3407ff6ccd34c6e7c7a9fdcfba67a45.jpg?s=120&d=mm&r=g)
On Fri, Aug 02, 2002 at 05:42:22PM +0800, Mukhsein Johari wrote: [..snip..]
How does twisted implement http? wrt the Basic/Simple/CGIHTTPServer modules in the python disrto. I remember there was some docs about this but I cannot remember where! It's not in the Twisted distro (that I could find anyway).
Well, it implements HTTP in twisted.protocols.http, rather than using the synchronous implementations in the python standard library. But, for Twisted at least, the protocol is still a considerable step short of being a server that knows how to do more than just parse requests. So while the protocol is implemented in twisted.protocols.http, the actual logic that knows how to read files, run CGIs, and whatnot, resides in twisted.web. Hopefully that gives you some idea of where to look to find what you need. Unfortunately, I don't understand the Twisted Web stuff in any further detail, and I also would quite like some nice lengthy docs to explain it to me in small digestible pieces :) -Andrew.
![](https://secure.gravatar.com/avatar/0b90087ed4aef703541f1cafdb4b49a1.jpg?s=120&d=mm&r=g)
On Fri, Aug 02, 2002 at 05:42:22PM +0800, Mukhsein Johari wrote:
understand how things all work together. Specifically, I'm interested to know how to build a custom webserver using twisted that does not serve any files at all. And does not publish objects like zope. The build your own server with twisted doc is not enough somehow. It'd be nice if a tutorial used http as an example. How do you use all those classes and modules? (twisted.web.server, twisted.internet.reactor)? The example Donovan gave (for my earlier
Well, one real-world example, totaling ~1600 lines of webby stuff, is ldaptor. There's an app in there called ldaptor-webui, that acts as an adaptor between WWW and LDAP. It uses twisted.web.widgets. You'll need an LDAP server to play -- apt-get install slapd. The latest release is at http://twistedmatrix.com/users/tv/ldaptor/ -- tv@{{hq.yok.utu,havoc,gaeshido}.fi,{debian,wanderer}.org,stonesoft.com} double a,b=4,c;main(){for(;++a<2e6;c-=(b=-b)/a++);printf("%f\n",c);}
participants (5)
-
Andrew Bennetts
-
Donovan Preston
-
Itamar Shtull-Trauring
-
Mukhsein Johari
-
Tommi Virtanen