0%

Als de buitenwereld niet meewerkt · oefening

Vang op waar de fout betekenis heeft

Het filter dat de grenswaarde insluit is gerepareerd. Het commando heeft nog één te ruime grens voor foutafhandeling uit hoofdstuk 8:

try:
    document = convert_catalog(input_path, output_path, minimum_quantity)
except (FileNotFoundError, UnicodeDecodeError, csv.Error, ValueError) as exc:
    ...

Die tuple noemt alleen verwachte typen, maar het try-blok omvat lezen, selecteren, valideren en schrijven. De locatie is net zo belangrijk als de klasse. Een ValueError bij het ontleden van een beschadigde CSV-rij is verwachte externe invoer. Een ValueError uit de validatie van de schrijver betekent dat projectcode een ongeldig intern document aan haar eigen schrijver gaf. Beide hetzelfde afhandelen zou dat onderscheid uitwissen.

Begrens één bewerking waarvan je de betekenis begrijpt

Maak run_catalog(input_path, output_path, minimum_quantity). De eerste grens omvat alleen het lezen:

try:
    items = read_catalog_csv(input_path)
except (OSError, UnicodeDecodeError, csv.Error, ValueError) as exc:
    print(f"Could not read {input_path}: {exc}", file=sys.stderr)
    return 1

OSError is de familie van besturingssysteemfouten, waaronder ontbrekende paden, ontbrekende rechten en andere mislukte bestandsbewerkingen. Hier heeft die één betekenis: de gevraagde invoer kon niet worden geopend of gelezen. UnicodeDecodeError, csv.Error en ValueError betekenen dat de bytes, de CSV-syntaxis of het opgegeven schema niet in een catalogus konden worden omgezet.

De melding bevat het exacte invoerpad en de oorzaak binnen die bewerking. stdout blijft leeg. Het belangrijkste is dat de schrijver nog niet is uitgevoerd, zodat een bestaand uitvoerbestand byte voor byte ongewijzigd blijft.

Voer de selectie na die handler uit, buiten elk try-blok:

selected = select_items(items, minimum_quantity)

Als de selectie KeyError, TypeError, ValueError, RuntimeError of zelfs OSError opwerpt, gaat de oorspronkelijke exceptie verder omhoog. Selectie raakt de buitenwereld niet. Een defect daarin een lees- of schrijffout noemen zou dus onjuist zijn.

Geef de uitvoerbewerking een eigen grens

Zet de grens alleen om de schrijver:

try:
    document = write_catalog_json(selected, output_path)
except OSError as exc:
    print(f"Could not write {output_path}: {exc}", file=sys.stderr)
    return 1

Hier is alleen een schrijffout van het besturingssysteem te verwachten. Een ValueError of TypeError uit de validatie schendt een interne invariant en moet ongewijzigd verdergaan.

Druk de succesregel pas af nadat write_catalog_json is teruggekeerd. “Wrote N items” is een bewering over een voltooide bewerking. Die eerder afdrukken zou ten onrechte succes melden.

De werkelijke beperking van het uitvoerbestand

Dit project schrijft rechtstreeks naar het gevraagde pad. Als het besturingssysteem faalt vóór het openen, kan een oud bestand ongewijzigd blijven. Als de fout optreedt na het leegmaken of nadat er al bytes zijn geschreven, kan het bestand ontbreken of onvolledig zijn. Dit hoofdstuk belooft niet dat wijzigingen worden teruggedraaid als dat niet is geïmplementeerd.

Er wordt ook niet automatisch opnieuw geprobeerd. Een ontbrekend pad of een rechtenfout opnieuw proberen zonder iets te veranderen, herhaalt alleen hetzelfde geval. De foutmelding geeft de aanroeper bewijs; ze verzint geen herstelbeleid.

Werk main(argv=None) bij zodat argparse verantwoordelijk blijft voor hulp en grammaticafouten. Geef daarna de drie ontlede waarden door aan run_catalog. Het volledige contract is:

Situatiestdoutstderrstatus
succesWrote N items to PATH.leeg0
lees-/invoerfoutleegCould not read INPUT: cause1
OSError bij uitvoerleegCould not write OUTPUT: cause1
parserfoutleeggebruiksinstructies en foutmelding van argparse2
onverwacht defectniets beloofdoorspronkelijke tracebackniet nul

Implementeer de twee grenzen die elk één bewerking omvatten.

Of het programma niet meer crasht, vertelt niet of je het goed hebt gedaan. Het gaat erom of dezelfde exceptieklasse anders wordt behandeld afhankelijk van waar ze vandaan komt. Een ValueError uit de lezer hoort een korte melding op te leveren; een ValueError uit de eigen validatie van de schrijver hoort nog steeds een traceback te geven. Als één breed try-blok beide omvat, kun jij die twee niet onderscheiden en degene die je programma uitvoert ook niet.

Een ValueError bereikt je code. Waar die vandaan komt bepaalt wat je moet doen. Waarom?

Waarom wordt select_items buiten elk try-blok uitgevoerd?

Opdracht

Refactor de aansturing van de CLI naar run_catalog(input_path, output_path, minimum_quantity).

  • Vang OSError, UnicodeDecodeError, csv.Error en ValueError alleen rond read_catalog_csv op, druk dan Could not read INPUT_PATH: CAUSE af op stderr en geef 1 terug.

  • Voer select_items buiten elk try-blok uit.

  • Vang alleen OSError rond write_catalog_json op, druk dan Could not write OUTPUT_PATH: CAUSE af op stderr en geef 1 terug.

  • Druk na geslaagd schrijven Wrote N items to OUTPUT_PATH. af op stdout en geef 0 terug.

  • Laat main(argv=None) argumenten ontleden en run_catalog(...) teruggeven.

Behoud hulp/status 0 en grammatica/status 2 van argparse. Laat defecten in de selectie en validatiefouten van de schrijver ongewijzigd verdergaan. Voeg geen nieuwe pogingen of beloften over terugdraaien toe.