fix: enforce minProperties on generated models - #55
Merged
damaz91 merged 5 commits intoJul 17, 2026
Conversation
…otocol#49) datamodel-code-generator drops minProperties on object schemas with declared properties, so Description() validated with zero fields in violation of description.json's minProperties: 1. (minProperties on free-form object properties is unaffected — the generator already maps those to Field(min_length=...) on the dict field.) A new post-generation step (postprocess_models.py, wired into generate_models.sh before formatting) scans the preprocessed schemas for root-level minProperties constraints and injects a model_validator(mode="after") into each matching generated class. JSON Schema counts the keys present on the object, so the validator counts provided fields (model_fields_set) unioned with extra keys (model_extra): an explicit null is a present key, and unknown keys on extra="allow" models count too. The step is idempotent, data-driven from the schemas (future minProperties additions are picked up on regeneration), and fails generation loudly if a constraint can't be mapped to a generated class. The committed Description model is regenerated with the validator. Tests cover the injector (dependency-free) and the enforced semantics on Description; the workflow now installs the package so those semantic tests run in CI.
damaz91
approved these changes
Jul 17, 2026
Deletes tests/test_min_properties.py and merges its test cases into tests/test_codegen_pipeline.py (renamed from test_preprocess_schemas.py to better reflect its expanded scope of testing both pre- and post-processing).
Replaces print statements with sys.stdout.write/sys.stderr.write (T201), splits long lines (E501), and adds docstring to main (D103) to comply with project lint configuration.
Replaces propert{y_ies} template variable with properties_noun to avoid codespell false positive on propert. Regenerated models to apply template change.
17 tasks
XiaolongZhang-TT
added a commit
to XiaolongZhang-TT/python-sdk
that referenced
this pull request
Jul 30, 2026
normalize_metadata_schemas hardcoded the UcpMetadata root union as five
members (platform, business, response_{checkout,order,cart}) and so
silently omitted response_catalog_schema, which is defined in ucp.json at
the 2026-04-08 release the SDK targets. The generated UcpMetadata
(src/ucp_sdk/models/schemas/ucp.py) therefore lacked ResponseCatalogSchema
(the class was generated from the $def, but the union omitted it), so a
UCP message carrying catalog-response metadata failed validation against
every model's `ucp` field.
Derive the union members from ucp.json's $defs instead: the discovery
profiles (platform/business) plus every response_*_schema. This fixes the
missing catalog entry and keeps the union complete as the protocol adds
response types, matching the data-driven approach used for minProperties
(Universal-Commerce-Protocol#55). Output order follows $defs insertion order, so existing members are
unchanged and catalog is appended.
Update the generated UcpMetadata in ucp.py accordingly. A full regeneration
is intentionally not bundled here: the tracked generated tree predates the
current ruff toolchain and regenerating adds unrelated formatting churn, so
only the one union line is edited — exactly what regeneration produces for
this fix.
Add MetadataUnionTest covering the derivation (profiles + all response
schemas, automatic pickup of new response types, exclusion of non-schema
defs such as request_schema, empty $defs) and update the existing
normalize_metadata_schemas test to assert the six-member union.
damaz91
added a commit
that referenced
this pull request
Jul 31, 2026
…og (#58) * fix(preprocess): derive UcpMetadata union from $defs to include catalog normalize_metadata_schemas hardcoded the UcpMetadata root union as five members (platform, business, response_{checkout,order,cart}) and so silently omitted response_catalog_schema, which is defined in ucp.json at the 2026-04-08 release the SDK targets. The generated UcpMetadata (src/ucp_sdk/models/schemas/ucp.py) therefore lacked ResponseCatalogSchema (the class was generated from the $def, but the union omitted it), so a UCP message carrying catalog-response metadata failed validation against every model's `ucp` field. Derive the union members from ucp.json's $defs instead: the discovery profiles (platform/business) plus every response_*_schema. This fixes the missing catalog entry and keeps the union complete as the protocol adds response types, matching the data-driven approach used for minProperties (#55). Output order follows $defs insertion order, so existing members are unchanged and catalog is appended. Update the generated UcpMetadata in ucp.py accordingly. A full regeneration is intentionally not bundled here: the tracked generated tree predates the current ruff toolchain and regenerating adds unrelated formatting churn, so only the one union line is edited — exactly what regeneration produces for this fix. Add MetadataUnionTest covering the derivation (profiles + all response schemas, automatic pickup of new response types, exclusion of non-schema defs such as request_schema, empty $defs) and update the existing normalize_metadata_schemas test to assert the six-member union. * fix(models): include catalog response in metadata request variants * style: fix end of file newlines in regenerated schema modules --------- Co-authored-by: damaz91 <federico.damato91@gmail.com>
17 tasks
damaz91
pushed a commit
to XiaolongZhang-TT/python-sdk
that referenced
this pull request
Aug 3, 2026
datamodel-code-generator drops `uniqueItems`, so generated list fields accept duplicate entries in violation of the schema. Three UCP array properties declare `uniqueItems: true` at 2026-04-08: context.eligibility, card_payment_instrument.brands, and identity_linking.required_claims. Extend postprocess_models.py to collect array property names declared with `uniqueItems` and inject a `field_validator(mode="after")` into each generated class that declares a matching list field. The check uses equality (`item in seen`) so it holds for both hashable (str) and unhashable (model) items. Mirrors the data-driven, idempotent approach used for minProperties (Universal-Commerce-Protocol#55). Two generated models carry the affected list fields and gain the validator: Context.eligibility and Constraints.brands. required_claims has no generated typed field (ScopePolicy is extra="allow" free-form), so it is not enforceable and is skipped. Add UniqueItemsInjectorTest (scan walks nested properties and ignores non-arrays; injection targets only matching list fields, is idempotent, and enforces uniqueness when exec'd) and UniqueItemsSemanticTest (Constraints rejects duplicate brands, accepts unique/None).
damaz91
added a commit
that referenced
this pull request
Aug 3, 2026
* fix: enforce uniqueItems on generated array fields datamodel-code-generator drops `uniqueItems`, so generated list fields accept duplicate entries in violation of the schema. Three UCP array properties declare `uniqueItems: true` at 2026-04-08: context.eligibility, card_payment_instrument.brands, and identity_linking.required_claims. Extend postprocess_models.py to collect array property names declared with `uniqueItems` and inject a `field_validator(mode="after")` into each generated class that declares a matching list field. The check uses equality (`item in seen`) so it holds for both hashable (str) and unhashable (model) items. Mirrors the data-driven, idempotent approach used for minProperties (#55). Two generated models carry the affected list fields and gain the validator: Context.eligibility and Constraints.brands. required_claims has no generated typed field (ScopePolicy is extra="allow" free-form), so it is not enforceable and is skipped. Add UniqueItemsInjectorTest (scan walks nested properties and ignores non-arrays; injection targets only matching list fields, is idempotent, and enforces uniqueness when exec'd) and UniqueItemsSemanticTest (Constraints rejects duplicate brands, accepts unique/None). * chore: bump version to 0.4.4 and regenerate models --------- Co-authored-by: damaz91 <federico.damato91@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #49:
datamodel-code-generatordropsminPropertieson object schemas with declared properties, soDescription()validates with zero fields in violation ofdescription.json'sminProperties: 1. (The free-form-object case is unaffected — the generator already maps that toField(min_length=...)on the dict field, e.g.AvailablePaymentInstrument.constraints.)Implements the post-processing option proposed in the issue: a new
postprocess_models.pystep (wired intogenerate_models.shbetween generation and formatting) scans the preprocessed schemas for root-levelminPropertiesconstraints and injects amodel_validator(mode="after")into each matching generated class.Counting semantics follow JSON Schema (keys present on the object), verified empirically against pydantic: provided fields (
model_fields_set) unioned with extra keys (model_extra) — an explicitnullis a present key, and unknown keys onextra="allow"models count too; the union avoids double-counting since pydantic includes extras inmodel_fields_set.Properties of the step:
minPropertiesadditions to any schema are picked up on regeneration, no code change needed (currentlydescription.jsonis the only root-level case onucpmain; there are no nested property-carrying cases).Descriptionmodel is regenerated with the validator.Validation
tests/test_min_properties.py: dependency-free injector tests (insertion, idempotency, schema scan, multi-class modules) + semantic tests on the realDescriptionmodel (empty rejected; single field, explicit-null key, and extra-only key accepted). Fullunittest discover: 23/23 green.pip install -e .so the semantic tests execute in CI rather than skipping.ruff format+ruff checkclean.