0%

requests en httpx, nu je het weet · oefening

httpx, dezelfde ideeën

httpx is een tweede -clientbibliotheek voor Python, nieuwer dan requests en gebouwd op dezelfde ideeën. Een httpx.Client is zijn sessie: hij bewaart in een cookiejar, standaardheaders en een pool van open verbindingen, en je sluit hem met with. Ook zijn aanroepen zien er vertrouwd uit, client.get(url), client.post(url, auth=...), client.request("GET", url), en de zelf heeft httpx.get en httpx.post, net zoals requests requests.get heeft. Dus de regel die je in les 2 schreef, leest hier hetzelfde:

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

De verschillen zitten in de standaardinstellingen, en elk ervan is een beslissing die je in deze cursus al met de hand hebt genomen:

requestshttpx
gevolgd, tenzij allow_redirects=Falseniet gevolgd, tenzij follow_redirects=True
time-outgeen, tenzij je er een meegeeft5 seconden voor elke fase
raise_for_status()werpt op bij 4xx en 5xxwerpt op bij elke status buiten 2xx, 3xx inbegrepen
auth= met tekstgecodeerd als Latin-1gecodeerd als
zijn subklassen van OSErrorgeen subklassen van OSError

Hier is waar je elke beslissing nam. Je follow volgde alleen omdat je hem aanriep; kale http.client deed dat nooit. Je time-outs waren altijd je eigen getallen, ingesteld op elke verbinding. classify zette een 3xx in een eigen klasse, redirection, los van client- en serverfouten. basic_header codeerde UTF-8. En je code uit hoofdstuk 3 ving TimeoutError af, die een OSError is, net als de exceptions van requests. httpx besliste bij elk ervan gewoon anders, dus code die van de ene bibliotheek naar de andere verhuist, moet je rij voor rij lezen, en niet blind vertrouwen dat hij zich hetzelfde gedraagt.

Alleen volgen als erom gevraagd wordt

Een standaard-httpx.Client volgt geen doorverwijzingen. Een 308 komt terug als het antwoord zelf, met een lege response.history en response.next_request ingesteld op het verzoek dat httpx als volgende zou sturen. Geef follow_redirects=True mee en httpx volgt, volgens dezelfde regels voor als requests, inclusief de browserregel voor 302 uit les 2.

De twee bibliotheken zijn het zelfs oneens over de naam van het . De allow_redirects= van requests aan httpx meegeven is een fout, en de melding wijst naar het juiste woord:

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

requests beantwoordt de omgekeerde vergissing, follow_redirects=, met het spiegelbeeld van die melding.

client.get(url) met een standaard-httpx.Client krijgt een 308. Wat krijg je terug?

Een standaardtime-out is nog steeds een keuze

httpx begrenst elke aanroep: 5 seconden om te verbinden, 5 voor elke leesactie, 5 voor elke schrijfactie en 5 om te wachten op een vrije verbinding in de pool. Dat is veiliger dan de standaard van requests, eeuwig wachten, maar het is nog steeds een getal dat jij niet hebt gekozen. Deze cursus kiest zijn eigen, HTTPX_TIMEOUT bovenaan side_by_side.py:

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

Dat is 1 seconde om te verbinden en 2 seconden voor elk van de andere drie fasen. Waarom niet het paar van requests, (1.0, 2.0)? httpx accepteert een , maar leest die alleen als verbinden en lezen, en laat schrijven en wachten op de pool helemaal onbegrensd.

client.get(url, timeout=(1.0, 2.0)) in httpx. Wat is er begrensd?

Fouten

De raise_for_status() van httpx is strenger dan die van requests. Hij werpt httpx.HTTPStatusError op voor alles buiten 200 tot en met 299, dus ook een 308 die niet gevolgd is, met een melding die begint met Redirect response '308 Permanent Redirect'. requests laat een 3xx door.

Welke aanroep werpt een exception op bij een 308-antwoord?

