"""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