Slotproject: een hulpmiddel voor oefenrapporten · oefening
Maak de CLI-grens eerlijk
Alles werkt. Niemand kan het nog gebruiken, want de enige manier om het uit te voeren is het importeren en functies met de hand aanroepen.
Deze les geeft het hulpmiddel een commandoregelinterface. Dat deed je in hoofdstuk 8 ook, dus de techniek zal vertrouwd voelen. Nieuw is dat dit programma nu twee bestanden schrijft. De volgorde daarvan is een keuze met gevolgen.
De interface
python practice_report.py REPORT_PATH [--cache PATH] [--page N] [--page-size N]
[--max-age SECONDS] [--refresh | --offline-source PATH]
Het rapportpad is positioneel, want bij een hulpmiddel dat een rapport schrijft hoor je niet te hoeven raden waar het is gebleven. Al het andere heeft een standaardwaarde: cache in practice-cache.json, pagina 1, paginagrootte 3 en een maximale leeftijd van 86400 seconden.
Drie regels die de parser afdwingt voordat er iets anders gebeurt:
--refresh en --offline-source sluiten elkaar uit. De ene zegt “geef me actuele gegevens”, de andere “raak het netwerk niet aan”. Samen zijn ze tegenstrijdig. Argparse kan een tegenstrijdigheid duidelijker afwijzen dan jouw code die kan ontwarren.
Afkorten staat uit. allow_abbrev=False, zoals in hoofdstuk 8. Zonder dat betekent --ref ongemerkt --refresh en wordt een typefout een ongedocumenteerde functie.
Botsende paden worden vóór elke invoer/uitvoer afgewezen. Als rapportpad en cachepad hetzelfde bestand zijn, stop je. Beide naar één doel schrijven levert een bestand op dat geen van beide is. Dat na de eerste schrijfbewerking ontdekken is te laat.
Bouw het rapport op voordat je opent
Dit is de volgorderegel waar deze les echt om draait.
1. choose the page (may fail: network, cache, validation)
2. render the whole report (may fail: bad data, defect in your code)
3. write the cache (only on the network path)
4. write the report
5. print one line to stdout
Stappen 1 en 2 kunnen mislukken zonder een van beide doelbestanden aan te raken. Stappen 3 en 4 kunnen vervangen wat er stond, en beide schrijfbewerkingen kunnen nog mislukken. Daarom moeten alle validatie en het volledige opbouwen van tekst in het geheugen klaar zijn voordat het eerste doelbestand wordt geopend.
Doe je het andersom, door het rapportbestand te openen en het gaandeweg op te bouwen, dan laat een fout halverwege de gebruiker achter met een afgebroken rapport en zonder vorige versie. Die raakt een goed bestand kwijt en krijgt een kapot bestand terug uit een uitvoering die toch al niet zou slagen.
Dit is de derde keer dat dit idee terugkomt: de rapportschrijver in hoofdstuk 5, de cacheschrijver in les 1 en nu het hulpmiddel als geheel. Elke keer dezelfde regel, op een grotere schaal.
Eerst de cache, dan het rapport
Schrijf op de netwerkroute de cache vóór het rapport.
De redenering gaat over welke fout je liever hebt. Als de cache schrijven slaagt en het rapport schrijven mislukt, heb je actuele cachegegevens en geen nieuw rapport. De volgende uitvoering is dan snel en maakt het rapport. Als het rapport schrijven slaagt en de cache schrijven mislukt, heb je een rapport en geen cache. De volgende uitvoering haalt dan onnodig opnieuw op.
Geen van beide is een ramp. Het eerste is beter. Kies de volgorde daarop.
De andere drie routes, actuele cache, verouderde cache en offline, schrijven alleen het rapport. Geen daarvan heeft iets nieuws om in de cache op te slaan.
Eén regel resultaat, één regel fout
Het afsluitcontract volgt nog steeds hetzelfde idee als in hoofdstuk 8:
| Uitkomst | stdout | stderr | Status |
|---|---|---|---|
| Succes | één regel die het rapport noemt | eventuele waarschuwingen | 0 |
| Verwachte fout | leeg | één regel | 1 |
| Ongeldige argumenten | leeg | gebruiksinstructies van argparse | 2 |
| Defect in je code | niets beloofd | traceback | niet nul |
Vang alleen de verwachte fouten op: Timeout, ConnectionError, HTTPError en JSON-decodeerfouten van Requests; ongeldige invoergegevens; en OSError bij lezen of schrijven van bestanden. Een andere RequestException, waaronder InvalidURL, InvalidSchema of MissingSchema, behoudt zijn traceback. Die programmeerfouten in verzoeken erven ook van ValueError en OSError. Werp overgebleven RequestException-waarden daarom opnieuw op na de handlers voor verwachte Requests-fouten en vóór beide brede handlers voor ingebouwde excepties.
Merk op dat waarschuwingen en fouten allebei stderr gebruiken, maar iets anders betekenen. Een waarschuwing hoort bij een geslaagde uitvoering die afsluit met 0. Een fout vervangt het resultaat en sluit af met 1.
Nu jij
Maak build_parser() en deze functie af in practice_report.py:
def run_report(
report_path, cache_path, page, page_size, max_age, *,
refresh=False, offline_source=None, session=None, now_epoch=None,
):
...
Druk bij succes Wrote N items to REPORT_PATH from SOURCE. en één regeleinde af. Vervang de tijdelijke aanduidingen door het geselecteerde aantal items, het gevraagde pad en de bron. Geef 0 terug. Geef 1 terug voor de verwachte operationele fouten hierboven; laat andere verzoekfouten en programmeerdefecten verdergaan.
Verbind de parser met choose_page, bouw de rapporttekst op, schrijf in de gedocumenteerde volgorde, druk de meegegeven waarschuwingen af en geef de juiste status terug. Houd main(argv=None) veilig om te importeren, precies zoals in hoofdstuk 8: ontleden, delegeren, teruggeven.
Breid tests/test_practice_report.py nu uit met deze CLI-grenzen: conflicterende modi en botsende paden falen vóór elke invoer/uitvoer; een fout bij het opbouwen van tekst behoudt het bestaande rapport en de cache; offlinemodus raakt netwerk noch cache; en een geslaagd commando drukt de gedocumenteerde melding af en geeft nul terug. Gebruik net als eerder tmp_path, namaaksessies en capsys. De referentietestmodule bevat ook de laatste CLI-controles, die kunnen slagen zodra de functies uit deze les zijn geïmplementeerd.
Probeer het daarna stuk te maken. Laat --cache en het rapport naar hetzelfde pad wijzen. Geef --page 0 mee. Geef zowel --refresh als --offline-source mee. Zet tekst in het rapportbestand en voer een commando uit dat zal mislukken. Controleer daarna of je tekst behouden bleef.
Waarom bouw je het volledige rapport op voordat je het doelbestand opent?
Waarom schrijf je bij een geslaagde netwerkuitvoering de cache vóór het rapport?
Het hulpmiddel valt terug op een verouderde cache en schrijft een rapport. Wat hoort het te doen?
Opdracht
Voltooi de exacte CLI-matrix, voorafgaande controle op botsende paden, gerichte exceptiemeldingen, de volgorde cache vóór rapport en behoud van herkenbare begininhoud bij fouten vóór het schrijven.