Model/domain/postcode.py
2026-05-20 12:57:03 +00:00

40 lines
1.4 KiB
Python

"""The :class:`Postcode` value object.
A frozen value object that owns postcode sanitisation. Constructing a
``Postcode`` always yields the canonical form -- uppercase with all
whitespace removed -- so no part of the domain can hold an un-normalised
postcode. This matches the legacy splitter's
``df["postcode"].str.upper().str.replace(" ", "")``.
``Postcode`` is the single sanitisation point: anywhere a postcode crosses a
domain boundary it should be wrapped in one, and ``str(postcode)`` gives the
canonical string back for serialisation.
"""
from __future__ import annotations
from dataclasses import dataclass
@dataclass(frozen=True)
class Postcode:
"""A postcode held in canonical form.
The ``value`` passed to the constructor is sanitised eagerly in
:meth:`__post_init__` -- uppercased, with all whitespace (spaces, tabs,
newlines) removed -- so every ``Postcode`` instance is canonical by
construction. Two postcodes that differ only in surface whitespace or
case therefore compare equal.
Attributes:
value: The canonical postcode string (e.g. ``"SW1A1AA"``).
"""
value: str
def __post_init__(self) -> None:
# Frozen dataclass: bypass the descriptor with object.__setattr__.
object.__setattr__(self, "value", "".join(self.value.split()).upper())
def __str__(self) -> str:
return self.value