requests und httpx, jetzt, wo du Bescheid weißt · Übung
httpx, dieselben Ideen
httpx ist eine zweite httpx.Client ist ihre Session: Er hält with. Auch seine Aufrufe sehen vertraut aus, client.get(url), client.post(url, auth=...), client.request("GET", url), und das 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:
| requests | httpx | |
|---|---|---|
gefolgt, außer bei allow_redirects=False | nicht gefolgt, außer bei follow_redirects=True | |
| Timeout | keiner, außer du übergibst einen | 5 Sekunden für jede Phase |
raise_for_status() | löst bei 4xx und 5xx aus | löst bei jedem Status außerhalb von 2xx aus, auch bei 3xx |
auth= mit Text | als Latin-1 codiert | als |
| seine | Unterklassen von OSError | keine 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
Die beiden Bibliotheken sind sich sogar beim Namen des 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
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:
who_am_i_httpx(base_url, user, password, *, client, timeout=HTTPX_TIMEOUT)tut, waswho_am_itut, auf dem httpx-Client, den es bekommt:POST base_url + "/login"mitauth=(user, password),raise_for_status(),GET base_url + "/me", und gibuserzurück, wobei beide Aufrufetimeout=timeoutbekommen. Zugangsdaten als einfacher Text sind hier in Ordnung, weil httpx sie als UTF-8 codiert.trail_httpx(url, *, client=None, timeout=HTTPX_TIMEOUT)sendet ein GET mitfollow_redirects=Trueundtimeout=timeoutund gibtredirect_trail(response)zurück. Benutzeclient = httpx if client is None else client. Deinredirect_trailfunktioniert unverändert mit einer httpx-Antwort, weil auch siestatus_code,urlundhistoryhat.
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 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 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:
who_am_i_httpx(base_url, user, password, *, client, timeout=HTTPX_TIMEOUT)sendet auf dem Client, den es bekommt,POST base_url + "/login"mitauth=(user, password), ruftraise_for_status()auf dieser Antwort auf, sendetGET base_url + "/me"und gibtuseraus dessenzurück. Beide Aufrufe übergeben timeout=timeout.trail_httpx(url, *, client=None, timeout=HTTPX_TIMEOUT)benutztclient = httpx if client is None else client, sendet ein GET vonurlmitfollow_redirects=Trueundtimeout=timeoutund gibtredirect_trail(response)zurück. Rufe hier nichtraise_for_status()auf.
Das follow_redirects; das allow_redirects von requests ist in httpx ein httpx.get oder httpx.post, der ihn ignoriert, fällt bei der Prüfung durch.
Im 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 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.