← Python/Foutafhandeling
Les 8 van 8
MODULE 06 · 08◷ 30 min

Een invoerpijplijn met verklaarde afwijzingen

Na deze les kun je
  • Verantwoordelijkheden scheiden tussen lezen, parseren, verzamelen en afsluiten.
  • Een verwachte regelfout met de echte oorzaak vertalen en bewaren.
  • Onverwachte fouten laten doorgaan terwijl de afsluiting wel plaatsvindt.

Een materiaallijst bevat geldige regels, ontbrekende aantallen en een onverwachte programmeerfout. Alle problemen hetzelfde behandelen levert óf een onbruikbare import óf stil gegevensverlies op. Je bouwt nu een pijplijn met aparte grenzen voor lezen, afwijzen en afsluiten.

Vier functies met één verantwoordelijkheid

Een invoerpijplijn verwerkt gegevens in opeenvolgende kleine stappen. Hier leest de eerste stap een aantal, maakt de tweede een materiaalrecord en verzamelt de derde de resultaten. De vierde stap sluit de bewerking af, ongeacht haar uitkomst.

FunctieVerantwoordelijkheid
read_amountEen niet-negatief ASCII-aantal lezen
parse_rowEén record maken of RowError geven
import_rowsRecords en verklaarde afwijzingen verzamelen
import_sessionDezelfde import uitvoeren en afsluiten

Dit zijn gewone functies met lijsten en dictionaries. Er wordt nog geen bestand geopend en er is geen project met meerdere bestanden. Je oefent vooral waar iedere fout thuishoort.

Eerst een ander formaat bekijken

Het voorbeeld verwerkt instellingen met een dubbele punt. Een eigen fouttype markeert verwachte invoerproblemen, terwijl iedere afgewezen oorspronkelijke regel bewaard blijft. Voorspel waarom de onvolledige instelling niet zomaar uit het verslag verdwijnt.

Instellingen en afwijzingen apart bewaren
class SettingError(ValueError):
    pass

def setting(line):
    parts = line.split(":")
    if len(parts) != 2 or not parts[0].strip() or not parts[1].strip():
        raise SettingError("onvolledige instelling")
    return {"key": parts[0].strip(), "value": parts[1].strip()}

records, rejected, audit = [], [], []
try:
    for line in ["kleur: groen", "kapot", "maat: groot"]:
        try:
            records.append(setting(line))
        except SettingError as error:
            rejected.append({"line": line, "reason": str(error)})
finally:
    audit.append("closed")
print(records)
print(rejected)
print(audit)
Verwachte uitvoer
[{'key': 'kleur', 'value': 'groen'}, {'key': 'maat', 'value': 'groot'}]
[{'line': 'kapot', 'reason': 'onvolledige instelling'}]
['closed']

De uitgewerkte vorm gebruikt een ander scheidingsteken en andere gegevens dan jouw materiaalimport. De beslissingen blijven herkenbaar: één regel controleren, uitsluitend de eigen invoerfout afvangen en de resultaten apart bewaren. De laatste logregel hoort bij afsluiting, niet bij succes.

Een duidelijk materiaalformaat

Een materiaalregel bevat precies twee velden, gescheiden door ;. De naam wordt gestript en met NFC genormaliseerd, zoals in de tekstmodule. Een lege naam is ongeldig; het resultaat is een nieuwe dictionary met name als tekst en count als integer.

Gebruik voor verwachte invoerproblemen de meegegeven RowError, een subklasse van ValueError. Haar bericht wordt de leesbare reden van een afwijzing.

Het aantal bestaat na strip uit minstens één ASCII-cijfer. Een lege waarde, minteken, plusteken, decimale punt of niet-ASCII-cijfer wordt afgewezen. Voorloopnullen en nul zelf zijn toegestaan; je kent de geschikte volledige patrooncontrole uit de vorige module.

read_amount geeft bij een ongeldig aantal ValueError("ongeldig aantal"). parse_row vertaalt die fout naar RowError("ongeldig aantal in regel") met de werkelijke oorzaak. Een verkeerd aantal velden en een lege naam krijgen ieder een eigen afgesproken reden.

