0%

Hoofdstuk 6 · oefening

De bibliotheek websockets, zonder asyncio

websockets installeren en vastpinnen

Je echoservice uit hoofdstuk 5 werkt: de zelfgebouwde client en de zelfgebouwde server zijn het over elk eens. Die overeenstemming bewijst minder dan het lijkt. Beide kanten komen uit dezelfde , 618 regels verdeeld over ws_handshake.py, ws_frames.py en loopback_demo.py, en een fout die aan beide kanten op dezelfde manier is gemaakt, valt tegen zichzelf weg, zoals een lengte van 16 bits die beide schreven in hoofdstuk 3 zou hebben gedaan. Om zo’n fout te vinden, moet iets wat door andere mensen is geschreven met je server praten.

Dit hoofdstuk haalt de bibliotheek websockets erbij, door andere mensen geschreven volgens dezelfde RFC 6455. Eerst test ze je code; vanaf les 2 neemt ze het loodgieterswerk over.

De projectboom bevat de afgewerkte versies van de cursus van loopback_demo.py, ws_frames.py en ws_handshake.py, zodat het hoofdstuk begint vanuit bekende bestanden, hoe je eigen code uit hoofdstuk 5 er ook uitzag; niets in dit hoofdstuk past ze aan. Vanaf les 2 schrijf je echo_library.py, en Run start in de editorlessen library_demo.py.

Welke versie hier staat

Deze les heeft een , en de sandbox ervan kan naast zichzelf één ding bereiken: de pakketmirror van de cursus, dezelfde interne waaruit Python in Practice installeerde. Niets op het openbare internet is bereikbaar. Begin met de eigen Python van de sandbox te vragen welke versie van de bibliotheek hij heeft:

python -c "import websockets; print(websockets.__version__)"

Het moet 17.4 afdrukken, de versie waartegen deze cursus is gecontroleerd en die de sandbox van de site heeft. Die kopie is geïnstalleerd voor de Python van de sandbox, dus elke latere les kan import websockets doen zonder iets te installeren.

Pin hem vast

requirements.txt bevat al één regel, pytest==9.1.1. Open het bestand in het tabblad Project, of met nano requirements.txt in de shell zoals in Python in Practice, en voeg de bibliotheek eronder toe, zodat het bestand zo luidt:

pytest==9.1.1
websockets==17.4

De == pint de exacte versie vast. Bibliotheken veranderen tussen releases: sommige die dit hoofdstuk meegeeft, bestaan niet in oudere versies van deze bibliotheek. Op je eigen computer geeft pip install -r requirements.txt je dan de bibliotheek die dit hoofdstuk gebruikt.

Waarom websockets==17.4 schrijven in plaats van websockets?

Installeer hem waar je het kunt zien

Bewijs nu dat het bestand genoeg is om de omgeving opnieuw op te bouwen. Maak een nieuwe , installeer vanuit het bestand, en toon wat de omgeving bevat:

python -m venv .venv
.venv/bin/python -m pip install -r requirements.txt
echo $?
.venv/bin/python -m pip freeze

pip leest beide regels en installeert vanuit de mirror van de cursus, en echo $? drukt de exitstatus af, 0 als de installatie werkte. Op de site was de status 0 en toonde pip freeze pytest en de pakketten die pytest nodig heeft, en de regel websockets==17.4. Vraag de nieuwe omgeving daarna welke versie hij kreeg:

.venv/bin/python -c "import websockets; print(websockets.__version__)"

Lokaal drukte het 17.4 af, dezelfde versie die de eigen Python van de sandbox zou moeten melden.

Als de installatie in plaats daarvan faalt, drukt pip een fout af, drukt echo $? een andere status dan 0 af, en toont pip freeze geen regel voor . Dan ligt het aan de verbinding van de sandbox met de mirror, niet aan je bestand: je pin is nog steeds wat de controle leest, en geen latere les gebruikt deze installatie.

De site bewaart .venv nooit. Gegenereerde mappen worden weggelaten als je project wordt opgeslagen, zoals Python in Practice liet zien, dus de omgeving die je net hebt gebouwd, is in de volgende les weg. Latere lessen gebruiken de kopie die voor de Python van de sandbox is geïnstalleerd, en dat is dezelfde versie. De pin is voor je eigen machine, en voor iedereen die dit project na jou installeert.

Je opent de volgende les. Is je .venv er nog?

De bibliotheek ontmoet je server

Open interop.py en lees het; elke wachttijd erin is begrensd. Het start de server uit hoofdstuk 5, loopback_demo.serve_clients, op een thread voor één client op 127.0.0.1 0. Daarna maakt het verbinding met de client van de bibliotheek, connect(...) uit websockets.sync.client, stuurt één tekstbericht en één binair bericht en leest elke echo, stuurt een en wacht hooguit 2 seconden op de pong, en verlaat het with-blok, wat de verbinding sluit. In les 3 schrijf je zo’n client. Draai het:

python interop.py

Een run van deze bestanden op de site drukte dit af. PORT staat voor het poortnummer dat het systeem koos; dat van jou zal anders zijn:

interop: the Chapter 5 server is listening on 127.0.0.1:PORT
library client: the handshake succeeded
library client: echo 'hello from the websockets library'
library client: echo b'\x00\x01\x02'
library client: ping answered: True
library client: closed the connection
Chapter 5 server: [{'messages': 2, 'code': 1000}]
interop: server thread still running: False

Lees het regel voor regel. De slaagde: de bibliotheek stuurde een upgradeverzoek met een eigen nieuwe sleutel, check_upgrade antwoordde met een 101 met de acceptwaarde die accept_value berekende, en de bibliotheek controleerde die zoals je check_accept_response dat doet. De twee echo’s worden afgedrukt met repr, dat een waarde schrijft zoals Python- dat zou doen: aanhalingstekens rond een str, een b vóór bytes. De bibliotheek maskeerde dus een tekstframe en een binair frame, de server uit hoofdstuk 5 demaskeerde en decodeerde ze, en de echo’s kwamen terug als tekst en als binair. De ping werd beantwoord: de bibliotheek stuurde een ping met b"interop", server_reply antwoordde met een pong met dezelfde bytes, en het wachten gaf binnen de 2 seconden True terug. Het verlaten van het blok stuurde close 1000, en het eigen verslag van de server laat zien dat hij die close terugstuurde: twee berichten, code 1000. Tot slot was de serverthread klaar.

Om je eigen code uit hoofdstuk 5 in deze run te zien, kopieer je het bestand van de cursus opzij met cp loopback_demo.py course_loopback_demo.py, plak je dat van jou over loopback_demo.py, en draai je python interop.py opnieuw. Niets controleert die run; zet daarna de versie van de cursus terug met cp course_loopback_demo.py loopback_demo.py, omdat latere lessen die gebruiken.

Wat laat de interoprun zien?

Controleer je werk

Druk op Check my work. Het leest requirements.txt en verder niets: precies één regel voor websockets, websockets==17.4, met de regel pytest==9.1.1 er nog in; commentaar en lege regels zijn prima. Het kijkt niet naar .venv, dat niet wordt opgeslagen, of naar de interoprun.

De bibliotheek heeft met je server gepraat en was het met hem eens. In de volgende les draaien de rollen om: je server uit hoofdstuk 5 wordt een server van de bibliotheek, een handler van een paar regels.

Opdracht

Pin de bibliotheek vast in requirements.txt: voeg de regel websockets==17.4 toe en houd de regel pytest==9.1.1.

Daarna, in de :

  1. python -c "import websockets; print(websockets.__version__)" om de eigen versie van de sandbox te zien;

  2. python -m venv .venv en .venv/bin/python -m pip install -r requirements.txt om vanuit de mirror van de cursus te installeren;

  3. .venv/bin/python -m pip freeze en zoek naar websockets==17.4;

  4. python interop.py om de client van de bibliotheek met de server uit hoofdstuk 5 te zien praten.

Druk op Check my work als de pin in het bestand staat. De controle leest alleen requirements.txt: ze verwacht precies één regel die noemt, geschreven als websockets==17.4, zonder >= of ~= en niet alleen in commentaar, en ze verwacht dat pytest==9.1.1 er nog in staat. Commentaar, lege regels en de volgorde van de twee regels bepaal je zelf. De controle kijkt niet naar .venv, dat de site niet opslaat, of naar wat interop.py afdrukte.