cadrumo.core.text_bounds module

The bounded scalar shapes that recur across every layer, stated once.

Most of this campaign’s aliases name a DOMAIN concept – a filing year, a share of one, a country code – and carry its bound as a consequence. These are different: they name a SHAPE. NonEmptyStr says only that a string carries something, and PositiveCount only that a count is at least one.

That is a weaker kind of canonicality and worth being honest about. The value is not that the name teaches a reader something new; it is that the shape is written down once instead of a hundred and eight times, so a payload cannot quietly disagree with the model it projects about whether empty is allowed. Where a real domain concept exists, its own alias is the better home and these should not be reached for – an evidence reference is EvidenceReference, not a bounded string that happens to be non-empty.

PositiveCount exists because pydantic’s PositiveInt is not a drop-in substitute at these sites. It is Gt(0) where these are Ge(1); for an integer those admit the same values, but they serialise to different JSON schemas – exclusiveMinimum: 0 against minimum: 1 – and these bounds sit on a published envelope contract. NonNegativeInt has no such problem and is used directly; only the positive case needs a local name.

CALENDAR_MONTH_MIN: Final[int]

The first month AEAT numbers.

CALENDAR_MONTH_MAX: Final[int]

The last month AEAT numbers.

is_calendar_month(value)[source]

Whether value names a month, inclusive at both ends.

For the callers that cannot use the annotation because they must raise their OWN refusal: the profile answer reader raises a ProfileAnswerTypeError naming the answer key, the descendant record a ProfileValidationError, the setup wizard returns a failed verdict carrying a locale key, and the CLI payload a plain ValueError pydantic wraps. Those differ on purpose – the operator’s first instructive surface should name the field they typed into – so what is shared is the question, not the answer.

The bound was open-coded as 1 <= month <= 12 at five sites, twice within a single module for the SAME field, which is how a bound stops being one rule and becomes five that happen to agree.

Return type:

bool

Parameters:

value (int)

is_canonical_month_set(months)[source]

Whether months is a canonical set: real months, no repeat, ascending.

The three rules travel together and were written out twice – once on the descendant record and once on the wire payload that projects it. Only the first of the three delegated; uniqueness and ordering were restated.

Ascending order is ENFORCED rather than applied, so a record has exactly one representation of a given set and a save-then-reload cannot reorder it. A repeat is refused rather than collapsed, because a month either qualified or it did not and silently dropping the second mention would hide a transcription slip while changing nothing the operator can see.

A predicate rather than a validator because the two callers raise different errors on purpose – the record a ProfileValidationError naming the repeated months, the payload a plain ValueError pydantic wraps – and the detail each builds is a message, not a rule.

Return type:

bool

Parameters:

months (tuple[int, ...])