Alleen verwachte afwijzingen verzamelen

Een afwijzingsrecord bevat de oorspronkelijke regel onder line en het bericht van RowError onder reason. Strip de bewaarde regel niet: de oorspronkelijke spaties kunnen juist verklaren wat er is ontvangen. Alleen volledig lege of uit witruimte bestaande regels worden overgeslagen.

import_rows vangt uitsluitend RowError uit de parser af. Een andere fout mag niet als gewone afwijzing verdwijnen, zelfs als zij toevallig ook van ValueError afstamt. Zo blijft een bug in een vervangen parser zichtbaar voor de aanroeper.

Het volgende voorbeeld laat zien dat afsluiting zo'n onverwachte fout niet wegwerkt. De logregel verschijnt, maar de oorspronkelijke fout blijft bestaan. Die volgorde is dezelfde als bij jouw import_session.

Afsluiten terwijl de programmeerfout doorgaat
try:
    raise TypeError("bug in verwerking")
finally:
    print("Afgesloten")
Verwachte fout
Afgesloten
TypeError: bug in verwerking

Veelgemaakte fout: alleen doorgaan telt

Een brede handler met continue lijkt de import robuust te maken, maar kan zowel de invoer als een fout in je programma verbergen. Betrouwbaar doorgaan vraagt een beperkte, herkenbare afwijzingsroute. Een onverwachte fout onderbreekt deze import en levert geen gewoon succesresultaat op.

Maak bovendien per geslaagde regel een nieuw record en per aanroep nieuwe resultatenlijsten. Twee identieke materiaalnamen mogen twee onafhankelijke records opleveren. De controles kijken naar de echte gegevens en objecten, niet alleen naar een geprinte melding.

AAN DE SLAG 0/4
1

Het aantallenformaat controleren

Maak read_amount(text): strip de tekst en accepteer uitsluitend minstens één ASCII-cijfer. Geef int terug; werp bij lege tekst, tekens, plus/min, decimalen of niet-ASCII-cijfers ValueError met ongeldig aantal. Nul en voorloopnullen zijn geldig.

2

Een eigen regelfout met oorzaak

Maak parse_row(line): eis twee velden met ;, anders RowError met verwacht naam;aantal. Strip en normaliseer de naam naar NFC; leeg geeft RowError met naam ontbreekt. Gebruik read_amount voor count en vertaal zijn ValueError naar RowError met ongeldig aantal in regel, vanuit dezelfde oorzaak. Geef een nieuw dict met name en count terug.

3

Verklaarde afwijzingen en onafhankelijke records

Maak import_rows(lines): sla witruimteregels over en geef (records, rejected) terug. Bewaar per RowError het oorspronkelijke line en reason = str(error); andere fouten gaan ongewijzigd door. Bewaar volgorde en maak per record en per aanroep nieuwe dictionaries/lijsten.

4

De import altijd afsluiten zonder foutverlies

Maak import_session(lines, audit): geef import_rows(lines) terug en voeg bij succes, afwijzingen en een doorgegeven fout precies eenmaal closed aan dezelfde audit-lijst toe. Bewaar eerdere loginhoud en de oorspronkelijke fout.

Onthoud dit

  • Iedere stap heeft een eigen contract en een passende foutgrens.
  • Bewaar zowel geldige records als oorspronkelijke, verklaarde afwijzingen.
  • Een onverwachte fout gaat verder; afsluiting blijft een aparte handeling.

Hierna gebruik je eigen typen om gegevens en hun regels bij elkaar te houden.

main.py
UitvoerPython
›
›_

Hier komt je resultaat.

Voer je code uit en ontdek
wat je hebt gemaakt.

0/4 stappen
EVEN MEEKIJKEN

Bekijk hoe het kan.

Je mag de oplossing bekijken. Deze poging telt dan niet mee als afgeronde les. Met Reset kun je daarna opnieuw zelf oefenen.