Abschlussprojekt: Eine Live-Anzeigetafel · Abschlussprojekt
Abschlussprojekt: Den ganzen Quizabend ausführen
Der Server und der Client sind noch nie zusammen gelaufen. Diese Lektion lässt sie einen ganzen Quizabend durchlaufen, fügt den Test hinzu, der festhält, wie ein Quiz endet, und prüft das ganze Projekt auf einmal. Ein Szenario von pl_ws.py bringt deine beiden Programme auf die Bühne:
python pl_ws.py quiz-night
Es ruft das main deines Servers in einem Thread auf und das main deines Clients für Zuschauer A und für jeden Punktezähler, spielt zwei eigene Zuschauer, B und S, und bremst S absichtlich. Eine fertige Version hat auf der Seite das Protokoll unten ausgegeben; lies es Block für Block. Zeilen mit | sind die Standardausgabe eines Programms, Zeilen mit ! seine Standardfehlerausgabe, und PORT steht für die
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
Dein Server hat das falsche Token vor dem Upgrade abgelehnt, und dein score hat die 401 in eine Zeile in der Standardfehlerausgabe und Exit-Status 1 verwandelt. Dann drei Wertungen, jede mit ihrem Snapshot beantwortet.
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
Dein watch hat jede Version von 0 bis 3 ausgegeben und sich beim Schließen durch den Server mit 0 beendet. Zuschauer S hat Version 2 nie gesehen: Während sein Thread noch damit beschäftigt war, Version 1 zu senden, hat Version 3 Version 2 in seiner Outbox ersetzt. Er hat trotzdem die letzte Version vor seinem Schließen mit 1000 empfangen, und genau das ist die Regel aus Lektion 1. Welche Versionen S empfängt, hängt vom Timing ab, dein Lauf kann also eine 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
Das zweite Quiz konnte nicht fertig werden: Zehn Updates waren fällig, und nur eines kam. An der Frist hat dein Server alle mit 1001 geschlossen, dein watch hat das als normales Ende gewertet, und main hat trotzdem 0 zurückgegeben. Die letzte Zeile ist eine Prüfung, die pl_ws.py an seiner eigenen Ausgabe vornimmt; eine weitere Zeile, nicht gezeigt, zählt Threads und ändert sich von Lauf zu Lauf.
Im Protokoll hat der gebremste Zuschauer S die Versionen 0, 1 und 3 empfangen, dann ein Schließen mit 1000. Ist das ein Fehler?
Der letzte Test
Das Ende ist die Regel, die dieses Kapitel hinzugefügt hat, und noch hält nichts in deiner Testsammlung sie fest. Ersetze den letzten Stub in tests/test_scoreboard.py, test_the_last_update_is_sent_before_the_close. Erstelle eine Tafel mit max_updates=1 und wende eine Wertung an, damit das Quiz bei Version 1 vorbei ist. Baue einen Stellvertreter-Zuschauer mit types.SimpleNamespace: eine id, ein send, das jede Nachricht geparst aufzeichnet, und ein close(code, reason), das den Code aufzeichnet. Rufe scoreboard_server.viewer_handler(viewer, board) auf. Es kehrt sofort zurück, weil der letzte Snapshot schon der Beitritts-Snapshot ist: Nichts muss warten, und kein recv ist nötig. Sichere dann zu, dass die zuletzt gesendete Nachricht Version 1 hat, dass die aufgezeichneten Schließvorgänge genau [1000] sind und dass board["finished"].is_set().
Was Check my work prüft
Das ist die ganze Abnahmeprüfung des Abschlussprojekts, in Worten. Das Importieren der finished wird erst gesetzt, wenn niemand mehr wartet. Ein gescheiterter, ein toter und ein abstürzender Zuschauer verlassen jeweils das Register. Die Antworten an den Punktezähler, die Token-Prüfung, die Platzgrenze, die Adresse des Servers, Grenzen, Enden und Exit-Status, die Ausgabe und die Exit-Status des Clients und die Tatsachen deines Vertrags werden geprüft wie bisher, und das Token erscheint nirgendwo. Dann laufen deine Tests gegen neun Versionen, die je einen realistischen Fehler machen: die acht aus Lektion 4 und einen Zuschauer, der mit 1000 schließt, ohne den letzten Snapshot zu senden.
Optional: auf deinem eigenen Computer ausführen
Dieser Teil wird nicht bewertet, und nichts auf der Seite sieht ihn: Keine Lektion verlangt ein Ergebnis daraus, und nichts wird zurückgesendet. Wenn du deine Anzeigetafel außerhalb des Browsers beobachten willst, klicke auf Download project, entpacke das ZIP in einen neuen, leeren Ordner und öffne in diesem Ordner ein websockets 17.4 es verlangt; jeder Tab beginnt mit einer Prüfung. Ist die Nummer niedriger, installiere ein neueres Python von python.org und verwende seinen Namen, etwa python3.13 (oder py -3.13 unter Windows), überall, wo die Befehle python3 (oder py) sagen. Die README.md des Projekts enthält dieselben Befehle.
Prüfe die Version, richte einmal alles ein und starte dann den Server im ersten 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
Schau in einem zweiten Terminal im selben Ordner zu;
./.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
Prüfe in PowerShell die Version, richte einmal alles ein und starte dann den Server im ersten 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
Schau in einem zweiten Terminal im selben Ordner zu; werte in einem dritten zweimal:
.\.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
Prüfe die Version, richte einmal alles ein und starte dann den Server im ersten 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
Schau in einem zweiten Terminal im selben Ordner zu; werte in einem dritten zweimal:
./.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
Jeder Befehl nennt das Python in .venv, also gibt es nichts zu aktivieren. Drei Terminals, weil watch sein Terminal bis zu seinem letzten Snapshot beschäftigt hält.
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 Worten: Ein Terminal hält den Server, eines den Zuschauer und eines den Punktezähler, und alle drei sprechen nur auf deinem eigenen Computer miteinander. Mit einer fertigen Version auf einem Mac haben diese Befehle diesen Lauf ergeben: Der Server hat listening on 127.0.0.1:8765 ausgegeben; die beiden Wertungen haben version 1: Owls 3, Foxes 0, Herons 0 und version 2: Owls 3, Foxes 2, Herons 0 ausgegeben, jede mit Exit-Status 0; watch hat die Versionen 0 bis 2 ausgegeben und sich mit 0 beendet; und der Server hat seine abschließende Zeile ausgegeben und sich mit 0 beendet. Ein danach gestartetes watch hat could not connect to ws://127.0.0.1:8765/watch ausgegeben und sich mit 1 beendet, weil nichts mehr lauschte.
Warum braucht der Lauf auf deinem eigenen Computer drei Terminals und nicht zwei?
Was weiß Python Land nach deinem Lauf auf deinem eigenen Computer darüber?
Die fertige Anzeigetafel
Du hast einen Server mit einem schriftlichen Vertrag, Grenzen und zwei sauberen Enden, einen Client, der jede Ablehnung und jedes Schließen in einen Exit-Status verwandelt, und Tests, die realistische Fehler bemerken. Viele
Aufgabe
Ersetze in tests/test_scoreboard.py den letzten Stub, test_the_last_update_is_sent_before_the_close, und behalte seinen Namen: Rufe auf einer Tafel, die mit scoreboard_server.make_board([...], max_updates=1) erstellt wurde und eine Wertung bekommen hat, scoreboard_server.viewer_handler mit einem Stellvertreter auf, dessen send jede Nachricht aufzeichnet und dessen close(code, reason) den Code aufzeichnet, und sichere dann zu, dass die zuletzt gesendete Nachricht Version 1 hat, dass die aufgezeichneten Codes [1000] sind und dass board["finished"].is_set(). Behalte jeden anderen Test, und halte den Code jeder früheren Lektion korrekt.
Führe python -m pytest -q aus, bis jeder Test besteht, und führe python pl_ws.py quiz-night aus. Klicke dann auf Check my work. Die Prüfung prüft das ganze Abschlussprojekt, mit Stellvertretern und ohne Thread oder hmac.compare_digest; die Platzgrenze; serve_scoreboard und main; watch, score und das vorgegebene Client-main; die Tatsachen in SCOREBOARD_PROTOCOL.md; und das Token in keinem Ausgabestrom, keiner viewer_handler, das mit 1000 schließt, ohne den letzten Snapshot zu senden. Gegen jede davon muss mindestens ein Test scheitern. Ein Lauf, der nach 3 Sekunden noch läuft, wird gestoppt, und gegen eine fehlerhafte Version zählt das als bemerkt; nach 8 Sekunden startet kein neuer Lauf mehr. Zusätzliche Tests, Helfer und Änderungen an README.md sind in Ordnung. Der optionale Lauf auf deinem eigenen Computer wird nicht geprüft.