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,652 @@
1
+ """GraphQL schema parser implementation.
2
+
3
+ Parses GraphQL schema files to generate Python data models including
4
+ objects, interfaces, enums, scalars, inputs, and union types.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from pathlib import Path
10
+ from typing import (
11
+ TYPE_CHECKING,
12
+ Any,
13
+ Callable,
14
+ )
15
+ from urllib.parse import ParseResult
16
+
17
+ from datamodel_code_generator import (
18
+ DEFAULT_SHARED_MODULE_NAME,
19
+ AllOfMergeMode,
20
+ DataclassArguments,
21
+ DefaultPutDict,
22
+ LiteralType,
23
+ PythonVersion,
24
+ PythonVersionMin,
25
+ ReadOnlyWriteOnlyModelType,
26
+ ReuseScope,
27
+ snooper_to_methods,
28
+ )
29
+ from datamodel_code_generator.format import DEFAULT_FORMATTERS, DatetimeClassType, Formatter
30
+ from datamodel_code_generator.model import DataModel, DataModelFieldBase
31
+ from datamodel_code_generator.model import pydantic as pydantic_model
32
+ from datamodel_code_generator.model.dataclass import DataClass
33
+ from datamodel_code_generator.model.enum import SPECIALIZED_ENUM_TYPE_MATCH, Enum
34
+ from datamodel_code_generator.model.scalar import DataTypeScalarBackport
35
+ from datamodel_code_generator.model.union import DataTypeUnionBackport
36
+ from datamodel_code_generator.parser.base import (
37
+ DataType,
38
+ Parser,
39
+ Source,
40
+ escape_characters,
41
+ )
42
+ from datamodel_code_generator.reference import ModelType, Reference
43
+ from datamodel_code_generator.types import DataTypeManager, StrictTypes, Types
44
+
45
+ try:
46
+ import graphql
47
+ except ImportError as exc: # pragma: no cover
48
+ msg = "Please run `$pip install 'datamodel-code-generator[graphql]`' to generate data-model from a GraphQL schema."
49
+ raise Exception(msg) from exc # noqa: TRY002
50
+
51
+
52
+ if TYPE_CHECKING:
53
+ from collections import defaultdict
54
+ from collections.abc import Iterable, Iterator, Mapping, Sequence
55
+
56
+ # graphql-core >=3.2.7 removed TypeResolvers in favor of TypeFields.kind.
57
+ # Normalize to a single callable for resolving type kinds.
58
+ try: # graphql-core < 3.2.7
59
+ graphql_resolver_kind = graphql.type.introspection.TypeResolvers().kind # pyright: ignore[reportAttributeAccessIssue]
60
+ except AttributeError: # pragma: no cover - executed on newer graphql-core
61
+ graphql_resolver_kind = graphql.type.introspection.TypeFields.kind # pyright: ignore[reportAttributeAccessIssue]
62
+
63
+
64
+ def build_graphql_schema(schema_str: str) -> graphql.GraphQLSchema:
65
+ """Build a graphql schema from a string."""
66
+ schema = graphql.build_schema(schema_str)
67
+ return graphql.lexicographic_sort_schema(schema)
68
+
69
+
70
+ @snooper_to_methods()
71
+ class GraphQLParser(Parser):
72
+ """Parser for GraphQL schema files."""
73
+
74
+ # raw graphql schema as `graphql-core` object
75
+ raw_obj: graphql.GraphQLSchema
76
+ # all processed graphql objects
77
+ # mapper from an object name (unique) to an object
78
+ all_graphql_objects: dict[str, graphql.GraphQLNamedType]
79
+ # a reference for each object
80
+ # mapper from an object name to his reference
81
+ references: dict[str, Reference] = {} # noqa: RUF012
82
+ # mapper from graphql type to all objects with this type
83
+ # `graphql.type.introspection.TypeKind` -- an enum with all supported types
84
+ # `graphql.GraphQLNamedType` -- base type for each graphql object
85
+ # see `graphql-core` for more details
86
+ support_graphql_types: dict[graphql.type.introspection.TypeKind, list[graphql.GraphQLNamedType]]
87
+ # graphql types order for render
88
+ # may be as a parameter in the future
89
+ parse_order: list[graphql.type.introspection.TypeKind] = [ # noqa: RUF012
90
+ graphql.type.introspection.TypeKind.SCALAR,
91
+ graphql.type.introspection.TypeKind.ENUM,
92
+ graphql.type.introspection.TypeKind.INTERFACE,
93
+ graphql.type.introspection.TypeKind.OBJECT,
94
+ graphql.type.introspection.TypeKind.INPUT_OBJECT,
95
+ graphql.type.introspection.TypeKind.UNION,
96
+ ]
97
+
98
+ def __init__( # noqa: PLR0913
99
+ self,
100
+ source: str | Path | ParseResult,
101
+ *,
102
+ data_model_type: type[DataModel] = pydantic_model.BaseModel,
103
+ data_model_root_type: type[DataModel] = pydantic_model.CustomRootType,
104
+ data_model_scalar_type: type[DataModel] = DataTypeScalarBackport,
105
+ data_model_union_type: type[DataModel] = DataTypeUnionBackport,
106
+ data_type_manager_type: type[DataTypeManager] = pydantic_model.DataTypeManager,
107
+ data_model_field_type: type[DataModelFieldBase] = pydantic_model.DataModelField,
108
+ base_class: str | None = None,
109
+ additional_imports: list[str] | None = None,
110
+ custom_template_dir: Path | None = None,
111
+ extra_template_data: defaultdict[str, dict[str, Any]] | None = None,
112
+ target_python_version: PythonVersion = PythonVersionMin,
113
+ dump_resolve_reference_action: Callable[[Iterable[str]], str] | None = None,
114
+ validation: bool = False,
115
+ field_constraints: bool = False,
116
+ snake_case_field: bool = False,
117
+ strip_default_none: bool = False,
118
+ aliases: Mapping[str, str] | None = None,
119
+ allow_population_by_field_name: bool = False,
120
+ apply_default_values_for_required_fields: bool = False,
121
+ allow_extra_fields: bool = False,
122
+ extra_fields: str | None = None,
123
+ force_optional_for_required_fields: bool = False,
124
+ class_name: str | None = None,
125
+ use_standard_collections: bool = False,
126
+ base_path: Path | None = None,
127
+ use_schema_description: bool = False,
128
+ use_field_description: bool = False,
129
+ use_attribute_docstrings: bool = False,
130
+ use_inline_field_description: bool = False,
131
+ use_default_kwarg: bool = False,
132
+ reuse_model: bool = False,
133
+ reuse_scope: ReuseScope | None = None,
134
+ shared_module_name: str = DEFAULT_SHARED_MODULE_NAME,
135
+ encoding: str = "utf-8",
136
+ enum_field_as_literal: LiteralType | None = None,
137
+ set_default_enum_member: bool = False,
138
+ use_subclass_enum: bool = False,
139
+ use_specialized_enum: bool = True,
140
+ strict_nullable: bool = False,
141
+ use_generic_container_types: bool = False,
142
+ enable_faux_immutability: bool = False,
143
+ remote_text_cache: DefaultPutDict[str, str] | None = None,
144
+ disable_appending_item_suffix: bool = False,
145
+ strict_types: Sequence[StrictTypes] | None = None,
146
+ empty_enum_field_name: str | None = None,
147
+ custom_class_name_generator: Callable[[str], str] | None = None,
148
+ field_extra_keys: set[str] | None = None,
149
+ field_include_all_keys: bool = False,
150
+ field_extra_keys_without_x_prefix: set[str] | None = None,
151
+ wrap_string_literal: bool | None = None,
152
+ use_title_as_name: bool = False,
153
+ use_operation_id_as_name: bool = False,
154
+ use_unique_items_as_set: bool = False,
155
+ allof_merge_mode: AllOfMergeMode = AllOfMergeMode.Constraints,
156
+ http_headers: Sequence[tuple[str, str]] | None = None,
157
+ http_ignore_tls: bool = False,
158
+ use_annotated: bool = False,
159
+ use_non_positive_negative_number_constrained_types: bool = False,
160
+ use_decimal_for_multiple_of: bool = False,
161
+ original_field_name_delimiter: str | None = None,
162
+ use_double_quotes: bool = False,
163
+ use_union_operator: bool = False,
164
+ allow_responses_without_content: bool = False,
165
+ collapse_root_models: bool = False,
166
+ skip_root_model: bool = False,
167
+ use_type_alias: bool = False,
168
+ special_field_name_prefix: str | None = None,
169
+ remove_special_field_name_prefix: bool = False,
170
+ capitalise_enum_members: bool = False,
171
+ keep_model_order: bool = False,
172
+ use_one_literal_as_default: bool = False,
173
+ use_enum_values_in_discriminator: bool = False,
174
+ known_third_party: list[str] | None = None,
175
+ custom_formatters: list[str] | None = None,
176
+ custom_formatters_kwargs: dict[str, Any] | None = None,
177
+ use_pendulum: bool = False,
178
+ http_query_parameters: Sequence[tuple[str, str]] | None = None,
179
+ treat_dot_as_module: bool = False,
180
+ use_exact_imports: bool = False,
181
+ default_field_extras: dict[str, Any] | None = None,
182
+ target_datetime_class: DatetimeClassType = DatetimeClassType.Datetime,
183
+ keyword_only: bool = False,
184
+ frozen_dataclasses: bool = False,
185
+ no_alias: bool = False,
186
+ formatters: list[Formatter] = DEFAULT_FORMATTERS,
187
+ parent_scoped_naming: bool = False,
188
+ dataclass_arguments: DataclassArguments | None = None,
189
+ type_mappings: list[str] | None = None,
190
+ read_only_write_only_model_type: ReadOnlyWriteOnlyModelType | None = None,
191
+ use_serialize_as_any: bool = False,
192
+ use_frozen_field: bool = False,
193
+ ) -> None:
194
+ """Initialize the GraphQL parser with configuration options."""
195
+ super().__init__(
196
+ source=source,
197
+ data_model_type=data_model_type,
198
+ data_model_root_type=data_model_root_type,
199
+ data_type_manager_type=data_type_manager_type,
200
+ data_model_field_type=data_model_field_type,
201
+ base_class=base_class,
202
+ additional_imports=additional_imports,
203
+ custom_template_dir=custom_template_dir,
204
+ extra_template_data=extra_template_data,
205
+ target_python_version=target_python_version,
206
+ dump_resolve_reference_action=dump_resolve_reference_action,
207
+ validation=validation,
208
+ field_constraints=field_constraints,
209
+ snake_case_field=snake_case_field,
210
+ strip_default_none=strip_default_none,
211
+ aliases=aliases,
212
+ allow_population_by_field_name=allow_population_by_field_name,
213
+ allow_extra_fields=allow_extra_fields,
214
+ extra_fields=extra_fields,
215
+ apply_default_values_for_required_fields=apply_default_values_for_required_fields,
216
+ force_optional_for_required_fields=force_optional_for_required_fields,
217
+ class_name=class_name,
218
+ use_standard_collections=use_standard_collections,
219
+ base_path=base_path,
220
+ use_schema_description=use_schema_description,
221
+ use_field_description=use_field_description,
222
+ use_attribute_docstrings=use_attribute_docstrings,
223
+ use_inline_field_description=use_inline_field_description,
224
+ use_default_kwarg=use_default_kwarg,
225
+ reuse_model=reuse_model,
226
+ reuse_scope=reuse_scope,
227
+ shared_module_name=shared_module_name,
228
+ encoding=encoding,
229
+ enum_field_as_literal=enum_field_as_literal,
230
+ use_one_literal_as_default=use_one_literal_as_default,
231
+ use_enum_values_in_discriminator=use_enum_values_in_discriminator,
232
+ set_default_enum_member=set_default_enum_member,
233
+ use_subclass_enum=use_subclass_enum,
234
+ use_specialized_enum=use_specialized_enum,
235
+ strict_nullable=strict_nullable,
236
+ use_generic_container_types=use_generic_container_types,
237
+ enable_faux_immutability=enable_faux_immutability,
238
+ remote_text_cache=remote_text_cache,
239
+ disable_appending_item_suffix=disable_appending_item_suffix,
240
+ strict_types=strict_types,
241
+ empty_enum_field_name=empty_enum_field_name,
242
+ custom_class_name_generator=custom_class_name_generator,
243
+ field_extra_keys=field_extra_keys,
244
+ field_include_all_keys=field_include_all_keys,
245
+ field_extra_keys_without_x_prefix=field_extra_keys_without_x_prefix,
246
+ wrap_string_literal=wrap_string_literal,
247
+ use_title_as_name=use_title_as_name,
248
+ use_operation_id_as_name=use_operation_id_as_name,
249
+ use_unique_items_as_set=use_unique_items_as_set,
250
+ allof_merge_mode=allof_merge_mode,
251
+ http_headers=http_headers,
252
+ http_ignore_tls=http_ignore_tls,
253
+ use_annotated=use_annotated,
254
+ use_non_positive_negative_number_constrained_types=use_non_positive_negative_number_constrained_types,
255
+ use_decimal_for_multiple_of=use_decimal_for_multiple_of,
256
+ original_field_name_delimiter=original_field_name_delimiter,
257
+ use_double_quotes=use_double_quotes,
258
+ use_union_operator=use_union_operator,
259
+ allow_responses_without_content=allow_responses_without_content,
260
+ collapse_root_models=collapse_root_models,
261
+ skip_root_model=skip_root_model,
262
+ use_type_alias=use_type_alias,
263
+ special_field_name_prefix=special_field_name_prefix,
264
+ remove_special_field_name_prefix=remove_special_field_name_prefix,
265
+ capitalise_enum_members=capitalise_enum_members,
266
+ keep_model_order=keep_model_order,
267
+ known_third_party=known_third_party,
268
+ custom_formatters=custom_formatters,
269
+ custom_formatters_kwargs=custom_formatters_kwargs,
270
+ use_pendulum=use_pendulum,
271
+ http_query_parameters=http_query_parameters,
272
+ treat_dot_as_module=treat_dot_as_module,
273
+ use_exact_imports=use_exact_imports,
274
+ default_field_extras=default_field_extras,
275
+ target_datetime_class=target_datetime_class,
276
+ keyword_only=keyword_only,
277
+ frozen_dataclasses=frozen_dataclasses,
278
+ no_alias=no_alias,
279
+ formatters=formatters,
280
+ parent_scoped_naming=parent_scoped_naming,
281
+ dataclass_arguments=dataclass_arguments,
282
+ type_mappings=type_mappings,
283
+ read_only_write_only_model_type=read_only_write_only_model_type,
284
+ use_serialize_as_any=use_serialize_as_any,
285
+ use_frozen_field=use_frozen_field,
286
+ )
287
+
288
+ self.data_model_scalar_type = data_model_scalar_type
289
+ self.data_model_union_type = data_model_union_type
290
+ self.use_standard_collections = use_standard_collections
291
+ self.use_union_operator = use_union_operator
292
+
293
+ def _get_context_source_path_parts(self) -> Iterator[tuple[Source, list[str]]]:
294
+ # TODO (denisart): Temporarily this method duplicates
295
+ # the method `datamodel_code_generator.parser.jsonschema.JsonSchemaParser._get_context_source_path_parts`.
296
+
297
+ if isinstance(self.source, list) or ( # pragma: no cover
298
+ isinstance(self.source, Path) and self.source.is_dir()
299
+ ): # pragma: no cover
300
+ self.current_source_path = Path()
301
+ self.model_resolver.after_load_files = {
302
+ self.base_path.joinpath(s.path).resolve().as_posix() for s in self.iter_source
303
+ }
304
+
305
+ for source in self.iter_source:
306
+ if isinstance(self.source, ParseResult): # pragma: no cover
307
+ path_parts = self.get_url_path_parts(self.source)
308
+ else:
309
+ path_parts = list(source.path.parts)
310
+ if self.current_source_path is not None: # pragma: no cover
311
+ self.current_source_path = source.path
312
+ with (
313
+ self.model_resolver.current_base_path_context(source.path.parent),
314
+ self.model_resolver.current_root_context(path_parts),
315
+ ):
316
+ yield source, path_parts
317
+
318
+ def _resolve_types(self, paths: list[str], schema: graphql.GraphQLSchema) -> None:
319
+ for type_name, type_ in schema.type_map.items():
320
+ if type_name.startswith("__"):
321
+ continue
322
+
323
+ if type_name in {"Query", "Mutation"}:
324
+ continue
325
+
326
+ resolved_type = graphql_resolver_kind(type_, None)
327
+
328
+ if resolved_type in self.support_graphql_types: # pragma: no cover
329
+ self.all_graphql_objects[type_.name] = type_
330
+ # TODO: need a special method for each graph type
331
+ self.references[type_.name] = Reference(
332
+ path=f"{paths!s}/{resolved_type.value}/{type_.name}",
333
+ name=type_.name,
334
+ original_name=type_.name,
335
+ )
336
+
337
+ self.support_graphql_types[resolved_type].append(type_)
338
+
339
+ def _create_data_model(self, model_type: type[DataModel] | None = None, **kwargs: Any) -> DataModel:
340
+ """Create data model instance with dataclass_arguments support for DataClass."""
341
+ data_model_class = model_type or self.data_model_type
342
+ if issubclass(data_model_class, DataClass):
343
+ # Use dataclass_arguments from kwargs, or fall back to self.dataclass_arguments
344
+ # If both are None, construct from legacy frozen_dataclasses/keyword_only flags
345
+ dataclass_arguments = kwargs.pop("dataclass_arguments", None)
346
+ if dataclass_arguments is None:
347
+ dataclass_arguments = self.dataclass_arguments
348
+ if dataclass_arguments is None:
349
+ # Construct from legacy flags for library API compatibility
350
+ dataclass_arguments = {}
351
+ if self.frozen_dataclasses:
352
+ dataclass_arguments["frozen"] = True
353
+ if self.keyword_only:
354
+ dataclass_arguments["kw_only"] = True
355
+ kwargs["dataclass_arguments"] = dataclass_arguments
356
+ kwargs.pop("frozen", None)
357
+ kwargs.pop("keyword_only", None)
358
+ else:
359
+ kwargs.pop("dataclass_arguments", None)
360
+ return data_model_class(**kwargs)
361
+
362
+ def _typename_field(self, name: str) -> DataModelFieldBase:
363
+ return self.data_model_field_type(
364
+ name="typename__",
365
+ data_type=DataType(
366
+ literals=[name],
367
+ use_union_operator=self.use_union_operator,
368
+ use_standard_collections=self.use_standard_collections,
369
+ ),
370
+ default=name,
371
+ use_annotated=self.use_annotated,
372
+ required=False,
373
+ alias="__typename",
374
+ use_one_literal_as_default=True,
375
+ use_default_kwarg=self.use_default_kwarg,
376
+ has_default=True,
377
+ )
378
+
379
+ def _get_default( # noqa: PLR6301
380
+ self,
381
+ field: graphql.GraphQLField | graphql.GraphQLInputField,
382
+ final_data_type: DataType,
383
+ *,
384
+ required: bool,
385
+ ) -> Any:
386
+ if isinstance(field, graphql.GraphQLInputField): # pragma: no cover
387
+ if field.default_value == graphql.pyutils.Undefined: # pragma: no cover
388
+ return None
389
+ return field.default_value
390
+ if required is False and final_data_type.is_list:
391
+ return None
392
+
393
+ return None
394
+
395
+ def parse_scalar(self, scalar_graphql_object: graphql.GraphQLScalarType) -> None:
396
+ """Parse a GraphQL scalar type and add it to results."""
397
+ self.results.append(
398
+ self.data_model_scalar_type(
399
+ reference=self.references[scalar_graphql_object.name],
400
+ fields=[],
401
+ custom_template_dir=self.custom_template_dir,
402
+ extra_template_data=self.extra_template_data,
403
+ description=scalar_graphql_object.description,
404
+ )
405
+ )
406
+
407
+ def should_parse_enum_as_literal(self, obj: graphql.GraphQLEnumType) -> bool:
408
+ """Determine if an enum should be parsed as a literal type."""
409
+ return self.enum_field_as_literal == LiteralType.All or (
410
+ self.enum_field_as_literal == LiteralType.One and len(obj.values) == 1
411
+ )
412
+
413
+ def parse_enum(self, enum_object: graphql.GraphQLEnumType) -> None:
414
+ """Parse a GraphQL enum type and add it to results."""
415
+ if self.should_parse_enum_as_literal(enum_object):
416
+ return self.parse_enum_as_literal(enum_object)
417
+ return self.parse_enum_as_enum_class(enum_object)
418
+
419
+ def parse_enum_as_literal(self, enum_object: graphql.GraphQLEnumType) -> None:
420
+ """Parse enum values as a Literal type."""
421
+ data_type = self.data_type(literals=list(enum_object.values.keys()))
422
+ data_model_type = self._create_data_model(
423
+ model_type=self.data_model_root_type,
424
+ reference=self.references[enum_object.name],
425
+ fields=[
426
+ self.data_model_field_type(
427
+ required=True,
428
+ data_type=data_type,
429
+ )
430
+ ],
431
+ custom_base_class=self.base_class,
432
+ custom_template_dir=self.custom_template_dir,
433
+ extra_template_data=self.extra_template_data,
434
+ path=self.current_source_path,
435
+ description=enum_object.description,
436
+ )
437
+ self.results.append(data_model_type)
438
+
439
+ def parse_enum_as_enum_class(self, enum_object: graphql.GraphQLEnumType) -> None:
440
+ """Parse enum values as an Enum class."""
441
+ enum_fields: list[DataModelFieldBase] = []
442
+ exclude_field_names: set[str] = set()
443
+
444
+ for value_name, value in enum_object.values.items():
445
+ default = f"'{value_name.translate(escape_characters)}'" if isinstance(value_name, str) else value_name
446
+
447
+ field_name = self.model_resolver.get_valid_field_name(
448
+ value_name, excludes=exclude_field_names, model_type=ModelType.ENUM
449
+ )
450
+ exclude_field_names.add(field_name)
451
+
452
+ enum_fields.append(
453
+ self.data_model_field_type(
454
+ name=field_name,
455
+ data_type=self.data_type_manager.get_data_type(
456
+ Types.string,
457
+ ),
458
+ default=default,
459
+ required=True,
460
+ strip_default_none=self.strip_default_none,
461
+ has_default=True,
462
+ use_field_description=value.description is not None,
463
+ original_name=None,
464
+ )
465
+ )
466
+
467
+ enum_cls: type[Enum] = Enum
468
+ if (
469
+ self.target_python_version.has_strenum
470
+ and self.use_specialized_enum
471
+ and (specialized_type := SPECIALIZED_ENUM_TYPE_MATCH.get(Types.string))
472
+ ):
473
+ # If specialized enum is available in the target Python version, use it
474
+ enum_cls = specialized_type
475
+
476
+ enum: Enum = enum_cls(
477
+ reference=self.references[enum_object.name],
478
+ fields=enum_fields,
479
+ path=self.current_source_path,
480
+ description=enum_object.description,
481
+ type_=Types.string if self.use_subclass_enum else None,
482
+ custom_template_dir=self.custom_template_dir,
483
+ )
484
+ self.results.append(enum)
485
+
486
+ def parse_field(
487
+ self,
488
+ field_name: str,
489
+ alias: str | None,
490
+ field: graphql.GraphQLField | graphql.GraphQLInputField,
491
+ ) -> DataModelFieldBase:
492
+ """Parse a GraphQL field and return a data model field."""
493
+ final_data_type = DataType(
494
+ is_optional=True,
495
+ use_union_operator=self.use_union_operator,
496
+ use_standard_collections=self.use_standard_collections,
497
+ )
498
+ data_type = final_data_type
499
+ obj = field.type
500
+
501
+ while graphql.is_list_type(obj) or graphql.is_non_null_type(obj):
502
+ if graphql.is_list_type(obj):
503
+ data_type.is_list = True
504
+
505
+ new_data_type = DataType(
506
+ is_optional=True,
507
+ use_union_operator=self.use_union_operator,
508
+ use_standard_collections=self.use_standard_collections,
509
+ )
510
+ data_type.data_types = [new_data_type]
511
+
512
+ data_type = new_data_type
513
+ elif graphql.is_non_null_type(obj): # pragma: no cover
514
+ data_type.is_optional = False
515
+
516
+ obj = graphql.assert_wrapping_type(obj)
517
+ obj = obj.of_type
518
+
519
+ obj = graphql.assert_named_type(obj)
520
+ if obj.name in self.references:
521
+ data_type.reference = self.references[obj.name]
522
+ else: # pragma: no cover
523
+ # Only happens for Query and Mutation root types
524
+ data_type.type = obj.name
525
+
526
+ required = (not self.force_optional_for_required_fields) and (not final_data_type.is_optional)
527
+
528
+ default = self._get_default(field, final_data_type, required=required)
529
+ extras = {} if self.default_field_extras is None else self.default_field_extras.copy()
530
+
531
+ if field.description is not None: # pragma: no cover
532
+ extras["description"] = field.description
533
+
534
+ return self.data_model_field_type(
535
+ name=field_name,
536
+ default=default,
537
+ data_type=final_data_type,
538
+ required=required,
539
+ extras=extras,
540
+ alias=alias,
541
+ strip_default_none=self.strip_default_none,
542
+ use_annotated=self.use_annotated,
543
+ use_serialize_as_any=self.use_serialize_as_any,
544
+ use_field_description=self.use_field_description,
545
+ use_inline_field_description=self.use_inline_field_description,
546
+ use_default_kwarg=self.use_default_kwarg,
547
+ original_name=field_name,
548
+ has_default=default is not None,
549
+ )
550
+
551
+ def parse_object_like(
552
+ self,
553
+ obj: graphql.GraphQLInterfaceType | graphql.GraphQLObjectType | graphql.GraphQLInputObjectType,
554
+ ) -> None:
555
+ """Parse a GraphQL object-like type and add it to results."""
556
+ fields = []
557
+ exclude_field_names: set[str] = set()
558
+
559
+ for field_name, field in obj.fields.items():
560
+ field_name_, alias = self.model_resolver.get_valid_field_name_and_alias(
561
+ field_name,
562
+ excludes=exclude_field_names,
563
+ model_type=self.field_name_model_type,
564
+ class_name=obj.name,
565
+ )
566
+ exclude_field_names.add(field_name_)
567
+
568
+ data_model_field_type = self.parse_field(field_name_, alias, field)
569
+ fields.append(data_model_field_type)
570
+
571
+ fields.append(self._typename_field(obj.name))
572
+
573
+ base_classes = []
574
+ if hasattr(obj, "interfaces"): # pragma: no cover
575
+ base_classes = [self.references[i.name] for i in obj.interfaces] # pyright: ignore[reportAttributeAccessIssue]
576
+
577
+ data_model_type = self._create_data_model(
578
+ reference=self.references[obj.name],
579
+ fields=fields,
580
+ base_classes=base_classes,
581
+ custom_base_class=self.base_class,
582
+ custom_template_dir=self.custom_template_dir,
583
+ extra_template_data=self.extra_template_data,
584
+ path=self.current_source_path,
585
+ description=obj.description,
586
+ keyword_only=self.keyword_only,
587
+ treat_dot_as_module=self.treat_dot_as_module,
588
+ dataclass_arguments=self.dataclass_arguments,
589
+ )
590
+ self.results.append(data_model_type)
591
+
592
+ def parse_interface(self, interface_graphql_object: graphql.GraphQLInterfaceType) -> None:
593
+ """Parse a GraphQL interface type and add it to results."""
594
+ self.parse_object_like(interface_graphql_object)
595
+
596
+ def parse_object(self, graphql_object: graphql.GraphQLObjectType) -> None:
597
+ """Parse a GraphQL object type and add it to results."""
598
+ self.parse_object_like(graphql_object)
599
+
600
+ def parse_input_object(self, input_graphql_object: graphql.GraphQLInputObjectType) -> None:
601
+ """Parse a GraphQL input object type and add it to results."""
602
+ self.parse_object_like(input_graphql_object) # pragma: no cover
603
+
604
+ def parse_union(self, union_object: graphql.GraphQLUnionType) -> None:
605
+ """Parse a GraphQL union type and add it to results."""
606
+ fields = [self.data_model_field_type(name=type_.name, data_type=DataType()) for type_ in union_object.types]
607
+
608
+ data_model_type = self.data_model_union_type(
609
+ reference=self.references[union_object.name],
610
+ fields=fields,
611
+ custom_base_class=self.base_class,
612
+ custom_template_dir=self.custom_template_dir,
613
+ extra_template_data=self.extra_template_data,
614
+ path=self.current_source_path,
615
+ description=union_object.description,
616
+ )
617
+ self.results.append(data_model_type)
618
+
619
+ def parse_raw(self) -> None:
620
+ """Parse the raw GraphQL schema and generate all data models."""
621
+ self.all_graphql_objects = {}
622
+ self.references: dict[str, Reference] = {}
623
+
624
+ self.support_graphql_types = {
625
+ graphql.type.introspection.TypeKind.SCALAR: [],
626
+ graphql.type.introspection.TypeKind.ENUM: [],
627
+ graphql.type.introspection.TypeKind.UNION: [],
628
+ graphql.type.introspection.TypeKind.INTERFACE: [],
629
+ graphql.type.introspection.TypeKind.OBJECT: [],
630
+ graphql.type.introspection.TypeKind.INPUT_OBJECT: [],
631
+ }
632
+
633
+ # may be as a parameter in the future (??)
634
+ mapper_from_graphql_type_to_parser_method = {
635
+ graphql.type.introspection.TypeKind.SCALAR: self.parse_scalar,
636
+ graphql.type.introspection.TypeKind.ENUM: self.parse_enum,
637
+ graphql.type.introspection.TypeKind.INTERFACE: self.parse_interface,
638
+ graphql.type.introspection.TypeKind.OBJECT: self.parse_object,
639
+ graphql.type.introspection.TypeKind.INPUT_OBJECT: self.parse_input_object,
640
+ graphql.type.introspection.TypeKind.UNION: self.parse_union,
641
+ }
642
+
643
+ for source, path_parts in self._get_context_source_path_parts():
644
+ schema: graphql.GraphQLSchema = build_graphql_schema(source.text)
645
+ self.raw_obj = schema
646
+
647
+ self._resolve_types(path_parts, schema)
648
+
649
+ for next_type in self.parse_order:
650
+ for obj in self.support_graphql_types[next_type]:
651
+ parser_ = mapper_from_graphql_type_to_parser_method[next_type]
652
+ parser_(obj)