De exceptions zelf horen bij een andere familie. Elke exception van requests is een subklasse van OSError, dus except OSError: rond een aanroep van requests vangt zijn time-outs en mislukte verbindingen af. Die van httpx niet: httpx.ReadTimeout is een TimeoutException, die een TransportError is, daarna een RequestError, daarna een httpx.HTTPError, daarna een gewone Exception. Vang de smalste klasse af die past bij wat je wilt afhandelen:

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

except httpx.HTTPError: is breder: hij vangt een time-out, een geweigerde verbinding en de HTTPStatusError van raise_for_status() allemaal af.

Een aanroep van httpx staat binnen try met except OSError:. De server antwoordt nooit. Wat gebeurt er?

httpx heeft ook een AsyncClient voor programma’s die geschreven zijn met async en await. Deze cursus gebruikt die niet.

Je opdracht

In side_by_side.py:

  1. who_am_i_httpx(base_url, user, password, *, client, timeout=HTTPX_TIMEOUT) doet wat who_am_i doet, op de httpx-client die hij krijgt: POST base_url + "/login" met auth=(user, password), raise_for_status(), GET base_url + "/me", en de user teruggeven, met timeout=timeout bij beide aanroepen. Inloggegevens als gewone tekst zijn hier prima, omdat httpx ze als UTF-8 codeert.

  2. trail_httpx(url, *, client=None, timeout=HTTPX_TIMEOUT) stuurt één GET met follow_redirects=True en timeout=timeout, en geeft redirect_trail(response) terug. Gebruik client = httpx if client is None else client. Je redirect_trail werkt ongewijzigd op een antwoord van httpx, omdat dat ook status_code, url en history heeft.

Voer daarna uit:

python pl_http.py httpx

Eerst laat het zien wat een standaardclient met het oude pad doet, voordat jouw code draait:

--- 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'

Daarna volgt je trail_httpx het, en de regels komen precies overeen met het requests-spoor uit les 2:

--- 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']

De zeggen nog steeds books.test omdat de client van pl_http elk verzoek via een eigen transport aan je server op deze machine doorgeeft, een onderdeel dat les 4 vervangt door een die jij schrijft. De run eindigt met who_am_i_httpx als zoë, wiens ✓ deze keer niets extra’s nodig heeft.

Klik op Check my work. Les 4 test beide bibliotheken zonder enige server.

Opdracht

Log in en volg een met httpx.

In side_by_side.py:

  1. who_am_i_httpx(base_url, user, password, *, client, timeout=HTTPX_TIMEOUT) stuurt POST base_url + "/login" met auth=(user, password) op de client die hij krijgt, roept raise_for_status() aan op dat antwoord, stuurt GET base_url + "/me" en geeft de user uit de daarvan terug. Beide aanroepen geven timeout=timeout mee.

  2. trail_httpx(url, *, client=None, timeout=HTTPX_TIMEOUT) gebruikt client = httpx if client is None else client, stuurt één GET van url met follow_redirects=True en timeout=timeout, en geeft redirect_trail(response) terug. Roep hier geen raise_for_status() aan.

Het van httpx heet follow_redirects; allow_redirects uit requests is in httpx een . Gebruik de client die je krijgt: een aanroep van httpx.get of httpx.post die hem negeert, laat de controle falen.

In de laat python pl_http.py httpx zien hoe een standaardclient stopt bij de 308 van een oud pad, en draait daarna je trail_httpx op dat pad en je who_am_i_httpx als zoë.

Klik daarna op Check my work. Die geeft je een httpx-client waarvan het transport in het geheugen antwoordt vanuit een kleine server binnen de controle, en die de time-out vastlegt die aan elk verzoek hangt. Met een gebruiker, een wachtwoord, een basis- en een time-out die je niet hebt gezien, moet de login slagen terwijl beide verzoeken jouw time-out meedragen, niet de 5 seconden van httpx, en moet een verkeerd wachtwoord httpx.HTTPStatusError opwerpen voordat /me gevraagd wordt. trail_httpx moet beide regels van een 308 en het boek waar die naartoe leidt teruggeven, met een client en zonder. Niets uit je terminal wordt beoordeeld.