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¶
from typing import List, Optional, Union
class User(BaseModel):
id: int
tags: List[str]
metadata: Optional[Union[str, int]] = None
Example: Python 3.10+¶
--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¶
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¶
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¶
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.
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¶
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:
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.