fix: reconcile models.py with pydantic 2.13's stricter validation - #4
Open
tbsvttr wants to merge 1 commit into
Open
fix: reconcile models.py with pydantic 2.13's stricter validation#4tbsvttr wants to merge 1 commit into
tbsvttr wants to merge 1 commit into
Conversation
The two known codegen bugs carried over from the fork are now fixed at the generator-post-process layer, not just in-place edits on the shipped `models.py`. This means a future regeneration from schema doesn't undo the fix. ## Bugs fixed 1. **String `pattern` constraint on `date` fields.** `SourceInfo.date` is typed as `datetime.date` but the schema's YYYY-MM-DD `pattern` was emitted into the generated `Field()`. Pydantic 2.13 correctly rejects a string-only pattern on a non-string field. 2. **Empty `Features` / `Dangers` / `Denizens` stubs.** Codegen emitted `class Denizens(BaseModel): pass` and then wrote `denizens: Annotated[ Denizens, Field(...)]` where the JSON actually contains a list of `DelveSiteDenizen` items. Same pattern for Features/Dangers on DelveSiteDomain and DelveSiteTheme. The post-processor rewrites the field types to the correct `list[X]` and deletes the orphaned stubs. Both fixes belong upstream in datamodel-code-generator; this script is the workaround until they land there. Documented in the script's docstring and in the README so a future regen doesn't accidentally skip the post-process step. ## Consequences - Pydantic pin widened from `<2.13` to `>=2.13.4,<2.14`. All 10 packages validate cleanly under 2.13.4. - Test `EXPECTED_FAILURES` set is now empty — delve loads without xfail. - README's Status section rewritten to reflect the new baseline (both bugs are documented as "patched by post_process_models.py" instead of "TODO: fix before first PyPI release"). ## Not fixed here The `RootModel[str]` → `TypeAlias = str` conversion for ID types is still outstanding — that's an ergonomics improvement (letting users read `rules.id` directly instead of `rules.id.root`), separate from the correctness bugs above.
Open
4 tasks
tbsvttr
added a commit
that referenced
this pull request
Jul 16, 2026
Extends post_process_models.py with a third rewrite pass that
converts every `class <ThingId>(RootModel[str]): root: Annotated[str,
Field(pattern=…)]` block into `<ThingId>: TypeAlias = Annotated[str,
Field(pattern=…)]`.
## Why
The generator emits every Datasworn ID type as a RootModel wrapper.
Consumers who want the actual string value have to do:
asset.id.root # RootModel unwrap
if asset.id.root.startswith("asset:"): ...
Instead of the more natural:
asset.id
if asset.id.startswith("asset:"): ...
Type-aliasing to `Annotated[str, Field(...)]` gives back the ergonomic
path (`.id` is a plain str) while keeping the pattern validation
intact — Pydantic honors Annotated Field metadata inside model fields
the same way it honors RootModel[str] wrappers.
## Scope
- 79 `RootModel[str]` wrappers converted to TypeAlias in one pass.
0 remain after the pass; running the post-processor a second time
reports no changes.
- Other RootModel variants (`[int]`, `[list[X]]`, `[Union[A, B]]`) are
deliberately left as RootModel — their runtime shape isn't
representable as a plain type alias.
- `TypeAlias` is auto-imported into `typing` if missing (kept the
helper simple: sorts the imports for reproducibility).
## Tests
New `tests/test_type_alias_ergonomics.py` locks in the ergonomic path
so a future regeneration doesn't silently regress:
- `RulesetId` reads as plain str (`.startswith`, `len` work directly)
- `AssetId` same (usable in f-strings without .root)
- MarkdownString same
Two tests (move_id, oracle_id) skip with a documented reason: `Move`
and `OracleRollable` are separately broken by an unrelated codegen
bug — they're emitted as empty discriminated-union bases with
extra='allow', so IDs land in `__pydantic_extra__` instead of as
attributes. That's a follow-up post-process, called out in the
README's Status section.
## Depends on
Stacked on top of #4 (fix: reconcile models.py with pydantic 2.13's
stricter validation). Merge #4 first.
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.
Summary
Fixes both known codegen bugs from
datamodel-code-generatorthat were carried over from the fork as `xfail` + `<2.13` pin. Addsscripts/post_process_models.pyso future regenerations ofmodels.pydon't undo the fix.Bugs
String
patternconstraint ondatefields.SourceInfo.dateis typed asdatetime.datebut the schema's YYYY-MM-DDpatternwas emitted into the generatedField(). Pydantic 2.13 correctly rejects a string-only pattern on a non-string field.Empty
Features/Dangers/Denizensstubs. Codegen emittedclass Denizens(BaseModel): passand referenced it asdenizens: Annotated[Denizens, Field(...)]where the JSON actually contains a list ofDelveSiteDenizenitems. Same pattern for Features/Dangers on both DelveSiteDomain and DelveSiteTheme.What the post-processor does
pattern='[0-9]{4}-…'fromField()blocks whose type isdate_aliased. Only date-shaped patterns; won't touch string field patterns nearby.attr: Stuborattr: Annotated[Stub, Field(…)]with the correctlist[X]type.Consequences
<2.13to>=2.13.4,<2.14. All 10 packages validate cleanly under 2.13.4.EXPECTED_FAILURESin the test is now empty. Delve loads withoutxfail.Test plan
uv run python scripts/post_process_models.pyon a fresh checkout: strips 0 date patterns (already handled in the initial port), rewrites 8 stub sites (features×2 + dangers×2 for both DelveSiteDomain and DelveSiteTheme, plus denizens for DelveSite, plus 3 stub class deletions).uv run pytest -q tests/— 9 passed, 0 xfail.Not fixed here
The
RootModel[str]→TypeAlias = strconversion for ID types (sorules.idreads as a plain string instead of via.root) is still outstanding — that's an ergonomics improvement, separate from these correctness bugs. Called out in the README.