2010-04-27 2 views
5

Ich würde gerne Twisted non-blocking getPage Methode innerhalb einer Webapp verwenden, aber es fühlt sich ziemlich kompliziert an, eine solche Funktion im Vergleich zu urlopen zu verwenden.Verwenden Sie Twisted's getPage als URLopen?

Dies ist ein Beispiel dessen, was ich bin versucht zu achive:

def web_request(request): 
    response = urllib.urlopen('http://www.example.org') 
    return HttpResponse(len(response.read())) 

Ist es so schwer etwas ähnliches mit getPage haben?

Antwort

20

Die Sache, die man über nicht blockierende Operationen (die man offensichtlich will) zu realisieren, ist, dass man mit ihnen keinen sequentiellen Code schreiben kann. Die Operationen blockieren nicht, weil sie nicht auf ein Ergebnis warten. Sie starten den Vorgang und geben die Steuerung an Ihre Funktion zurück. Also, getPage gibt kein dateiähnliches Objekt zurück, von dem Sie lesen können, wie zum Beispiel urllib.urlopen. Und selbst wenn, dann könnten Sie nicht lesen, bis die Daten verfügbar waren (oder es würde blockieren.) Und so können Sie nicht len() darauf aufrufen, da das zuerst alle Daten lesen muss (was blockieren würde.)

Der Weg, mit nicht blockierenden Operationen in Twisted umzugehen ist durch Deferreds, die Objekte für die Verwaltung von Rückrufen sind. getPage gibt Deferred zurück, was bedeutet, dass Sie dieses Ergebnis später erhalten werden. Sie können nichts mit dem Ergebnis tun, bis Sie es erhalten, so dass Sie Rückrufe an die Deferred hinzufügen, und die Deferred wird diese Rückrufe aufrufen, wenn das Ergebnis verfügbar ist. Das Rückruf kann dann tun, was Sie wollen:

def web_request(request) 
    def callback(data): 
     HttpResponse(len(data)) 
    d = getPage("http://www.example.org") 
    d.addCallback(callback) 
    return d 

Ein zusätzliches Problem bei Ihrem Beispiel ist, dass Ihre web_request Funktion selbst blockiert. Was möchten Sie tun, während Sie darauf warten, dass das Ergebnis getPage verfügbar wird? Tun Sie etwas anderes innerhalb web_request, oder warten Sie einfach? Oder möchten Sie web_request selbst nicht blockieren? Wenn ja, wie wollen Sie das Ergebnis produzieren? (Die offensichtliche Wahl in Twisted ist, eine andere Deferred - oder sogar die gleiche wie getPage zurückgibt, wie im obigen Beispiel. Dies ist jedoch nicht immer angemessen, wenn Sie Code in einem anderen Framework schreiben.)

Dort ist eine Möglichkeit, sequenziellen Code mit Deferreds schreiben, obwohl es etwas restriktiv ist, schwieriger zu debuggen, und Kern Twisted Menschen weinen, wenn Sie es verwenden: twisted.internet.defer.inlineCallbacks. Es verwendet die neue Generator-Funktion in Python 2.5, wo Sie Daten in einen Generator senden, und der Code aussehen würde etwas wie folgt aus:

@defer.inlineCallbacks 
def web_request(request) 
    data = yield getPage("http://www.example.org") 
    HttpResponse(len(data)) 

Wie das Beispiel, das explizit die d Latente zurückgegeben, dies würde nur funktionieren, wenn Der Aufrufer erwartet, dass web_request nicht blockierend ist - der defer.inlineCallbacks Dekorator verwandelt den Generator in eine Funktion, die Deferred zurückgibt.

+0

@Thomas: danke für die tolle Antwort !! : D Leider gibt das erste Beispiel verzögertes Objekt anstelle des HttpResponse-Objekts zurück !! – RadiantHex

+0

Ihr Code hat das HttpResponse-Objekt auch nicht zurückgegeben, aber ja, es kommt darauf an, was ich über den Anrufer erklärt habe, der erwartet, dass web_request blockiert wird. Sie können nicht etwas von Blockieren in Nicht-Blockieren ändern, ohne zu ändern, wie es heißt. –

+0

@Thomas so kann diese einfache Sache nicht getan werden? :( – RadiantHex

4

I posted a response zu einem similar question vor kurzem, das die minimale Menge an Code zur Verfügung stellt, um den Inhalt von einer URL unter Verwendung getPage zu erhalten.Hier ist es der Vollständigkeit halber:

from twisted.web.client import getPage 
from twisted.internet import reactor 

url = 'http://aol.com' 

def print_and_stop(output): 
    print output 
    if reactor.running: 
     reactor.stop() 

if __name__ == '__main__': 
    print 'fetching', url 
    d = getPage(url) 
    d.addCallback(print_and_stop) 
    reactor.run() 

Beachten Sie, dass Sie wahrscheinlich eine tiefer gehende Verständnis der reactor pattern verwendet von Twisted müssen Ereignisse behandeln (getPage Brennen ein Ereignis in diesem Fall ist).

Verwandte Themen