De standaardclient en statusklassen · oefening
Een time-out is geen 504
Elk antwoord dat aankomt, heeft nu een klasse en een body. Deze les gaat over het verzoek dat geen antwoord krijgt. In Python in Practice leken een time-out en een 504 Gateway Timeout misschien dezelfde mislukking: iets was traag, en de aanroep slaagde niet. Het zijn verschillende gebeurtenissen, beslist door verschillende programma’s, en een client die de ene meldt als de andere, stuurt wie zijn uitvoer leest op de verkeerde plek zoeken.
Twee scenario’s die de tijd nemen
Twee routes van practice-api wachten voordat ze antwoorden. /v1/scenarios/slow?delay_ms=250 wacht 250 milliseconden en stuurt dan een 200. /v1/scenarios/timeout?delay_ms=750 wacht 750 milliseconden en stuurt dan een 504 Gateway Timeout, de status die een server stuurt als iets waarop hij wachtte niet op tijd antwoordde. Hier speelt de dienst die rol met opzet.
Probeer eerst de trage, met een time-out van één seconde:
python -c "import wire; print(wire.get_json('/v1/scenarios/slow?delay_ms=250', timeout=1.0)['status'])"
Het drukt na een korte pauze 200 af. Een kwart seconde past binnen één seconde, dus het wachten was niets meer dan wachten.
Nu het time-outscenario, twee keer, met twee verschillende time-outs:
python -c "import wire; print(wire.get_json('/v1/scenarios/timeout?delay_ms=750', timeout=2.0)['status'])"
python -c "import wire; print(wire.get_json('/v1/scenarios/timeout?delay_ms=750', timeout=0.5)['status'])"
Met twee seconden wachttijd ontvangt je client de 504 en drukt hij 504 af. Met een halve seconde geeft hij het op voordat de server antwoordt: het tweede commando eindigt met een TimeoutError: timed out is. Hetzelfde verzoek, dezelfde server; alleen je time-out veranderde. Werpt het eerste commando ook TimeoutError op, dan duurde het langer dan twee seconden voordat het antwoord terug was in je sandbox; het punt hieronder hangt daar niet van af.
Wie besliste
Op een tijdlijn zien de twee uitvoeringen er zo uit. Vergelijk waar elke lijn eindigt, en wie haar beëindigde:

Met timeout=0.5 gaat er een halve seconde voorbij zonder kop op de verbinding, en werpt http.client TimeoutError op vanuit getresponse. Er bestaan geen status, geen headers en geen body; je client besloot te stoppen. Met timeout=2.0 komt het antwoord van de server na driekwart seconde aan. Het is een compleet antwoord als elk ander, met een statusregel, headers en een get_json geeft het terug met "class": "server error".
De vraag “wie gaf het op?” heeft dus in elk geval een ander antwoord. Een 504 betekent dat een server het opgaf met iets verderop en je dat vertelde. Een TimeoutError betekent dat je client het opgaf met de server, en de server kan nog steeds met het verzoek bezig zijn. Het eerste is een feit over de server; het tweede is een feit over je time-out.
Met timeout=0.5 werpt een GET van /v1/scenarios/timeout?delay_ms=750 TimeoutError op. Wie gaf het op?
report: zeg welke van de twee er gebeurde
report(target, *, make_connection, timeout) geeft één regel terug die zegt wat er gebeurde met een GET van target:
als
get_jsoneen record teruggeeft, de status en zijn klasse, zoals in504 server errorof200 success;als
get_jsonTimeoutErroropwerpt, een regel die zegt dat de client een time-out kreeg en hoe lang hij wachtte, en die geen status noemt, omdat er geen aankwam.
De try staat rond de aanroep van get_json, en de except noemt alleen TimeoutError. Dat is de smalle grens uit Python in Practice: vang alleen de
except OSErrorvangt ookConnectionRefusedError, omdat beide soortenOSErrorzijn. Een geweigerde verbinding zou dan als time-out worden gemeld, en dan gaat iemand op zoek naar een trage server terwijl er geen server luisterde.except Exceptionvangt ook deValueErrordieget_jsonopwerpt voor een body die als JSON gelabeld is maar geen JSON is, en zou een kapot antwoord als time-out melden.
Beide mislukkingen moeten report als exception verlaten, zodat wie het aanriep ziet wat er echt gebeurde.
get_json zelf hoeft niet te veranderen. Sinds les 1 verlaat TimeoutError het via het blok with contextlib.closing(...), dat de verbinding onderweg naar buiten sluit, en report is de enige plek die er een regel van maakt. Vang hem niet binnen get_json: er is geen antwoord om in een record te zetten.
Je opdracht
Schrijf in
wire.pydereport(target, *, make_connection, timeout)zoals hierboven beschreven. Geefmake_connectionentimeoutdoor aanget_json.Voer het uit op het time-outscenario met beide time-outs:
python -c "import wire; print(wire.report('/v1/scenarios/timeout?delay_ms=750', timeout=0.5))"
python -c "import wire; print(wire.report('/v1/scenarios/timeout?delay_ms=750'))"
De eerste regel zegt in je eigen woorden dat je client na 0,5 seconde een time-out kreeg; de tweede luidt 504 server error. Zegt ook de tweede dat je client een time-out kreeg, dan duurde het langer dan twee seconden voordat de 504 je sandbox bereikte, zoals bij de eerdere uitvoering; report sprak beide keren de waarheid.
Druk daarna op Check my work. Je client heeft nu een antwoord op elk antwoord en op geen antwoord. De volgende les bewijst het allemaal met je eigen tests.
Opdracht
Schrijf report(target, *, make_connection, timeout) in wire.py. Het roept get_json(target, make_connection=make_connection, timeout=timeout) aan en geeft één regel tekst terug:
als er een antwoord aankomt, de status en zijn klasse, beginnend met de status, zoals in
504 server error;als
get_jsonTimeoutErroropwerpt, een regel met de woordentimed outen de time-out die het wachtte, zoals0.5, en zonder.
Vang alleen TimeoutError. Een ConnectionRefusedError, of de report als exception verlaten. get_json blijft TimeoutError doorlaten, met de verbinding gesloten.
Voer report uit op /v1/scenarios/timeout?delay_ms=750 met timeout=0.5 en met de standaardtime-out, zoals de les laat zien.
Druk daarna op Check my work. Die roept report en get_json aan met een vervanger voor make_connection die een gescripte 504 of 200 teruggeeft, TimeoutError opwerpt vanuit getresponse() voor een stille server, ConnectionRefusedError opwerpt vanuit request(), of een body teruggeeft die als JSON gelabeld is maar geen JSON is. Ze controleert elke regel, dat de time-out de verbinding bereikte, dat de verbinding na de time-out gesloten werd, en dat de andere twee mislukkingen niet gevangen werden. De formulering rond timed out bepaal je zelf. Niets uit je