cadwyn 3.1.0__py3-none-any.whl → 3.1.2__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/_compat.py +0 -2
- cadwyn/_package_utils.py +1 -0
- cadwyn/codegen/_asts.py +4 -2
- cadwyn/codegen/_common.py +1 -1
- cadwyn/codegen/_main.py +4 -9
- cadwyn/codegen/_plugins/class_migrations.py +2 -4
- cadwyn/codegen/_plugins/class_rebuilding.py +0 -2
- cadwyn/codegen/_plugins/latest_version_aliasing.py +5 -6
- cadwyn/routing.py +27 -23
- cadwyn/structure/data.py +2 -2
- cadwyn/structure/schemas.py +1 -1
- cadwyn/structure/versions.py +23 -21
- {cadwyn-3.1.0.dist-info → cadwyn-3.1.2.dist-info}/METADATA +6 -1
- {cadwyn-3.1.0.dist-info → cadwyn-3.1.2.dist-info}/RECORD +17 -17
- {cadwyn-3.1.0.dist-info → cadwyn-3.1.2.dist-info}/LICENSE +0 -0
- {cadwyn-3.1.0.dist-info → cadwyn-3.1.2.dist-info}/WHEEL +0 -0
- {cadwyn-3.1.0.dist-info → cadwyn-3.1.2.dist-info}/entry_points.txt +0 -0
cadwyn/_compat.py
CHANGED
|
@@ -63,7 +63,6 @@ def get_attrs_that_are_not_from_field_and_that_are_from_field(value: type):
|
|
|
63
63
|
|
|
64
64
|
@dataclasses.dataclass(slots=True)
|
|
65
65
|
class PydanticFieldWrapper:
|
|
66
|
-
# TODO: What's the difference between these two? I keep forgetting
|
|
67
66
|
annotation: Any
|
|
68
67
|
|
|
69
68
|
init_model_field: dataclasses.InitVar[ModelField] # pyright: ignore[reportGeneralTypeIssues]
|
|
@@ -92,7 +91,6 @@ class PydanticFieldWrapper:
|
|
|
92
91
|
else:
|
|
93
92
|
setattr(self.field_info, name, value)
|
|
94
93
|
|
|
95
|
-
# TODO: If we ever have performance issues with this -- it makes sense to cache and update it in update_attribute
|
|
96
94
|
@property
|
|
97
95
|
def passed_field_attributes(self):
|
|
98
96
|
if PYDANTIC_V2:
|
cadwyn/_package_utils.py
CHANGED
|
@@ -29,6 +29,7 @@ def get_version_dir_path(template_package: ModuleType, version: date) -> Path:
|
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
def get_package_path_from_module(template_package: ModuleType) -> Path:
|
|
32
|
+
# Can be cached in the future to gain some speedups
|
|
32
33
|
file = inspect.getsourcefile(template_package)
|
|
33
34
|
|
|
34
35
|
# I am too lazy to reproduce this error correctly
|
cadwyn/codegen/_asts.py
CHANGED
|
@@ -126,9 +126,10 @@ def transform_type(value: type) -> Any:
|
|
|
126
126
|
+ ")"
|
|
127
127
|
)
|
|
128
128
|
else:
|
|
129
|
-
# TODO: Finish this comment :D
|
|
130
129
|
# In pydantic V1:
|
|
131
|
-
# MRO of
|
|
130
|
+
# MRO of constr looks like: [ConstrainedStrValue, pydantic.types.ConstrainedStr, str, object]
|
|
131
|
+
# -2 -1
|
|
132
|
+
# ^^^
|
|
132
133
|
value = value.mro()[-2]
|
|
133
134
|
|
|
134
135
|
return value.__name__
|
|
@@ -181,6 +182,7 @@ def _get_lambda_source_from_default_factory(source: str) -> str:
|
|
|
181
182
|
|
|
182
183
|
|
|
183
184
|
def read_python_module(module: ModuleType) -> str:
|
|
185
|
+
# Can be cached in the future to gain some speedups
|
|
184
186
|
try:
|
|
185
187
|
return inspect.getsource(module)
|
|
186
188
|
except OSError as e:
|
cadwyn/codegen/_common.py
CHANGED
|
@@ -113,7 +113,7 @@ class GlobalCodegenContext:
|
|
|
113
113
|
@dataclasses.dataclass(slots=True, kw_only=True)
|
|
114
114
|
class CodegenContext(GlobalCodegenContext):
|
|
115
115
|
# This attribute is extremely useful for calculating relative imports
|
|
116
|
-
|
|
116
|
+
index_of_latest_package_dir_in_module_python_path: int
|
|
117
117
|
module_python_path: str
|
|
118
118
|
module_path: Path
|
|
119
119
|
template_module: ModuleType
|
cadwyn/codegen/_main.py
CHANGED
|
@@ -92,7 +92,6 @@ def _generate_versioned_directories(
|
|
|
92
92
|
codegen_plugins: Collection[CodegenPlugin],
|
|
93
93
|
migration_plugins: Collection[MigrationPlugin],
|
|
94
94
|
):
|
|
95
|
-
# TODO: An alternative structure for module python path: An object similar to pathlib.Path with .name, etc
|
|
96
95
|
for version in versions:
|
|
97
96
|
global_context = GlobalCodegenContext(
|
|
98
97
|
current_version=version,
|
|
@@ -113,7 +112,6 @@ def _generate_directory_for_version(
|
|
|
113
112
|
version: Version,
|
|
114
113
|
global_context: GlobalCodegenContext,
|
|
115
114
|
):
|
|
116
|
-
# TODO: This call can be optimized
|
|
117
115
|
template_dir = get_package_path_from_module(template_package)
|
|
118
116
|
version_dir = get_version_dir_path(template_package, version.value)
|
|
119
117
|
for (
|
|
@@ -178,27 +176,24 @@ def _build_context(
|
|
|
178
176
|
parsed_file,
|
|
179
177
|
module_python_path,
|
|
180
178
|
)
|
|
181
|
-
|
|
182
|
-
index_of_latest_schema_dir_in_module_python_path = get_index_of_latest_schema_dir_in_module_python_path(
|
|
179
|
+
index_of_latest_package_dir_in_module_python_path = get_index_of_latest_schema_dir_in_module_python_path(
|
|
183
180
|
template_module,
|
|
184
|
-
version_dir,
|
|
181
|
+
template_dir.with_name(version_dir.name),
|
|
185
182
|
)
|
|
186
|
-
|
|
183
|
+
return CodegenContext(
|
|
187
184
|
current_version=global_context.current_version,
|
|
188
185
|
versions=global_context.versions,
|
|
189
186
|
schemas=global_context.schemas,
|
|
190
187
|
enums=global_context.enums,
|
|
191
188
|
modules=global_context.modules,
|
|
192
189
|
extra=global_context.extra,
|
|
193
|
-
|
|
190
|
+
index_of_latest_package_dir_in_module_python_path=index_of_latest_package_dir_in_module_python_path,
|
|
194
191
|
module_python_path=module_python_path,
|
|
195
192
|
all_names_defined_on_toplevel_of_file=all_names_defined_at_toplevel_of_file,
|
|
196
193
|
template_module=template_module,
|
|
197
194
|
module_path=parallel_file,
|
|
198
195
|
)
|
|
199
196
|
|
|
200
|
-
return context
|
|
201
|
-
|
|
202
197
|
|
|
203
198
|
def _generate_parallel_directory(
|
|
204
199
|
template_module: ModuleType,
|
|
@@ -38,8 +38,6 @@ def _apply_alter_schema_instructions(
|
|
|
38
38
|
alter_schema_instructions: Sequence[AlterSchemaSubInstruction | AlterSchemaInstruction],
|
|
39
39
|
version_change_name: str,
|
|
40
40
|
):
|
|
41
|
-
# TODO: If we have a request migration for an endpoint instead of a schema and we haven't found that endpoint
|
|
42
|
-
# during codegen -- raise an error or maybe add an argument that controlls that. Or maybe this is overengineering..
|
|
43
41
|
for alter_schema_instruction in alter_schema_instructions:
|
|
44
42
|
schema = alter_schema_instruction.schema
|
|
45
43
|
schema_path = get_cls_pythonpath(schema)
|
|
@@ -118,10 +116,10 @@ def _add_field_to_model(
|
|
|
118
116
|
)
|
|
119
117
|
|
|
120
118
|
model.fields[alter_schema_instruction.field_name] = PydanticFieldWrapper(
|
|
121
|
-
annotation_ast=None,
|
|
119
|
+
annotation_ast=None,
|
|
122
120
|
annotation=alter_schema_instruction.type,
|
|
123
121
|
init_model_field=alter_schema_instruction.field,
|
|
124
|
-
field_ast=None,
|
|
122
|
+
field_ast=None,
|
|
125
123
|
)
|
|
126
124
|
|
|
127
125
|
|
|
@@ -114,13 +114,11 @@ def _render_annotation(annotation: Any):
|
|
|
114
114
|
|
|
115
115
|
|
|
116
116
|
def _generate_field_ast(field: PydanticFieldWrapper):
|
|
117
|
-
# TODO: Make sure that cadwyn doesn't remove OLD property definitions
|
|
118
117
|
if field.field_ast is not None:
|
|
119
118
|
# We do this because next plugins **might** use a transformer which will edit the ast within the field
|
|
120
119
|
# and break rendering
|
|
121
120
|
return copy.deepcopy(field.field_ast)
|
|
122
121
|
passed_attrs = field.passed_field_attributes
|
|
123
|
-
# TODO: This is None check feels buggy
|
|
124
122
|
if is_pydantic_constrained_type(field.annotation) and field.annotation_ast is None:
|
|
125
123
|
(
|
|
126
124
|
attrs_that_are_only_in_contype,
|
|
@@ -21,7 +21,7 @@ class LatestVersionAliasingPlugin:
|
|
|
21
21
|
imports = _prepare_imports_from_version_dirs(
|
|
22
22
|
context.template_module,
|
|
23
23
|
["latest"],
|
|
24
|
-
context.
|
|
24
|
+
context.index_of_latest_package_dir_in_module_python_path,
|
|
25
25
|
)
|
|
26
26
|
|
|
27
27
|
import_text = ast.unparse(imports[0].get_ast()) + " # noqa: F403"
|
|
@@ -64,7 +64,6 @@ class _ImportedModule:
|
|
|
64
64
|
module = f"{self.path}.{self.name}"
|
|
65
65
|
name = ast.alias(name="*")
|
|
66
66
|
level = self.how_far_up_is_base_schema_dir_from_current_module
|
|
67
|
-
# TODO: Add a testcase where is_package == True and level == 3
|
|
68
67
|
if self.is_package and level == 2:
|
|
69
68
|
level -= 1
|
|
70
69
|
return ast.ImportFrom(
|
|
@@ -77,7 +76,7 @@ class _ImportedModule:
|
|
|
77
76
|
def _prepare_imports_from_version_dirs(
|
|
78
77
|
original_module: ModuleType,
|
|
79
78
|
version_dir_names: Collection[str],
|
|
80
|
-
|
|
79
|
+
index_of_latest_package_dir_in_pythonpath: int,
|
|
81
80
|
) -> list[_ImportedModule]:
|
|
82
81
|
# package.latest -> from .. import latest
|
|
83
82
|
# package.latest.module -> from ...latest import module
|
|
@@ -90,16 +89,16 @@ def _prepare_imports_from_version_dirs(
|
|
|
90
89
|
# package.latest.subpackage.module -> from ...latest.subpackage.module import *
|
|
91
90
|
|
|
92
91
|
original_module_parts = original_module.__name__.split(".")
|
|
93
|
-
original_module_parts[
|
|
92
|
+
original_module_parts[index_of_latest_package_dir_in_pythonpath] = "{}"
|
|
94
93
|
how_far_up_is_base_schema_dir_from_current_module = (
|
|
95
|
-
len(original_module_parts) -
|
|
94
|
+
len(original_module_parts) - index_of_latest_package_dir_in_pythonpath
|
|
96
95
|
)
|
|
97
96
|
is_package = original_module_parts[-1] == "__init__"
|
|
98
97
|
if is_package:
|
|
99
98
|
original_module_parts.pop(-1)
|
|
100
99
|
|
|
101
100
|
package_name = original_module_parts[-1]
|
|
102
|
-
package_path = original_module_parts[
|
|
101
|
+
package_path = original_module_parts[index_of_latest_package_dir_in_pythonpath:-1]
|
|
103
102
|
import_pythonpath_template = ".".join(package_path)
|
|
104
103
|
absolute_python_path_template = ".".join(original_module_parts)
|
|
105
104
|
return [
|
cadwyn/routing.py
CHANGED
|
@@ -41,7 +41,6 @@ from starlette.routing import (
|
|
|
41
41
|
request_response,
|
|
42
42
|
)
|
|
43
43
|
from typing_extensions import Self, assert_never
|
|
44
|
-
from verselect.routing import VERSION_HEADER_FORMAT
|
|
45
44
|
|
|
46
45
|
from cadwyn._compat import model_fields, rebuild_fastapi_body_param
|
|
47
46
|
from cadwyn._package_utils import get_package_path_from_module, get_version_dir_path
|
|
@@ -56,12 +55,6 @@ from cadwyn.structure.endpoints import (
|
|
|
56
55
|
)
|
|
57
56
|
from cadwyn.structure.versions import _CADWYN_REQUEST_PARAM_NAME, _CADWYN_RESPONSE_PARAM_NAME, VersionChange
|
|
58
57
|
|
|
59
|
-
__all__ = [
|
|
60
|
-
"generate_versioned_routers",
|
|
61
|
-
"VersionedAPIRouter",
|
|
62
|
-
"VERSION_HEADER_FORMAT",
|
|
63
|
-
]
|
|
64
|
-
|
|
65
58
|
_T = TypeVar("_T", bound=Callable[..., Any])
|
|
66
59
|
_R = TypeVar("_R", bound=fastapi.routing.APIRouter)
|
|
67
60
|
# This is a hack we do because we can't guarantee how the user will use the router.
|
|
@@ -127,8 +120,10 @@ class _EndpointTransformer(Generic[_R]):
|
|
|
127
120
|
]
|
|
128
121
|
|
|
129
122
|
def transform(self) -> dict[VersionDate, _R]:
|
|
123
|
+
schema_to_internal_request_body_representation = _extract_internal_request_schemas_from_router(
|
|
124
|
+
self.parent_router
|
|
125
|
+
)
|
|
130
126
|
router = deepcopy(self.parent_router)
|
|
131
|
-
schema_to_internal_request_body_representation = _extract_internal_request_schemas_from_router(router)
|
|
132
127
|
router_infos: dict[VersionDate, _RouterInfo] = {}
|
|
133
128
|
routes_with_migrated_requests = {}
|
|
134
129
|
route_bodies_with_migrated_requests: set[type[BaseModel]] = set()
|
|
@@ -157,8 +152,6 @@ class _EndpointTransformer(Generic[_R]):
|
|
|
157
152
|
f"{self.routes_that_never_existed}",
|
|
158
153
|
)
|
|
159
154
|
|
|
160
|
-
# BEWARE: We assume that the order of routes didn't change.
|
|
161
|
-
# TODO: Make a test suite checking that it doesn't change
|
|
162
155
|
for route_index, latest_route in enumerate(self.parent_router.routes):
|
|
163
156
|
if not isinstance(latest_route, APIRoute):
|
|
164
157
|
continue
|
|
@@ -166,11 +159,10 @@ class _EndpointTransformer(Generic[_R]):
|
|
|
166
159
|
copy_of_dependant = deepcopy(latest_route.dependant)
|
|
167
160
|
# Remember this: if len(body_params) == 1, then route.body_schema == route.dependant.body_params[0]
|
|
168
161
|
if len(copy_of_dependant.body_params) == 1:
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
copy_of_dependant.body_params = [new_body_param]
|
|
162
|
+
self._replace_internal_representation_with_the_versioned_schema(
|
|
163
|
+
copy_of_dependant,
|
|
164
|
+
schema_to_internal_request_body_representation,
|
|
165
|
+
)
|
|
174
166
|
|
|
175
167
|
for older_router_info in list(router_infos.values()):
|
|
176
168
|
older_route = older_router_info.router.routes[route_index]
|
|
@@ -188,11 +180,11 @@ class _EndpointTransformer(Generic[_R]):
|
|
|
188
180
|
template_older_body_model = None
|
|
189
181
|
_add_data_migrations_to_route(
|
|
190
182
|
older_route,
|
|
183
|
+
# NOTE: The fact that we use latest here assumes that the route can never change its response schema
|
|
184
|
+
latest_route,
|
|
191
185
|
template_older_body_model,
|
|
192
186
|
older_route.body_field.alias if older_route.body_field is not None else None,
|
|
193
187
|
copy_of_dependant,
|
|
194
|
-
# NOTE: The fact that we use latest here assumes that the route can never change its response schema
|
|
195
|
-
latest_route.response_model,
|
|
196
188
|
self.versions,
|
|
197
189
|
)
|
|
198
190
|
for _, router_info in router_infos.items():
|
|
@@ -203,7 +195,18 @@ class _EndpointTransformer(Generic[_R]):
|
|
|
203
195
|
]
|
|
204
196
|
return {version: router_info.router for version, router_info in router_infos.items()}
|
|
205
197
|
|
|
206
|
-
|
|
198
|
+
def _replace_internal_representation_with_the_versioned_schema(
|
|
199
|
+
self,
|
|
200
|
+
copy_of_dependant: Dependant,
|
|
201
|
+
schema_to_internal_request_body_representation: dict[type[BaseModel], type[BaseModel]],
|
|
202
|
+
):
|
|
203
|
+
body_param: FastAPIModelField = copy_of_dependant.body_params[0]
|
|
204
|
+
body_schema = body_param.type_
|
|
205
|
+
new_type = schema_to_internal_request_body_representation.get(body_schema, body_schema)
|
|
206
|
+
new_body_param = rebuild_fastapi_body_param(body_param, new_type)
|
|
207
|
+
copy_of_dependant.body_params = [new_body_param]
|
|
208
|
+
|
|
209
|
+
# TODO (https://github.com/zmievsa/cadwyn/issues/28): Simplify
|
|
207
210
|
def _apply_endpoint_changes_to_router( # noqa: C901
|
|
208
211
|
self,
|
|
209
212
|
router: fastapi.routing.APIRouter,
|
|
@@ -223,7 +226,6 @@ class _EndpointTransformer(Generic[_R]):
|
|
|
223
226
|
methods_we_should_have_applied_changes_to = instruction.endpoint_methods.copy()
|
|
224
227
|
|
|
225
228
|
if isinstance(instruction, EndpointDidntExistInstruction):
|
|
226
|
-
# TODO OPTIMIZATION:
|
|
227
229
|
deleted_routes = _get_routes(
|
|
228
230
|
routes,
|
|
229
231
|
instruction.endpoint_path,
|
|
@@ -250,7 +252,6 @@ class _EndpointTransformer(Generic[_R]):
|
|
|
250
252
|
' "{version_change_name}" doesn\'t exist in a newer version'
|
|
251
253
|
)
|
|
252
254
|
elif isinstance(instruction, EndpointExistedInstruction):
|
|
253
|
-
# TODO Optimization
|
|
254
255
|
if original_routes:
|
|
255
256
|
method_union = set()
|
|
256
257
|
for original_route in original_routes:
|
|
@@ -330,7 +331,9 @@ class _EndpointTransformer(Generic[_R]):
|
|
|
330
331
|
)
|
|
331
332
|
|
|
332
333
|
|
|
333
|
-
def _extract_internal_request_schemas_from_router(
|
|
334
|
+
def _extract_internal_request_schemas_from_router(
|
|
335
|
+
router: fastapi.routing.APIRouter,
|
|
336
|
+
) -> dict[type[BaseModel], type[BaseModel]]:
|
|
334
337
|
"""Please note that this functon replaces internal bodies with original bodies in the router"""
|
|
335
338
|
schema_to_internal_request_body_representation = {}
|
|
336
339
|
|
|
@@ -354,6 +357,7 @@ def _extract_internal_request_schemas_from_router(router: fastapi.routing.APIRou
|
|
|
354
357
|
route.endpoint,
|
|
355
358
|
modify_annotations=_extract_internal_request_schemas_from_annotations,
|
|
356
359
|
)
|
|
360
|
+
_remake_endpoint_dependencies(route)
|
|
357
361
|
return schema_to_internal_request_body_representation
|
|
358
362
|
|
|
359
363
|
|
|
@@ -580,10 +584,10 @@ def _add_request_and_response_params(route: APIRoute):
|
|
|
580
584
|
|
|
581
585
|
def _add_data_migrations_to_route(
|
|
582
586
|
route: APIRoute,
|
|
587
|
+
latest_route: Any,
|
|
583
588
|
template_body_field: type[BaseModel] | None,
|
|
584
589
|
template_body_field_name: str | None,
|
|
585
590
|
dependant_for_request_migrations: Dependant,
|
|
586
|
-
latest_response_model: Any,
|
|
587
591
|
versions: VersionBundle,
|
|
588
592
|
):
|
|
589
593
|
if not (route.dependant.request_param_name and route.dependant.response_param_name): # pragma: no cover
|
|
@@ -596,8 +600,8 @@ def _add_data_migrations_to_route(
|
|
|
596
600
|
template_body_field,
|
|
597
601
|
template_body_field_name,
|
|
598
602
|
route,
|
|
603
|
+
latest_route,
|
|
599
604
|
dependant_for_request_migrations,
|
|
600
|
-
latest_response_model,
|
|
601
605
|
request_param_name=route.dependant.request_param_name,
|
|
602
606
|
response_param_name=route.dependant.response_param_name,
|
|
603
607
|
)(route.endpoint)
|
cadwyn/structure/data.py
CHANGED
|
@@ -12,7 +12,7 @@ from cadwyn._utils import same_definition_as_in
|
|
|
12
12
|
_P = ParamSpec("_P")
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
# TODO
|
|
15
|
+
# TODO (https://github.com/zmievsa/cadwyn/issues/49): Add form handling
|
|
16
16
|
class RequestInfo:
|
|
17
17
|
__slots__ = ("body", "headers", "_cookies", "_query_params", "_request")
|
|
18
18
|
|
|
@@ -33,7 +33,7 @@ class RequestInfo:
|
|
|
33
33
|
return self._query_params
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
# TODO: handle media_type and background
|
|
36
|
+
# TODO (https://github.com/zmievsa/cadwyn/issues/111): handle _response.media_type and _response.background
|
|
37
37
|
class ResponseInfo:
|
|
38
38
|
__slots__ = ("body", "_response")
|
|
39
39
|
|
cadwyn/structure/schemas.py
CHANGED
|
@@ -67,7 +67,7 @@ class OldSchemaFieldExistedWith:
|
|
|
67
67
|
field: FieldInfo
|
|
68
68
|
|
|
69
69
|
|
|
70
|
-
# TODO: Add an ability to add extras
|
|
70
|
+
# TODO (https://github.com/zmievsa/cadwyn/issues/112): Add an ability to add extras
|
|
71
71
|
@dataclass(slots=True)
|
|
72
72
|
class AlterFieldInstructionFactory:
|
|
73
73
|
schema: type[BaseModel]
|
cadwyn/structure/versions.py
CHANGED
|
@@ -305,6 +305,7 @@ class VersionBundle:
|
|
|
305
305
|
response: FastapiResponse,
|
|
306
306
|
request_info: RequestInfo,
|
|
307
307
|
current_version: VersionDate,
|
|
308
|
+
latest_route: APIRoute,
|
|
308
309
|
):
|
|
309
310
|
path = request.scope["path"]
|
|
310
311
|
method = request.method
|
|
@@ -329,8 +330,7 @@ class VersionBundle:
|
|
|
329
330
|
response=response,
|
|
330
331
|
dependant=dependant,
|
|
331
332
|
body=request_info.body,
|
|
332
|
-
|
|
333
|
-
dependency_overrides_provider=None,
|
|
333
|
+
dependency_overrides_provider=latest_route.dependency_overrides_provider,
|
|
334
334
|
)
|
|
335
335
|
if errors:
|
|
336
336
|
raise RequestValidationError(_normalize_errors(errors), body=request_info.body)
|
|
@@ -340,7 +340,7 @@ class VersionBundle:
|
|
|
340
340
|
self,
|
|
341
341
|
response_info: ResponseInfo,
|
|
342
342
|
current_version: VersionDate,
|
|
343
|
-
|
|
343
|
+
latest_route: APIRoute,
|
|
344
344
|
path: str,
|
|
345
345
|
method: str,
|
|
346
346
|
) -> ResponseInfo:
|
|
@@ -361,24 +361,24 @@ class VersionBundle:
|
|
|
361
361
|
break
|
|
362
362
|
for version_change in v.version_changes:
|
|
363
363
|
if (
|
|
364
|
-
|
|
365
|
-
and
|
|
364
|
+
latest_route.response_model
|
|
365
|
+
and latest_route.response_model in version_change.alter_response_by_schema_instructions
|
|
366
366
|
):
|
|
367
|
-
version_change.alter_response_by_schema_instructions[
|
|
367
|
+
version_change.alter_response_by_schema_instructions[latest_route.response_model](response_info)
|
|
368
368
|
if path in version_change.alter_response_by_path_instructions:
|
|
369
369
|
for instruction in version_change.alter_response_by_path_instructions[path]:
|
|
370
370
|
if method in instruction.methods:
|
|
371
371
|
instruction(response_info)
|
|
372
372
|
return response_info
|
|
373
373
|
|
|
374
|
-
# TODO
|
|
374
|
+
# TODO (https://github.com/zmievsa/cadwyn/issues/113): Refactor this function and all functions it calls.
|
|
375
375
|
def _versioned(
|
|
376
376
|
self,
|
|
377
377
|
template_module_body_field_for_request_migrations: type[BaseModel] | None,
|
|
378
378
|
module_body_field_name: str | None,
|
|
379
379
|
route: APIRoute,
|
|
380
|
+
latest_route: APIRoute,
|
|
380
381
|
dependant_for_request_migrations: Dependant,
|
|
381
|
-
latest_response_model: Any,
|
|
382
382
|
*,
|
|
383
383
|
request_param_name: str,
|
|
384
384
|
response_param_name: str,
|
|
@@ -399,11 +399,12 @@ class VersionBundle:
|
|
|
399
399
|
kwargs,
|
|
400
400
|
response,
|
|
401
401
|
route,
|
|
402
|
+
latest_route,
|
|
402
403
|
)
|
|
403
404
|
|
|
404
405
|
return await self._convert_endpoint_response_to_version(
|
|
405
406
|
endpoint,
|
|
406
|
-
|
|
407
|
+
latest_route,
|
|
407
408
|
path,
|
|
408
409
|
method,
|
|
409
410
|
response_param_name,
|
|
@@ -424,7 +425,7 @@ class VersionBundle:
|
|
|
424
425
|
async def _convert_endpoint_response_to_version(
|
|
425
426
|
self,
|
|
426
427
|
func_to_get_response_from: Endpoint,
|
|
427
|
-
|
|
428
|
+
latest_route: APIRoute,
|
|
428
429
|
path: str,
|
|
429
430
|
method: str,
|
|
430
431
|
response_param_name: str,
|
|
@@ -434,8 +435,6 @@ class VersionBundle:
|
|
|
434
435
|
) -> Any:
|
|
435
436
|
if response_param_name == _CADWYN_RESPONSE_PARAM_NAME:
|
|
436
437
|
kwargs.pop(response_param_name)
|
|
437
|
-
# TODO: Verify that we handle fastapi.Response here
|
|
438
|
-
# TODO: Verify that we handle fastapi.Response descendants
|
|
439
438
|
if endpoint_is_async_callable:
|
|
440
439
|
response_or_response_body: FastapiResponse | object = await func_to_get_response_from(**kwargs)
|
|
441
440
|
else:
|
|
@@ -449,22 +448,24 @@ class VersionBundle:
|
|
|
449
448
|
if isinstance(response_or_response_body, FastapiResponse):
|
|
450
449
|
response_info = ResponseInfo(
|
|
451
450
|
response_or_response_body,
|
|
452
|
-
# TODO:
|
|
453
|
-
# TODO: Only do this if there are migrations
|
|
451
|
+
# TODO (https://github.com/zmievsa/cadwyn/issues/51): Only do this if there are migrations
|
|
454
452
|
json.loads(response_or_response_body.body) if response_or_response_body.body else None,
|
|
455
453
|
)
|
|
456
454
|
else:
|
|
457
|
-
# TODO: We probably need to call this in the same way as in fastapi instead of hardcoding exclude_unset.
|
|
458
|
-
# We have such an ability if we force passing the route into this wrapper. Or maybe not... Important!
|
|
459
455
|
response_info = ResponseInfo(
|
|
460
456
|
fastapi_response_dependency,
|
|
461
|
-
_prepare_response_content(
|
|
457
|
+
_prepare_response_content(
|
|
458
|
+
response_or_response_body,
|
|
459
|
+
exclude_unset=latest_route.response_model_exclude_unset,
|
|
460
|
+
exclude_defaults=latest_route.response_model_exclude_defaults,
|
|
461
|
+
exclude_none=latest_route.response_model_exclude_none,
|
|
462
|
+
),
|
|
462
463
|
)
|
|
463
464
|
|
|
464
465
|
response_info = self._migrate_response(
|
|
465
466
|
response_info,
|
|
466
467
|
api_version,
|
|
467
|
-
|
|
468
|
+
latest_route,
|
|
468
469
|
path,
|
|
469
470
|
method,
|
|
470
471
|
)
|
|
@@ -476,8 +477,7 @@ class VersionBundle:
|
|
|
476
477
|
# `RuntimeError: Response content longer than Content-Length` or
|
|
477
478
|
# `Too much data for declared Content-Length`, based on the protocol
|
|
478
479
|
if response_info.body is not None:
|
|
479
|
-
# TODO:
|
|
480
|
-
# TODO: Only do this if there are migrations
|
|
480
|
+
# TODO (https://github.com/zmievsa/cadwyn/issues/51): Only do this if there are migrations
|
|
481
481
|
response_info._response.body = json.dumps(response_info.body).encode()
|
|
482
482
|
return response_info._response
|
|
483
483
|
return response_info.body
|
|
@@ -491,6 +491,7 @@ class VersionBundle:
|
|
|
491
491
|
kwargs: dict[str, Any],
|
|
492
492
|
response: FastapiResponse,
|
|
493
493
|
route: APIRoute,
|
|
494
|
+
latest_route: APIRoute,
|
|
494
495
|
):
|
|
495
496
|
request: FastapiRequest = kwargs[request_param_name]
|
|
496
497
|
if request_param_name == _CADWYN_REQUEST_PARAM_NAME:
|
|
@@ -500,7 +501,7 @@ class VersionBundle:
|
|
|
500
501
|
if api_version is None:
|
|
501
502
|
return kwargs
|
|
502
503
|
|
|
503
|
-
# TODO:
|
|
504
|
+
# TODO (https://github.com/zmievsa/cadwyn/issues/51): Make this zero-cost for migration-less cases
|
|
504
505
|
if (
|
|
505
506
|
len(route.dependant.body_params) == 1
|
|
506
507
|
and template_module_body_field_for_request_migrations is not None
|
|
@@ -524,6 +525,7 @@ class VersionBundle:
|
|
|
524
525
|
response,
|
|
525
526
|
request_info,
|
|
526
527
|
api_version,
|
|
528
|
+
latest_route,
|
|
527
529
|
)
|
|
528
530
|
# Because we re-added it into our kwargs when we did solve_dependencies
|
|
529
531
|
if request_param_name == _CADWYN_REQUEST_PARAM_NAME:
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cadwyn
|
|
3
|
-
Version: 3.1.
|
|
3
|
+
Version: 3.1.2
|
|
4
4
|
Summary: Production-ready community-driven modern Stripe-like API versioning in FastAPI
|
|
5
5
|
Home-page: https://github.com/zmievsa/cadwyn
|
|
6
6
|
License: MIT
|
|
7
|
+
Keywords: python,api,json-schema,stripe,versioning,code-generation,hints,api-versioning,pydantic,fastapi,python310,python311,python312
|
|
7
8
|
Author: Stanislav Zmiev
|
|
8
9
|
Author-email: zmievsa@gmail.com
|
|
9
10
|
Requires-Python: >=3.10,<4.0
|
|
@@ -22,6 +23,9 @@ Classifier: Programming Language :: Python :: 3
|
|
|
22
23
|
Classifier: Programming Language :: Python :: 3.10
|
|
23
24
|
Classifier: Programming Language :: Python :: 3.11
|
|
24
25
|
Classifier: Programming Language :: Python :: 3
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
27
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
28
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
25
29
|
Classifier: Topic :: Internet
|
|
26
30
|
Classifier: Topic :: Internet :: WWW/HTTP
|
|
27
31
|
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
|
|
@@ -37,6 +41,7 @@ Requires-Dist: pydantic (>=1.0.0)
|
|
|
37
41
|
Requires-Dist: typer (>=0.7.0); extra == "cli"
|
|
38
42
|
Requires-Dist: typing-extensions
|
|
39
43
|
Requires-Dist: verselect (>=0.0.6)
|
|
44
|
+
Project-URL: Documentation, https://docs.cadwyn.dev
|
|
40
45
|
Project-URL: Repository, https://github.com/zmievsa/cadwyn
|
|
41
46
|
Description-Content-Type: text/markdown
|
|
42
47
|
|
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
cadwyn/__init__.py,sha256=gVLVH3SSBGH0IQYGL5tbro4s0vk--9sAym0UvoG3s1w,478
|
|
2
2
|
cadwyn/__main__.py,sha256=JUNmAhwn7tG1EeXI82QmFZE-fpjfAOv2kxFNDfxWbhQ,2851
|
|
3
|
-
cadwyn/_compat.py,sha256=
|
|
4
|
-
cadwyn/_package_utils.py,sha256=
|
|
3
|
+
cadwyn/_compat.py,sha256=yUXMFK_PS7jOzKktHDcnwwCt4iT3O3eqrgBRliOSKrY,5782
|
|
4
|
+
cadwyn/_package_utils.py,sha256=trxTYLmppv-10SKhScfyDQJh21rsQGFoLaOtHycKKR0,1443
|
|
5
5
|
cadwyn/_utils.py,sha256=gEv_1Qqp7uzvdY0u_l77d6lppAsNsOCjD8FEFBKi3Js,3758
|
|
6
6
|
cadwyn/codegen/README.md,sha256=V2Kz2IOz1cTxrC-RnQ7YbWEVCIGYr3tR4IPCvepeq0M,1047
|
|
7
7
|
cadwyn/codegen/__init__.py,sha256=JgddDjxMTjSfVrMXHwNu1ODgdn2QfPWpccrRKquBV6k,355
|
|
8
|
-
cadwyn/codegen/_asts.py,sha256=
|
|
9
|
-
cadwyn/codegen/_common.py,sha256=
|
|
10
|
-
cadwyn/codegen/_main.py,sha256=
|
|
8
|
+
cadwyn/codegen/_asts.py,sha256=vWQRIpVYA63ku2Su8a3i_uTF0l81LHZmuWltnLoZYLk,9213
|
|
9
|
+
cadwyn/codegen/_common.py,sha256=iy7CoM6wRaFcouE1L_opHMgz0CDQybRjTxrJGj5QZtw,4586
|
|
10
|
+
cadwyn/codegen/_main.py,sha256=IW-J_9fRfOIkfxiPbkal2QjDzpn4Y2fW-4YeTtB_sgs,9032
|
|
11
11
|
cadwyn/codegen/_plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
-
cadwyn/codegen/_plugins/class_migrations.py,sha256=
|
|
13
|
-
cadwyn/codegen/_plugins/class_rebuilding.py,sha256=
|
|
12
|
+
cadwyn/codegen/_plugins/class_migrations.py,sha256=m22lfgEvMw2DFXVdaFjbHAQ4JDM7io7QFNNS6EK7pM0,9549
|
|
13
|
+
cadwyn/codegen/_plugins/class_rebuilding.py,sha256=JrALq6B8YfPclGG4zG03fXxtoOsP38Jt2yCSkWhqEzw,4677
|
|
14
14
|
cadwyn/codegen/_plugins/class_renaming.py,sha256=5ka2W1c18i4maNbEkEpELvGLEFbd8tthvQX3YA3Bu0A,1843
|
|
15
15
|
cadwyn/codegen/_plugins/import_auto_adding.py,sha256=cYH7Fj_za-5_1h6yOfSiwxymg0X_vVJGyfQ5iJxb4FQ,2314
|
|
16
|
-
cadwyn/codegen/_plugins/latest_version_aliasing.py,sha256=
|
|
16
|
+
cadwyn/codegen/_plugins/latest_version_aliasing.py,sha256=s8TgpBL9FofKyJaF4AB7UxJYkq-f9LiMVL33LJxFVp4,4010
|
|
17
17
|
cadwyn/codegen/_plugins/module_migrations.py,sha256=TeWJk4Iu4SRQ9K2iI3v3sCs1110jrltKlPdfU9mXIsQ,722
|
|
18
18
|
cadwyn/exceptions.py,sha256=XOLsT4EH1uGNirmKlkgEk03PjUMtD7tgaCDadt_eBbE,695
|
|
19
19
|
cadwyn/main.py,sha256=_hC2Ke1uwtnjg2WueDXlg_QnzFgJbTcAlpHgqUBTmg4,4899
|
|
20
20
|
cadwyn/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
-
cadwyn/routing.py,sha256=
|
|
21
|
+
cadwyn/routing.py,sha256=3H8GKT10wAYroAOCtGCNBMuA63SGAQSESze3EhBLwo0,34433
|
|
22
22
|
cadwyn/structure/__init__.py,sha256=BjFPlQYCw8ds_4zxdCi2LimarUGqSzyTNmOdT-FkGms,661
|
|
23
23
|
cadwyn/structure/common.py,sha256=6Z4nI97XPWTCinn6np73m-rLPyYNrz2fWXKJlqjsiaQ,269
|
|
24
|
-
cadwyn/structure/data.py,sha256=
|
|
24
|
+
cadwyn/structure/data.py,sha256=g9UmCkcdLJA8SlFMmYJIrBEuuIO8DNkhbfUHiOrYnds,5785
|
|
25
25
|
cadwyn/structure/endpoints.py,sha256=VngfAydGBwekhV2tBOtNDPVgl3X1IgYxUCw--VZ5cQY,5627
|
|
26
26
|
cadwyn/structure/enums.py,sha256=iMokxA2QYJ61SzyB-Pmuq3y7KL7-e6TsnjLVUaVZQnw,954
|
|
27
27
|
cadwyn/structure/modules.py,sha256=1FK-lLm-zOTXEvn-QtyBH38aDRht5PDQiZrOPCsBlM4,1268
|
|
28
|
-
cadwyn/structure/schemas.py,sha256=
|
|
29
|
-
cadwyn/structure/versions.py,sha256=
|
|
30
|
-
cadwyn-3.1.
|
|
31
|
-
cadwyn-3.1.
|
|
32
|
-
cadwyn-3.1.
|
|
33
|
-
cadwyn-3.1.
|
|
34
|
-
cadwyn-3.1.
|
|
28
|
+
cadwyn/structure/schemas.py,sha256=JmatYHXTaA8lZZcgctGtRyR_HDc-lUqyz8c8NhQgprU,6050
|
|
29
|
+
cadwyn/structure/versions.py,sha256=1dL46je599RiIpT7FlQDclwrzMjDJfWhlnL8pPovSdc,26309
|
|
30
|
+
cadwyn-3.1.2.dist-info/entry_points.txt,sha256=eO05hLn9GoRzzpwT9GONPmXKsonjuMNssM2D2WHWKGk,46
|
|
31
|
+
cadwyn-3.1.2.dist-info/LICENSE,sha256=KeCWewiDQYpmSnzF-p_0YpoWiyDcUPaCuG8OWQs4ig4,1072
|
|
32
|
+
cadwyn-3.1.2.dist-info/WHEEL,sha256=vxFmldFsRN_Hx10GDvsdv1wroKq8r5Lzvjp6GZ4OO8c,88
|
|
33
|
+
cadwyn-3.1.2.dist-info/METADATA,sha256=hFJ6wmUPABzRsO9AxOnpxGaX4ZiKTVL_nw4d8W4ngUI,3972
|
|
34
|
+
cadwyn-3.1.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|