CodeRigor

FREE LESSON

Typed records & validated configuration

Dataclasses, type annotations, immutable configuration and runtime checks at untrusted boundaries.

Type annotations describe intent to readers and tools such as Pyright; Python does not automatically enforce them at runtime. Validate configuration read from JSON, environment variables or command-line text before using it.

Type annotations help tools, while the parser enforces the runtime boundary.

Validate before constructing configuration
Reject unknown names and bad values before constructing a frozen RunConfig. Freezing does not validate field types.
Validate before constructing configuration
Reject unknown names and bad values before constructing a frozen RunConfig. Freezing does not validate field types.
Python
def twice(x: int) -> int:
    return x * 2


print(twice(3))
print(twice("a"))  # annotation did not prevent this

A dataclass supplies common record behavior such as initialization and value equality. A frozen dataclass prevents field reassignment; it does not deeply freeze a list stored in a field. Use immutable field values for configuration.

Python
from dataclasses import dataclass


@dataclass(frozen=True)
class Run:
    seed: int
    labels: tuple[str, ...]


run = Run(7, ("control", "shift"))
print(run.seed, run.labels[1])

At a boundary, be precise about Python's numeric hierarchy: bool is a subclass of int. isinstance(True, int) succeeds. If a seed must be an actual integer, reject bool explicitly or compare type(value) is int. Converting with int() can silently truncate a float or accept text when strict validation was needed.

Python treats bool as an int subclass, so a strict integer seed needs a more precise test than isinstance.

Convertible is not the same as valid
The strict parser accepts an actual nonnegative integer. It rejects bool, float and text instead of coercing them.
Convertible is not the same as valid
The strict parser accepts an actual nonnegative integer. It rejects bool, float and text instead of coercing them.

The build converts a plain mapping to a frozen, fully validated RunConfig. Unknown keys are rejected because a misspelled batch_size should not quietly use a default. Defaults are part of the documented contract. Extra record classes are unnecessary; prefer a small obvious data model over inheritance.

Watch & read

Free lectures, talks and readings that cover this skill from another angle. All resources →

Dataclasses reduce record boilerplate, but annotations do not validate runtime input. Use the lesson’s explicit parser checks for unknown fields, exact integer types and value ranges.