feat: inheritance mode, body-field descriptions, keep soft keywords - #1
Closed
K1rL3s wants to merge 2 commits into
Closed
feat: inheritance mode, body-field descriptions, keep soft keywords#1K1rL3s wants to merge 2 commits into
K1rL3s wants to merge 2 commits into
Conversation
Three additions, driven by generating a hand-written client's shape:
- `--inheritance` renders `allOf: [{$ref: Base}, ...]` as a real base class
instead of merging the parent's fields into every subtype. A discriminated
base stays a model (its own properties survive) and its mapped subtypes
inherit from it, re-declaring only the discriminator tag. Declarations are
emitted parent-first so a `class Sub(Base)` statement resolves, and model
constructors become keyword-only because a subclass may pin an inherited
field to a default while adding required fields of its own.
- `IRBodyField.description` carries the schema description of a spread request
body field, which was silently dropped. `IRParameter` already had it.
- `sanitize_identifier` no longer suffixes soft keywords: `type` is a legal
attribute name and an extremely common spec field, so `type_` was noise.
`_` stays reserved.
Verified on a real 130-schema spec: both file layouts x all three serializers
generate code that passes `ruff --isolated` and `mypy --strict`.
Review of 4cdd787 found the inheritance path silently changing decoded data in several shapes. Fixes, most severe first: - A `oneOf` + `discriminator` union holder is no longer force-built as a class. It declares no properties, so `--inheritance` produced `class Button: pass` and every `list[Button]` payload decoded into it, dropping each variant's fields. It stays a union alias; only bases with their own properties become classes. Verified: pydantic again decodes `CallbackButton(payload=...)` and `isinstance(x, ButtonBase)` still holds. - The pinned discriminator tag is reserved against the subtype's existing field names. A sibling property whose wire name only differs in case (`Type` vs `type`) snake-cases to the same identifier, and the two class attributes collapsed into one -- destroying the tag, with ruff reporting nothing. - A subtype that restates an inherited property only to attach prose, or to relax it to nullable, now inherits it instead of emitting an override. `v: str | None` over the base's `v: str` is an `[assignment]` error under `mypy --strict`, so `--inheritance --check` failed on ordinary specs. Genuine narrowings (a `Literal` tag over a `str`) are kept. - The base class is resolved from the schema, not from the half-built `_declarations` registry. A base whose own body refers back to its subtype (a recursive hierarchy) has no entry yet, so inheritance silently degraded to a field merge based on nothing but graph traversal order. - Keyword-only constructors are limited to the models in a hierarchy. One `allOf` subtype used to flip every model in the package, breaking positional construction for models with no relation to it. - A discriminated base kept as a class emits its mapping as a comment. No serializer resolves a subtype from a base-class annotation on its own, and `IRModel.discriminator` was read by nobody, so that was dropped on the floor. - `IRBodyField.description` (and `IRParameter.description`) now reach the generated code as PEP 258 attribute docstrings. Both were carried through the IR and rendered nowhere. Also: `IRModel.base` -> `base_model`, so it can't be confused with `IREnum.base` (the enum's "str"/"int" value type); soft keywords use an allow-list over `keyword.issoftkeyword` so a future Python's new soft keyword stays guarded; `_ordered_declarations` breaks an inheritance cycle instead of emitting a class before its base; the two discriminator-mapping loops share one helper; `_inherited_ref` is computed once and passed down. Verified: both file layouts x all three serializers generate code that passes `ruff check --isolated` and `mypy --strict` (only the pre-existing `BaseMethod.__init_subclass__` no-untyped-call remains). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PZwQzJPfNk2RLwZ8X84vbD
Owner
Author
|
Superseded: the real PR is goduni#5 (same branch). Opened this by mistake because |
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.
Adds
--inheritance(renderallOf: [{$ref: Base}, ...]as a real base class),IRBodyField.description, and stops suffixing soft keywords liketype.See the commits for detail:
4cdd787is the feature,da4b94cis the review pass.🤖 Generated with Claude Code
https://claude.ai/code/session_01PZwQzJPfNk2RLwZ8X84vbD