I would like to use Twisted non-blocking getPage method within a webapp, but it feels quite complicated to use such function compared to urlopen.
This is an example of what I’m trying to achive:
def web_request(request):
response = urllib.urlopen('http://www.example.org')
return HttpResponse(len(response.read()))
Is it so hard to have something similar with getPage?
The thing to realize about non-blocking operations (which you seem to explicitly want) is that you can’t really write sequential code with them. The operations don’t block because they don’t wait for a result. They start the operation and return control to your function. So,
getPagedoesn’t return a file-like object you can read from likeurllib.urlopendoes. And even if it did, you couldn’t read from it until the data was available (or it would block.) And so you can’t calllen()on it, since that needs to read all the data first (which would block.)The way to deal with non-blocking operations in Twisted is through
Deferreds, which are objects for managing callbacks.getPagereturns aDeferred, which means “you will get this result later”. You can’t do anything with the result until you get it, so you add callbacks to theDeferred, and theDeferredwill call these callbacks when the result is available. That callback can then do what you want it to:An additional problem with your example is that your
web_requestfunction itself is blocking. What do you want to do while you wait for the result ofgetPageto become available? Do something else withinweb_request, or just wait? Or do you want to turnweb_requestitself non-blocking? If so, how do you want to produce the result? (The obvious choice in Twisted is to return anotherDeferred— or even the same one asgetPagereturns, as in the example above. This may not always be appropriate if you’re writing code in another framework, though.)There is a way to write sequential code using
Deferreds, although it’s somewhat restrictive, harder to debug, and core Twisted people cry when you use it:twisted.internet.defer.inlineCallbacks. It uses the new generator feature in Python 2.5 where you can send data into a generator, and the code would look somewhat like this:Like the example that explicitly returned the
dDeferred, this’ll only work if the caller expectsweb_requestto be non-blocking — thedefer.inlineCallbacksdecorator turns the generator into a function that returns aDeferred.