requests en httpx, nu je het weet · oefening
Elke functie is een mechanisme
In les 1 droeg een sessie een wire.py of in je server. Hier staan ze naast elkaar:
| requests | Wat het doet | Jouw versie |
|---|---|---|
response.history | de | follow en zijn history, les 6.4 |
allow_redirects=False | geef de eerste 3xx terug in plaats van hem te volgen | http.client, dat nooit volgt, les 3.1 |
auth=(user, password) | de regel Authorization: Basic | basic_header, les 7.5 |
session.cookies | de cookiejar: bewaar Set-Cookie, stuur Cookie | make_cookie_opener, les 7.3 |
raise_for_status() | werp een | classify en zijn statusklassen voor client- en serverfouten, hoofdstuk 3 |
| de verbindingspool van de sessie | meerdere verzoeken op één open gehouden verbinding | fetch_many, les 9.4 |
.json() | decodeer de body, gelezen tot de lengte die de headers gaven | read_body en json_body, hoofdstukken 1 en 3 |
timeout=(1.0, 2.0) | een begrensd verbinden, daarna begrensde leesacties | de time-outs van les 3.1 en 3.5 |
De tabel heeft geen rij voor een cache. Een antwoord bewaren, beslissen of het vers is en If-None-Match sturen deed je cached_get in les 8.5, en requests doet daar niets van. Daarover hieronder meer.
Neem de eerste twee rijen in code. Na response = session.get("http://books.test/v0/books/2") bevat response.history het 308-antwoord, en response.url is de uiteindelijke http://books.test/books/2. Elk antwoord in history heeft zijn eigen status_code en url, dezelfde twee feiten die je follow in zijn history-items bewaarde. Met allow_redirects=False gedraagt requests zich als http.client: de 308 zelf komt terug, response.is_redirect is True, en response.next is het verzoek dat hij als volgende zou hebben verstuurd.
De meeste rijen gedragen zich precies zoals jouw code deed. Drie niet: de regel voor 302, de standaardwaarde van head() en de codering die auth= gebruikt. Die zijn het weten waard voordat je de bibliotheek vertrouwt.
Waar de bibliotheek van jou verschilt
Je follow verandert de POST; na een 307 of 308 houdt hij de methode en de body. requests is het eens over 301, 307 en 308, en over 303 voor elke methode behalve HEAD, die hij houdt. Bij 302 doet hij iets anders: hij maakt van elke methode behalve HEAD een GET, dus een PUT die met 302 beantwoord wordt, gaat verder als een GET zonder body. De POST in GET te veranderen. httpx, in les 3, heeft dezelfde browserregel.
Dat is een van de redenen waarom je server 308 gebruikt voor de oude /v0-paden: onder een 308 houdt elke client de methode en de body.
Een server beantwoordt PUT /books/2 met 302 en location: /books/2-new. Wat stuurt requests daarna?
Het andere verschil is een standaardwaarde. session.get, post, put en delete volgen doorverwijzingen, tenzij je allow_redirects=False meegeeft. session.head doet het omgekeerde: hij geeft een 3xx terug zoals die is, tenzij je allow_redirects=True meegeeft.
session.head(url) krijgt een 308. Wat komt er terug?
Inloggegevens zijn bytes
auth=(user, password) bouwt dezelfde header als je basic_header: user:password, daarna basic_header codeerde de tekst als
Voor ASCII-namen verandert dat niets, omdat ASCII-bytes in beide hetzelfde zijn. Voor zoë wel:
Wat drukt dit af?
print("zoë".encode("latin-1"), "zoë".encode("utf-8"))
De strikte UTF-8-decodering van je server kan de losse \xeb van Latin-1 niet lezen, dus een login als zoë krijgt 401 met het juiste wachtwoord. Een teken dat Latin-1 helemaal niet heeft, zoals ✓, is erger: requests werpt UnicodeEncodeError op voordat er iets wordt verstuurd.
De oplossing is requests bytes te geven, die hij verstuurt zoals ze zijn:
credentials = (user.encode("utf-8"), password.encode("utf-8"))
response = session.post(base_url + "/login", auth=credentials, timeout=timeout)
httpx, in les 3, codeert inloggegevens die als tekst gegeven zijn als UTF-8 en heeft zo’n stap niet nodig.
Geen cache in requests
Een sessie bewaart cookies, headers en verbindingen. Antwoorden bewaart hij niet. Twee aanroepen van session.get(url) met een seconde ertussen sturen twee volledige verzoeken, ook als het eerste antwoord cache-control: max-age=60 zei. Hervalideren blijft jouw werk: jij houdt de tag vast en stuurt hem.
response = session.get(url, headers={"if-none-match": etag}, timeout=timeout)
Een 304 betekent dat je kopie nog actueel is; een 200 brengt een nieuwe body en een nieuwe tag.
Twee aanroepen van session.get(url) met een seconde ertussen; het eerste antwoord had cache-control: max-age=60 en een etag. Wat stuurt de tweede aanroep?
Je opdracht
In side_by_side.py:
redirect_trail(response)geeft één regel,f"{r.status_code} {r.url}", terug voor elk antwoord inresponse.history, oudste eerst, en daarna één voorresponsezelf. Zonder doorverwijzing is dat één regel.revalidate(url, etag, *, session=None, timeout=TIMEOUT)stuurt éénGET urlmet de headerif-none-match: etagentimeout=timeout, en geeft deterug. Gebruik client = requests if session is None else session, de regel uit Python in Practice, en laat requests elke doorverwijzing volgen.Verander
who_am_izo dat hij zijn inloggegevens als UTF-8-bytes meegeeft.
Voer daarna uit:
python pl_http.py mechanisms
pl_http voegt een gebruiker, zoë, met het wachtwoord pässwörd ✓, toe aan de state van je server. Vóór je wijziging eindigt het laatste deel in de weigering van requests:
--- your who_am_i() as zoë ---
raised UnicodeEncodeError: 'latin-1' codec can't encode character '\u2713' in position 9: ordinal not in range(256)
Daarna toont het spoor de 308 van het oude pad en het boek, en de tag die je vasthoudt beslist tussen 200 en 304:
--- pl_http's session.get() of /v0/books/2?x=1, then your redirect_trail() ---
value: ['308 http://books.test/v0/books/2?x=1', '200 http://books.test/books/2?x=1']
--- requests and replies ---
request 1 GET /books/2 (If-None-Match: "stale") -> 200 OK (connection 1)
--- your revalidate() of /books/2 with the tag "stale" ---
value: 200
Het volgende deel stuurt de huidige tag van het boek en krijgt value: 304, en who_am_i als zoë geeft value: 'zoë' terug. Klik op Check my work. Les 3 doet dezelfde klussen met httpx, waarvan de standaardinstellingen op vijf punten verschillen van die van requests.
Opdracht
Lees een spoor van
In side_by_side.py:
redirect_trail(response)geeft eenterug: één f"{r.status_code} {r.url}"voor elk antwoord inresponse.history, oudste eerst, daarna één voorresponsezelf.revalidate(url, etag, *, session=None, timeout=TIMEOUT)stuurt éénGET urlmet de headerif-none-match: etag, precies zoals gegeven, entimeout=timeout, en geeft destatus_codevan het antwoord terug. Gebruikclient = requests if session is None else session. Laat requests doorverwijzingen volgen, zoals hij standaard doet: geef geenallow_redirects=Falsemee.who_am_igeeft zijn inloggegevens mee als UTF-8-bytes:auth=(user.encode("utf-8"), password.encode("utf-8")). Al het andere erin blijft zoals in les 1.
In de python pl_http.py mechanisms de gebruiker zoë toe aan je server, en draait daarna je redirect_trail op een verzoek voor een oud /v0-pad, je revalidate met een verouderde tag en met de huidige, en je who_am_i als zoë.
Klik daarna op Check my work. Die draait je redirect_trail moet een 308 en het boek waar die naartoe leidt opsommen, en de volgorde van een langere history behouden; revalidate moet via een 308 een 304 krijgen voor de huidige tag en een 200 voor een verouderde, en op beide hops de tag en de time-out sturen, met een sessie en zonder. Niets uit je terminal wordt beoordeeld.