Sub-issue of #639 — surface 1 of 3 ("What flows where"), and the one we need first.
Scope
Carry the deprecation message from the Pydantic models through extraction, and render it in the generated Markdown reference. Self-contained and shippable on its own: it delivers the reference-doc value without touching validation or the changelog.
The extraction carrier lands here because Markdown is its first consumer. The PySpark surface (#676) and the deprecation manifest (#677) both read the same carrier, so this issue is their prerequisite.
Marking mechanism
Native Python, no custom marker:
- Fields — Pydantic's
Field(deprecated=...), which accepts True, a message string, or a PEP 702 deprecated(...) marker (also usable directly in Annotated metadata). All forms normalize to FieldInfo.deprecation_message (str | None).
- Models — PEP 702's
@deprecated("message") (typing_extensions.deprecated on our 3.10 floor), which sets __deprecated__ on the class. Read with getattr(cls, "__deprecated__", None).
old_field: str | None = Field(
default=None,
deprecated="Use `new_field` instead. Deprecated in v1.18.0.",
)
@deprecated("Use `NewFeature` instead. Deprecated in v1.18.0.")
class OldFeature(Feature):
...
Work
- Add
deprecated: str | None to FieldSpec and to the model/record spec.
- Populate both in
model_extraction.py: field_info.deprecation_message for fields, getattr(model_class, "__deprecated__", None) for the model.
- Field-level rendering: append a
**Deprecated:** <message> note to the field's Description cell through the existing constraint-note appender, and tag the Type column with (deprecated) alongside (optional).
- Model-level rendering: a
**Deprecated:** <message> banner at the top of the feature page.
- Mark one field and one model deprecated in a test fixture (not in the published schema) and assert the rendered output.
Out of scope
Enum members and bare type aliases (Annotated[...], NewType) have no native @deprecated hook and would need a project-specific convention. Deferred — see the open question on #639.
Acceptance
- A field marked
Field(deprecated=...) in any of the three accepted forms renders its message in the reference.
- A class marked
@deprecated(...) renders a banner on its feature page.
deprecated=True (bare) renders the generic deprecated message rather than dropping the signal.
- Nothing changes in the rendered output for undeprecated fields and models.
Integration points
- Extraction:
packages/overture-schema-codegen/.../codegen/extraction/model_extraction.py — reads FieldInfo at ~159, builds FieldSpec at ~175-183 (description at ~179).
- Carrier:
packages/overture-schema-codegen/.../codegen/extraction/specs.py — FieldSpec at ~122-135, RecordSpec at ~139.
- Markdown:
packages/overture-schema-codegen/.../codegen/markdown/renderer.py — _field_template_context (~203) building _FieldRow (~122), appending via _annotate_constraint_notes (~218). Template codegen/markdown/templates/feature.md.jinja2: field loop (~11-12), page header (~2-4).
Sub-issue of #639 — surface 1 of 3 ("What flows where"), and the one we need first.
Scope
Carry the deprecation message from the Pydantic models through extraction, and render it in the generated Markdown reference. Self-contained and shippable on its own: it delivers the reference-doc value without touching validation or the changelog.
The extraction carrier lands here because Markdown is its first consumer. The PySpark surface (#676) and the deprecation manifest (#677) both read the same carrier, so this issue is their prerequisite.
Marking mechanism
Native Python, no custom marker:
Field(deprecated=...), which acceptsTrue, a message string, or a PEP 702deprecated(...)marker (also usable directly inAnnotatedmetadata). All forms normalize toFieldInfo.deprecation_message(str | None).@deprecated("message")(typing_extensions.deprecatedon our 3.10 floor), which sets__deprecated__on the class. Read withgetattr(cls, "__deprecated__", None).Work
deprecated: str | NonetoFieldSpecand to the model/record spec.model_extraction.py:field_info.deprecation_messagefor fields,getattr(model_class, "__deprecated__", None)for the model.**Deprecated:** <message>note to the field's Description cell through the existing constraint-note appender, and tag the Type column with(deprecated)alongside(optional).**Deprecated:** <message>banner at the top of the feature page.Out of scope
Enum members and bare type aliases (
Annotated[...],NewType) have no native@deprecatedhook and would need a project-specific convention. Deferred — see the open question on #639.Acceptance
Field(deprecated=...)in any of the three accepted forms renders its message in the reference.@deprecated(...)renders a banner on its feature page.deprecated=True(bare) renders the genericdeprecatedmessage rather than dropping the signal.Integration points
packages/overture-schema-codegen/.../codegen/extraction/model_extraction.py— readsFieldInfoat ~159, buildsFieldSpecat ~175-183 (descriptionat ~179).packages/overture-schema-codegen/.../codegen/extraction/specs.py—FieldSpecat ~122-135,RecordSpecat ~139.packages/overture-schema-codegen/.../codegen/markdown/renderer.py—_field_template_context(~203) building_FieldRow(~122), appending via_annotate_constraint_notes(~218). Templatecodegen/markdown/templates/feature.md.jinja2: field loop (~11-12), page header (~2-4).