datamodel-code-generator 0.11.12__py3-none-any.whl → 0.45.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. datamodel_code_generator/__init__.py +654 -185
  2. datamodel_code_generator/__main__.py +872 -388
  3. datamodel_code_generator/arguments.py +798 -0
  4. datamodel_code_generator/cli_options.py +295 -0
  5. datamodel_code_generator/format.py +292 -54
  6. datamodel_code_generator/http.py +85 -10
  7. datamodel_code_generator/imports.py +152 -43
  8. datamodel_code_generator/model/__init__.py +138 -1
  9. datamodel_code_generator/model/base.py +531 -120
  10. datamodel_code_generator/model/dataclass.py +211 -0
  11. datamodel_code_generator/model/enum.py +133 -12
  12. datamodel_code_generator/model/imports.py +22 -0
  13. datamodel_code_generator/model/msgspec.py +462 -0
  14. datamodel_code_generator/model/pydantic/__init__.py +30 -25
  15. datamodel_code_generator/model/pydantic/base_model.py +304 -100
  16. datamodel_code_generator/model/pydantic/custom_root_type.py +11 -2
  17. datamodel_code_generator/model/pydantic/dataclass.py +15 -4
  18. datamodel_code_generator/model/pydantic/imports.py +40 -27
  19. datamodel_code_generator/model/pydantic/types.py +188 -96
  20. datamodel_code_generator/model/pydantic_v2/__init__.py +51 -0
  21. datamodel_code_generator/model/pydantic_v2/base_model.py +268 -0
  22. datamodel_code_generator/model/pydantic_v2/imports.py +15 -0
  23. datamodel_code_generator/model/pydantic_v2/root_model.py +35 -0
  24. datamodel_code_generator/model/pydantic_v2/types.py +143 -0
  25. datamodel_code_generator/model/scalar.py +124 -0
  26. datamodel_code_generator/model/template/Enum.jinja2 +15 -2
  27. datamodel_code_generator/model/template/ScalarTypeAliasAnnotation.jinja2 +6 -0
  28. datamodel_code_generator/model/template/ScalarTypeAliasType.jinja2 +6 -0
  29. datamodel_code_generator/model/template/ScalarTypeStatement.jinja2 +6 -0
  30. datamodel_code_generator/model/template/TypeAliasAnnotation.jinja2 +20 -0
  31. datamodel_code_generator/model/template/TypeAliasType.jinja2 +20 -0
  32. datamodel_code_generator/model/template/TypeStatement.jinja2 +20 -0
  33. datamodel_code_generator/model/template/TypedDict.jinja2 +5 -0
  34. datamodel_code_generator/model/template/TypedDictClass.jinja2 +25 -0
  35. datamodel_code_generator/model/template/TypedDictFunction.jinja2 +24 -0
  36. datamodel_code_generator/model/template/UnionTypeAliasAnnotation.jinja2 +10 -0
  37. datamodel_code_generator/model/template/UnionTypeAliasType.jinja2 +10 -0
  38. datamodel_code_generator/model/template/UnionTypeStatement.jinja2 +10 -0
  39. datamodel_code_generator/model/template/dataclass.jinja2 +50 -0
  40. datamodel_code_generator/model/template/msgspec.jinja2 +55 -0
  41. datamodel_code_generator/model/template/pydantic/BaseModel.jinja2 +17 -4
  42. datamodel_code_generator/model/template/pydantic/BaseModel_root.jinja2 +12 -4
  43. datamodel_code_generator/model/template/pydantic/Config.jinja2 +1 -1
  44. datamodel_code_generator/model/template/pydantic/dataclass.jinja2 +15 -2
  45. datamodel_code_generator/model/template/pydantic_v2/BaseModel.jinja2 +57 -0
  46. datamodel_code_generator/model/template/pydantic_v2/ConfigDict.jinja2 +5 -0
  47. datamodel_code_generator/model/template/pydantic_v2/RootModel.jinja2 +48 -0
  48. datamodel_code_generator/model/type_alias.py +70 -0
  49. datamodel_code_generator/model/typed_dict.py +161 -0
  50. datamodel_code_generator/model/types.py +106 -0
  51. datamodel_code_generator/model/union.py +105 -0
  52. datamodel_code_generator/parser/__init__.py +30 -12
  53. datamodel_code_generator/parser/_graph.py +67 -0
  54. datamodel_code_generator/parser/_scc.py +171 -0
  55. datamodel_code_generator/parser/base.py +2426 -380
  56. datamodel_code_generator/parser/graphql.py +652 -0
  57. datamodel_code_generator/parser/jsonschema.py +2518 -647
  58. datamodel_code_generator/parser/openapi.py +631 -222
  59. datamodel_code_generator/py.typed +0 -0
  60. datamodel_code_generator/pydantic_patch.py +28 -0
  61. datamodel_code_generator/reference.py +672 -290
  62. datamodel_code_generator/types.py +521 -145
  63. datamodel_code_generator/util.py +155 -0
  64. datamodel_code_generator/watch.py +65 -0
  65. datamodel_code_generator-0.45.0.dist-info/METADATA +301 -0
  66. datamodel_code_generator-0.45.0.dist-info/RECORD +69 -0
  67. {datamodel_code_generator-0.11.12.dist-info → datamodel_code_generator-0.45.0.dist-info}/WHEEL +1 -1
  68. datamodel_code_generator-0.45.0.dist-info/entry_points.txt +2 -0
  69. datamodel_code_generator/version.py +0 -1
  70. datamodel_code_generator-0.11.12.dist-info/METADATA +0 -440
  71. datamodel_code_generator-0.11.12.dist-info/RECORD +0 -31
  72. datamodel_code_generator-0.11.12.dist-info/entry_points.txt +0 -3
  73. {datamodel_code_generator-0.11.12.dist-info → datamodel_code_generator-0.45.0.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,20 @@
1
+ {%- macro get_type_annotation(_field) -%}
2
+ {%- if _field.annotated -%}
3
+ {{ _field.annotated }}
4
+ {%- elif _field.field -%}
5
+ Annotated[{{ _field.type_hint }}, {{ _field.field }}]
6
+ {%- else -%}
7
+ {{ _field.type_hint }}
8
+ {%- endif -%}
9
+ {%- endmacro -%}
10
+
11
+ type {{ class_name }} = {{ get_type_annotation(fields[0]) }}{% if comment is defined %} # {{ comment }}{% endif %}
12
+ {%- if description %}
13
+ """
14
+ {{ description | indent(0) }}
15
+ """
16
+ {%- elif fields and fields[0].docstring %}
17
+ """
18
+ {{ fields[0].docstring | indent(0) }}
19
+ """
20
+ {%- endif %}
@@ -0,0 +1,5 @@
1
+ {%- if is_functional_syntax %}
2
+ {% include 'TypedDictFunction.jinja2' %}
3
+ {%- else %}
4
+ {% include 'TypedDictClass.jinja2' %}
5
+ {%- endif %}
@@ -0,0 +1,25 @@
1
+ class {{ class_name }}({{ base_class }}):
2
+ {%- if description %}
3
+ """
4
+ {{ description | indent(4) }}
5
+ """
6
+ {%- endif %}
7
+ {%- if not fields and not description %}
8
+ pass
9
+ {%- endif %}
10
+ {%- for field in fields %}
11
+ {{ field.name }}: {{ field.type_hint }}
12
+ {%- if field.docstring %}
13
+ """
14
+ {{ field.docstring | indent(4) }}
15
+ """
16
+ {%- if field.use_inline_field_description and not loop.last %}
17
+
18
+ {% endif %}
19
+ {%- elif field.inline_field_docstring %}
20
+ {{ field.inline_field_docstring }}
21
+ {%- if not loop.last %}
22
+
23
+ {% endif %}
24
+ {%- endif %}
25
+ {%- endfor -%}
@@ -0,0 +1,24 @@
1
+ {%- if description %}
2
+ """
3
+ {{ description | indent(4) }}
4
+ """
5
+ {%- endif %}
6
+ {{ class_name }} = TypedDict('{{ class_name }}', {
7
+ {%- for field in all_fields %}
8
+ '{{ field.key }}': {{ field.type_hint }},
9
+ {%- if field.docstring %}
10
+ """
11
+ {{ field.docstring | indent(4) }}
12
+ """
13
+ {%- if field.use_inline_field_description and not loop.last %}
14
+
15
+ {% endif %}
16
+ {%- elif field.inline_field_docstring %}
17
+ {{ field.inline_field_docstring }}
18
+ {%- if not loop.last %}
19
+
20
+ {% endif %}
21
+ {%- endif %}
22
+ {%- endfor -%}
23
+ })
24
+
@@ -0,0 +1,10 @@
1
+ {%- if description %}
2
+ # {{ description | replace('\n', '\n# ') }}
3
+ {%- endif %}
4
+ {%- if fields|length > 1 %}
5
+ {{ class_name }}: TypeAlias = Union[
6
+ {%- for field in fields %}
7
+ '{{ field.name }}',
8
+ {%- endfor %}
9
+ ]{% else %}
10
+ {{ class_name }}: TypeAlias = {{ fields[0].name }}{% endif %}
@@ -0,0 +1,10 @@
1
+ {%- if description %}
2
+ # {{ description | replace('\n', '\n# ') }}
3
+ {%- endif %}
4
+ {%- if fields|length > 1 %}
5
+ {{ class_name }} = TypeAliasType("{{ class_name }}", Union[
6
+ {%- for field in fields %}
7
+ '{{ field.name }}',
8
+ {%- endfor %}
9
+ ]){% else %}
10
+ {{ class_name }} = TypeAliasType("{{ class_name }}", {{ fields[0].name }}){% endif %}
@@ -0,0 +1,10 @@
1
+ {%- if description %}
2
+ # {{ description | replace('\n', '\n# ') }}
3
+ {%- endif %}
4
+ {%- if fields|length > 1 %}
5
+ type {{ class_name }} = Union[
6
+ {%- for field in fields %}
7
+ '{{ field.name }}',
8
+ {%- endfor %}
9
+ ]{% else %}
10
+ type {{ class_name }} = {{ fields[0].name }}{% endif %}
@@ -0,0 +1,50 @@
1
+ {% for decorator in decorators -%}
2
+ {{ decorator }}
3
+ {% endfor -%}
4
+ {%- set args = [] %}
5
+ {%- for k, v in (dataclass_arguments or {}).items() %}
6
+ {%- if v is not none and v is not false %}
7
+ {%- set _ = args.append(k ~ '=' ~ (v|pprint)) %}
8
+ {%- endif %}
9
+ {%- endfor %}
10
+ {%- if args %}
11
+ @dataclass({{ args | join(', ') }})
12
+ {%- else %}
13
+ @dataclass
14
+ {%- endif %}
15
+ {%- if base_class %}
16
+ class {{ class_name }}({{ base_class }}):
17
+ {%- else %}
18
+ class {{ class_name }}:
19
+ {%- endif %}
20
+ {%- if description %}
21
+ """
22
+ {{ description | indent(4) }}
23
+ """
24
+ {%- endif %}
25
+ {%- if not fields and not description %}
26
+ pass
27
+ {%- endif %}
28
+ {%- for field in fields -%}
29
+ {%- if field.field %}
30
+ {{ field.name }}: {{ field.type_hint }} = {{ field.field }}
31
+ {%- else %}
32
+ {{ field.name }}: {{ field.type_hint }}
33
+ {%- if not (field.required or (field.represented_default == 'None' and field.strip_default_none))
34
+ %} = {{ field.represented_default }}
35
+ {%- endif -%}
36
+ {%- endif %}
37
+ {%- if field.docstring %}
38
+ """
39
+ {{ field.docstring | indent(4) }}
40
+ """
41
+ {%- if field.use_inline_field_description and not loop.last %}
42
+
43
+ {% endif %}
44
+ {%- elif field.inline_field_docstring %}
45
+ {{ field.inline_field_docstring }}
46
+ {%- if not loop.last %}
47
+
48
+ {% endif %}
49
+ {%- endif %}
50
+ {%- endfor -%}
@@ -0,0 +1,55 @@
1
+ {% for decorator in decorators -%}
2
+ {{ decorator }}
3
+ {% endfor -%}
4
+ {%- if base_class %}
5
+ class {{ class_name }}({{ base_class }}{%- for key, value in (base_class_kwargs|default({})).items() -%}
6
+ , {{ key }}={{ value }}
7
+ {%- endfor -%}):
8
+ {%- else %}
9
+ class {{ class_name }}:
10
+ {%- endif %}
11
+ {%- if description %}
12
+ """
13
+ {{ description | indent(4) }}
14
+ """
15
+ {%- endif %}
16
+ {%- set ns = namespace(has_rendered_field=false) -%}
17
+ {%- for field in fields -%}
18
+ {%- if field.extras.get('is_classvar') %}
19
+ {#- Skip fields with is_classvar=True - they are managed by msgspec tag_field -#}
20
+ {%- elif not field.annotated and field.field %}
21
+ {%- set ns.has_rendered_field = true %}
22
+ {{ field.name }}: {{ field.type_hint }} = {{ field.field }}
23
+ {%- else %}
24
+ {%- set ns.has_rendered_field = true %}
25
+ {%- if field.annotated and not field.field %}
26
+ {{ field.name }}: {{ field.annotated }}
27
+ {%- elif field.annotated and field.field %}
28
+ {{ field.name }}: {{ field.annotated }} = {{ field.field }}
29
+ {%- else %}
30
+ {{ field.name }}: {{ field.type_hint }}
31
+ {%- endif %}
32
+ {%- if not field.field and (not field.required or field.data_type.is_optional or field.nullable)
33
+ %} = {{ field.represented_default }}
34
+ {%- endif -%}
35
+ {%- endif %}
36
+
37
+
38
+
39
+ {%- if not field.extras.get('is_classvar') and field.docstring %}
40
+ """
41
+ {{ field.docstring | indent(4) }}
42
+ """
43
+ {%- if field.use_inline_field_description and not loop.last %}
44
+
45
+ {% endif %}
46
+ {%- elif not field.extras.get('is_classvar') and field.inline_field_docstring %}
47
+ {{ field.inline_field_docstring }}
48
+ {%- if not loop.last %}
49
+
50
+ {% endif %}
51
+ {%- endif %}
52
+ {%- endfor -%}
53
+ {%- if not ns.has_rendered_field and not description %}
54
+ pass
55
+ {%- endif -%}
@@ -1,13 +1,13 @@
1
1
  {% for decorator in decorators -%}
2
2
  {{ decorator }}
3
3
  {% endfor -%}
4
- class {{ class_name }}({{ base_class }}):
4
+ class {{ class_name }}({{ base_class }}):{% if comment is defined %} # {{ comment }}{% endif %}
5
5
  {%- if description %}
6
6
  """
7
- {{ description }}
7
+ {{ description | indent(4) }}
8
8
  """
9
9
  {%- endif %}
10
- {%- if not fields %}
10
+ {%- if not fields and not description %}
11
11
  pass
12
12
  {%- endif %}
13
13
  {%- if config %}
@@ -24,10 +24,23 @@ class {{ class_name }}({{ base_class }}):
24
24
  {%- else %}
25
25
  {{ field.name }}: {{ field.type_hint }}
26
26
  {%- endif %}
27
- {%- if not (field.required or (field.represented_default == 'None' and field.strip_default_none))
27
+ {%- if not field.has_default_factory_in_field and not (field.required or (field.represented_default == 'None' and field.strip_default_none))
28
28
  %} = {{ field.represented_default }}
29
29
  {%- endif -%}
30
30
  {%- endif %}
31
+ {%- if field.docstring %}
32
+ """
33
+ {{ field.docstring | indent(4) }}
34
+ """
35
+ {%- if field.use_inline_field_description and not loop.last %}
36
+
37
+ {% endif %}
38
+ {%- elif field.inline_field_docstring %}
39
+ {{ field.inline_field_docstring }}
40
+ {%- if not loop.last %}
41
+
42
+ {% endif %}
43
+ {%- endif %}
31
44
  {%- for method in methods -%}
32
45
  {{ method }}
33
46
  {%- endfor -%}
@@ -1,10 +1,10 @@
1
1
  {% for decorator in decorators -%}
2
2
  {{ decorator }}
3
3
  {% endfor -%}
4
- class {{ class_name }}({{ base_class }}):
4
+ class {{ class_name }}({{ base_class }}):{% if comment is defined %} # {{ comment }}{% endif %}
5
5
  {%- if description %}
6
6
  """
7
- {{ description }}
7
+ {{ description | indent(4) }}
8
8
  """
9
9
  {%- endif %}
10
10
  {%- if config %}
@@ -12,7 +12,7 @@ class {{ class_name }}({{ base_class }}):
12
12
  {% include 'Config.jinja2' %}
13
13
  {%- endfilter %}
14
14
  {%- endif %}
15
- {%- if not fields %}
15
+ {%- if not fields and not description %}
16
16
  pass
17
17
  {%- else %}
18
18
  {%- set field = fields[0] %}
@@ -24,8 +24,16 @@ class {{ class_name }}({{ base_class }}):
24
24
  {%- else %}
25
25
  __root__: {{ field.type_hint }}
26
26
  {%- endif %}
27
- {%- if not (field.required or (field.represented_default == 'None' and field.strip_default_none))
27
+ {%- if not field.has_default_factory_in_field and not (field.required or (field.represented_default == 'None' and field.strip_default_none))
28
28
  %} = {{ field.represented_default }}
29
29
  {%- endif -%}
30
30
  {%- endif %}
31
+ {%- if field.docstring %}
32
+ """
33
+ {{ field.docstring | indent(4) }}
34
+ """
35
+ {%- elif field.inline_field_docstring %}
36
+ {{ field.inline_field_docstring }}
37
+
38
+ {%- endif %}
31
39
  {%- endif %}
@@ -1,4 +1,4 @@
1
1
  class Config:
2
2
  {%- for field_name, value in config.dict(exclude_unset=True).items() %}
3
3
  {{ field_name }} = {{ value }}
4
- {%- endfor %}
4
+ {%- endfor %}
@@ -9,7 +9,7 @@ class {{ class_name }}:
9
9
  {%- endif %}
10
10
  {%- if description %}
11
11
  """
12
- {{ description }}
12
+ {{ description | indent(4) }}
13
13
  """
14
14
  {%- endif %}
15
15
  {%- if not fields %}
@@ -21,4 +21,17 @@ class {{ class_name }}:
21
21
  {%- else %}
22
22
  {{ field.name }}: {{ field.type_hint }}
23
23
  {%- endif %}
24
- {%- endfor -%}
24
+ {%- if field.docstring %}
25
+ """
26
+ {{ field.docstring | indent(4) }}
27
+ """
28
+ {%- if field.use_inline_field_description and not loop.last %}
29
+
30
+ {% endif %}
31
+ {%- elif field.inline_field_docstring %}
32
+ {{ field.inline_field_docstring }}
33
+ {%- if not loop.last %}
34
+
35
+ {% endif %}
36
+ {%- endif %}
37
+ {%- endfor -%}
@@ -0,0 +1,57 @@
1
+ {% if base_class != "BaseModel" and "," not in base_class and not fields and not config and not description -%}
2
+
3
+ {# if this is just going to be `class Foo(Bar): pass`, then might as well just make Foo
4
+ an alias for Bar: every pydantic model class consumes considerable memory. #}
5
+ {{ class_name }} = {{ base_class }}
6
+
7
+ {% else -%}
8
+
9
+ {% for decorator in decorators -%}
10
+ {{ decorator }}
11
+ {% endfor -%}
12
+ class {{ class_name }}({{ base_class }}):{% if comment is defined %} # {{ comment }}{% endif %}
13
+ {%- if description %}
14
+ """
15
+ {{ description | indent(4) }}
16
+ """
17
+ {%- endif %}
18
+ {%- if not fields and not description %}
19
+ pass
20
+ {%- endif %}
21
+ {%- if config %}
22
+ {%- filter indent(4) %}
23
+ {% include 'ConfigDict.jinja2' %}
24
+ {%- endfilter %}
25
+ {%- endif %}
26
+ {%- for field in fields %}
27
+ {%- if not field.annotated and field.field %}
28
+ {{ field.name }}: {{ field.type_hint }} = {{ field.field }}
29
+ {%- else %}
30
+ {%- if field.annotated %}
31
+ {{ field.name }}: {{ field.annotated }}
32
+ {%- else %}
33
+ {{ field.name }}: {{ field.type_hint }}
34
+ {%- endif %}
35
+ {%- if not field.has_default_factory_in_field and (not (field.required or (field.represented_default == 'None' and field.strip_default_none)) or field.data_type.is_optional)
36
+ %} = {{ field.represented_default }}
37
+ {%- endif -%}
38
+ {%- endif %}
39
+ {%- if field.docstring %}
40
+ """
41
+ {{ field.docstring | indent(4) }}
42
+ """
43
+ {%- if field.use_inline_field_description and not loop.last %}
44
+
45
+ {% endif %}
46
+ {%- elif field.inline_field_docstring %}
47
+ {{ field.inline_field_docstring }}
48
+ {%- if not loop.last %}
49
+
50
+ {% endif %}
51
+ {%- endif %}
52
+ {%- for method in methods -%}
53
+ {{ method }}
54
+ {%- endfor -%}
55
+ {%- endfor -%}
56
+
57
+ {%- endif %}
@@ -0,0 +1,5 @@
1
+ model_config = ConfigDict(
2
+ {%- for field_name, value in config.dict(exclude_unset=True).items() %}
3
+ {{ field_name }}={{ value }},
4
+ {%- endfor %}
5
+ )
@@ -0,0 +1,48 @@
1
+ {%- macro get_type_hint(_fields) -%}
2
+ {%- if _fields -%}
3
+ {#There will only ever be a single field for RootModel#}
4
+ {{- _fields[0].type_hint}}
5
+ {%- endif -%}
6
+ {%- endmacro -%}
7
+
8
+
9
+ {% for decorator in decorators -%}
10
+ {{ decorator }}
11
+ {% endfor -%}
12
+
13
+ class {{ class_name }}({{ base_class }}{%- if fields -%}[{{get_type_hint(fields)}}]{%- endif -%}):{% if comment is defined %} # {{ comment }}{% endif %}
14
+ {%- if description %}
15
+ """
16
+ {{ description | indent(4) }}
17
+ """
18
+ {%- endif %}
19
+ {%- if config %}
20
+ {%- filter indent(4) %}
21
+ {% include 'ConfigDict.jinja2' %}
22
+ {%- endfilter %}
23
+ {%- endif %}
24
+ {%- if not fields and not description %}
25
+ pass
26
+ {%- else %}
27
+ {%- set field = fields[0] %}
28
+ {%- if not field.annotated and field.field %}
29
+ root: {{ field.type_hint }} = {{ field.field }}
30
+ {%- else %}
31
+ {%- if field.annotated %}
32
+ root: {{ field.annotated }}
33
+ {%- else %}
34
+ root: {{ field.type_hint }}
35
+ {%- endif %}
36
+ {%- if not field.has_default_factory_in_field and not (field.required or (field.represented_default == 'None' and field.strip_default_none))
37
+ %} = {{ field.represented_default }}
38
+ {%- endif -%}
39
+ {%- endif %}
40
+ {%- if field.docstring %}
41
+ """
42
+ {{ field.docstring | indent(4) }}
43
+ """
44
+ {%- elif field.inline_field_docstring %}
45
+ {{ field.inline_field_docstring }}
46
+
47
+ {%- endif %}
48
+ {%- endif %}
@@ -0,0 +1,70 @@
1
+ """Type alias model generators.
2
+
3
+ Provides classes for generating type aliases using different Python syntax:
4
+ TypeAlias annotation, TypeAliasType, and type statement (Python 3.12+).
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import ClassVar
10
+
11
+ from datamodel_code_generator.imports import (
12
+ IMPORT_ANNOTATED,
13
+ IMPORT_TYPE_ALIAS,
14
+ IMPORT_TYPE_ALIAS_BACKPORT,
15
+ IMPORT_TYPE_ALIAS_TYPE,
16
+ Import,
17
+ )
18
+ from datamodel_code_generator.model import DataModel
19
+ from datamodel_code_generator.types import chain_as_tuple
20
+
21
+
22
+ class TypeAliasBase(DataModel):
23
+ """Base class for all type alias implementations."""
24
+
25
+ IS_ALIAS: bool = True
26
+
27
+ @property
28
+ def imports(self) -> tuple[Import, ...]:
29
+ """Get imports including Annotated if needed."""
30
+ imports = super().imports
31
+ if self.fields and (self.fields[0].annotated or self.fields[0].field):
32
+ imports = chain_as_tuple(imports, (IMPORT_ANNOTATED,))
33
+
34
+ return imports
35
+
36
+
37
+ class TypeAlias(TypeAliasBase):
38
+ """TypeAlias annotation for Python 3.10+ (Name: TypeAlias = type)."""
39
+
40
+ TEMPLATE_FILE_PATH: ClassVar[str] = "TypeAliasAnnotation.jinja2"
41
+ BASE_CLASS: ClassVar[str] = ""
42
+ DEFAULT_IMPORTS: ClassVar[tuple[Import, ...]] = (IMPORT_TYPE_ALIAS,)
43
+
44
+
45
+ class TypeAliasBackport(TypeAliasBase):
46
+ """TypeAlias annotation for Python 3.9 (Name: TypeAlias = type) using typing_extensions."""
47
+
48
+ TEMPLATE_FILE_PATH: ClassVar[str] = "TypeAliasAnnotation.jinja2"
49
+ BASE_CLASS: ClassVar[str] = ""
50
+ DEFAULT_IMPORTS: ClassVar[tuple[Import, ...]] = (IMPORT_TYPE_ALIAS_BACKPORT,)
51
+
52
+
53
+ class TypeAliasTypeBackport(TypeAliasBase):
54
+ """TypeAliasType for Python 3.9-3.11 (Name = TypeAliasType("Name", type))."""
55
+
56
+ TEMPLATE_FILE_PATH: ClassVar[str] = "TypeAliasType.jinja2"
57
+ BASE_CLASS: ClassVar[str] = ""
58
+ DEFAULT_IMPORTS: ClassVar[tuple[Import, ...]] = (IMPORT_TYPE_ALIAS_TYPE,)
59
+
60
+
61
+ class TypeStatement(TypeAliasBase):
62
+ """Type statement for Python 3.12+ (type Name = type).
63
+
64
+ Note: Python 3.12+ type statements use deferred evaluation,
65
+ so forward references don't need to be quoted.
66
+ """
67
+
68
+ TEMPLATE_FILE_PATH: ClassVar[str] = "TypeStatement.jinja2"
69
+ BASE_CLASS: ClassVar[str] = ""
70
+ DEFAULT_IMPORTS: ClassVar[tuple[Import, ...]] = ()
@@ -0,0 +1,161 @@
1
+ """TypedDict model generator.
2
+
3
+ Generates Python TypedDict classes for use with type checkers.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import keyword
9
+ from typing import TYPE_CHECKING, Any, ClassVar
10
+
11
+ from datamodel_code_generator.model import DataModel, DataModelFieldBase
12
+ from datamodel_code_generator.model.base import UNDEFINED
13
+ from datamodel_code_generator.model.imports import (
14
+ IMPORT_NOT_REQUIRED,
15
+ IMPORT_NOT_REQUIRED_BACKPORT,
16
+ IMPORT_TYPED_DICT,
17
+ )
18
+ from datamodel_code_generator.types import NOT_REQUIRED_PREFIX
19
+
20
+ if TYPE_CHECKING:
21
+ from collections import defaultdict
22
+ from collections.abc import Iterator
23
+ from pathlib import Path
24
+
25
+ from datamodel_code_generator.imports import Import
26
+ from datamodel_code_generator.reference import Reference
27
+
28
+
29
+ escape_characters = str.maketrans({
30
+ "\\": r"\\",
31
+ "'": r"\'",
32
+ "\b": r"\b",
33
+ "\f": r"\f",
34
+ "\n": r"\n",
35
+ "\r": r"\r",
36
+ "\t": r"\t",
37
+ })
38
+
39
+
40
+ def _is_valid_field_name(field: DataModelFieldBase) -> bool:
41
+ name = field.original_name or field.name
42
+ if name is None: # pragma: no cover
43
+ return False
44
+ return name.isidentifier() and not keyword.iskeyword(name)
45
+
46
+
47
+ class TypedDict(DataModel):
48
+ """DataModel implementation for Python TypedDict."""
49
+
50
+ TEMPLATE_FILE_PATH: ClassVar[str] = "TypedDict.jinja2"
51
+ BASE_CLASS: ClassVar[str] = "typing.TypedDict"
52
+ DEFAULT_IMPORTS: ClassVar[tuple[Import, ...]] = (IMPORT_TYPED_DICT,)
53
+
54
+ def __init__( # noqa: PLR0913
55
+ self,
56
+ *,
57
+ reference: Reference,
58
+ fields: list[DataModelFieldBase],
59
+ decorators: list[str] | None = None,
60
+ base_classes: list[Reference] | None = None,
61
+ custom_base_class: str | None = None,
62
+ custom_template_dir: Path | None = None,
63
+ extra_template_data: defaultdict[str, dict[str, Any]] | None = None,
64
+ methods: list[str] | None = None,
65
+ path: Path | None = None,
66
+ description: str | None = None,
67
+ default: Any = UNDEFINED,
68
+ nullable: bool = False,
69
+ keyword_only: bool = False,
70
+ treat_dot_as_module: bool = False,
71
+ ) -> None:
72
+ """Initialize TypedDict model."""
73
+ super().__init__(
74
+ reference=reference,
75
+ fields=fields,
76
+ decorators=decorators,
77
+ base_classes=base_classes,
78
+ custom_base_class=custom_base_class,
79
+ custom_template_dir=custom_template_dir,
80
+ extra_template_data=extra_template_data,
81
+ methods=methods,
82
+ path=path,
83
+ description=description,
84
+ default=default,
85
+ nullable=nullable,
86
+ keyword_only=keyword_only,
87
+ treat_dot_as_module=treat_dot_as_module,
88
+ )
89
+
90
+ @property
91
+ def is_functional_syntax(self) -> bool:
92
+ """Check if TypedDict requires functional syntax."""
93
+ return any(not _is_valid_field_name(f) for f in self.fields)
94
+
95
+ @property
96
+ def all_fields(self) -> Iterator[DataModelFieldBase]:
97
+ """Iterate over all fields including inherited ones."""
98
+ yield from self.iter_all_fields()
99
+
100
+ def render(self, *, class_name: str | None = None) -> str:
101
+ """Render TypedDict class with appropriate syntax."""
102
+ return self._render(
103
+ class_name=class_name or self.class_name,
104
+ fields=self.fields,
105
+ decorators=self.decorators,
106
+ base_class=self.base_class,
107
+ methods=self.methods,
108
+ description=self.description,
109
+ is_functional_syntax=self.is_functional_syntax,
110
+ all_fields=self.all_fields,
111
+ **self.extra_template_data,
112
+ )
113
+
114
+
115
+ class DataModelField(DataModelFieldBase):
116
+ """Field implementation for TypedDict models."""
117
+
118
+ DEFAULT_IMPORTS: ClassVar[tuple[Import, ...]] = (IMPORT_NOT_REQUIRED,)
119
+
120
+ def process_const(self) -> None:
121
+ """Process const field constraint using literal type."""
122
+ self._process_const_as_literal()
123
+
124
+ @property
125
+ def key(self) -> str:
126
+ """Get escaped field key for TypedDict."""
127
+ return (self.original_name or self.name or "").translate( # pragma: no cover
128
+ escape_characters
129
+ )
130
+
131
+ @property
132
+ def type_hint(self) -> str:
133
+ """Get type hint with NotRequired wrapper if needed."""
134
+ type_hint = super().type_hint
135
+ if self._not_required:
136
+ return f"{NOT_REQUIRED_PREFIX}{type_hint}]"
137
+ return type_hint
138
+
139
+ @property
140
+ def _not_required(self) -> bool:
141
+ """Check if field should be marked as NotRequired."""
142
+ return not self.required and isinstance(self.parent, TypedDict)
143
+
144
+ @property
145
+ def fall_back_to_nullable(self) -> bool:
146
+ """Check if field should fall back to nullable."""
147
+ return not self._not_required
148
+
149
+ @property
150
+ def imports(self) -> tuple[Import, ...]:
151
+ """Get imports including NotRequired if needed."""
152
+ return (
153
+ *super().imports,
154
+ *(self.DEFAULT_IMPORTS if self._not_required else ()),
155
+ )
156
+
157
+
158
+ class DataModelFieldBackport(DataModelField):
159
+ """Field implementation for TypedDict models using typing_extensions."""
160
+
161
+ DEFAULT_IMPORTS: ClassVar[tuple[Import, ...]] = (IMPORT_NOT_REQUIRED_BACKPORT,)