LEARN PYTHON | Validation And Exception Handling

Lesson objective

Validate type, format, range and presence, then handle specific exceptions without hiding unexpected faults or corrupting state.

Learn

05.3 | VALIDATION AND EXCEPTION HANDLING

01 | A CONVERSION IS NOT THE WHOLE CHECK

int("-4") succeeds, but a negative age or expense might still break your requirements. int("four") fails before a range comparison can happen. These are different problems: conversion interprets text; validation checks whether the resulting data is acceptable.

Start with rules. For a small ticket quantity, you might allow whole numbers from zero through twenty. A blank response fails presence; "2.5" fails the integer-text conversion; -1 and 21 fail the range. Zero can be valid, so a truthiness check alone is unsuitable.

Validation does not prove factual accuracy. A plausible date and permitted expense amount may still be entered incorrectly. A user review can help catch that separate problem.

02 | USE CHECKS THAT MATCH THE REQUIREMENT

A presence check asks whether required input exists. A type check asks what kind of value it is. A range check checks limits. A format check checks structure, such as four digits, hyphen, two digits, hyphen, two digits. A membership check restricts a category to an allowed collection.

A date matching that structure may still be impossible. 2026-02-30 looks like YYYY-MM-DD but is not a calendar date. Use the calendar parser as well as a strict format rule when your requirements need both.

Do not strip every field automatically. Outer whitespace may be irrelevant to a menu command but meaningful in some data. State where normalisation is intentional before validating.

03 | AN EXCEPTION INTERRUPTS NORMAL FLOW

An exception is a signal that normal execution cannot continue as requested. int("four") raises ValueError. Opening a missing file for reading raises FileNotFoundError. Accessing a missing dictionary key raises KeyError.

When an exception occurs, Python searches for a matching handler. If none handles it, a traceback describes the call path and the program stops. The final traceback line usually names the exception and message; earlier lines help locate its origin.

Do not catch an exception merely to erase its evidence. Catch it at a place that can recover, give a useful explanation or stop cleanly. A hidden exception can make a damaged program appear to have worked.

04 | KEEP THE TRY BLOCK FOCUSED

The try block contains operations that may fail in a way you intend to handle. An except ValueError block handles that specific exception type, including its subclasses. Exceptions of other types continue outward unless another handler matches.

Keep unrelated calculations outside the try block where practical. If too much code sits inside one broad handler, a genuine internal bug may be reported as bad user input.

The example converts a response, reports conversion failure, then separately checks the range. Its input is a known string, which fits the int conversion contract.

text = "21"
try:
    quantity = int(text)
except ValueError:
    print("Enter a whole number")
else:
    if 0 <= quantity <= 20:
        print("Accepted:", quantity)
    else:
        print("Use a quantity from 0 to 20")

EXPECTED OUTPUT

Use a quantity from 0 to 20

05 | ELSE AND FINALLY HAVE DIFFERENT PURPOSES

The else block of try runs when the try block completes without an exception. It is useful for work that depends on conversion succeeding. An exception from else is not handled by that same try statement’s except clauses.

finally normally runs as execution leaves the try structure, whether the operation succeeds or an exception propagates. It suits cleanup, but a file context manager is usually clearer for closing files. finally does not imply that the operation succeeded.

Avoid return inside finally in these examples: it can replace an earlier result or suppress an exception. Cleanup should not quietly change the meaning of success or failure.

try:
    count = int("7")
except ValueError:
    print("Conversion failed")
else:
    print("Converted:", count)
finally:
    print("Attempt finished")

EXPECTED OUTPUT

Converted: 7
Attempt finished

06 | RAISE AN ERROR AT THE FUNCTION BOUNDARY

A reusable validation function can raise ValueError when a supplied value is unsuitable. Its caller decides whether to retry, report failure or stop. This keeps input prompts separate from the validation rule.

A deliberate raise communicates the contract clearly. Returning zero for every invalid quantity would be ambiguous when zero is itself a permitted quantity.

Use exception chaining with raise ValueError(...) from error when replacing a low-level message while retaining the original cause. It makes debugging possible without requiring the user-facing prompt to reproduce a technical traceback.

def parse_quantity(text):
    try:
        quantity = int(text)
    except ValueError as error:
        raise ValueError("Enter a whole-number quantity") from error
    if not 0 <= quantity <= 20:
        raise ValueError("Quantity must be from 0 to 20")
    return quantity

print(parse_quantity("0"))

EXPECTED OUTPUT

0

07 | RETRY WITHOUT CHANGING ACCEPTED STATE

A retry loop should collect and validate a proposed value before changing the accepted data. For a quiz, invalid input should leave the current question and score unchanged. For an expense, invalid input should not append a partly formed record.

Prepare a candidate, validate it and only then commit the change. In the project, saving must also succeed before the in-memory list is replaced by the candidate. Otherwise the screen could claim an addition that did not reach the file.

A retry loop needs a successful exit or a cancellation route. Do not repeatedly test the same invalid value without reading a new response.

def parse_quantity(text):
    value = int(text)
    if not 0 <= value <= 20:
        raise ValueError("Use 0 to 20")
    return value

responses = ["bad", "21", "3"]
for text in responses:
    try:
        quantity = parse_quantity(text)
    except ValueError:
        print("Rejected:", text)
    else:
        print("Accepted:", quantity)
        break

EXPECTED OUTPUT

Rejected: bad
Rejected: 21
Accepted: 3

08 | CATCH THE FAILURE YOU CAN ADDRESS

FileNotFoundError is a kind of OSError. If separate handlers need different behaviour, put the more specific handler first. A broad OSError handler before FileNotFoundError would already catch that missing-file case.

A missing optional save file may justify returning an empty list. Permission failures or malformed existing data do not justify that same response. Catch only the cases you have deliberately designed to recover from.

except Exception can be useful at a carefully designed application boundary, but it should not become a blanket replacement for specific handlers. A bare except also catches signals such as KeyboardInterrupt, which users may expect to stop a program.

09 | TRACE CONVERSION AND RANGE SEPARATELY

The fixed model accepts ASCII integer text with an optional sign and a quantity from zero through twenty. It intentionally uses a stricter text grammar than every form Python int() can accept. The trace shows conversion, range checking and commitment as separate stages.

    10 | LIMITS ARE PART OF THE DESIGN

    A useful handler gives an actionable message: which rule failed and what is allowed. It should not invent a successful default merely to keep the program running. Keep the original file and accepted state intact when recovery is uncertain.

    For Decimal or float input, consider non-finite values such as NaN and infinity if you permit that conversion. The project’s restricted decimal-text grammar excludes them before converting to integer cents.

    Test each rule separately, then combine them. An ordinary valid value checks the happy path; blank, malformed, just-outside and exact-boundary cases expose different branches. More prompts are not a substitute for validation.

    PUT YOUR SKILLS TO WORK

    Use the Section 5 challenges to practise these ideas independently. Work in your Python editor, predict results and keep a short test record.

    Section 5 challenges Python course navigation

    MATCH THE PYTHON TERM TO ITS MEANING

    Terminology

    Terminology

    Validation

    Checking whether data meets stated application rules.

    Conversion

    Interpreting a value in another representation or type.

    Presence check

    Checking required data is supplied.

    Range check

    Checking a value lies within permitted bounds.

    Format check

    Checking an agreed structural pattern.

    Membership check

    Checking a value is one of permitted options.

    Normalisation

    Making intentional representation changes before use.

    Exception

    A signal interrupting ordinary execution because an operation cannot complete normally.

    Traceback

    A report showing the call path associated with an exception.

    try

    A block whose exceptions may be handled by matching except clauses.

    except

    A clause handling matching exceptions.

    else

    A try clause run after the try block completes without an exception.

    finally

    A clause normally run as control leaves the try structure.

    raise

    A statement signalling an exception.

    ValueError

    An exception for an unsuitable value in an expected kind of operation.

    OSError

    A family of operating-system related exceptions.

    Exception chaining

    Retaining an original cause when raising another exception.

    Commit

    Accepting a checked proposed change into the program’s authoritative state.

    Questions

    Questions

    CHECK YOUR UNDERSTANDING

    Select all correct choices. Each exact set earns one point.

    1. int("-4") succeeds. Does that prove a quantity is acceptable?
    2. What does except ValueError handle?
    3. When does a try else block run?
    4. What does finally normally signify?
    5. Which are useful validation rules for a date field?
    6. Why validate before appending an expense?
    7. Which handler order gives a missing file its special route?
    8. Which is a sensible response to a corrupted existing save?

    EXPLAIN AND COMPARE

    Write your answers before revealing each sample.

    1. Distinguish conversion from range validation.

    2. Explain a format check versus a calendar check.

    3. Explain focused exception handling.

    4. Compare else and finally in a try statement.

    5. Explain raising a validation error.

    6. Explain why invalid proposals must not alter accepted state.

    7. Evaluate returning an empty list for every loading exception.

    8. Construct a validation test set.

    CODE IT | YOUR INDEPENDENT CHALLENGE

    Create a quantity parser with conversion and range rules, then test rejection and unchanged state.

    Work in your Python editor. Keep a working version, explain one design choice and record at least two tests. Use the Learn tab for worked examples before comparing your result.

    Flashcards

    Flashcards

    Click to flip. Select the ideas you need to revisit.

    0 cards selected for revision.

      Selections are kept while this page is open.

      Workbook

      Workbook

      COMING SOON

      The workbook for Validation And Exception Handling is coming soon. Complete the matching task and written questions in your exercise book.