cadwyn 4.0.0__py3-none-any.whl → 4.2.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.
- cadwyn/__init__.py +4 -1
- cadwyn/_render.py +4 -4
- cadwyn/applications.py +25 -2
- cadwyn/changelogs.py +499 -0
- cadwyn/route_generation.py +8 -11
- cadwyn/schema_generation.py +53 -39
- cadwyn/structure/common.py +6 -0
- cadwyn/structure/endpoints.py +6 -5
- cadwyn/structure/enums.py +4 -2
- cadwyn/structure/schemas.py +15 -38
- cadwyn/structure/versions.py +25 -16
- {cadwyn-4.0.0.dist-info → cadwyn-4.2.0.dist-info}/METADATA +2 -1
- cadwyn-4.2.0.dist-info/RECORD +28 -0
- cadwyn-4.0.0.dist-info/RECORD +0 -27
- {cadwyn-4.0.0.dist-info → cadwyn-4.2.0.dist-info}/LICENSE +0 -0
- {cadwyn-4.0.0.dist-info → cadwyn-4.2.0.dist-info}/WHEEL +0 -0
- {cadwyn-4.0.0.dist-info → cadwyn-4.2.0.dist-info}/entry_points.txt +0 -0
cadwyn/schema_generation.py
CHANGED
|
@@ -20,6 +20,7 @@ from typing import (
|
|
|
20
20
|
final,
|
|
21
21
|
get_args,
|
|
22
22
|
get_origin,
|
|
23
|
+
overload,
|
|
23
24
|
)
|
|
24
25
|
|
|
25
26
|
import fastapi.params
|
|
@@ -104,6 +105,8 @@ class PydanticFieldWrapper:
|
|
|
104
105
|
init_model_field: dataclasses.InitVar[FieldInfo]
|
|
105
106
|
|
|
106
107
|
annotation: Any
|
|
108
|
+
name_from_newer_version: str
|
|
109
|
+
|
|
107
110
|
passed_field_attributes: dict[str, Any] = dataclasses.field(init=False)
|
|
108
111
|
|
|
109
112
|
def __post_init__(self, init_model_field: FieldInfo):
|
|
@@ -115,7 +118,7 @@ class PydanticFieldWrapper:
|
|
|
115
118
|
def delete_attribute(self, *, name: str) -> None:
|
|
116
119
|
self.passed_field_attributes.pop(name)
|
|
117
120
|
|
|
118
|
-
def generate_field_copy(self, generator: "
|
|
121
|
+
def generate_field_copy(self, generator: "SchemaGenerator") -> pydantic.fields.FieldInfo:
|
|
119
122
|
return pydantic.Field(
|
|
120
123
|
**generator.annotation_transformer.change_version_of_annotation(self.passed_field_attributes)
|
|
121
124
|
)
|
|
@@ -136,7 +139,7 @@ def _extract_passed_field_attributes(field_info: FieldInfo):
|
|
|
136
139
|
@dataclasses.dataclass(slots=True)
|
|
137
140
|
class _ModelBundle:
|
|
138
141
|
enums: dict[type[Enum], "_EnumWrapper"]
|
|
139
|
-
schemas: dict[type[BaseModel], "
|
|
142
|
+
schemas: dict[type[BaseModel], "_PydanticModelWrapper"]
|
|
140
143
|
|
|
141
144
|
|
|
142
145
|
@dataclasses.dataclass(slots=True, kw_only=True)
|
|
@@ -173,7 +176,7 @@ def migrate_response_body(
|
|
|
173
176
|
|
|
174
177
|
version = versions._get_closest_lesser_version(version)
|
|
175
178
|
|
|
176
|
-
versioned_response_model: type[pydantic.BaseModel] =
|
|
179
|
+
versioned_response_model: type[pydantic.BaseModel] = generate_versioned_models(versions)[str(version)][
|
|
177
180
|
latest_response_model
|
|
178
181
|
]
|
|
179
182
|
return versioned_response_model.model_validate(migrated_response.body)
|
|
@@ -222,7 +225,7 @@ def _is_dunder(attr_name: str):
|
|
|
222
225
|
return attr_name.startswith("__") and attr_name.endswith("__")
|
|
223
226
|
|
|
224
227
|
|
|
225
|
-
def _wrap_pydantic_model(model: type[_T_PYDANTIC_MODEL]) -> "
|
|
228
|
+
def _wrap_pydantic_model(model: type[_T_PYDANTIC_MODEL]) -> "_PydanticModelWrapper[_T_PYDANTIC_MODEL]":
|
|
226
229
|
decorators = _get_model_decorators(model)
|
|
227
230
|
validators = {}
|
|
228
231
|
for decorator_wrapper in decorators:
|
|
@@ -232,7 +235,7 @@ def _wrap_pydantic_model(model: type[_T_PYDANTIC_MODEL]) -> "_PydanticRuntimeMod
|
|
|
232
235
|
wrapped_validator = _wrap_validator(decorator_wrapper.func, decorator_wrapper.shim, decorator_wrapper.info)
|
|
233
236
|
validators[decorator_wrapper.cls_var_name] = wrapped_validator
|
|
234
237
|
fields = {
|
|
235
|
-
field_name: PydanticFieldWrapper(model.model_fields[field_name], model.__annotations__[field_name])
|
|
238
|
+
field_name: PydanticFieldWrapper(model.model_fields[field_name], model.__annotations__[field_name], field_name)
|
|
236
239
|
for field_name in model.__annotations__
|
|
237
240
|
}
|
|
238
241
|
|
|
@@ -248,7 +251,7 @@ def _wrap_pydantic_model(model: type[_T_PYDANTIC_MODEL]) -> "_PydanticRuntimeMod
|
|
|
248
251
|
"__module__": model.__module__,
|
|
249
252
|
"__qualname__": model.__qualname__,
|
|
250
253
|
}
|
|
251
|
-
return
|
|
254
|
+
return _PydanticModelWrapper(
|
|
252
255
|
model,
|
|
253
256
|
name=model.__name__,
|
|
254
257
|
doc=model.__doc__,
|
|
@@ -261,20 +264,20 @@ def _wrap_pydantic_model(model: type[_T_PYDANTIC_MODEL]) -> "_PydanticRuntimeMod
|
|
|
261
264
|
|
|
262
265
|
@final
|
|
263
266
|
@dataclasses.dataclass(slots=True)
|
|
264
|
-
class
|
|
265
|
-
cls: type[_T_PYDANTIC_MODEL]
|
|
267
|
+
class _PydanticModelWrapper(Generic[_T_PYDANTIC_MODEL]):
|
|
268
|
+
cls: type[_T_PYDANTIC_MODEL] = dataclasses.field(repr=False)
|
|
266
269
|
name: str
|
|
267
|
-
doc: str | None
|
|
270
|
+
doc: str | None = dataclasses.field(repr=False)
|
|
268
271
|
fields: Annotated[
|
|
269
272
|
dict["_FieldName", PydanticFieldWrapper],
|
|
270
273
|
Doc(
|
|
271
274
|
"Fields that belong to this model, not to its parents. I.e. The ones that were either defined or overriden "
|
|
272
275
|
),
|
|
273
|
-
]
|
|
274
|
-
validators: dict[str, _PerFieldValidatorWrapper | _ValidatorWrapper]
|
|
275
|
-
other_attributes: dict[str, Any]
|
|
276
|
-
annotations: dict[str, Any]
|
|
277
|
-
_parents: list[Self] | None = dataclasses.field(init=False, default=None)
|
|
276
|
+
] = dataclasses.field(repr=False)
|
|
277
|
+
validators: dict[str, _PerFieldValidatorWrapper | _ValidatorWrapper] = dataclasses.field(repr=False)
|
|
278
|
+
other_attributes: dict[str, Any] = dataclasses.field(repr=False)
|
|
279
|
+
annotations: dict[str, Any] = dataclasses.field(repr=False)
|
|
280
|
+
_parents: list[Self] | None = dataclasses.field(init=False, default=None, repr=False)
|
|
278
281
|
|
|
279
282
|
def __post_init__(self):
|
|
280
283
|
# This isn't actually supposed to run, it's just a precaution
|
|
@@ -291,7 +294,7 @@ class _PydanticRuntimeModelWrapper(Generic[_T_PYDANTIC_MODEL]):
|
|
|
291
294
|
)
|
|
292
295
|
|
|
293
296
|
def __deepcopy__(self, memo: dict[int, Any]):
|
|
294
|
-
result =
|
|
297
|
+
result = _PydanticModelWrapper(
|
|
295
298
|
self.cls,
|
|
296
299
|
name=self.name,
|
|
297
300
|
doc=self.doc,
|
|
@@ -303,6 +306,9 @@ class _PydanticRuntimeModelWrapper(Generic[_T_PYDANTIC_MODEL]):
|
|
|
303
306
|
memo[id(self)] = result
|
|
304
307
|
return result
|
|
305
308
|
|
|
309
|
+
def __hash__(self) -> int:
|
|
310
|
+
return hash(id(self))
|
|
311
|
+
|
|
306
312
|
def _get_parents(self, schemas: "dict[type, Self]"):
|
|
307
313
|
if self._parents is not None:
|
|
308
314
|
return self._parents
|
|
@@ -331,7 +337,7 @@ class _PydanticRuntimeModelWrapper(Generic[_T_PYDANTIC_MODEL]):
|
|
|
331
337
|
|
|
332
338
|
return annotations | self.annotations
|
|
333
339
|
|
|
334
|
-
def generate_model_copy(self, generator: "
|
|
340
|
+
def generate_model_copy(self, generator: "SchemaGenerator") -> type[_T_PYDANTIC_MODEL]:
|
|
335
341
|
per_field_validators = {
|
|
336
342
|
name: validator.decorator(*validator.fields, **validator.kwargs)(validator.func)
|
|
337
343
|
for name, validator in self.validators.items()
|
|
@@ -345,7 +351,7 @@ class _PydanticRuntimeModelWrapper(Generic[_T_PYDANTIC_MODEL]):
|
|
|
345
351
|
fields = {name: field.generate_field_copy(generator) for name, field in self.fields.items()}
|
|
346
352
|
model_copy = type(self.cls)(
|
|
347
353
|
self.name,
|
|
348
|
-
tuple(generator[base] for base in self.cls.__bases__),
|
|
354
|
+
tuple(generator[cast(type[BaseModel], base)] for base in self.cls.__bases__),
|
|
349
355
|
self.other_attributes
|
|
350
356
|
| per_field_validators
|
|
351
357
|
| root_validators
|
|
@@ -398,7 +404,7 @@ class _AsyncCallableWrapper(_CallableWrapper):
|
|
|
398
404
|
|
|
399
405
|
@final
|
|
400
406
|
class _AnnotationTransformer:
|
|
401
|
-
def __init__(self, generator: "
|
|
407
|
+
def __init__(self, generator: "SchemaGenerator") -> None:
|
|
402
408
|
# This cache is not here for speeding things up. It's for preventing the creation of copies of the same object
|
|
403
409
|
# because such copies could produce weird behaviors at runtime, especially if you/fastapi do any comparisons.
|
|
404
410
|
# It's defined here and not on the method because of this: https://youtu.be/sVjtp6tGo0g
|
|
@@ -587,7 +593,7 @@ def _add_request_and_response_params(route: APIRoute):
|
|
|
587
593
|
|
|
588
594
|
|
|
589
595
|
@final
|
|
590
|
-
class
|
|
596
|
+
class SchemaGenerator:
|
|
591
597
|
__slots__ = "annotation_transformer", "model_bundle", "concrete_models"
|
|
592
598
|
|
|
593
599
|
def __init__(self, model_bundle: _ModelBundle) -> None:
|
|
@@ -599,9 +605,9 @@ class _SchemaGenerator:
|
|
|
599
605
|
for k, wrapper in (self.model_bundle.schemas | self.model_bundle.enums).items()
|
|
600
606
|
}
|
|
601
607
|
|
|
602
|
-
def __getitem__(self, model: type, /) ->
|
|
608
|
+
def __getitem__(self, model: type[_T_ANY_MODEL], /) -> type[_T_ANY_MODEL]:
|
|
603
609
|
if not isinstance(model, type) or not issubclass(model, BaseModel | Enum) or model in (BaseModel, RootModel):
|
|
604
|
-
return model
|
|
610
|
+
return model # pyright: ignore[reportReturnType]
|
|
605
611
|
model = _unwrap_model(model)
|
|
606
612
|
|
|
607
613
|
if model in self.concrete_models:
|
|
@@ -614,9 +620,14 @@ class _SchemaGenerator:
|
|
|
614
620
|
self.concrete_models[model] = model_copy
|
|
615
621
|
return model_copy
|
|
616
622
|
|
|
623
|
+
@overload
|
|
624
|
+
def _get_wrapper_for_model(self, model: type[BaseModel]) -> "_PydanticModelWrapper[BaseModel]": ...
|
|
625
|
+
@overload
|
|
626
|
+
def _get_wrapper_for_model(self, model: type[Enum]) -> "_EnumWrapper[Enum]": ...
|
|
627
|
+
|
|
617
628
|
def _get_wrapper_for_model(
|
|
618
629
|
self, model: type[BaseModel | Enum]
|
|
619
|
-
) -> "
|
|
630
|
+
) -> "_PydanticModelWrapper[BaseModel] | _EnumWrapper[Enum]":
|
|
620
631
|
model = _unwrap_model(model)
|
|
621
632
|
|
|
622
633
|
if model in self.model_bundle.schemas:
|
|
@@ -636,7 +647,7 @@ class _SchemaGenerator:
|
|
|
636
647
|
|
|
637
648
|
|
|
638
649
|
@cache
|
|
639
|
-
def
|
|
650
|
+
def generate_versioned_models(versions: "VersionBundle") -> "dict[str, SchemaGenerator]":
|
|
640
651
|
models = _create_model_bundle(versions)
|
|
641
652
|
|
|
642
653
|
version_to_context_map = {}
|
|
@@ -645,7 +656,7 @@ def _generate_versioned_models(versions: "VersionBundle") -> "dict[str, _SchemaG
|
|
|
645
656
|
|
|
646
657
|
for version in versions.versions:
|
|
647
658
|
context = _RuntimeSchemaGenContext(current_version=version, models=models, version_bundle=versions)
|
|
648
|
-
version_to_context_map[str(version.value)] =
|
|
659
|
+
version_to_context_map[str(version.value)] = SchemaGenerator(copy.deepcopy(models))
|
|
649
660
|
# note that the last migration will not contain any version changes so we don't need to save the results
|
|
650
661
|
_migrate_classes(context)
|
|
651
662
|
|
|
@@ -660,7 +671,7 @@ def _create_model_bundle(versions: "VersionBundle"):
|
|
|
660
671
|
|
|
661
672
|
|
|
662
673
|
def _migrate_classes(context: _RuntimeSchemaGenContext) -> None:
|
|
663
|
-
for version_change in context.current_version.
|
|
674
|
+
for version_change in context.current_version.changes:
|
|
664
675
|
_apply_alter_schema_instructions(
|
|
665
676
|
context.models.schemas,
|
|
666
677
|
version_change.alter_schema_instructions,
|
|
@@ -674,7 +685,7 @@ def _migrate_classes(context: _RuntimeSchemaGenContext) -> None:
|
|
|
674
685
|
|
|
675
686
|
|
|
676
687
|
def _apply_alter_schema_instructions(
|
|
677
|
-
modified_schemas: dict[type,
|
|
688
|
+
modified_schemas: dict[type, _PydanticModelWrapper],
|
|
678
689
|
alter_schema_instructions: Sequence[AlterSchemaSubInstruction | SchemaHadInstruction],
|
|
679
690
|
version_change_name: str,
|
|
680
691
|
) -> None:
|
|
@@ -747,7 +758,7 @@ def _apply_alter_enum_instructions(
|
|
|
747
758
|
|
|
748
759
|
|
|
749
760
|
def _change_model(
|
|
750
|
-
model:
|
|
761
|
+
model: _PydanticModelWrapper,
|
|
751
762
|
alter_schema_instruction: SchemaHadInstruction,
|
|
752
763
|
version_change_name: str,
|
|
753
764
|
):
|
|
@@ -761,8 +772,8 @@ def _change_model(
|
|
|
761
772
|
|
|
762
773
|
|
|
763
774
|
def _add_field_to_model(
|
|
764
|
-
model:
|
|
765
|
-
schemas: "dict[type,
|
|
775
|
+
model: _PydanticModelWrapper,
|
|
776
|
+
schemas: "dict[type, _PydanticModelWrapper]",
|
|
766
777
|
alter_schema_instruction: FieldExistedAsInstruction,
|
|
767
778
|
version_change_name: str,
|
|
768
779
|
):
|
|
@@ -773,14 +784,16 @@ def _add_field_to_model(
|
|
|
773
784
|
f'in "{version_change_name}" but there is already a field with that name.',
|
|
774
785
|
)
|
|
775
786
|
|
|
776
|
-
field = PydanticFieldWrapper(
|
|
787
|
+
field = PydanticFieldWrapper(
|
|
788
|
+
alter_schema_instruction.field, alter_schema_instruction.field.annotation, alter_schema_instruction.name
|
|
789
|
+
)
|
|
777
790
|
model.fields[alter_schema_instruction.name] = field
|
|
778
791
|
model.annotations[alter_schema_instruction.name] = alter_schema_instruction.field.annotation
|
|
779
792
|
|
|
780
793
|
|
|
781
794
|
def _change_field_in_model(
|
|
782
|
-
model:
|
|
783
|
-
schemas: "dict[type,
|
|
795
|
+
model: _PydanticModelWrapper,
|
|
796
|
+
schemas: "dict[type, _PydanticModelWrapper]",
|
|
784
797
|
alter_schema_instruction: FieldHadInstruction | FieldDidntHaveInstruction,
|
|
785
798
|
version_change_name: str,
|
|
786
799
|
):
|
|
@@ -817,7 +830,7 @@ def _change_field_in_model(
|
|
|
817
830
|
|
|
818
831
|
|
|
819
832
|
def _change_field(
|
|
820
|
-
model:
|
|
833
|
+
model: _PydanticModelWrapper,
|
|
821
834
|
alter_schema_instruction: FieldHadInstruction,
|
|
822
835
|
version_change_name: str,
|
|
823
836
|
defined_annotations: dict[str, Any],
|
|
@@ -861,7 +874,7 @@ def _change_field(
|
|
|
861
874
|
|
|
862
875
|
|
|
863
876
|
def _delete_field_attributes(
|
|
864
|
-
model:
|
|
877
|
+
model: _PydanticModelWrapper,
|
|
865
878
|
alter_schema_instruction: FieldDidntHaveInstruction,
|
|
866
879
|
version_change_name: str,
|
|
867
880
|
field: PydanticFieldWrapper,
|
|
@@ -884,7 +897,7 @@ def _delete_field_attributes(
|
|
|
884
897
|
)
|
|
885
898
|
|
|
886
899
|
|
|
887
|
-
def _delete_field_from_model(model:
|
|
900
|
+
def _delete_field_from_model(model: _PydanticModelWrapper, field_name: str, version_change_name: str):
|
|
888
901
|
if field_name not in model.fields:
|
|
889
902
|
raise InvalidGenerationInstructionError(
|
|
890
903
|
f'You tried to delete a field "{field_name}" from "{model.name}" '
|
|
@@ -906,10 +919,11 @@ class _DummyEnum(Enum):
|
|
|
906
919
|
|
|
907
920
|
@final
|
|
908
921
|
class _EnumWrapper(Generic[_T_ENUM]):
|
|
909
|
-
__slots__ = "cls", "members"
|
|
922
|
+
__slots__ = "cls", "members", "name"
|
|
910
923
|
|
|
911
924
|
def __init__(self, cls: type[_T_ENUM]):
|
|
912
925
|
self.cls = _unwrap_model(cls)
|
|
926
|
+
self.name = cls.__name__
|
|
913
927
|
self.members = {member.name: member.value for member in cls}
|
|
914
928
|
|
|
915
929
|
def __deepcopy__(self, memo: Any):
|
|
@@ -918,14 +932,14 @@ class _EnumWrapper(Generic[_T_ENUM]):
|
|
|
918
932
|
memo[id(self)] = result
|
|
919
933
|
return result
|
|
920
934
|
|
|
921
|
-
def generate_model_copy(self, generator: "
|
|
922
|
-
enum_dict = Enum.__prepare__(self.
|
|
935
|
+
def generate_model_copy(self, generator: "SchemaGenerator") -> type[_T_ENUM]:
|
|
936
|
+
enum_dict = Enum.__prepare__(self.name, self.cls.__bases__)
|
|
923
937
|
|
|
924
938
|
raw_member_map = {k: v.value if isinstance(v, Enum) else v for k, v in self.members.items()}
|
|
925
939
|
initialization_namespace = self._get_initialization_namespace_for_enum(self.cls) | raw_member_map
|
|
926
940
|
for attr_name, attr in initialization_namespace.items():
|
|
927
941
|
enum_dict[attr_name] = attr
|
|
928
|
-
model_copy = cast(type[_T_ENUM], type(self.
|
|
942
|
+
model_copy = cast(type[_T_ENUM], type(self.name, self.cls.__bases__, enum_dict))
|
|
929
943
|
model_copy.__cadwyn_original_model__ = self.cls # pyright: ignore[reportAttributeAccessIssue]
|
|
930
944
|
return model_copy
|
|
931
945
|
|
cadwyn/structure/common.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
from collections.abc import Callable
|
|
3
|
+
from dataclasses import dataclass
|
|
3
4
|
from typing import ParamSpec, TypeAlias, TypeVar
|
|
4
5
|
|
|
5
6
|
from pydantic import BaseModel
|
|
@@ -9,3 +10,8 @@ VersionDate = datetime.date
|
|
|
9
10
|
_P = ParamSpec("_P")
|
|
10
11
|
_R = TypeVar("_R")
|
|
11
12
|
Endpoint: TypeAlias = Callable[_P, _R]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(slots=True, kw_only=True)
|
|
16
|
+
class _HiddenAttributeMixin:
|
|
17
|
+
is_hidden_from_changelog: bool = False
|
cadwyn/structure/endpoints.py
CHANGED
|
@@ -11,6 +11,7 @@ from starlette.routing import BaseRoute
|
|
|
11
11
|
from cadwyn.exceptions import LintingError
|
|
12
12
|
|
|
13
13
|
from .._utils import Sentinel
|
|
14
|
+
from .common import _HiddenAttributeMixin
|
|
14
15
|
|
|
15
16
|
HTTP_METHODS = {"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"}
|
|
16
17
|
|
|
@@ -44,7 +45,7 @@ class EndpointAttributesPayload:
|
|
|
44
45
|
response_description: str
|
|
45
46
|
responses: dict[int | str, dict[str, Any]]
|
|
46
47
|
deprecated: bool
|
|
47
|
-
methods:
|
|
48
|
+
methods: set[str]
|
|
48
49
|
operation_id: str
|
|
49
50
|
include_in_schema: bool
|
|
50
51
|
response_class: type[Response]
|
|
@@ -55,7 +56,7 @@ class EndpointAttributesPayload:
|
|
|
55
56
|
|
|
56
57
|
|
|
57
58
|
@dataclass(slots=True)
|
|
58
|
-
class EndpointHadInstruction:
|
|
59
|
+
class EndpointHadInstruction(_HiddenAttributeMixin):
|
|
59
60
|
endpoint_path: str
|
|
60
61
|
endpoint_methods: set[str]
|
|
61
62
|
endpoint_func_name: str | None
|
|
@@ -63,14 +64,14 @@ class EndpointHadInstruction:
|
|
|
63
64
|
|
|
64
65
|
|
|
65
66
|
@dataclass(slots=True)
|
|
66
|
-
class EndpointExistedInstruction:
|
|
67
|
+
class EndpointExistedInstruction(_HiddenAttributeMixin):
|
|
67
68
|
endpoint_path: str
|
|
68
69
|
endpoint_methods: set[str]
|
|
69
70
|
endpoint_func_name: str | None
|
|
70
71
|
|
|
71
72
|
|
|
72
73
|
@dataclass(slots=True)
|
|
73
|
-
class EndpointDidntExistInstruction:
|
|
74
|
+
class EndpointDidntExistInstruction(_HiddenAttributeMixin):
|
|
74
75
|
endpoint_path: str
|
|
75
76
|
endpoint_methods: set[str]
|
|
76
77
|
endpoint_func_name: str | None
|
|
@@ -135,7 +136,7 @@ class EndpointInstructionFactory:
|
|
|
135
136
|
response_description=response_description,
|
|
136
137
|
responses=responses,
|
|
137
138
|
deprecated=deprecated,
|
|
138
|
-
methods=methods,
|
|
139
|
+
methods=set(methods) if methods is not Sentinel else Sentinel,
|
|
139
140
|
operation_id=operation_id,
|
|
140
141
|
include_in_schema=include_in_schema,
|
|
141
142
|
response_class=response_class,
|
cadwyn/structure/enums.py
CHANGED
|
@@ -3,15 +3,17 @@ from dataclasses import dataclass
|
|
|
3
3
|
from enum import Enum
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
|
+
from .common import _HiddenAttributeMixin
|
|
7
|
+
|
|
6
8
|
|
|
7
9
|
@dataclass(slots=True)
|
|
8
|
-
class EnumHadMembersInstruction:
|
|
10
|
+
class EnumHadMembersInstruction(_HiddenAttributeMixin):
|
|
9
11
|
enum: type[Enum]
|
|
10
12
|
members: Mapping[str, Any]
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
@dataclass(slots=True)
|
|
14
|
-
class EnumDidntHaveMembersInstruction:
|
|
16
|
+
class EnumDidntHaveMembersInstruction(_HiddenAttributeMixin):
|
|
15
17
|
enum: type[Enum]
|
|
16
18
|
members: tuple[str, ...]
|
|
17
19
|
|
cadwyn/structure/schemas.py
CHANGED
|
@@ -10,6 +10,8 @@ from pydantic.fields import FieldInfo
|
|
|
10
10
|
from cadwyn._utils import Sentinel, fully_unwrap_decorator
|
|
11
11
|
from cadwyn.exceptions import CadwynStructureError
|
|
12
12
|
|
|
13
|
+
from .common import _HiddenAttributeMixin
|
|
14
|
+
|
|
13
15
|
if TYPE_CHECKING:
|
|
14
16
|
from pydantic.typing import AbstractSetIntStr, MappingIntStrAny
|
|
15
17
|
|
|
@@ -21,24 +23,21 @@ PossibleFieldAttributes = Literal[
|
|
|
21
23
|
"title",
|
|
22
24
|
"description",
|
|
23
25
|
"exclude",
|
|
24
|
-
"include",
|
|
25
26
|
"const",
|
|
26
27
|
"gt",
|
|
27
28
|
"ge",
|
|
28
29
|
"lt",
|
|
29
30
|
"le",
|
|
31
|
+
"deprecated",
|
|
32
|
+
"fail_fast",
|
|
30
33
|
"strict",
|
|
31
34
|
"multiple_of",
|
|
32
35
|
"allow_inf_nan",
|
|
33
36
|
"max_digits",
|
|
34
37
|
"decimal_places",
|
|
35
|
-
"min_items",
|
|
36
|
-
"max_items",
|
|
37
|
-
"unique_items",
|
|
38
38
|
"min_length",
|
|
39
39
|
"max_length",
|
|
40
40
|
"allow_mutation",
|
|
41
|
-
"regex",
|
|
42
41
|
"pattern",
|
|
43
42
|
"discriminator",
|
|
44
43
|
"repr",
|
|
@@ -53,8 +52,9 @@ class FieldChanges:
|
|
|
53
52
|
title: str
|
|
54
53
|
description: str
|
|
55
54
|
exclude: "AbstractSetIntStr | MappingIntStrAny | Any"
|
|
56
|
-
include: "AbstractSetIntStr | MappingIntStrAny | Any"
|
|
57
55
|
const: bool
|
|
56
|
+
deprecated: bool
|
|
57
|
+
fail_fast: bool
|
|
58
58
|
gt: float
|
|
59
59
|
ge: float
|
|
60
60
|
lt: float
|
|
@@ -64,20 +64,16 @@ class FieldChanges:
|
|
|
64
64
|
allow_inf_nan: bool
|
|
65
65
|
max_digits: int
|
|
66
66
|
decimal_places: int
|
|
67
|
-
min_items: int
|
|
68
|
-
max_items: int
|
|
69
|
-
unique_items: bool
|
|
70
67
|
min_length: int
|
|
71
68
|
max_length: int
|
|
72
69
|
allow_mutation: bool
|
|
73
|
-
regex: str
|
|
74
70
|
pattern: str
|
|
75
71
|
discriminator: str
|
|
76
72
|
repr: bool
|
|
77
73
|
|
|
78
74
|
|
|
79
75
|
@dataclass(slots=True)
|
|
80
|
-
class FieldHadInstruction:
|
|
76
|
+
class FieldHadInstruction(_HiddenAttributeMixin):
|
|
81
77
|
schema: type[BaseModel]
|
|
82
78
|
name: str
|
|
83
79
|
type: type
|
|
@@ -86,20 +82,20 @@ class FieldHadInstruction:
|
|
|
86
82
|
|
|
87
83
|
|
|
88
84
|
@dataclass(slots=True)
|
|
89
|
-
class FieldDidntHaveInstruction:
|
|
85
|
+
class FieldDidntHaveInstruction(_HiddenAttributeMixin):
|
|
90
86
|
schema: type[BaseModel]
|
|
91
87
|
name: str
|
|
92
88
|
attributes: tuple[str, ...]
|
|
93
89
|
|
|
94
90
|
|
|
95
91
|
@dataclass(slots=True)
|
|
96
|
-
class FieldDidntExistInstruction:
|
|
92
|
+
class FieldDidntExistInstruction(_HiddenAttributeMixin):
|
|
97
93
|
schema: type[BaseModel]
|
|
98
94
|
name: str
|
|
99
95
|
|
|
100
96
|
|
|
101
97
|
@dataclass(slots=True)
|
|
102
|
-
class FieldExistedAsInstruction:
|
|
98
|
+
class FieldExistedAsInstruction(_HiddenAttributeMixin):
|
|
103
99
|
schema: type[BaseModel]
|
|
104
100
|
name: str
|
|
105
101
|
field: FieldInfo
|
|
@@ -122,41 +118,25 @@ class AlterFieldInstructionFactory:
|
|
|
122
118
|
title: str = Sentinel,
|
|
123
119
|
description: str = Sentinel,
|
|
124
120
|
exclude: "AbstractSetIntStr | MappingIntStrAny | Any" = Sentinel,
|
|
125
|
-
include: "AbstractSetIntStr | MappingIntStrAny | Any" = Sentinel,
|
|
126
121
|
const: bool = Sentinel,
|
|
127
122
|
gt: float = Sentinel,
|
|
128
123
|
ge: float = Sentinel,
|
|
129
124
|
lt: float = Sentinel,
|
|
130
125
|
le: float = Sentinel,
|
|
131
126
|
strict: bool = Sentinel,
|
|
127
|
+
deprecated: bool = Sentinel,
|
|
132
128
|
multiple_of: float = Sentinel,
|
|
133
129
|
allow_inf_nan: bool = Sentinel,
|
|
134
130
|
max_digits: int = Sentinel,
|
|
135
131
|
decimal_places: int = Sentinel,
|
|
136
|
-
min_items: int = Sentinel,
|
|
137
|
-
max_items: int = Sentinel,
|
|
138
|
-
unique_items: bool = Sentinel,
|
|
139
132
|
min_length: int = Sentinel,
|
|
140
133
|
max_length: int = Sentinel,
|
|
141
134
|
allow_mutation: bool = Sentinel,
|
|
142
|
-
regex: str = Sentinel,
|
|
143
135
|
pattern: str = Sentinel,
|
|
144
136
|
discriminator: str = Sentinel,
|
|
145
137
|
repr: bool = Sentinel,
|
|
138
|
+
fail_fast: bool = Sentinel,
|
|
146
139
|
) -> FieldHadInstruction:
|
|
147
|
-
if regex is not Sentinel:
|
|
148
|
-
raise CadwynStructureError("`regex` was removed in Pydantic 2. Use `pattern` instead")
|
|
149
|
-
if include is not Sentinel:
|
|
150
|
-
raise CadwynStructureError("`include` was removed in Pydantic 2. Use `exclude` instead")
|
|
151
|
-
if min_items is not Sentinel:
|
|
152
|
-
raise CadwynStructureError("`min_items` was removed in Pydantic 2. Use `min_length` instead")
|
|
153
|
-
if max_items is not Sentinel:
|
|
154
|
-
raise CadwynStructureError("`max_items` was removed in Pydantic 2. Use `max_length` instead")
|
|
155
|
-
if unique_items is not Sentinel:
|
|
156
|
-
raise CadwynStructureError(
|
|
157
|
-
"`unique_items` was removed in Pydantic 2. Use `Set` type annotation instead"
|
|
158
|
-
"(this feature is discussed in https://github.com/pydantic/pydantic-core/issues/296)",
|
|
159
|
-
)
|
|
160
140
|
return FieldHadInstruction(
|
|
161
141
|
schema=self.schema,
|
|
162
142
|
name=self.name,
|
|
@@ -169,27 +149,24 @@ class AlterFieldInstructionFactory:
|
|
|
169
149
|
title=title,
|
|
170
150
|
description=description,
|
|
171
151
|
exclude=exclude,
|
|
172
|
-
include=include,
|
|
173
152
|
const=const,
|
|
174
153
|
gt=gt,
|
|
175
154
|
ge=ge,
|
|
176
155
|
lt=lt,
|
|
177
156
|
le=le,
|
|
157
|
+
deprecated=deprecated,
|
|
178
158
|
strict=strict,
|
|
179
159
|
multiple_of=multiple_of,
|
|
180
160
|
allow_inf_nan=allow_inf_nan,
|
|
181
161
|
max_digits=max_digits,
|
|
182
162
|
decimal_places=decimal_places,
|
|
183
|
-
min_items=min_items,
|
|
184
|
-
max_items=max_items,
|
|
185
|
-
unique_items=unique_items,
|
|
186
163
|
min_length=min_length,
|
|
187
164
|
max_length=max_length,
|
|
188
165
|
allow_mutation=allow_mutation,
|
|
189
|
-
regex=regex,
|
|
190
166
|
pattern=pattern,
|
|
191
167
|
discriminator=discriminator,
|
|
192
168
|
repr=repr,
|
|
169
|
+
fail_fast=fail_fast,
|
|
193
170
|
),
|
|
194
171
|
)
|
|
195
172
|
|
|
@@ -266,7 +243,7 @@ AlterSchemaSubInstruction = (
|
|
|
266
243
|
|
|
267
244
|
|
|
268
245
|
@dataclass(slots=True)
|
|
269
|
-
class SchemaHadInstruction:
|
|
246
|
+
class SchemaHadInstruction(_HiddenAttributeMixin):
|
|
270
247
|
schema: type[BaseModel]
|
|
271
248
|
name: str
|
|
272
249
|
|