cadwyn 3.15.6__tar.gz → 3.15.8__tar.gz
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-3.15.6 → cadwyn-3.15.8}/PKG-INFO +1 -1
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/_asts.py +0 -1
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/codegen/_common.py +2 -1
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/route_generation.py +83 -38
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/structure/modules.py +2 -1
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/structure/versions.py +4 -1
- {cadwyn-3.15.6 → cadwyn-3.15.8}/pyproject.toml +1 -1
- {cadwyn-3.15.6 → cadwyn-3.15.8}/LICENSE +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/README.md +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/__init__.py +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/__main__.py +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/_compat.py +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/_package_utils.py +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/_utils.py +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/applications.py +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/codegen/README.md +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/codegen/__init__.py +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/codegen/_main.py +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/codegen/_plugins/__init__.py +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/codegen/_plugins/class_migrations.py +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/codegen/_plugins/class_rebuilding.py +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/codegen/_plugins/class_renaming.py +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/codegen/_plugins/import_auto_adding.py +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/codegen/_plugins/module_migrations.py +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/exceptions.py +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/main.py +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/middleware.py +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/py.typed +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/routing.py +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/static/__init__.py +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/static/docs.html +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/structure/__init__.py +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/structure/common.py +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/structure/data.py +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/structure/endpoints.py +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/structure/enums.py +0 -0
- {cadwyn-3.15.6 → cadwyn-3.15.8}/cadwyn/structure/schemas.py +0 -0
|
@@ -156,7 +156,6 @@ def transform_other(value: Any) -> Any:
|
|
|
156
156
|
def _get_lambda_source_from_default_factory(source: str) -> str:
|
|
157
157
|
found_lambdas: list[ast.Lambda] = []
|
|
158
158
|
|
|
159
|
-
ast.parse(source)
|
|
160
159
|
for node in ast.walk(ast.parse(source)):
|
|
161
160
|
if isinstance(node, ast.keyword) and node.arg == "default_factory" and isinstance(node.value, ast.Lambda):
|
|
162
161
|
found_lambdas.append(node.value)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import ast
|
|
2
2
|
import dataclasses
|
|
3
3
|
import inspect
|
|
4
|
+
import textwrap
|
|
4
5
|
from dataclasses import dataclass
|
|
5
6
|
from enum import Enum
|
|
6
7
|
from functools import cache
|
|
@@ -89,7 +90,7 @@ def get_fields_and_validators_from_model(
|
|
|
89
90
|
{},
|
|
90
91
|
)
|
|
91
92
|
else:
|
|
92
|
-
cls_ast = cast(ast.ClassDef, ast.parse(source).body[0])
|
|
93
|
+
cls_ast = cast(ast.ClassDef, ast.parse(textwrap.dedent(source)).body[0])
|
|
93
94
|
validators: dict[str, _ValidatorWrapper] = {}
|
|
94
95
|
|
|
95
96
|
validators_and_nones = (
|
|
@@ -29,11 +29,7 @@ import fastapi.params
|
|
|
29
29
|
import fastapi.routing
|
|
30
30
|
import fastapi.security.base
|
|
31
31
|
import fastapi.utils
|
|
32
|
-
from fastapi import
|
|
33
|
-
APIRouter,
|
|
34
|
-
Request, # noqa: F401 # We import Request for libraries like svcs that expect it to be in globals
|
|
35
|
-
Response, # noqa: F401 # We import Request for libraries like svcs that expect it to be in globals
|
|
36
|
-
)
|
|
32
|
+
from fastapi import APIRouter
|
|
37
33
|
from fastapi._compat import ModelField as FastAPIModelField
|
|
38
34
|
from fastapi._compat import create_body_model
|
|
39
35
|
from fastapi.params import Depends
|
|
@@ -67,7 +63,7 @@ from cadwyn.structure.versions import _CADWYN_REQUEST_PARAM_NAME, _CADWYN_RESPON
|
|
|
67
63
|
if TYPE_CHECKING:
|
|
68
64
|
from fastapi.dependencies.models import Dependant
|
|
69
65
|
|
|
70
|
-
|
|
66
|
+
_Call = TypeVar("_Call", bound=Callable[..., Any])
|
|
71
67
|
_R = TypeVar("_R", bound=fastapi.routing.APIRouter)
|
|
72
68
|
# This is a hack we do because we can't guarantee how the user will use the router.
|
|
73
69
|
_DELETED_ROUTE_TAG = "_CADWYN_DELETED_ROUTE"
|
|
@@ -121,7 +117,7 @@ def generate_versioned_routers(
|
|
|
121
117
|
|
|
122
118
|
|
|
123
119
|
class VersionedAPIRouter(fastapi.routing.APIRouter):
|
|
124
|
-
def only_exists_in_older_versions(self, endpoint:
|
|
120
|
+
def only_exists_in_older_versions(self, endpoint: _Call) -> _Call:
|
|
125
121
|
route = _get_route_from_func(self.routes, endpoint)
|
|
126
122
|
if route is None:
|
|
127
123
|
raise LookupError(
|
|
@@ -466,9 +462,10 @@ def _extract_internal_request_schemas_from_router(
|
|
|
466
462
|
|
|
467
463
|
for route in router.routes:
|
|
468
464
|
if isinstance(route, APIRoute): # pragma: no branch
|
|
469
|
-
route.endpoint =
|
|
465
|
+
route.endpoint = _modify_callable_annotations(
|
|
470
466
|
route.endpoint,
|
|
471
467
|
modify_annotations=_extract_internal_request_schemas_from_annotations,
|
|
468
|
+
annotation_modifying_wrapper_factory=_copy_endpoint,
|
|
472
469
|
)
|
|
473
470
|
_remake_endpoint_dependencies(route)
|
|
474
471
|
return schema_to_internal_request_body_representation
|
|
@@ -575,7 +572,12 @@ class _AnnotationTransformer:
|
|
|
575
572
|
def modifier(annotation: Any):
|
|
576
573
|
return self._change_version_of_annotations(annotation, version_dir)
|
|
577
574
|
|
|
578
|
-
return
|
|
575
|
+
return _modify_callable_annotations(
|
|
576
|
+
annotation,
|
|
577
|
+
modifier,
|
|
578
|
+
modifier,
|
|
579
|
+
annotation_modifying_wrapper_factory=_copy_function_through_class_based_wrapper,
|
|
580
|
+
)
|
|
579
581
|
else:
|
|
580
582
|
return annotation
|
|
581
583
|
|
|
@@ -636,12 +638,14 @@ class _AnnotationTransformer:
|
|
|
636
638
|
)
|
|
637
639
|
|
|
638
640
|
|
|
639
|
-
def
|
|
640
|
-
call:
|
|
641
|
+
def _modify_callable_annotations(
|
|
642
|
+
call: _Call,
|
|
641
643
|
modify_annotations: Callable[[dict[str, Any]], dict[str, Any]] = lambda a: a,
|
|
642
644
|
modify_defaults: Callable[[tuple[Any, ...]], tuple[Any, ...]] = lambda a: a,
|
|
643
|
-
|
|
644
|
-
|
|
645
|
+
*,
|
|
646
|
+
annotation_modifying_wrapper_factory: Callable[[_Call], _Call],
|
|
647
|
+
) -> _Call:
|
|
648
|
+
annotation_modifying_wrapper = annotation_modifying_wrapper_factory(call)
|
|
645
649
|
old_params = inspect.signature(call).parameters
|
|
646
650
|
callable_annotations = annotation_modifying_wrapper.__annotations__
|
|
647
651
|
annotation_modifying_wrapper.__annotations__ = modify_annotations(callable_annotations)
|
|
@@ -798,37 +802,78 @@ def _get_route_from_func(
|
|
|
798
802
|
return None
|
|
799
803
|
|
|
800
804
|
|
|
801
|
-
def
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
805
|
+
def _copy_endpoint(function: Any) -> Any:
|
|
806
|
+
function = _unwrap_callable(function)
|
|
807
|
+
function_copy: Any = types.FunctionType(
|
|
808
|
+
function.__code__,
|
|
809
|
+
function.__globals__,
|
|
810
|
+
name=function.__name__,
|
|
811
|
+
argdefs=function.__defaults__,
|
|
812
|
+
closure=function.__closure__,
|
|
813
|
+
)
|
|
814
|
+
function_copy = functools.update_wrapper(function_copy, function)
|
|
815
|
+
# Otherwise it will have the same signature as __wrapped__ due to how inspect module works
|
|
816
|
+
del function_copy.__wrapped__
|
|
817
|
+
|
|
818
|
+
function_copy._original_callable = function
|
|
819
|
+
function.__kwdefaults__ = function.__kwdefaults__.copy() if function.__kwdefaults__ is not None else {}
|
|
820
|
+
|
|
821
|
+
return function_copy
|
|
808
822
|
|
|
809
|
-
@functools.wraps(function)
|
|
810
|
-
async def annotation_modifying_wrapper( # pyright: ignore[reportRedeclaration]
|
|
811
|
-
*args: Any,
|
|
812
|
-
**kwargs: Any,
|
|
813
|
-
) -> Any:
|
|
814
|
-
return await function(*args, **kwargs)
|
|
815
823
|
|
|
824
|
+
class _CallableWrapper:
|
|
825
|
+
"""__eq__ and __hash__ are needed to make sure that dependency overrides work correctly.
|
|
826
|
+
They are based on putting dependencies (functions) as keys for the dictionary so if we want to be able to
|
|
827
|
+
override the wrapper, we need to make sure that it is equivalent to the original in __hash__ and __eq__
|
|
828
|
+
"""
|
|
829
|
+
|
|
830
|
+
def __init__(self, original_callable: Callable) -> None:
|
|
831
|
+
super().__init__()
|
|
832
|
+
self._original_callable = original_callable
|
|
833
|
+
functools.update_wrapper(self, original_callable)
|
|
834
|
+
|
|
835
|
+
@property
|
|
836
|
+
def __globals__(self):
|
|
837
|
+
"""FastAPI uses __globals__ to resolve forward references in type hints
|
|
838
|
+
It's supposed to be an attribute on the function but we use it as property to prevent python
|
|
839
|
+
from trying to pickle globals when we deepcopy this wrapper
|
|
840
|
+
"""
|
|
841
|
+
#
|
|
842
|
+
return self._original_callable.__globals__
|
|
843
|
+
|
|
844
|
+
def __call__(self, *args: Any, **kwargs: Any):
|
|
845
|
+
return self._original_callable(*args, **kwargs)
|
|
846
|
+
|
|
847
|
+
def __hash__(self):
|
|
848
|
+
return hash(self._original_callable)
|
|
849
|
+
|
|
850
|
+
def __eq__(self, value: object) -> bool:
|
|
851
|
+
return self._original_callable == value # pyright: ignore[reportUnnecessaryComparison]
|
|
852
|
+
|
|
853
|
+
|
|
854
|
+
class _AsyncCallableWrapper(_CallableWrapper):
|
|
855
|
+
async def __call__(self, *args: Any, **kwargs: Any):
|
|
856
|
+
return await self._original_callable(*args, **kwargs)
|
|
857
|
+
|
|
858
|
+
|
|
859
|
+
def _copy_function_through_class_based_wrapper(call: Any):
|
|
860
|
+
"""Separate from copy_endpoint because endpoints MUST be functions in FastAPI, they cannot be cls instances"""
|
|
861
|
+
call = _unwrap_callable(call)
|
|
862
|
+
|
|
863
|
+
if inspect.iscoroutinefunction(call):
|
|
864
|
+
return _AsyncCallableWrapper(call)
|
|
816
865
|
else:
|
|
866
|
+
return _CallableWrapper(call)
|
|
817
867
|
|
|
818
|
-
@functools.wraps(function)
|
|
819
|
-
def annotation_modifying_wrapper(
|
|
820
|
-
*args: Any,
|
|
821
|
-
**kwargs: Any,
|
|
822
|
-
) -> Any:
|
|
823
|
-
return function(*args, **kwargs)
|
|
824
868
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
)
|
|
829
|
-
|
|
869
|
+
def _unwrap_callable(call: Any) -> Any:
|
|
870
|
+
while hasattr(call, "_original_callable"):
|
|
871
|
+
call = call._original_callable
|
|
872
|
+
if not isinstance(call, types.FunctionType | types.MethodType):
|
|
873
|
+
# This means that the callable is actually an instance of a regular class
|
|
874
|
+
call = call.__call__
|
|
830
875
|
|
|
831
|
-
return
|
|
876
|
+
return call
|
|
832
877
|
|
|
833
878
|
|
|
834
879
|
def _route_has_a_simple_body_schema(route: APIRoute) -> bool:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import ast
|
|
2
2
|
import dataclasses
|
|
3
|
+
import textwrap
|
|
3
4
|
from dataclasses import InitVar, dataclass
|
|
4
5
|
from types import ModuleType
|
|
5
6
|
|
|
@@ -13,7 +14,7 @@ class AlterModuleInstruction:
|
|
|
13
14
|
import_: ast.Import | ast.ImportFrom = dataclasses.field(init=False)
|
|
14
15
|
|
|
15
16
|
def __post_init__(self, raw_import: str):
|
|
16
|
-
parsed_body = ast.parse(raw_import).body
|
|
17
|
+
parsed_body = ast.parse(textwrap.dedent(raw_import)).body
|
|
17
18
|
if len(parsed_body) > 1:
|
|
18
19
|
raise CadwynStructureError(
|
|
19
20
|
f"You have specified more than just a single import. This is prohibited. "
|
|
@@ -671,7 +671,10 @@ class VersionBundle:
|
|
|
671
671
|
# that do not have it. We don't support it too.
|
|
672
672
|
if response_info.body is not None and hasattr(response_info._response, "body"):
|
|
673
673
|
# TODO (https://github.com/zmievsa/cadwyn/issues/51): Only do this if there are migrations
|
|
674
|
-
if
|
|
674
|
+
if (
|
|
675
|
+
isinstance(response_info.body, str)
|
|
676
|
+
and response_info._response.headers.get("content-type") != "application/json"
|
|
677
|
+
):
|
|
675
678
|
response_info._response.body = response_info.body.encode(response_info._response.charset)
|
|
676
679
|
else:
|
|
677
680
|
response_info._response.body = json.dumps(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|