Skip to content

Enhancement: Support true model inheritance for allOf composed schemas #110

@dougborg

Description

@dougborg

Summary

Currently the generator performs a limited flattening of allOf when (and only when) the composed schema has no own properties. This helps fill in otherwise empty models (ex: schemas composed only from parents) but it does not implement true inheritance semantics.

Problems With Current Behavior

  1. Mixed augmentation is lost: A schema that has its own properties and allOf parents only emits its direct properties; inherited ones are ignored.
  2. Pure reference composition stays empty: allOf: [{ "$ref": A }, { "$ref": B }] does not surface any fields.
  3. Required propagation incomplete: required from referenced parents is not carried over unless the parent was inline with properties (and only in the current edge flattening case).
  4. Diagnostic ambiguity: Generated models give no indication that inheritance occurred or was intended.

Desired Behavior (Preferred Approach)

Implement true inheritance for allOf chains by generating base classes first and then emitting subclasses that inherit from them rather than flattening fields. This preserves structural intent, yields cleaner diffs against the source spec, and reduces accidental name collisions.

Example (conceptual):

components:
  schemas:
    Base:
      type: object
      properties:
        id:
          type: string
      required: [id]
    Child:
      allOf:
        - $ref: '#/components/schemas/Base'
        - type: object
          properties:
            name: { type: string }
          required: [name]

Generated (Pydantic v2 template example):

class Base(BaseModel):
    id: str

class Child(Base):
    name: str

If multiple parents:

Child:
  allOf:
    - $ref: '#/components/schemas/BaseA'
    - $ref: '#/components/schemas/BaseB'
    - type: object
      properties: { extra: { type: integer } }

Becomes:

class Child(BaseA, BaseB):
    extra: int

Proposed Implementation Phases

  1. Graph Analysis

    • Build dependency graph of schemas using allOf references.
    • Topologically sort to ensure parent models are emitted before children.
    • Detect cycles (fail fast with clear error or skip inheritance).
  2. Parent Extraction

    • For each schema with allOf, separate referenced parents ($ref) from inline augmentation schemas (object schemas that add properties / required / description).
  3. Code Generation Adjustments

    • Modify model template context to accept a list of base class names (instead of always BaseModel).
    • Preserve existing flatten fallback only for pathological cases (e.g., cycle, invalid parent reference, no valid object parent found).
  4. Required Handling

    • Do NOT re-declare inherited properties in child class.
    • Only emit properties from inline augmentation fragments.
  5. Edge Cases / Guards

    • Multiple parents: maintain order of appearance in allOf.
    • Conflicting property names: inline augmentation property names that clash with parent names should raise a warning (or skip with note) instead of silently overriding.
    • Non-object fragments inside allOf (e.g., constraints-only fragments) should be merged into validation metadata later (out of scope for first cut—document limitation).
  6. Backwards Compatibility

    • Keep current flatten logic behind a feature flag (e.g., --flatten-allof vs default inheritance) OR
    • Introduce new flag --inherit-allof defaulting to True in next minor version; mark flattening behavior as deprecated.

Alternatives (for discussion)

  1. Always Flatten (Current + merging refs) – simplest but loses intent.
  2. Hybrid: Inherit if only refs + one inline object, otherwise flatten.
  3. Recursive Full Merge – produce a single class with all merged fields; potential name conflicts & loss of structure.
  4. True Inheritance (Preferred) – described above.

Preference

Adopt Option 4 (True Inheritance) with implementation phases outlined.

Acceptance Criteria

  • A schema composed purely of references generates a subclass inheriting all parents (empty body -> pass).
  • A schema with inline augmentation emits only augmentation fields; inherited ones appear via base classes.
  • Multiple parent references preserved in declared order.
  • Tests cover: single parent, multiple parents, parent + augmentation, pure refs, cycle detection.
  • Flag / configuration documented in README and quick start.

Follow-Up / Future Enhancements

  • Merge validation keywords from parent and inline fragments (minLength, pattern, etc.) into field constraints.
  • Support discriminator-based polymorphism for oneOf / anyOf.

Requested Labels

enhancement, openapi-3.1, models, inheritance


Requested by user; preference strongly for true inheritance approach.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions