Skip to content

--enum-field-as-literal=all + --use-type-alias inlines property-referenced enum schemas instead of emitting a named Literal alias #3104

@vdusek

Description

@vdusek

Describe the bug

With --enum-field-as-literal=all + --use-type-alias on a Pydantic v2 OpenAPI spec, named enum schemas in components.schemas only become a top-level TypeAliasType("Name", Literal[...]) if they are referenced from at least one operation parameter. Enums referenced only from another schema's properties are inlined at every reference site, with the original name reduced to a Field(title="<Name>") hint.

This contradicts the resolution claimed for #1285 in PR #2505, which describes the combination as producing a named alias for any enum schema regardless of how it is referenced.

To reproduce

EnumFromParam is referenced from a query parameter; EnumFromProperty is referenced from a schema property.

Example schema:

{
  "openapi": "3.0.3",
  "info": { "title": "repro", "version": "0.0.1" },
  "paths": {
    "/things": {
      "get": {
        "parameters": [
          {
            "name": "ownership",
            "in": "query",
            "schema": { "$ref": "#/components/schemas/EnumFromParam" }
          }
        ],
        "responses": {
          "200": {
            "description": "ok",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Thing" }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "EnumFromParam":    { "type": "string", "enum": ["a", "b"] },
      "EnumFromProperty": { "type": "string", "enum": ["x", "y"] },
      "Thing": {
        "type": "object",
        "properties": {
          "kind": { "$ref": "#/components/schemas/EnumFromProperty" }
        }
      }
    }
  }
}

Used command line:

$ datamodel-codegen \
    --input repro.json \
    --input-file-type openapi \
    --output out.py \
    --target-python-version 3.11 \
    --output-model-type pydantic_v2.BaseModel \
    --use-annotated \
    --use-union-operator \
    --enum-field-as-literal all \
    --use-type-alias

Observed output:

EnumFromParam = TypeAliasType("EnumFromParam", Literal['a', 'b'])

class Thing(BaseModel):
    kind: Annotated[Literal['x', 'y'] | None, Field(title='EnumFromProperty')] = None

Expected behavior

Both named enum schemas should produce a top-level alias, identically — not just the parameter-referenced one:

EnumFromParam = TypeAliasType("EnumFromParam", Literal['a', 'b'])
EnumFromProperty = TypeAliasType("EnumFromProperty", Literal['x', 'y'])

class Thing(BaseModel):
    kind: EnumFromProperty | None = None

Version

  • OS: Fedora Linux 43
  • Python version: 3.11
  • datamodel-code-generator version: 0.56.0

Additional context

Variations that don't change the behavior: --collapse-root-models on/off, --reuse-model, removing --use-schema-description, stripping title/description from the enum schema. --enum-field-as-literal=one instead of all falls back to class Name(StrEnum): ... for every named enum.

Without a stable named alias, the enum's value list is duplicated at every reference site (painful for large enums — e.g. an ErrorType with hundreds of members) and downstream code can't import the name to reuse in its own annotations. The Field(title=...) hint is documentation-only.

Two possible fixes:

  1. Make --enum-field-as-literal=all + --use-type-alias emit a named alias for every enum schema in components.schemas, regardless of how it is referenced.
  2. Or add a dedicated flag (e.g. --enum-as-literal-alias) that emits Name: TypeAlias = Literal[...] for named enum schemas only, independently of --use-type-alias.

Related: #1285, #2505 (original request and the PR that was supposed to cover this), #3009, #3092 (related bugs blocking adjacent workarounds).

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