§3 landed — one pint-defined canonical unit authority ✓ landed 2026-07-22
Evergreen: sn-dd-unit-curation §3.
Resolves followup f-sdu-001. Delivers the ISN-side
canonical_unit() helper the downstream imas-codex mismatch axis imports,
collapses the three divergent unit-formatting paths (§2) onto one authority, and adds the
stability + round-trip tests that prove ordering-only and spelling-only mismatches vanish.
What shipped
| # | Change | File |
|---|---|---|
| 1 | canonical_unit(s) added — the single source-of-truth formatter
(pint parse + ~F, integer exponents, ASCII short symbols).
Exported as from imas_standard_names import canonical_unit. |
imas_standard_names/__init__.py |
| 2 | The divergent "U" formatter (long names, float exponents, unsorted)
is removed. Grep confirmed no in-repo consumer of it or of the
units module registry; the DD-alias registry is retained. |
imas_standard_names/units/__init__.py |
| 3 | The scalar/vector/name-only unit validators (_canonicalize_unit_order)
delegate ordering to canonical_unit() instead of the hand-rolled
regex parse+sort — one ordering authority. Authoring guards (no whitespace,
no //*, "1" for dimensionless) stay. |
imas_standard_names/models.py |
| 4 | Stability test: canonical_unit() output must match the dot-exponent
grammar (sorted ASCII short tokens, integer exponents, no .0) over the
full catalog unit set — a pint bump or symbol re-spelling fails CI. Plus round-trip
and validator-parity tests. |
tests/name_schema/test_canonical_unit.py |
The spelling-drift trap §2 warned about — caught and closed
§2 finding #3 flagged that pint's short symbols can diverge from authored ASCII tokens.
A catalog scan proved it live: the authored unit m.ohm renders through pint
~F as m.Ω (Unicode Ω) — and micro-prefixed units would render
µs, degrees Celsius °C. A naïve pint switch would have silently
rewritten stored units and injected non-ASCII glyphs, breaking the migration invariant
(do not rewrite stored units).
Fix: the single ~F formatter now maps pint's non-ASCII short
glyphs to their ASCII spellings (Ω→ohm, µ→u, °→deg)
before sorting, so ordering matches the retired regex byte-for-byte. Verified: across
all 20 distinct catalog units, the pint form and the old regex form now agree
exactly (0 divergences). Any future glyph not in the ASCII map survives into the
output and trips the stability test rather than corrupting a stored unit.
Reconciliation property proven
Ordering and spelling variants of one physical unit normalize equal — the exact property the downstream axis relies on:
| input | canonical_unit() |
|---|---|
s^-1.m, m.s^-1 | m.s^-1 (float-exp .0 artifact gone) |
T.m^-2.A, m^-2.A.T | A.T.m^-2 |
s.kg.m^2, s.m^2.kg | kg.m^2.s |
m.ohm, ohm.m | m.ohm (ASCII, not m.Ω) |
Downstream comparison becomes
canonical_unit(sn_unit) == canonical_unit(dd_unit) — a pure string compare after
both sides are normalized through the one authority.
Verification
New tests/name_schema/test_canonical_unit.py green; the unit-focused groups
(catalog validation, storage/loader, codegen drift, JSON-schema contract, semantic units,
name-schema, name-only) pass. The one pre-existing suite failure
(test_find_git_root_raises_when_no_repo, an environmental tmp-path flake) is
unrelated to units and predates this change.