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.10 X \| Y list[T], dict[K, V] Union operator available
3.11 X \| Y list[T], dict[K, V] Self type available
3.12+ X \| Y list[T], dict[K, V] type statement available

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.10+ at runtime, OR - from __future__ import annotations


--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: - 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: Minimum Supported Python (3.10)

For projects targeting Python 3.10 (minimum supported version):

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

Output:

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

Pattern 3: Maximum compatibility

For libraries that need to work across multiple Python versions (3.10+):

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

The generator will use modern Python 3.10+ syntax including union operators and built-in generic types.

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.10 3.11 3.12+
list[T] syntax native native native
X \| Y union native native native
Annotated native native native
TypeAlias native native native
Self typing_extensions native native
type statement N/A N/A native

Troubleshooting

TypeError: 'type' object is not subscriptable

This occurs when using list[T] syntax on older Python versions without __future__ imports.

Solution: Ensure you are running Python 3.10+ or use from __future__ import annotations.

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.

Python 3.13 DeprecationWarning with typing._eval_type

When running on Python 3.13+ with from __future__ import annotations, you may see:

DeprecationWarning: Failing to pass a value to the 'type_params' parameter
of 'typing._eval_type' is deprecated...

This occurs because Python 3.13 deprecated calling typing._eval_type() without the type_params parameter. Libraries that evaluate forward references (like older Pydantic versions) trigger this warning.

Solutions:

  1. Upgrade Pydantic (recommended):
  2. Pydantic v1: Upgrade to version 1.10.18 or later
  3. Pydantic v2: Upgrade to the latest version

  4. Use --disable-future-imports as a workaround:

    datamodel-codegen --input schema.json --output models.py --disable-future-imports
    

  5. Suppress the warning in pytest (temporary fix):

    # pyproject.toml
    [tool.pytest.ini_options]
    filterwarnings = [
        # For Pydantic v2's v1 compatibility layer (pydantic.v1)
        "ignore::DeprecationWarning:pydantic.v1.typing",
        # For standalone Pydantic v1
        "ignore::DeprecationWarning:pydantic.typing",
    ]
    

Note

Python 3.14+ will use native deferred annotations (PEP 649), and the generator will no longer add from __future__ import annotations for those versions.

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