0%

requests und httpx, jetzt, wo du Bescheid weißt · Übung

httpx, dieselben Ideen

httpx ist eine zweite -Client-Bibliothek für Python, neuer als requests und auf denselben Ideen aufgebaut. Ein httpx.Client ist ihre Session: Er hält in einem Cookie-Speicher, Standard-Header und einen Pool offener Verbindungen, und du schließt ihn mit with. Auch seine Aufrufe sehen vertraut aus, client.get(url), client.post(url, auth=...), client.request("GET", url), und das selbst hat httpx.get und httpx.post, so wie requests requests.get hat. Die Zeile, die du in Lektion 2 geschrieben hast, liest sich hier also genauso:

client = httpx if client is None else client
response = client.get(url, timeout=timeout)

Die Unterschiede liegen in den Standardwerten, und jeder davon ist eine Entscheidung, die du in diesem Kurs schon von Hand getroffen hast:

requestshttpx
gefolgt, außer bei allow_redirects=Falsenicht gefolgt, außer bei follow_redirects=True
Timeoutkeiner, außer du übergibst einen5 Sekunden für jede Phase
raise_for_status()löst bei 4xx und 5xx auslöst bei jedem Status außerhalb von 2xx aus, auch bei 3xx
auth= mit Textals Latin-1 codiertals codiert
seine Unterklassen von OSErrorkeine Unterklassen von OSError

Hier hast du jede davon getroffen. Dein follow ist nur gefolgt, weil du es aufgerufen hast; das bloße http.client hat das nie getan. Deine Timeouts waren immer deine eigenen Zahlen, bei jeder Verbindung gesetzt. classify hat eine 3xx in ihre eigene Klasse gesteckt, redirection, getrennt von Client- und Serverfehlern. basic_header hat UTF-8 codiert. Und dein Code aus Kapitel 3 hat TimeoutError abgefangen, das ein OSError ist, so wie die Ausnahmen von requests. httpx hat sich bei jedem Punkt einfach anders entschieden. Code, der von einer Bibliothek in die andere wandert, musst du also Zeile für Zeile lesen, statt darauf zu vertrauen, dass er sich gleich verhält.

Nur folgen, wenn danach gefragt wird

Ein httpx.Client mit Standardwerten folgt keinen Weiterleitungen. Eine 308 kommt als die Antwort selbst zurück, mit leerer response.history und mit response.next_request auf die Anfrage gesetzt, die httpx als Nächstes senden würde. Übergib follow_redirects=True, und httpx folgt, nach denselben Regeln für die wie requests, einschließlich der Browser-Regel für 302 aus Lektion 2.

Die beiden Bibliotheken sind sich sogar beim Namen des uneins. Das allow_redirects= von requests an httpx zu übergeben ist ein Fehler, und die Meldung zeigt auf das richtige Wort:

Client.get() got an unexpected keyword argument 'allow_redirects'. Did you mean 'follow_redirects'?

requests beantwortet den umgekehrten Fehler, follow_redirects=, mit dem Spiegelbild dieser Meldung.

client.get(url) mit einem httpx.Client mit Standardwerten trifft auf eine 308. Was bekommst du?

Ein Standard-Timeout ist trotzdem eine Entscheidung

httpx begrenzt jeden Aufruf: 5 Sekunden für den Verbindungsaufbau, 5 für jeden Lesevorgang, 5 für jeden Schreibvorgang und 5 für das Warten auf eine freie Verbindung im Pool. Das ist sicherer als der Standard von requests, ewig zu warten, aber es bleibt eine Zahl, die du nicht gewählt hast. Dieser Kurs wählt seine eigene, HTTPX_TIMEOUT oben in side_by_side.py:

HTTPX_TIMEOUT = httpx.Timeout(2.0, connect=1.0)

Das ist 1 Sekunde für den Verbindungsaufbau und 2 Sekunden für jede der drei anderen Phasen. Warum nicht das Paar von requests, (1.0, 2.0)? httpx akzeptiert ein , liest es aber nur als Verbindungsaufbau und Lesen und lässt Schreiben und das Warten auf den Pool ganz ohne Grenze.

client.get(url, timeout=(1.0, 2.0)) in httpx. Was ist begrenzt?

Fehler

Das raise_for_status() von httpx ist strenger als das von requests. Es löst httpx.HTTPStatusError für alles außerhalb von 200 bis 299 aus, also löst auch eine 308 aus, der nicht gefolgt wurde, mit einer Meldung, die mit Redirect response '308 Permanent Redirect' beginnt. requests lässt eine 3xx durch.

Welcher Aufruf löst bei einer 308-Antwort aus?

