Skip to content

Python Version Compatibility

datamodel-code-generator can generate code compatible with different Python versions. This page explains how to control type annotation syntax and imports for your target environment.

Quick Overview

Option Description
--target-python-version Set the minimum Python version for generated code
--use-union-operator Use X \| Y instead of Union[X, Y]
--use-standard-collections Use list, dict instead of List, Dict
--use-annotated Use Annotated for field metadata
--use-generic-container-types Use generic types like List[T]
--disable-future-imports Don't add from __future__ import annotations

--target-python-version

Sets the minimum Python version for the generated code. This automatically adjusts type annotation syntax.

Version Union Syntax Collection Syntax Notes
3.8 Union[X, Y] List[T], Dict[K, V] Requires typing imports
3.9 Union[X, Y] list[T], dict[K, V] Built-in generics available
3.10+ X \| Y list[T], dict[K, V] Union operator available

Example: Python 3.8

datamodel-codegen --input schema.json --output models.py \
  --target-python-version 3.8
from typing import List, Optional, Union

class User(BaseModel):
    id: int
    tags: List[str]
    metadata: Optional[Union[str, int]] = None

Example: Python 3.10+

datamodel-codegen --input schema.json --output models.py \
  --target-python-version 3.10
class User(BaseModel):
    id: int
    tags: list[str]
    metadata: str | int | None = None

--use-union-operator

Uses the | operator for union types instead of Union[X, Y].

Without --use-union-operator

from typing import Union, Optional

class Item(BaseModel):
    value: Union[str, int]
    label: Optional[str] = None  # Same as Union[str, None]

With --use-union-operator

datamodel-codegen --input schema.json --output models.py --use-union-operator
class Item(BaseModel):
    value: str | int
    label: str | None = None

Compatibility Note

The union operator | requires: - Python 3.10+ at runtime, OR - from __future__ import annotations (Python 3.7+) for postponed evaluation


--use-standard-collections

Uses built-in collection types instead of typing module generics.

Without --use-standard-collections

from typing import List, Dict, Set, Tuple

class Data(BaseModel):
    items: List[str]
    mapping: Dict[str, int]
    unique: Set[str]
    pair: Tuple[str, int]

With --use-standard-collections

datamodel-codegen --input schema.json --output models.py --use-standard-collections
class Data(BaseModel):
    items: list[str]
    mapping: dict[str, int]
    unique: set[str]
    pair: tuple[str, int]

Compatibility Note

Built-in generic syntax requires: - Python 3.9+ at runtime, OR - from __future__ import annotations (Python 3.7+)


--use-annotated

Uses typing.Annotated to attach metadata to types, which is the modern approach for Pydantic v2.

Without --use-annotated

from pydantic import Field

class User(BaseModel):
    name: str = Field(..., min_length=1, max_length=100)
    age: int = Field(..., ge=0, le=150)

With --use-annotated

datamodel-codegen --input schema.json --output models.py --use-annotated
from typing import Annotated
from pydantic import Field

class User(BaseModel):
    name: Annotated[str, Field(min_length=1, max_length=100)]
    age: Annotated[int, Field(ge=0, le=150)]

Benefits

  • Cleaner separation of type and constraints
  • Better IDE support
  • More compatible with other tools that understand Annotated
  • Required types are more explicit

--use-generic-container-types

Uses generic container types from typing module explicitly.

datamodel-codegen --input schema.json --output models.py --use-generic-container-types
from typing import List, Dict, Set

class Data(BaseModel):
    items: List[str]
    mapping: Dict[str, int]

This is useful when: - Targeting Python 3.8 or earlier - Working with tools that require explicit typing imports - Maintaining compatibility with legacy codebases


--disable-future-imports

Prevents adding from __future__ import annotations to generated files.

Default behavior (with future imports)

from __future__ import annotations

class User(BaseModel):
    friends: list[User]  # Forward reference works due to PEP 563

With --disable-future-imports

datamodel-codegen --input schema.json --output models.py --disable-future-imports
from typing import List, TYPE_CHECKING

if TYPE_CHECKING:
    from typing import ForwardRef

class User(BaseModel):
    friends: List["User"]  # String forward reference

When to disable

  • Compatibility with runtime annotation inspection
  • Libraries that don't support __future__.annotations
  • Pydantic v1 in some configurations
  • When using get_type_hints() at runtime

Common Patterns

Pattern 1: Modern Python (3.10+)

For projects targeting Python 3.10 or later:

datamodel-codegen --input schema.json --output models.py \
  --target-python-version 3.10 \
  --use-union-operator \
  --use-standard-collections \
  --use-annotated

Output:

from typing import Annotated
from pydantic import BaseModel, Field

class User(BaseModel):
    id: int
    name: Annotated[str, Field(min_length=1)]
    tags: list[str]
    metadata: dict[str, str] | None = None

Pattern 2: Legacy Python (3.8)

For projects requiring Python 3.8 compatibility:

datamodel-codegen --input schema.json --output models.py \
  --target-python-version 3.8 \
  --use-generic-container-types \
  --disable-future-imports

Output:

from typing import Dict, List, Optional, Union

class User(BaseModel):
    id: int
    name: str
    tags: List[str]
    metadata: Optional[Dict[str, str]] = None

Pattern 3: Maximum compatibility

For libraries that need to work across multiple Python versions:

datamodel-codegen --input schema.json --output models.py \
  --target-python-version 3.8

The generator will use from __future__ import annotations to enable modern syntax while maintaining runtime compatibility.

Pattern 4: CI/CD consistency

Pin the Python version in pyproject.toml to ensure consistent output:

[tool.datamodel-codegen]
target-python-version = "3.10"
use-union-operator = true
use-standard-collections = true
use-annotated = true

Version Feature Matrix

Feature 3.8 3.9 3.10 3.11+
list[T] syntax via __future__ native native native
X \| Y union via __future__ via __future__ native native
Annotated typing_extensions native native native
TypeAlias typing_extensions native native native
Self typing_extensions typing_extensions typing_extensions native

Troubleshooting

TypeError: 'type' object is not subscriptable

This occurs when using list[T] syntax on Python < 3.9 without __future__ imports.

Solution: Either use --target-python-version 3.8 or ensure from __future__ import annotations is present.

Pydantic validation fails with forward references

This can happen when __future__.annotations interacts poorly with Pydantic's type resolution.

Solution: Try --disable-future-imports or update to Pydantic v2.

IDE shows type errors

Some IDEs don't fully understand from __future__ import annotations.

Solution: Configure your IDE's Python version or use explicit type syntax matching your runtime version.


See Also