Eindproject: een live scorebord · eindproject
Eindproject: Draai de hele quizavond
De server en de client hebben nog nooit samen gedraaid. Deze les laat ze een hele quizavond doorlopen, voegt de test toe die vastlegt hoe een quiz eindigt, en controleert het hele project in één keer. Eén scenario van pl_ws.py zet je twee programma’s op het podium:
python pl_ws.py quiz-night
Het roept de main van je server aan in één thread en de main van je client voor kijker A en voor elke scorebijhouder, speelt twee eigen kijkers, B en S, en vertraagt S met opzet. Een afgemaakte versie, op de site uitgevoerd, drukte het transcript hieronder af; lees het blok voor blok. Regels met | zijn de standard output van een programma, regels met ! zijn standard error, en PORT staat voor het
scenario quiz-night: your server and your client through a whole quiz night
viewer A is your watch; B is this program's; S is this program's and is slowed down on purpose
part 1: a quiz of three updates; first a scorekeeper with the wrong token, then three scores
your server's main() is called with --port 0 --max-updates 3 --deadline 6
viewer B joined; its first message was a snapshot with version 0
viewer S joined; its first message was a snapshot with version 0
scorekeeper (wrong token) ! the scoreboard refused the connection: HTTP 401
scorekeeper (wrong token) exited with 1
scorekeeper | version 1: Owls 3, Foxes 0, Herons 0
scorekeeper exited with 0
scorekeeper | version 2: Owls 3, Foxes 2, Herons 0
scorekeeper exited with 0
scorekeeper | version 3: Owls 3, Foxes 2, Herons 5
scorekeeper exited with 0
Je server weigerde het verkeerde token vóór de upgrade, en je score maakte van de 401 één regel op standard error en exitstatus 1. Daarna drie scores, elk beantwoord met zijn snapshot.
viewer A (your client) | version 0: Owls 0, Foxes 0, Herons 0
viewer A (your client) | version 1: Owls 3, Foxes 0, Herons 0
viewer A (your client) | version 2: Owls 3, Foxes 2, Herons 0
viewer A (your client) | version 3: Owls 3, Foxes 2, Herons 5
viewer A (your client) exited with 0
viewer B received versions [0, 1, 2, 3], then the server closed it with 1000 'the quiz is over'
viewer S received versions [0, 1, 3], then the server closed it with 1000 'the quiz is over'
your server | listening on 127.0.0.1:PORT
your server | the quiz is over at version 3; every viewer was closed with 1000
your main() returned 0
Je watch drukte elke versie van 0 tot en met 3 af en vertrok met 0 bij de close van de server. Kijker S zag versie 2 nooit: terwijl zijn thread nog bezig was met het sturen van versie 1, verving versie 3 versie 2 in zijn outbox. Hij ontving toch de laatste versie vóór zijn close met 1000, en dat is precies de regel uit les 1. Welke versies S ontvangt, hangt af van timing, dus je run kan een andere
part 2: a quiz of ten updates with a deadline of 1.5 seconds; one score
your server's main() is called with --port 0 --max-updates 10 --deadline 1.5
viewer B joined; its first message was a snapshot with version 0
viewer S joined; its first message was a snapshot with version 0
scorekeeper | version 1: Owls 2, Foxes 0, Herons 0
scorekeeper exited with 0
viewer A (your client) | version 0: Owls 0, Foxes 0, Herons 0
viewer A (your client) | version 1: Owls 2, Foxes 0, Herons 0
viewer A (your client) exited with 0
viewer B received versions [0, 1], then the server closed it with 1001 'the scoreboard is closing'
viewer S received versions [0, 1], then the server closed it with 1001 'the scoreboard is closing'
your server | listening on 127.0.0.1:PORT
your server | the deadline passed at version 1; every connection was closed with 1001
your main() returned 0
the token from token.txt appears in no line of this transcript
De tweede quiz kon niet af: er werden tien updates verwacht en er kwam er maar één. Bij de deadline sloot je server iedereen met 1001, je watch zag dat als een normaal einde, en main gaf nog steeds 0 terug. De laatste regel is een controle die pl_ws.py op zijn eigen uitvoer doet; nog een regel, die niet getoond wordt, telt threads en verschilt van run tot run.
In het transcript ontving de vertraagde kijker S versie 0, 1 en 3, en daarna een close met 1000. Is dat een fout?
De laatste test
Het einde is de regel die dit hoofdstuk toevoegde, en nog niets in je testsuite legt hem vast. Vervang de laatste stub in tests/test_scoreboard.py, test_the_last_update_is_sent_before_the_close. Maak een bord met max_updates=1 en verwerk één score, zodat de quiz voorbij is op versie 1. Bouw een stand-in-kijker met types.SimpleNamespace: een id, een send die elk bericht, geparset, vastlegt, en een close(code, reason) die de code vastlegt. Roep scoreboard_server.viewer_handler(viewer, board) aan. Die keert meteen terug, omdat de laatste snapshot al de snapshot bij binnenkomst is: er hoeft niets te wachten en er is geen recv nodig. Controleer daarna dat het laatst verstuurde bericht versie 1 heeft, dat de vastgelegde closes precies [1000] zijn, en dat board["finished"].is_set().
Wat Check my work controleert
Dit is de hele acceptatiecontrole van het eindproject, in woorden. Het importeren van de finished pas omhoog als niemand meer wacht. Een mislukte, een dode en een crashende kijker verlaten elk het register. De antwoorden aan de scorebijhouder, de tokencontrole, de ruimtelimiet, het adres, de grenzen, de eindes en de exitstatussen van de server, de uitvoer en de exitstatussen van de client, en de feiten van je contract worden gecontroleerd zoals eerder, en het token verschijnt nergens. Daarna draaien je tests tegen negen versies die elk één realistische fout maken: de acht van les 4 en een kijker die sluit met 1000 zonder de laatste snapshot te sturen.
Optioneel: draai het op je eigen computer
Dit deel wordt niet beoordeeld, en niets op de site ziet het: geen enkele les vraagt er een resultaat van, en er wordt niets teruggestuurd. Als je je scorebord buiten de browser wilt zien werken, klik je op Download project, pak je de ZIP uit in een nieuwe, lege map, en open je een websockets 17.4 dat vereist; elk tabblad begint met een controle. Is het nummer lager, installeer dan een nieuwere Python van python.org en gebruik de naam daarvan, zoals python3.13 (of py -3.13 op Windows), overal waar de opdrachten python3 (of py) zeggen. De README.md van het project bevat dezelfde opdrachten.
Controleer de versie, richt eenmalig alles in, en start daarna de server in de eerste terminal:
python3 --version
python3 -m venv .venv
./.venv/bin/python -m pip install -r requirements.txt
./.venv/bin/python -m pytest -q
./.venv/bin/python scoreboard_server.py --max-updates 2 --deadline 60
Kijk mee in een tweede terminal in dezelfde map; scoor in een derde twee keer:
./.venv/bin/python scoreboard_client.py watch --url ws://127.0.0.1:8765/watch --max-messages 3 --wait 60
./.venv/bin/python scoreboard_client.py score --url ws://127.0.0.1:8765/score --team Owls --points 3
./.venv/bin/python scoreboard_client.py score --url ws://127.0.0.1:8765/score --team Foxes --points 2
Controleer in PowerShell de versie, richt eenmalig alles in, en start daarna de server in de eerste terminal:
py --version
py -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
.\.venv\Scripts\python.exe -m pytest -q
.\.venv\Scripts\python.exe scoreboard_server.py --max-updates 2 --deadline 60
Kijk mee in een tweede terminal in dezelfde map; scoor in een derde twee keer:
.\.venv\Scripts\python.exe scoreboard_client.py watch --url ws://127.0.0.1:8765/watch --max-messages 3 --wait 60
.\.venv\Scripts\python.exe scoreboard_client.py score --url ws://127.0.0.1:8765/score --team Owls --points 3
.\.venv\Scripts\python.exe scoreboard_client.py score --url ws://127.0.0.1:8765/score --team Foxes --points 2
Controleer de versie, richt eenmalig alles in, en start daarna de server in de eerste terminal:
python3 --version
python3 -m venv .venv
./.venv/bin/python -m pip install -r requirements.txt
./.venv/bin/python -m pytest -q
./.venv/bin/python scoreboard_server.py --max-updates 2 --deadline 60
Kijk mee in een tweede terminal in dezelfde map; scoor in een derde twee keer:
./.venv/bin/python scoreboard_client.py watch --url ws://127.0.0.1:8765/watch --max-messages 3 --wait 60
./.venv/bin/python scoreboard_client.py score --url ws://127.0.0.1:8765/score --team Owls --points 3
./.venv/bin/python scoreboard_client.py score --url ws://127.0.0.1:8765/score --team Foxes --points 2
Elke opdracht noemt de Python binnen .venv, dus er hoeft niets geactiveerd te worden. Drie terminals, omdat watch zijn terminal bezet houdt tot zijn laatste snapshot.
terminal 1 server: listening on 127.0.0.1:8765 ... closing line, exit 0
terminal 2 watch: version 0, version 1, version 2, exit 0
terminal 3 score Owls 3: version 1 score Foxes 2: version 2
all three talk over 127.0.0.1, which never leaves your computer
In woorden: de ene terminal houdt de server, een andere de kijker en een derde de scorebijhouder, en alle drie praten ze alleen op je eigen computer met elkaar. Met een afgemaakte versie op een Mac gaven deze opdrachten deze run: de server drukte listening on 127.0.0.1:8765 af; de twee scores drukten version 1: Owls 3, Foxes 0, Herons 0 en version 2: Owls 3, Foxes 2, Herons 0 af, elk met exitstatus 0; watch drukte versie 0 tot en met 2 af en stopte met 0; en de server drukte zijn afsluitende regel af en stopte met 0. Een watch die daarna werd gestart, drukte could not connect to ws://127.0.0.1:8765/watch af en stopte met 1, omdat er niets meer luisterde.
Waarom heeft de run op je eigen computer drie terminals nodig, en niet twee?
Wat weet Python Land na je run op je eigen computer daarover?
Het afgemaakte scorebord
Je hebt een server met een geschreven contract, grenzen en twee nette eindes, een client die elke weigering en close omzet in een exitstatus, en tests die realistische fouten opmerken. Veel
Opdracht
Vervang in tests/test_scoreboard.py de laatste stub, test_the_last_update_is_sent_before_the_close, en houd zijn naam: roep op een bord dat is gemaakt met scoreboard_server.make_board([...], max_updates=1) en dat één score heeft gekregen scoreboard_server.viewer_handler aan met een stand-in waarvan send elk bericht vastlegt en waarvan close(code, reason) de code vastlegt, en controleer daarna dat het laatst verstuurde bericht versie 1 heeft, dat de vastgelegde codes [1000] zijn, en dat board["finished"].is_set(). Houd elke andere test, en houd de code van elke eerdere les correct.
Draai python -m pytest -q totdat elke test slaagt, en draai python pl_ws.py quiz-night. Klik daarna op Check my work. Die controleert het hele eindproject, met stand-ins en zonder thread of hmac.compare_digest; de ruimtelimiet; serve_scoreboard en main; watch, score en de meegeleverde client-main; de feiten in SCOREBOARD_PROTOCOL.md; en het token op geen enkele stream, in geen enkele viewer_handler die sluit met 1000 zonder de laatste snapshot te sturen. Tegen elk ervan moet minstens één test falen. Een run die na 3 seconden nog loopt, wordt gestopt, en tegen een foutieve versie telt dat als opgemerkt; er start geen nieuwe run meer zodra er 8 seconden voorbij zijn. Extra tests, hulpfuncties en aanpassingen aan README.md zijn prima. De optionele run op je eigen computer wordt niet gecontroleerd.