Die Ausnahmen selbst unterscheiden sich in ihrer Familie. Jede Ausnahme von requests ist eine Unterklasse von OSError, also fängt except OSError: um einen requests-Aufruf dessen Timeouts und Verbindungsfehler ab. Die von httpx sind das nicht: httpx.ReadTimeout ist eine TimeoutException, das ist ein TransportError, dann ein RequestError, dann ein httpx.HTTPError, dann eine gewöhnliche Exception. Fange die engste Klasse ab, die zu dem passt, was du behandeln willst:

try:
    response = client.get(url, timeout=HTTPX_TIMEOUT)
except httpx.TimeoutException:
    print("no answer in time")

except httpx.HTTPError: ist breiter: Es fängt einen Timeout, eine abgelehnte Verbindung und den HTTPStatusError aus raise_for_status() gleichermaßen ab.

Ein httpx-Aufruf steht in einem try mit except OSError:. Der Server antwortet nie. Was passiert?

httpx hat außerdem einen AsyncClient für Programme, die mit async und await geschrieben sind. Dieser Kurs benutzt ihn nicht.

Deine Aufgabe

In side_by_side.py:

  1. who_am_i_httpx(base_url, user, password, *, client, timeout=HTTPX_TIMEOUT) tut, was who_am_i tut, auf dem httpx-Client, den es bekommt: POST base_url + "/login" mit auth=(user, password), raise_for_status(), GET base_url + "/me", und gib user zurück, wobei beide Aufrufe timeout=timeout bekommen. Zugangsdaten als einfacher Text sind hier in Ordnung, weil httpx sie als UTF-8 codiert.

  2. trail_httpx(url, *, client=None, timeout=HTTPX_TIMEOUT) sendet ein GET mit follow_redirects=True und timeout=timeout und gibt redirect_trail(response) zurück. Benutze client = httpx if client is None else client. Dein redirect_trail funktioniert unverändert mit einer httpx-Antwort, weil auch sie status_code, url und history hat.

Führe dann aus:

python pl_http.py httpx

Zuerst zeigt es, was ein Client mit Standardwerten mit dem alten Pfad macht, bevor dein Code läuft:

--- pl_http's own httpx client.get() of /v0/books/2?x=1, without follow_redirects ---
status_code: 308
history: []
next_request: 'http://books.test/books/2?x=1'

Dann folgt dein trail_httpx ihm, und seine Zeilen stimmen genau mit der requests-Spur aus Lektion 2 überein:

--- your trail_httpx() of /v0/books/2?x=1 ---
value: ['308 http://books.test/v0/books/2?x=1', '200 http://books.test/books/2?x=1']

Die sagen immer noch books.test, weil der Client von pl_http jede Anfrage über einen eigenen Transport an deinen Server auf diesem Rechner weitergibt, ein Teil, den Lektion 4 durch eine ersetzt, die du schreibst. Der Lauf endet mit who_am_i_httpx als zoë, dessen ✓ diesmal nichts Zusätzliches braucht.

Klicke auf Check my work. Lektion 4 testet beide Bibliotheken ganz ohne Server.

Aufgabe

Melde dich mit httpx an und folge einer .

In side_by_side.py:

  1. who_am_i_httpx(base_url, user, password, *, client, timeout=HTTPX_TIMEOUT) sendet auf dem Client, den es bekommt, POST base_url + "/login" mit auth=(user, password), ruft raise_for_status() auf dieser Antwort auf, sendet GET base_url + "/me" und gibt user aus dessen zurück. Beide Aufrufe übergeben timeout=timeout.

  2. trail_httpx(url, *, client=None, timeout=HTTPX_TIMEOUT) benutzt client = httpx if client is None else client, sendet ein GET von url mit follow_redirects=True und timeout=timeout und gibt redirect_trail(response) zurück. Rufe hier nicht raise_for_status() auf.

Das von httpx heißt follow_redirects; das allow_redirects von requests ist in httpx ein . Benutze den Client, den du bekommst: Ein Aufruf von httpx.get oder httpx.post, der ihn ignoriert, fällt bei der Prüfung durch.

Im zeigt python pl_http.py httpx, wie ein Client mit Standardwerten an der 308 eines alten Pfads anhält, und führt dann dein trail_httpx mit diesem Pfad und dein who_am_i_httpx als zoë aus.

Klicke dann auf Check my work. Es übergibt deinen einen httpx-Client, dessen Transport von einem kleinen Server innerhalb der Prüfung im Speicher antwortet, und zeichnet den Timeout auf, der an jeder Anfrage hängt. Mit einem Benutzer, einem Passwort, einer Basis- und einem Timeout, die du noch nicht gesehen hast, muss das Login gelingen, wobei beide Anfragen deinen Timeout tragen, nicht die 5 Sekunden von httpx, und ein falsches Passwort muss httpx.HTTPStatusError auslösen, bevor /me angefragt wird. trail_httpx muss beide Zeilen einer 308 und des Buchs, zu dem sie führt, zurückgeben, mit Client und ohne. Nichts aus deinem Terminal wird bewertet.