Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/cli-reference/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ This documentation is auto-generated from test cases.
| Category | Options | Description |
|----------|---------|-------------|
| 📁 [Base Options](base-options.md) | 5 | Input/output configuration |
| 🔧 [Typing Customization](typing-customization.md) | 17 | Type annotation and import behavior |
| 🔧 [Typing Customization](typing-customization.md) | 18 | Type annotation and import behavior |
| 🏷️ [Field Customization](field-customization.md) | 21 | Field naming and docstring behavior |
| 🏗️ [Model Customization](model-customization.md) | 29 | Model generation behavior |
| 🎨 [Template Customization](template-customization.md) | 16 | Output formatting and custom rendering |
Expand Down Expand Up @@ -178,6 +178,7 @@ This documentation is auto-generated from test cases.
- [`--use-pendulum`](typing-customization.md#use-pendulum)
- [`--use-schema-description`](field-customization.md#use-schema-description)
- [`--use-serialize-as-any`](model-customization.md#use-serialize-as-any)
- [`--use-standard-primitive-types`](typing-customization.md#use-standard-primitive-types)
- [`--use-status-code-in-response-name`](openapi-only-options.md#use-status-code-in-response-name)
- [`--use-subclass-enum`](model-customization.md#use-subclass-enum)
- [`--use-title-as-name`](field-customization.md#use-title-as-name)
Expand Down
2 changes: 2 additions & 0 deletions docs/cli-reference/quick-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ datamodel-codegen [OPTIONS]
| [`--use-generic-container-types`](typing-customization.md#use-generic-container-types) | Use typing.Dict/List instead of dict/list for container types. |
| [`--use-non-positive-negative-number-constrained-types`](typing-customization.md#use-non-positive-negative-number-constrained-types) | Use NonPositive/NonNegative types for number constraints. |
| [`--use-pendulum`](typing-customization.md#use-pendulum) | Use pendulum types for date/time fields instead of datetime module. |
| [`--use-standard-primitive-types`](typing-customization.md#use-standard-primitive-types) | Use Python standard library types for string formats instead of str. |
| [`--use-type-alias`](typing-customization.md#use-type-alias) | Use TypeAlias instead of root models for type definitions (experimental). |
| [`--use-unique-items-as-set`](typing-customization.md#use-unique-items-as-set) | Generate set types for arrays with uniqueItems constraint. |

Expand Down Expand Up @@ -274,6 +275,7 @@ All options sorted alphabetically:
- [`--use-pendulum`](typing-customization.md#use-pendulum) - Use pendulum types for date/time fields instead of datetime ...
- [`--use-schema-description`](field-customization.md#use-schema-description) - Use schema description as class docstring.
- [`--use-serialize-as-any`](model-customization.md#use-serialize-as-any) - Wrap fields with subtypes in Pydantic's SerializeAsAny.
- [`--use-standard-primitive-types`](typing-customization.md#use-standard-primitive-types) - Use Python standard library types for string formats instead...
- [`--use-status-code-in-response-name`](openapi-only-options.md#use-status-code-in-response-name) - Include HTTP status code in response model names.
- [`--use-subclass-enum`](model-customization.md#use-subclass-enum) - Generate typed Enum subclasses for enums with specific field...
- [`--use-title-as-name`](field-customization.md#use-title-as-name) - Use schema title as the generated class name.
Expand Down
68 changes: 68 additions & 0 deletions docs/cli-reference/typing-customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
| [`--use-generic-container-types`](#use-generic-container-types) | Use typing.Dict/List instead of dict/list for container type... |
| [`--use-non-positive-negative-number-constrained-types`](#use-non-positive-negative-number-constrained-types) | Use NonPositive/NonNegative types for number constraints. |
| [`--use-pendulum`](#use-pendulum) | Use pendulum types for date/time fields instead of datetime ... |
| [`--use-standard-primitive-types`](#use-standard-primitive-types) | Use Python standard library types for string formats instead... |
| [`--use-type-alias`](#use-type-alias) | Use TypeAlias instead of root models for type definitions (e... |
| [`--use-unique-items-as-set`](#use-unique-items-as-set) | Generate set types for arrays with uniqueItems constraint. |

Expand Down Expand Up @@ -2955,6 +2956,73 @@ working with the pendulum library for enhanced timezone and date handling.

---

## `--use-standard-primitive-types` {#use-standard-primitive-types}

Use Python standard library types for string formats instead of str.

The `--use-standard-primitive-types` flag configures the code generation to use
Python standard library types (UUID, IPv4Address, IPv6Address, Path) for corresponding
string formats instead of plain str. This affects dataclass, msgspec, and TypedDict
output types. Pydantic already uses these types by default.

**Related:** [`--output-datetime-class`](typing-customization.md#output-datetime-class), [`--output-model-type`](model-customization.md#output-model-type)

!!! tip "Usage"

```bash
datamodel-codegen --input schema.json --output-model-type dataclasses.dataclass --use-standard-primitive-types # (1)!
```

1. :material-arrow-left: `--use-standard-primitive-types` - the option documented here

??? example "Examples"

**Input Schema:**

```json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid"
},
"ip_address": {
"type": "string",
"format": "ipv4"
},
"config_path": {
"type": "string",
"format": "path"
}
}
}
```

**Output:**

```python
# generated by datamodel-codegen:
# filename: use_standard_primitive_types.json

from __future__ import annotations

from dataclasses import dataclass
from ipaddress import IPv4Address
from pathlib import Path
from uuid import UUID


@dataclass
class Model:
id: UUID | None = None
ip_address: IPv4Address | None = None
config_path: Path | None = None
```

---

## `--use-type-alias` {#use-type-alias}

Use TypeAlias instead of root models for type definitions (experimental).
Expand Down
2 changes: 2 additions & 0 deletions src/datamodel_code_generator/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,7 @@ def generate( # noqa: PLR0912, PLR0913, PLR0914, PLR0915
custom_formatters: list[str] | None = None,
custom_formatters_kwargs: dict[str, Any] | None = None,
use_pendulum: bool = False,
use_standard_primitive_types: bool = False,
http_query_parameters: Sequence[tuple[str, str]] | None = None,
treat_dot_as_module: bool | None = None,
use_exact_imports: bool = False,
Expand Down Expand Up @@ -726,6 +727,7 @@ def get_header_and_first_line(csv_file: IO[str]) -> dict[str, Any]:
custom_formatters=custom_formatters,
custom_formatters_kwargs=custom_formatters_kwargs,
use_pendulum=use_pendulum,
use_standard_primitive_types=use_standard_primitive_types,
http_query_parameters=http_query_parameters,
treat_dot_as_module=treat_dot_as_module,
use_exact_imports=use_exact_imports,
Expand Down
2 changes: 2 additions & 0 deletions src/datamodel_code_generator/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ def validate_all_exports_collision_strategy(cls, values: dict[str, Any]) -> dict
custom_formatters: Optional[list[str]] = None # noqa: UP045
custom_formatters_kwargs: Optional[TextIOBase] = None # noqa: UP045
use_pendulum: bool = False
use_standard_primitive_types: bool = False
http_query_parameters: Optional[Sequence[tuple[str, str]]] = None # noqa: UP045
treat_dot_as_module: Optional[bool] = None # noqa: UP045
use_exact_imports: bool = False
Expand Down Expand Up @@ -759,6 +760,7 @@ def run_generate_from_config( # noqa: PLR0913, PLR0917
custom_formatters=config.custom_formatters,
custom_formatters_kwargs=custom_formatters_kwargs,
use_pendulum=config.use_pendulum,
use_standard_primitive_types=config.use_standard_primitive_types,
http_query_parameters=config.http_query_parameters,
treat_dot_as_module=config.treat_dot_as_module,
use_exact_imports=config.use_exact_imports,
Expand Down
8 changes: 8 additions & 0 deletions src/datamodel_code_generator/arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,14 @@ def start_section(self, heading: str | None) -> None:
action="store_true",
default=None,
)
model_options.add_argument(
"--use-standard-primitive-types",
help="Use Python standard library types for string formats (UUID, IPv4Address, etc.) "
"instead of str. Affects dataclass, msgspec, TypedDict output. "
"Pydantic already uses these types by default.",
action="store_true",
default=None,
)
model_options.add_argument(
"--use-exact-imports",
help='import exact types instead of modules, for example: "from .foo import Bar" instead of '
Expand Down
3 changes: 3 additions & 0 deletions src/datamodel_code_generator/cli_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@ class CLIOptionMeta:
"--ignore-enum-constraints": CLIOptionMeta(name="--ignore-enum-constraints", category=OptionCategory.TYPING),
"--disable-future-imports": CLIOptionMeta(name="--disable-future-imports", category=OptionCategory.TYPING),
"--use-pendulum": CLIOptionMeta(name="--use-pendulum", category=OptionCategory.TYPING),
"--use-standard-primitive-types": CLIOptionMeta(
name="--use-standard-primitive-types", category=OptionCategory.TYPING
),
"--output-datetime-class": CLIOptionMeta(name="--output-datetime-class", category=OptionCategory.TYPING),
"--use-decimal-for-multiple-of": CLIOptionMeta(
name="--use-decimal-for-multiple-of", category=OptionCategory.TYPING
Expand Down
4 changes: 4 additions & 0 deletions src/datamodel_code_generator/imports.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,10 @@ def remove_unused(self, used_names: set[str]) -> None:
IMPORT_PATH = Import.from_full_path("pathlib.Path")
IMPORT_TIME = Import.from_full_path("datetime.time")
IMPORT_UUID = Import.from_full_path("uuid.UUID")
IMPORT_IPV4ADDRESS = Import.from_full_path("ipaddress.IPv4Address")
IMPORT_IPV6ADDRESS = Import.from_full_path("ipaddress.IPv6Address")
IMPORT_IPV4NETWORK = Import.from_full_path("ipaddress.IPv4Network")
IMPORT_IPV6NETWORK = Import.from_full_path("ipaddress.IPv6Network")
IMPORT_PENDULUM_DATE = Import.from_full_path("pendulum.Date")
IMPORT_PENDULUM_DATETIME = Import.from_full_path("pendulum.DateTime")
IMPORT_PENDULUM_DURATION = Import.from_full_path("pendulum.Duration")
Expand Down
9 changes: 8 additions & 1 deletion src/datamodel_code_generator/model/dataclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from datamodel_code_generator.model.imports import IMPORT_DATACLASS, IMPORT_FIELD
from datamodel_code_generator.model.pydantic.base_model import Constraints # noqa: TC001 # needed for pydantic
from datamodel_code_generator.model.types import DataTypeManager as _DataTypeManager
from datamodel_code_generator.model.types import type_map_factory
from datamodel_code_generator.model.types import standard_primitive_type_map_factory, type_map_factory
Comment thread
koxudaxi marked this conversation as resolved.
from datamodel_code_generator.reference import Reference
from datamodel_code_generator.types import DataType, StrictTypes, Types, chain_as_tuple

Expand Down Expand Up @@ -217,6 +217,7 @@
use_decimal_for_multiple_of: bool = False, # noqa: FBT001, FBT002
use_union_operator: bool = False, # noqa: FBT001, FBT002
use_pendulum: bool = False, # noqa: FBT001, FBT002
use_standard_primitive_types: bool = False, # noqa: FBT001, FBT002
target_datetime_class: DatetimeClassType = DatetimeClassType.Datetime,
treat_dot_as_module: bool | None = None, # noqa: FBT001
use_serialize_as_any: bool = False, # noqa: FBT001, FBT002
Expand All @@ -231,6 +232,7 @@
use_decimal_for_multiple_of,
use_union_operator,
use_pendulum,
use_standard_primitive_types,
target_datetime_class,
treat_dot_as_module,
use_serialize_as_any,
Expand All @@ -247,7 +249,12 @@
else {}
)

standard_primitive_map = (
standard_primitive_type_map_factory(self.data_type) if use_standard_primitive_types else {}
)

self.type_map: dict[Types, DataType] = {
**type_map_factory(self.data_type),
**datetime_map,
**standard_primitive_map,
}
9 changes: 8 additions & 1 deletion src/datamodel_code_generator/model/msgspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
)
from datamodel_code_generator.model.type_alias import TypeAliasBase
from datamodel_code_generator.model.types import DataTypeManager as _DataTypeManager
from datamodel_code_generator.model.types import type_map_factory
from datamodel_code_generator.model.types import standard_primitive_type_map_factory, type_map_factory
Comment thread
koxudaxi marked this conversation as resolved.
from datamodel_code_generator.types import (
NONE,
OPTIONAL_PREFIX,
Expand Down Expand Up @@ -503,6 +503,7 @@
use_decimal_for_multiple_of: bool = False, # noqa: FBT001, FBT002
use_union_operator: bool = False, # noqa: FBT001, FBT002
use_pendulum: bool = False, # noqa: FBT001, FBT002
use_standard_primitive_types: bool = False, # noqa: FBT001, FBT002
target_datetime_class: DatetimeClassType | None = None,
treat_dot_as_module: bool | None = None, # noqa: FBT001
use_serialize_as_any: bool = False, # noqa: FBT001, FBT002
Expand All @@ -517,6 +518,7 @@
use_decimal_for_multiple_of,
use_union_operator,
use_pendulum,
use_standard_primitive_types,
target_datetime_class,
treat_dot_as_module,
use_serialize_as_any,
Expand All @@ -533,7 +535,12 @@
else {}
)

standard_primitive_map = (
standard_primitive_type_map_factory(self.data_type) if use_standard_primitive_types else {}
)

self.type_map: dict[Types, DataType] = {
**type_map_factory(self.data_type),
**datetime_map,
**standard_primitive_map,
}
23 changes: 12 additions & 11 deletions src/datamodel_code_generator/model/pydantic/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,23 +180,24 @@ def __init__( # noqa: PLR0913, PLR0917
use_decimal_for_multiple_of: bool = False, # noqa: FBT001, FBT002
use_union_operator: bool = False, # noqa: FBT001, FBT002
use_pendulum: bool = False, # noqa: FBT001, FBT002
use_standard_primitive_types: bool = False, # noqa: FBT001, FBT002, ARG002
target_datetime_class: DatetimeClassType | None = None,
treat_dot_as_module: bool | None = None, # noqa: FBT001
use_serialize_as_any: bool = False, # noqa: FBT001, FBT002
) -> None:
"""Initialize the DataTypeManager with Pydantic v1 type mappings."""
super().__init__(
python_version,
use_standard_collections,
use_generic_container_types,
strict_types,
use_non_positive_negative_number_constrained_types,
use_decimal_for_multiple_of,
use_union_operator,
use_pendulum,
target_datetime_class,
treat_dot_as_module,
use_serialize_as_any,
python_version=python_version,
use_standard_collections=use_standard_collections,
use_generic_container_types=use_generic_container_types,
strict_types=strict_types,
use_non_positive_negative_number_constrained_types=use_non_positive_negative_number_constrained_types,
use_decimal_for_multiple_of=use_decimal_for_multiple_of,
use_union_operator=use_union_operator,
use_pendulum=use_pendulum,
target_datetime_class=target_datetime_class,
treat_dot_as_module=treat_dot_as_module,
use_serialize_as_any=use_serialize_as_any,
)

self.type_map: dict[Types, DataType] = self.type_map_factory(
Expand Down
1 change: 1 addition & 0 deletions src/datamodel_code_generator/model/pydantic_v2/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ def __init__( # noqa: PLR0913, PLR0917
use_decimal_for_multiple_of: bool = False, # noqa: FBT001, FBT002
use_union_operator: bool = False, # noqa: FBT001, FBT002
use_pendulum: bool = False, # noqa: FBT001, FBT002
use_standard_primitive_types: bool = False, # noqa: FBT001, FBT002, ARG002
target_datetime_class: DatetimeClassType | None = None,
treat_dot_as_module: bool | None = None, # noqa: FBT001
use_serialize_as_any: bool = False, # noqa: FBT001, FBT002
Expand Down
60 changes: 48 additions & 12 deletions src/datamodel_code_generator/model/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@
from datamodel_code_generator.imports import (
IMPORT_ANY,
IMPORT_DECIMAL,
IMPORT_IPV4ADDRESS,
IMPORT_IPV4NETWORK,
IMPORT_IPV6ADDRESS,
IMPORT_IPV6NETWORK,
IMPORT_PATH,
IMPORT_TIMEDELTA,
IMPORT_UUID,
)
from datamodel_code_generator.types import DataType, StrictTypes, Types
from datamodel_code_generator.types import DataTypeManager as _DataTypeManager
Expand Down Expand Up @@ -68,6 +74,28 @@ def type_map_factory(data_type: type[DataType]) -> dict[Types, DataType]:
}


def standard_primitive_type_map_factory(data_type: type[DataType]) -> dict[Types, DataType]:
"""Create type mapping for standard library primitive types.

Maps string formats to their corresponding Python standard library types
(UUID, IPv4Address, IPv6Address, Path, etc.) instead of plain str.
"""
uuid_type = data_type.from_import(IMPORT_UUID)
return {
Types.uuid: uuid_type,
Types.uuid1: uuid_type,
Types.uuid2: uuid_type,
Types.uuid3: uuid_type,
Types.uuid4: uuid_type,
Types.uuid5: uuid_type,
Types.ipv4: data_type.from_import(IMPORT_IPV4ADDRESS),
Types.ipv6: data_type.from_import(IMPORT_IPV6ADDRESS),
Types.ipv4_network: data_type.from_import(IMPORT_IPV4NETWORK),
Types.ipv6_network: data_type.from_import(IMPORT_IPV6NETWORK),
Types.path: data_type.from_import(IMPORT_PATH),
}


class DataTypeManager(_DataTypeManager):
"""Base type manager for model modules."""

Expand All @@ -83,26 +111,34 @@ def __init__( # noqa: PLR0913, PLR0917
use_decimal_for_multiple_of: bool = False, # noqa: FBT001, FBT002
use_union_operator: bool = False, # noqa: FBT001, FBT002
use_pendulum: bool = False, # noqa: FBT001, FBT002
use_standard_primitive_types: bool = False, # noqa: FBT001, FBT002
target_datetime_class: DatetimeClassType | None = None,
treat_dot_as_module: bool | None = None, # noqa: FBT001
use_serialize_as_any: bool = False, # noqa: FBT001, FBT002
) -> None:
"""Initialize type manager with basic type mapping."""
super().__init__(
python_version,
use_standard_collections,
use_generic_container_types,
strict_types,
use_non_positive_negative_number_constrained_types,
use_decimal_for_multiple_of,
use_union_operator,
use_pendulum,
target_datetime_class,
treat_dot_as_module,
use_serialize_as_any,
python_version=python_version,
use_standard_collections=use_standard_collections,
use_generic_container_types=use_generic_container_types,
strict_types=strict_types,
use_non_positive_negative_number_constrained_types=use_non_positive_negative_number_constrained_types,
use_decimal_for_multiple_of=use_decimal_for_multiple_of,
use_union_operator=use_union_operator,
use_pendulum=use_pendulum,
target_datetime_class=target_datetime_class,
treat_dot_as_module=treat_dot_as_module,
use_serialize_as_any=use_serialize_as_any,
)

standard_primitive_map = (
standard_primitive_type_map_factory(self.data_type) if use_standard_primitive_types else {}
)

self.type_map: dict[Types, DataType] = type_map_factory(self.data_type)
self.type_map: dict[Types, DataType] = {
**type_map_factory(self.data_type),
**standard_primitive_map,
}

def get_data_type(
self,
Expand Down
Loading
Loading