cadwyn 3.15.10__py3-none-any.whl → 4.0.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.

Potentially problematic release.


This version of cadwyn might be problematic. Click here for more details.

@@ -1,423 +0,0 @@
1
- import ast
2
- from collections.abc import Sequence
3
- from typing import Annotated, Any, cast, get_args, get_origin
4
-
5
- from typing_extensions import assert_never
6
-
7
- from cadwyn._asts import add_keyword_to_call, delete_keyword_from_call, get_fancy_repr
8
- from cadwyn._compat import (
9
- PYDANTIC_V2,
10
- FieldInfo,
11
- PydanticFieldWrapper,
12
- dict_of_empty_field_info,
13
- is_constrained_type,
14
- )
15
- from cadwyn._package_utils import IdentifierPythonPath, get_cls_pythonpath
16
- from cadwyn._utils import Sentinel
17
- from cadwyn.codegen._common import GlobalCodegenContext, PydanticModelWrapper, _EnumWrapper
18
- from cadwyn.exceptions import InvalidGenerationInstructionError
19
- from cadwyn.structure.enums import AlterEnumSubInstruction, EnumDidntHaveMembersInstruction, EnumHadMembersInstruction
20
- from cadwyn.structure.schemas import (
21
- AlterSchemaSubInstruction,
22
- FieldDidntExistInstruction,
23
- FieldDidntHaveInstruction,
24
- FieldExistedAsInstruction,
25
- FieldHadInstruction,
26
- SchemaHadInstruction,
27
- ValidatorDidntExistInstruction,
28
- ValidatorExistedInstruction,
29
- )
30
-
31
-
32
- def class_migration_plugin(context: GlobalCodegenContext) -> None:
33
- for version_change in context.current_version.version_changes:
34
- _apply_alter_schema_instructions(
35
- context.schemas,
36
- version_change.alter_schema_instructions,
37
- version_change.__name__,
38
- )
39
- _apply_alter_enum_instructions(
40
- context.enums,
41
- version_change.alter_enum_instructions,
42
- version_change.__name__,
43
- )
44
-
45
-
46
- def _apply_alter_schema_instructions(
47
- modified_schemas: dict[IdentifierPythonPath, PydanticModelWrapper],
48
- alter_schema_instructions: Sequence[AlterSchemaSubInstruction | SchemaHadInstruction],
49
- version_change_name: str,
50
- ):
51
- for alter_schema_instruction in alter_schema_instructions:
52
- schema = alter_schema_instruction.schema
53
- schema_path = get_cls_pythonpath(schema)
54
- schema_info = modified_schemas[schema_path]
55
- if isinstance(alter_schema_instruction, FieldExistedAsInstruction):
56
- _add_field_to_model(schema_info, modified_schemas, alter_schema_instruction, version_change_name)
57
- elif isinstance(alter_schema_instruction, FieldHadInstruction | FieldDidntHaveInstruction):
58
- _change_field_in_model(
59
- schema_info,
60
- modified_schemas,
61
- alter_schema_instruction,
62
- version_change_name,
63
- )
64
- elif isinstance(alter_schema_instruction, FieldDidntExistInstruction):
65
- _delete_field_from_model(schema_info, alter_schema_instruction.name, version_change_name)
66
- elif isinstance(alter_schema_instruction, ValidatorExistedInstruction):
67
- validator_name = alter_schema_instruction.validator.__name__
68
- schema_info.validators[validator_name] = alter_schema_instruction.validator_info
69
- elif isinstance(alter_schema_instruction, ValidatorDidntExistInstruction):
70
- if alter_schema_instruction.name not in schema_info.validators:
71
- raise InvalidGenerationInstructionError(
72
- f'You tried to delete a validator "{alter_schema_instruction.name}" from "{schema_info.name}" '
73
- f'in "{version_change_name}" but it doesn\'t have such a validator.',
74
- )
75
- if schema_info.validators[alter_schema_instruction.name].is_deleted:
76
- raise InvalidGenerationInstructionError(
77
- f'You tried to delete a validator "{alter_schema_instruction.name}" from "{schema_info.name}" '
78
- f'in "{version_change_name}" but it is already deleted.',
79
- )
80
- schema_info.validators[alter_schema_instruction.name].is_deleted = True
81
- elif isinstance(alter_schema_instruction, SchemaHadInstruction):
82
- _change_model(schema_info, alter_schema_instruction, version_change_name)
83
- else:
84
- assert_never(alter_schema_instruction)
85
-
86
-
87
- def _apply_alter_enum_instructions(
88
- enums: dict[IdentifierPythonPath, _EnumWrapper],
89
- alter_enum_instructions: Sequence[AlterEnumSubInstruction],
90
- version_change_name: str,
91
- ):
92
- for alter_enum_instruction in alter_enum_instructions:
93
- enum = alter_enum_instruction.enum
94
- enum_path = get_cls_pythonpath(enum)
95
- enum = enums[enum_path]
96
- if isinstance(alter_enum_instruction, EnumDidntHaveMembersInstruction):
97
- for member in alter_enum_instruction.members:
98
- if member not in enum.members:
99
- raise InvalidGenerationInstructionError(
100
- f'You tried to delete a member "{member}" from "{enum.cls.__name__}" '
101
- f'in "{version_change_name}" but it doesn\'t have such a member.',
102
- )
103
- enum.members.pop(member)
104
- elif isinstance(alter_enum_instruction, EnumHadMembersInstruction):
105
- for member, member_value in alter_enum_instruction.members.items():
106
- if member in enum.members and enum.members[member] == member_value:
107
- raise InvalidGenerationInstructionError(
108
- f'You tried to add a member "{member}" to "{enum.cls.__name__}" '
109
- f'in "{version_change_name}" but there is already a member with that name and value.',
110
- )
111
- enum.members[member] = member_value
112
- else:
113
- assert_never(alter_enum_instruction)
114
-
115
-
116
- def _change_model(
117
- model: PydanticModelWrapper,
118
- alter_schema_instruction: SchemaHadInstruction,
119
- version_change_name: str,
120
- ):
121
- # We only handle names right now so we just go ahead and check
122
- if alter_schema_instruction.name == model.name:
123
- raise InvalidGenerationInstructionError(
124
- f'You tried to change the name of "{model.name}" in "{version_change_name}" '
125
- "but it already has the name you tried to assign.",
126
- )
127
-
128
- model.name = alter_schema_instruction.name
129
-
130
-
131
- def _add_field_to_model(
132
- model: PydanticModelWrapper,
133
- schemas: "dict[IdentifierPythonPath, PydanticModelWrapper]",
134
- alter_schema_instruction: FieldExistedAsInstruction,
135
- version_change_name: str,
136
- ):
137
- defined_fields = model._get_defined_fields_through_mro(schemas)
138
- if alter_schema_instruction.name in defined_fields:
139
- raise InvalidGenerationInstructionError(
140
- f'You tried to add a field "{alter_schema_instruction.name}" to "{model.name}" '
141
- f'in "{version_change_name}" but there is already a field with that name.',
142
- )
143
-
144
- fancy_type_repr = get_fancy_repr(alter_schema_instruction.type)
145
- field = PydanticFieldWrapper(
146
- annotation_ast=ast.parse(fancy_type_repr, mode="eval").body,
147
- annotation=alter_schema_instruction.type,
148
- init_model_field=alter_schema_instruction.field,
149
- value_ast=None,
150
- )
151
- model.fields[alter_schema_instruction.name] = field
152
-
153
- passed_field_attributes = field.passed_field_attributes
154
- if passed_field_attributes:
155
- field_call_ast = cast(ast.Call, ast.parse("Field()", mode="eval").body)
156
- for attr_name, attr_value in passed_field_attributes.items():
157
- add_keyword_to_call(attr_name, attr_value, field_call_ast)
158
- field.value_ast = field_call_ast
159
- model.annotations[alter_schema_instruction.name] = alter_schema_instruction.type
160
-
161
-
162
- def _change_field_in_model(
163
- model: PydanticModelWrapper,
164
- schemas: "dict[IdentifierPythonPath, PydanticModelWrapper]",
165
- alter_schema_instruction: FieldHadInstruction | FieldDidntHaveInstruction,
166
- version_change_name: str,
167
- ):
168
- defined_annotations = model._get_defined_annotations_through_mro(schemas)
169
- defined_fields = model._get_defined_fields_through_mro(schemas)
170
- if alter_schema_instruction.name not in defined_fields:
171
- raise InvalidGenerationInstructionError(
172
- f'You tried to change the field "{alter_schema_instruction.name}" from '
173
- f'"{model.name}" in "{version_change_name}" but it doesn\'t have such a field.',
174
- )
175
-
176
- field = defined_fields[alter_schema_instruction.name]
177
- model.fields[alter_schema_instruction.name] = field
178
- model.annotations[alter_schema_instruction.name] = defined_annotations[alter_schema_instruction.name]
179
-
180
- annotation_ast, field_call_ast, contype_is_definitely_used = _get_constraint_asts_and_field_call_ast(
181
- schemas, model, alter_schema_instruction.name, field
182
- )
183
-
184
- constrained_type_annotation = None
185
-
186
- if annotation_ast is not None:
187
- field_type_is_annotated = True # I.e. typing.Annotated
188
- # PydanticV2 changed field annotation handling so field.annotation lies to us
189
- real_annotation = model._get_defined_annotations_through_mro(schemas)[alter_schema_instruction.name]
190
- type_annotation = get_args(real_annotation)[0]
191
- if is_constrained_type(type_annotation):
192
- constrained_type_annotation = type_annotation
193
- else:
194
- field_type_is_annotated = False
195
- type_annotation = field.annotation
196
- if is_constrained_type(type_annotation):
197
- constrained_type_annotation = type_annotation
198
- annotation_ast = field.annotation_ast
199
- if field_call_ast is None:
200
- field_call_ast = field.value_ast
201
-
202
- if isinstance(alter_schema_instruction, FieldHadInstruction):
203
- # TODO: This naming sucks
204
- _change_field(
205
- model,
206
- alter_schema_instruction,
207
- version_change_name,
208
- defined_annotations,
209
- field,
210
- annotation_ast,
211
- field_call_ast,
212
- type_annotation,
213
- field_type_is_annotated,
214
- constrained_type_annotation,
215
- )
216
- else:
217
- _delete_field_attributes(
218
- model,
219
- alter_schema_instruction,
220
- version_change_name,
221
- field,
222
- annotation_ast,
223
- field_call_ast,
224
- type_annotation,
225
- constrained_type_annotation,
226
- contype_is_definitely_used,
227
- )
228
-
229
-
230
- def _change_field( # noqa: C901
231
- model: PydanticModelWrapper,
232
- alter_schema_instruction: FieldHadInstruction,
233
- version_change_name: str,
234
- defined_annotations: dict[str, Any],
235
- field: PydanticFieldWrapper,
236
- annotation_ast: ast.expr | None,
237
- field_call_ast: ast.expr | None,
238
- type_annotation: Any,
239
- field_type_is_annotated: bool,
240
- constrained_type_annotation: Any | None,
241
- ):
242
- if alter_schema_instruction.type is not Sentinel:
243
- if field.annotation == alter_schema_instruction.type:
244
- raise InvalidGenerationInstructionError(
245
- f'You tried to change the type of field "{alter_schema_instruction.name}" to '
246
- f'"{alter_schema_instruction.type}" from "{model.name}" in "{version_change_name}" '
247
- f'but it already has type "{field.annotation}"',
248
- )
249
- field.annotation = alter_schema_instruction.type
250
- model.annotations[alter_schema_instruction.name] = alter_schema_instruction.type
251
- fancy_type_repr = get_fancy_repr(alter_schema_instruction.type)
252
- field.annotation_ast = ast.parse(fancy_type_repr, mode="eval").body
253
-
254
- if alter_schema_instruction.new_name is not Sentinel:
255
- if alter_schema_instruction.new_name == alter_schema_instruction.name:
256
- raise InvalidGenerationInstructionError(
257
- f'You tried to change the name of field "{alter_schema_instruction.name}" '
258
- f'from "{model.name}" in "{version_change_name}" '
259
- "but it already has that name.",
260
- )
261
- model.fields[alter_schema_instruction.new_name] = model.fields.pop(alter_schema_instruction.name)
262
- model.annotations[alter_schema_instruction.new_name] = model.annotations.pop(
263
- alter_schema_instruction.name,
264
- defined_annotations[alter_schema_instruction.name],
265
- )
266
-
267
- field_info = field.field_info
268
-
269
- dict_of_field_info = {k: getattr(field_info, k) for k in field_info.__slots__}
270
- if dict_of_field_info == dict_of_empty_field_info:
271
- field_info = FieldInfo()
272
- field.field_info = field_info
273
- for attr_name in alter_schema_instruction.field_changes.__dataclass_fields__:
274
- attr_value = getattr(alter_schema_instruction.field_changes, attr_name)
275
- if attr_value is not Sentinel:
276
- if field.passed_field_attributes.get(attr_name, Sentinel) == attr_value:
277
- raise InvalidGenerationInstructionError(
278
- f'You tried to change the attribute "{attr_name}" of field '
279
- f'"{alter_schema_instruction.name}" '
280
- f'from "{model.name}" to {attr_value!r} in "{version_change_name}" '
281
- "but it already has that value.",
282
- )
283
- if constrained_type_annotation is not None and hasattr(constrained_type_annotation, attr_name):
284
- _setattr_on_constrained_type(constrained_type_annotation, attr_name, attr_value)
285
- if isinstance(annotation_ast, ast.Call):
286
- add_keyword_to_call(attr_name, attr_value, annotation_ast)
287
- elif not PYDANTIC_V2: # pragma: no branch
288
- field.update_attribute(name=attr_name, value=attr_value)
289
- if isinstance(field_call_ast, ast.Call): # pragma: no branch
290
- add_keyword_to_call(attr_name, attr_value, field_call_ast)
291
-
292
- else:
293
- field.update_attribute(name=attr_name, value=attr_value)
294
- if field_type_is_annotated and attr_name == "default":
295
- field.value_ast = ast.parse(get_fancy_repr(attr_value), mode="eval").body
296
- elif isinstance(field_call_ast, ast.Call):
297
- add_keyword_to_call(attr_name, attr_value, field_call_ast)
298
- elif field.value_ast is not None:
299
- field_call_ast = cast(ast.Call, ast.parse("Field()", mode="eval").body)
300
- add_keyword_to_call("default", field.value_ast, field_call_ast)
301
- add_keyword_to_call(attr_name, attr_value, field_call_ast)
302
- field.value_ast = field_call_ast
303
- else:
304
- field.value_ast = cast(ast.Call, ast.parse("Field()", mode="eval").body)
305
- field_call_ast = field.value_ast
306
- add_keyword_to_call(attr_name, attr_value, field_call_ast)
307
-
308
-
309
- def _setattr_on_constrained_type(constrained_type_annotation: Any, attr_name: str, attr_value: Any) -> None:
310
- setattr(constrained_type_annotation, attr_name, attr_value)
311
-
312
-
313
- def _delete_field_attributes(
314
- model: PydanticModelWrapper,
315
- alter_schema_instruction: FieldDidntHaveInstruction,
316
- version_change_name: str,
317
- field: PydanticFieldWrapper,
318
- type_annotation_ast: ast.expr | None,
319
- field_call_ast: ast.expr | None,
320
- type_annotation: Any,
321
- constrained_type_annotation: Any,
322
- contype_is_definitely_used: bool,
323
- ) -> None:
324
- for attr_name in alter_schema_instruction.attributes:
325
- if attr_name in field.passed_field_attributes:
326
- field.delete_attribute(name=attr_name)
327
- if isinstance(field_call_ast, ast.Call):
328
- delete_keyword_from_call(attr_name, field_call_ast)
329
- elif attr_name == "default": # pragma: no branch
330
- field.value_ast = None
331
- # In case annotation_ast is a conint/constr/etc. Notice how we do not support
332
- # the same operation for **adding** constraints for simplicity.
333
- elif (hasattr(constrained_type_annotation, attr_name)) or contype_is_definitely_used:
334
- if hasattr(constrained_type_annotation, attr_name):
335
- _setattr_on_constrained_type(constrained_type_annotation, attr_name, None)
336
- if isinstance(type_annotation_ast, ast.Call): # pragma: no branch
337
- delete_keyword_from_call(attr_name, type_annotation_ast)
338
- else:
339
- raise InvalidGenerationInstructionError(
340
- f'You tried to delete the attribute "{attr_name}" of field "{alter_schema_instruction.name}" '
341
- f'from "{model.name}" in "{version_change_name}" '
342
- "but it already doesn't have that attribute.",
343
- )
344
-
345
-
346
- ContypeIsDefinitelyUsed = bool
347
- CONTYPES = (
348
- "conbytes",
349
- "condate",
350
- "condecimal",
351
- "confloat",
352
- "conint",
353
- "conlist",
354
- "conset",
355
- "constr",
356
- )
357
-
358
-
359
- def _get_constraint_asts_and_field_call_ast(
360
- schemas: dict[IdentifierPythonPath, PydanticModelWrapper],
361
- model: PydanticModelWrapper,
362
- field_name: str,
363
- field: PydanticFieldWrapper,
364
- ) -> tuple[ast.expr | None, ast.Call | None, ContypeIsDefinitelyUsed]:
365
- """If the field type is Annotated and contains "Field" """
366
- # We return both annotation ast and field call ast because annotation might be a constrained type such as conint
367
- # and therefore contain constraints that we might want to remove.
368
-
369
- # ContypeIsDefinitely used is used to determine whether constr/conint/etc is used in Pydantic 2
370
- # because pydantic 2 changes original type hints to make sure that conint/constr/etc do not appear in annotations.
371
-
372
- real_annotation = model._get_defined_annotations_through_mro(schemas)[field_name]
373
- # typing.Annotated scenario
374
- if get_origin(real_annotation) == Annotated:
375
- index_of_field_info = _find_index_of_field_info_in_annotated(real_annotation)
376
- if not (isinstance(field.annotation_ast, ast.Subscript) and isinstance(field.annotation_ast.slice, ast.Tuple)):
377
- return (field.annotation_ast, None, True)
378
-
379
- unparsed_annotation = ast.unparse(field.annotation_ast.slice.elts[0])
380
- contype_is_definitely_used = any(contype in unparsed_annotation for contype in CONTYPES)
381
-
382
- # In pydantic 2, this means that in fact there is conint/constr/etc instead of an actual Annotated.
383
- # Yes, pydantic 2 changes original type hints to make sure that conint/constr/etc do not appear in types.
384
-
385
- if index_of_field_info is not None:
386
- field_call_ast = field.annotation_ast.slice.elts[index_of_field_info]
387
-
388
- return (field.annotation_ast.slice.elts[0], cast(ast.Call, field_call_ast), contype_is_definitely_used)
389
- return (field.annotation_ast.slice.elts[0], None, contype_is_definitely_used)
390
- return (None, None, False)
391
-
392
-
393
- def _find_index_of_field_info_in_annotated(real_annotation: Any):
394
- # Pydantic turns `Annotated[conint(lt=2 + 5), Field(default=11), annotated_types.Gt(0)]` into:
395
- # `Annotated[int, None, Interval(lt=7), None, FieldInfo(default=11), annotated_types.Gt(0)]`
396
- # Why? No idea. Probably due to its weird handling of constrained types. So we gotta go from the last element
397
- # because constrained types can only appear and mess up indexing in the first element.
398
- for i, arg in enumerate(reversed(get_args(real_annotation)), start=1):
399
- if isinstance(arg, FieldInfo):
400
- return -i
401
- return None
402
-
403
-
404
- def _delete_field_from_model(model: PydanticModelWrapper, field_name: str, version_change_name: str):
405
- if field_name not in model.fields:
406
- raise InvalidGenerationInstructionError(
407
- f'You tried to delete a field "{field_name}" from "{model.name}" '
408
- f'in "{version_change_name}" but it doesn\'t have such a field.',
409
- )
410
- model.fields.pop(field_name)
411
- for validator_name, validator in model.validators.copy().items():
412
- if validator.field_names is not None and field_name in validator.field_names:
413
- validator.field_names.remove(field_name)
414
-
415
- validator_decorator = cast(
416
- ast.Call, validator.func_ast.decorator_list[validator.index_of_validator_decorator]
417
- )
418
- for arg in validator_decorator.args.copy():
419
- if isinstance(arg, ast.Constant) and arg.value == field_name:
420
- validator_decorator.args.remove(arg)
421
- validator.func_ast.decorator_list[0]
422
- if not validator.field_names:
423
- model.validators[validator_name].is_deleted = True
@@ -1,109 +0,0 @@
1
- import ast
2
- import copy
3
- from typing import Any
4
-
5
- from cadwyn._asts import (
6
- get_fancy_repr,
7
- pop_docstring_from_cls_body,
8
- )
9
- from cadwyn._package_utils import IdentifierPythonPath, get_absolute_python_path_of_import
10
- from cadwyn.codegen._common import CodegenContext, PydanticModelWrapper, _EnumWrapper
11
-
12
-
13
- class ClassRebuildingPlugin:
14
- node_type = ast.Module
15
-
16
- @staticmethod
17
- def __call__(node: ast.Module, context: CodegenContext) -> Any:
18
- node.body = [_migrate_ast_node_to_another_version(n, context) for n in node.body]
19
- return node
20
-
21
-
22
- def _migrate_ast_node_to_another_version(
23
- node: ast.stmt,
24
- context: CodegenContext,
25
- ):
26
- if isinstance(node, ast.ClassDef):
27
- return _migrate_cls_to_another_version(node, context)
28
- elif isinstance(node, ast.ImportFrom):
29
- python_path = get_absolute_python_path_of_import(node, context.module_python_path)
30
- node.names = [
31
- name
32
- if (name_path := f"{python_path}.{name.name}") not in context.schemas
33
- else ast.alias(name=context.schemas[name_path].name, asname=name.asname)
34
- for name in node.names
35
- ]
36
-
37
- return node
38
-
39
-
40
- def _migrate_cls_to_another_version(
41
- cls_node: ast.ClassDef,
42
- context: CodegenContext,
43
- ) -> ast.ClassDef:
44
- cls_python_path = f"{context.module_python_path}.{cls_node.name}"
45
-
46
- if cls_python_path in context.schemas:
47
- cls_node = _modify_schema_cls(cls_node, context.schemas, cls_python_path)
48
- elif cls_python_path in context.enums:
49
- cls_node = _modify_enum_cls(cls_node, context.enums[cls_python_path])
50
-
51
- if not cls_node.body:
52
- cls_node.body = [ast.Pass()]
53
- return cls_node
54
-
55
-
56
- def _modify_enum_cls(cls_node: ast.ClassDef, enum: _EnumWrapper) -> ast.ClassDef:
57
- new_body = [
58
- ast.Assign(
59
- targets=[ast.Name(member, ctx=ast.Store())],
60
- value=ast.Name(get_fancy_repr(member_value)),
61
- lineno=0,
62
- )
63
- for member, member_value in enum.members.items()
64
- ]
65
-
66
- old_body = [n for n in cls_node.body if not isinstance(n, ast.AnnAssign | ast.Assign | ast.Pass | ast.Constant)]
67
- docstring = pop_docstring_from_cls_body(old_body)
68
-
69
- cls_node.body = docstring + new_body + old_body
70
- return cls_node
71
-
72
-
73
- def _modify_schema_cls(
74
- cls_node: ast.ClassDef,
75
- modified_schemas: dict[IdentifierPythonPath, PydanticModelWrapper],
76
- cls_python_path: str,
77
- ) -> ast.ClassDef:
78
- model_info = modified_schemas[cls_python_path]
79
- # This is for possible schema renaming
80
- cls_node.name = model_info.name
81
-
82
- field_definitions = [
83
- ast.AnnAssign( # pyright: ignore[reportCallIssue]
84
- target=ast.Name(name, ctx=ast.Store()),
85
- annotation=copy.deepcopy(field.annotation_ast), # pyright: ignore[reportArgumentType]
86
- # We do this because next plugins **might** use a transformer which will edit the ast within the field
87
- # and break rendering
88
- value=copy.deepcopy(field.value_ast),
89
- simple=1,
90
- )
91
- for name, field in model_info.fields.items()
92
- ]
93
- validator_definitions = [
94
- validator.func_ast for validator in model_info.validators.values() if not validator.is_deleted
95
- ]
96
-
97
- old_body = [
98
- n
99
- for n in cls_node.body
100
- if not (
101
- isinstance(n, ast.AnnAssign | ast.Assign | ast.Pass | ast.Constant)
102
- or (isinstance(n, ast.FunctionDef) and n.name in model_info.validators)
103
- )
104
- ]
105
- docstring = pop_docstring_from_cls_body(old_body)
106
- cls_node.body = docstring + field_definitions + validator_definitions + old_body
107
- if not cls_node.body:
108
- cls_node.body = [ast.Pass()]
109
- return cls_node
@@ -1,49 +0,0 @@
1
- import ast
2
- from typing import Any
3
-
4
- from cadwyn._package_utils import IdentifierPythonPath
5
- from cadwyn.codegen._common import CodegenContext, PydanticModelWrapper
6
-
7
-
8
- class ClassRenamingPlugin:
9
- node_type = ast.Module
10
-
11
- @staticmethod
12
- def __call__(
13
- node: ast.Module,
14
- context: CodegenContext,
15
- ):
16
- return _AnnotationASTNodeTransformerWithSchemaRenaming(
17
- context.schemas,
18
- context.all_names_defined_on_toplevel_of_file,
19
- context.module_python_path,
20
- ).visit(node)
21
-
22
-
23
- class _AnnotationASTNodeTransformerWithSchemaRenaming(ast.NodeTransformer):
24
- def __init__(
25
- self,
26
- modified_schemas: dict[IdentifierPythonPath, PydanticModelWrapper],
27
- all_names_in_module: dict[str, str],
28
- module_python_path: str,
29
- ):
30
- super().__init__()
31
- self.modified_schemas = modified_schemas
32
- self.module_python_path = module_python_path
33
- self.all_names_in_module = all_names_in_module
34
-
35
- def visit_AnnAssign(self, node: ast.AnnAssign): # noqa: N802
36
- # Handles Pydantic annotations that are strings
37
- if isinstance(node.annotation, ast.Constant) and isinstance(node.annotation.value, str):
38
- altered = self.visit(ast.parse(node.annotation.value, mode="eval").body)
39
- node.annotation.value = ast.unparse(altered)
40
- return self.generic_visit(node)
41
-
42
- def visit_Name(self, node: ast.Name) -> Any: # noqa: N802
43
- return self._get_name(node, node.id)
44
-
45
- def _get_name(self, node: ast.AST, name: str):
46
- model_info = self.modified_schemas.get(f"{self.all_names_in_module.get(name, self.module_python_path)}.{name}")
47
- if model_info is not None:
48
- return ast.Name(model_info.name)
49
- return node
@@ -1,64 +0,0 @@
1
- import ast
2
-
3
- from cadwyn._compat import PYDANTIC_V2
4
- from cadwyn.codegen._common import CodegenContext
5
-
6
- _extra_imports: list[tuple[str, str]] = [
7
- ("typing", "import typing"),
8
- ("pydantic", "import pydantic"),
9
- ("Any", "from typing import Any"),
10
- ("Annotated", "from typing import Annotated"),
11
- ("Field", "from pydantic import Field"),
12
- ("conbytes", "from pydantic import conbytes"),
13
- ("conlist", "from pydantic import conlist"),
14
- ("conset", "from pydantic import conset"),
15
- ("constr", "from pydantic import constr"),
16
- ("conint", "from pydantic import conint"),
17
- ("confloat", "from pydantic import confloat"),
18
- ("condecimal", "from pydantic import condecimal"),
19
- ("condate", "from pydantic import condate"),
20
- ("validator", "from pydantic import validator"),
21
- ("root_validator", "from pydantic import root_validator"),
22
- ]
23
-
24
-
25
- if PYDANTIC_V2:
26
- _extra_imports.extend(
27
- [
28
- ("confrozenset", "from pydantic import conset"),
29
- ("StringConstraints", "from pydantic import StringConstraints"),
30
- ("StrictBool", "from pydantic import StrictBool"),
31
- ("StrictBytes", "from pydantic import StrictBytes"),
32
- ("StrictFloat", "from pydantic import StrictFloat"),
33
- ("StrictInt", "from pydantic import StrictInt"),
34
- ("StrictStr", "from pydantic import StrictStr"),
35
- ("model_validator", "from pydantic import model_validator"),
36
- ("field_validator", "from pydantic import field_validator"),
37
- ],
38
- )
39
-
40
- _rendered_extra_imports = [(seek_str, ast.parse(imp).body[0]) for seek_str, imp in _extra_imports]
41
-
42
-
43
- class ImportAutoAddingPlugin:
44
- node_type = ast.Module
45
-
46
- @staticmethod
47
- def __call__(node: ast.Module, context: CodegenContext):
48
- source = ast.unparse(node)
49
- extra_lib_imports = [
50
- import_
51
- for seek_str, import_ in _rendered_extra_imports
52
- if seek_str in source and seek_str not in context.all_names_defined_on_toplevel_of_file
53
- ]
54
- # We do this because when we import our module, we import not the package but
55
- # the __init__.py file directly which produces this extra `.__init__` suffix in the name
56
- module_name = context.template_module.__name__.removesuffix(".__init__")
57
- if module_name in context.modules:
58
- manual_imports = context.modules[module_name].extra_imports
59
- else:
60
- manual_imports = []
61
-
62
- node.body = extra_lib_imports + manual_imports + node.body
63
-
64
- return node
@@ -1,15 +0,0 @@
1
- from typing_extensions import assert_never
2
-
3
- from cadwyn.codegen._common import GlobalCodegenContext
4
- from cadwyn.structure.modules import AlterModuleInstruction
5
-
6
-
7
- def module_migration_plugin(context: GlobalCodegenContext):
8
- for version_change in context.current_version.version_changes:
9
- for alter_module_instruction in version_change.alter_module_instructions:
10
- module = alter_module_instruction.module
11
- mutable_module = context.modules[module.__name__]
12
- if isinstance(alter_module_instruction, AlterModuleInstruction):
13
- mutable_module.extra_imports.append(alter_module_instruction.import_)
14
- else:
15
- assert_never(alter_module_instruction)
cadwyn/main.py DELETED
@@ -1,11 +0,0 @@
1
- from warnings import warn
2
-
3
- from .applications import Cadwyn
4
-
5
- warn(
6
- "'cadwyn.main' module is deprecated. Please use 'cadwyn.applications' instead.",
7
- DeprecationWarning,
8
- stacklevel=2,
9
- )
10
-
11
- __all__ = ["Cadwyn"]