Skip to content

Commit 655a011

Browse files
committed
chore(models): regenerate against the pinned 2026-08-25 UCP schema
Regenerates via ./generate_models.sh 2026-08-25 (the same command the model-drift CI job runs) to pick up the postprocessing fix in the prior commit. Six files change: - profile.py: JwkPublicKey gains both a conditional-required validator (EC needs crv/x/y, OKP needs crv/x) and a conditional-bounds validator (P-256/ES256, P-384/ES384, Ed25519/EdDSA pairing). - common/types/unit.py (+ its create/update request variants): Unit gains a conditional-bounds validator pinning scale to 0 when unit is C62. - common/types/total.py, common/types/totals.py: unchanged behavior, picked up only because the conditional-bounds checks dict (embedded verbatim in every class using this validator family) now also carries the const entry. Verified: - Full suite: 109 tests, 0 failures, 4 documented skips (all six new tests from the RED commit now pass). - Double-regen: ran generate_models.sh 2026-08-25 twice; diff -rq between both outputs (excluding __pycache__) is empty. - Kill-test: reverted postprocess_models.py to its pre-fix state, regenerated, reinstalled -- the same 9 failures + 1 error from the RED commit reappeared verbatim. Restored the fix and regenerated again to confirm the suite returns to green. - pre-commit run on the changed files: clean (ruff, ruff-format, codespell, trailing-whitespace, end-of-file-fixer all pass). Not committed: README.md, which ruff format also reformats (a pre-existing docstring-code-block spacing drift in main, unrelated to this fix -- the model-drift CI job only diffs src/ucp_sdk/models/schemas, so this was never caught there). Left untouched to keep this diff scoped to the constraint fix.
1 parent 7eca00b commit 655a011

6 files changed

Lines changed: 185 additions & 4 deletions

File tree

src/ucp_sdk/models/schemas/common/types/total.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ def _enforce_conditional_bounds(self):
6363
"maximum": ("<=", "gt"),
6464
"exclusiveMinimum": (">", "le"),
6565
"exclusiveMaximum": ("<", "ge"),
66+
"const": ("==", "ne"),
6667
}
6768
for rule in rules:
6869
actual = getattr(self, rule["discriminator"], None)

src/ucp_sdk/models/schemas/common/types/totals.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ def _enforce_conditional_bounds(self):
7979
"maximum": ("<=", "gt"),
8080
"exclusiveMinimum": (">", "le"),
8181
"exclusiveMaximum": ("<", "ge"),
82+
"const": ("==", "ne"),
8283
}
8384
for rule in rules:
8485
actual = getattr(self, rule["discriminator"], None)

src/ucp_sdk/models/schemas/common/types/unit.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818

1919
from __future__ import annotations
2020

21-
from pydantic import BaseModel, ConfigDict, Field
21+
import operator
22+
23+
from pydantic import BaseModel, ConfigDict, Field, model_validator
2224

2325

2426
class Unit(BaseModel):
@@ -41,3 +43,37 @@ class Unit(BaseModel):
4143
"""
4244
Required printable unit label provided by the Business. The Platform MUST use it when it does not recognize `unit`; for a recognized UN/CEFACT Rec 20 Common Code, the Platform MAY substitute its own localized label. It does not participate in unit identity or mismatch comparison.
4345
"""
46+
47+
@model_validator(mode="after")
48+
def _enforce_conditional_bounds(self):
49+
"""JSON Schema if/then: enforce conditional numeric bounds."""
50+
rules = [
51+
{
52+
"discriminator": "unit",
53+
"values": ["C62"],
54+
"bounds": {"scale": {"const": 0}},
55+
}
56+
]
57+
checks = {
58+
"minimum": (">=", "lt"),
59+
"maximum": ("<=", "gt"),
60+
"exclusiveMinimum": (">", "le"),
61+
"exclusiveMaximum": ("<", "ge"),
62+
"const": ("==", "ne"),
63+
}
64+
for rule in rules:
65+
actual = getattr(self, rule["discriminator"], None)
66+
if actual not in rule["values"]:
67+
continue
68+
for field, bounds in rule["bounds"].items():
69+
value = getattr(self, field, None)
70+
if value is None:
71+
continue
72+
for keyword, limit in bounds.items():
73+
symbol, op_name = checks[keyword]
74+
if getattr(operator, op_name)(value, limit):
75+
raise ValueError(
76+
f"Field {field!r} must be {symbol} {limit} "
77+
f"when {rule['discriminator']} is {actual!r}"
78+
)
79+
return self

src/ucp_sdk/models/schemas/common/types/unit_create_request.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818

1919
from __future__ import annotations
2020

21-
from pydantic import BaseModel, ConfigDict, Field
21+
import operator
22+
23+
from pydantic import BaseModel, ConfigDict, Field, model_validator
2224

2325

2426
class UnitCreateRequest(BaseModel):
@@ -41,3 +43,37 @@ class UnitCreateRequest(BaseModel):
4143
"""
4244
Required printable unit label provided by the Business. The Platform MUST use it when it does not recognize `unit`; for a recognized UN/CEFACT Rec 20 Common Code, the Platform MAY substitute its own localized label. It does not participate in unit identity or mismatch comparison.
4345
"""
46+
47+
@model_validator(mode="after")
48+
def _enforce_conditional_bounds(self):
49+
"""JSON Schema if/then: enforce conditional numeric bounds."""
50+
rules = [
51+
{
52+
"discriminator": "unit",
53+
"values": ["C62"],
54+
"bounds": {"scale": {"const": 0}},
55+
}
56+
]
57+
checks = {
58+
"minimum": (">=", "lt"),
59+
"maximum": ("<=", "gt"),
60+
"exclusiveMinimum": (">", "le"),
61+
"exclusiveMaximum": ("<", "ge"),
62+
"const": ("==", "ne"),
63+
}
64+
for rule in rules:
65+
actual = getattr(self, rule["discriminator"], None)
66+
if actual not in rule["values"]:
67+
continue
68+
for field, bounds in rule["bounds"].items():
69+
value = getattr(self, field, None)
70+
if value is None:
71+
continue
72+
for keyword, limit in bounds.items():
73+
symbol, op_name = checks[keyword]
74+
if getattr(operator, op_name)(value, limit):
75+
raise ValueError(
76+
f"Field {field!r} must be {symbol} {limit} "
77+
f"when {rule['discriminator']} is {actual!r}"
78+
)
79+
return self

src/ucp_sdk/models/schemas/common/types/unit_update_request.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818

1919
from __future__ import annotations
2020

21-
from pydantic import BaseModel, ConfigDict, Field
21+
import operator
22+
23+
from pydantic import BaseModel, ConfigDict, Field, model_validator
2224

2325

2426
class UnitUpdateRequest(BaseModel):
@@ -41,3 +43,37 @@ class UnitUpdateRequest(BaseModel):
4143
"""
4244
Required printable unit label provided by the Business. The Platform MUST use it when it does not recognize `unit`; for a recognized UN/CEFACT Rec 20 Common Code, the Platform MAY substitute its own localized label. It does not participate in unit identity or mismatch comparison.
4345
"""
46+
47+
@model_validator(mode="after")
48+
def _enforce_conditional_bounds(self):
49+
"""JSON Schema if/then: enforce conditional numeric bounds."""
50+
rules = [
51+
{
52+
"discriminator": "unit",
53+
"values": ["C62"],
54+
"bounds": {"scale": {"const": 0}},
55+
}
56+
]
57+
checks = {
58+
"minimum": (">=", "lt"),
59+
"maximum": ("<=", "gt"),
60+
"exclusiveMinimum": (">", "le"),
61+
"exclusiveMaximum": ("<", "ge"),
62+
"const": ("==", "ne"),
63+
}
64+
for rule in rules:
65+
actual = getattr(self, rule["discriminator"], None)
66+
if actual not in rule["values"]:
67+
continue
68+
for field, bounds in rule["bounds"].items():
69+
value = getattr(self, field, None)
70+
if value is None:
71+
continue
72+
for keyword, limit in bounds.items():
73+
symbol, op_name = checks[keyword]
74+
if getattr(operator, op_name)(value, limit):
75+
raise ValueError(
76+
f"Field {field!r} must be {symbol} {limit} "
77+
f"when {rule['discriminator']} is {actual!r}"
78+
)
79+
return self

src/ucp_sdk/models/schemas/profile.py

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818

1919
from __future__ import annotations
2020

21-
from pydantic import BaseModel, ConfigDict, Field
21+
import operator
22+
23+
from pydantic import BaseModel, ConfigDict, Field, model_validator
2224

2325
from . import ucp as ucp_1
2426

@@ -60,6 +62,75 @@ class JwkPublicKey(BaseModel):
6062
JWK public key use. UCP examples use sig for signatures.
6163
"""
6264

65+
@model_validator(mode="after")
66+
def _enforce_conditional_required(self):
67+
"""JSON Schema if/then: enforce conditionally required fields."""
68+
rules = [
69+
{
70+
"discriminator": "kty",
71+
"values": ["EC"],
72+
"required": ["crv", "x", "y"],
73+
},
74+
{
75+
"discriminator": "kty",
76+
"values": ["OKP"],
77+
"required": ["crv", "x"],
78+
},
79+
]
80+
for rule in rules:
81+
if getattr(self, rule["discriminator"], None) not in rule["values"]:
82+
continue
83+
for field in rule["required"]:
84+
if field not in self.model_fields_set:
85+
raise ValueError(
86+
f"Field {field!r} is required by a schema condition"
87+
)
88+
return self
89+
90+
@model_validator(mode="after")
91+
def _enforce_conditional_bounds(self):
92+
"""JSON Schema if/then: enforce conditional numeric bounds."""
93+
rules = [
94+
{
95+
"discriminator": "crv",
96+
"values": ["P-256"],
97+
"bounds": {"alg": {"const": "ES256"}},
98+
},
99+
{
100+
"discriminator": "crv",
101+
"values": ["P-384"],
102+
"bounds": {"alg": {"const": "ES384"}},
103+
},
104+
{
105+
"discriminator": "crv",
106+
"values": ["Ed25519"],
107+
"bounds": {"alg": {"const": "EdDSA"}},
108+
},
109+
]
110+
checks = {
111+
"minimum": (">=", "lt"),
112+
"maximum": ("<=", "gt"),
113+
"exclusiveMinimum": (">", "le"),
114+
"exclusiveMaximum": ("<", "ge"),
115+
"const": ("==", "ne"),
116+
}
117+
for rule in rules:
118+
actual = getattr(self, rule["discriminator"], None)
119+
if actual not in rule["values"]:
120+
continue
121+
for field, bounds in rule["bounds"].items():
122+
value = getattr(self, field, None)
123+
if value is None:
124+
continue
125+
for keyword, limit in bounds.items():
126+
symbol, op_name = checks[keyword]
127+
if getattr(operator, op_name)(value, limit):
128+
raise ValueError(
129+
f"Field {field!r} must be {symbol} {limit} "
130+
f"when {rule['discriminator']} is {actual!r}"
131+
)
132+
return self
133+
63134

64135
class UcpProfileDocument(BaseModel):
65136
"""

0 commit comments

Comments
 (0)