cadwyn 3.15.6__py3-none-any.whl → 3.15.7__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.

@@ -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
- _T = TypeVar("_T", bound=Callable[..., Any])
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: _T) -> _T:
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 = _modify_callable(
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 _modify_callable(annotation, modifier, modifier)
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 _modify_callable(
640
- call: Callable,
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
- annotation_modifying_wrapper = _copy_function(call)
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 _copy_function(function: _T) -> _T:
802
- while hasattr(function, "__alt_wrapped__"):
803
- function = function.__alt_wrapped__
804
- if not isinstance(function, types.FunctionType | types.MethodType):
805
- # This means that the callable is actually an instance of a regular class
806
- function = function.__call__
807
- if inspect.iscoroutinefunction(function):
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
- # Otherwise it will have the same signature as __wrapped__ due to how inspect module works
826
- annotation_modifying_wrapper.__alt_wrapped__ = ( # pyright: ignore[reportAttributeAccessIssue]
827
- annotation_modifying_wrapper.__wrapped__
828
- )
829
- del annotation_modifying_wrapper.__wrapped__
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 cast(_T, annotation_modifying_wrapper)
876
+ return call
832
877
 
833
878
 
834
879
  def _route_has_a_simple_body_schema(route: APIRoute) -> bool:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cadwyn
3
- Version: 3.15.6
3
+ Version: 3.15.7
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
@@ -19,7 +19,7 @@ cadwyn/exceptions.py,sha256=aJKx1qgzZqShL4MX3COjS780qzNJcdZFeGzYYa5gbzw,1726
19
19
  cadwyn/main.py,sha256=kt2Vn7TIA4ZnD_xrgz57TOjUk-4zVP8SV8nuTZBEaaU,218
20
20
  cadwyn/middleware.py,sha256=8cuBri_yRkl0goe6G0MLwtL04WGbW9Infah3wy9hUVM,3372
21
21
  cadwyn/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
- cadwyn/route_generation.py,sha256=mMfAy-5bz6JmW3JlzM1elG4CFrQgMWoUKLlnqXB5S00,39827
22
+ cadwyn/route_generation.py,sha256=14_CP_oJBhiO1dNgtYwYkuZnL7o7wyL-EenSvMx39ig,41571
23
23
  cadwyn/routing.py,sha256=o6IMjxTxARPa5BxFfsXqOP3bVw9Ya6OBAEbUwH9lMVM,7445
24
24
  cadwyn/static/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
25
  cadwyn/static/docs.html,sha256=WNm5ANJVy51TcIUFOaqKf1Z8eF86CC85TTHPxACtkzw,3455
@@ -31,8 +31,8 @@ cadwyn/structure/enums.py,sha256=iMokxA2QYJ61SzyB-Pmuq3y7KL7-e6TsnjLVUaVZQnw,954
31
31
  cadwyn/structure/modules.py,sha256=1FK-lLm-zOTXEvn-QtyBH38aDRht5PDQiZrOPCsBlM4,1268
32
32
  cadwyn/structure/schemas.py,sha256=0ylArAkUw626VkUOJSulOwJs7CS6lrGBRECEG5HFD4Q,8897
33
33
  cadwyn/structure/versions.py,sha256=_NthvvuN7l01J-E2zCZmYtpJZjiVO-wqSDvMSFXhzno,37400
34
- cadwyn-3.15.6.dist-info/LICENSE,sha256=KeCWewiDQYpmSnzF-p_0YpoWiyDcUPaCuG8OWQs4ig4,1072
35
- cadwyn-3.15.6.dist-info/METADATA,sha256=lZHxh7mAPk71OebTBR9qv-k6OU5oUnARbzFYbVWqV_k,4397
36
- cadwyn-3.15.6.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
37
- cadwyn-3.15.6.dist-info/entry_points.txt,sha256=eO05hLn9GoRzzpwT9GONPmXKsonjuMNssM2D2WHWKGk,46
38
- cadwyn-3.15.6.dist-info/RECORD,,
34
+ cadwyn-3.15.7.dist-info/LICENSE,sha256=KeCWewiDQYpmSnzF-p_0YpoWiyDcUPaCuG8OWQs4ig4,1072
35
+ cadwyn-3.15.7.dist-info/METADATA,sha256=IplLmVX2LHqfcRse_swuTQ9cf-9MMbbhcKs-10rNhok,4397
36
+ cadwyn-3.15.7.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
37
+ cadwyn-3.15.7.dist-info/entry_points.txt,sha256=eO05hLn9GoRzzpwT9GONPmXKsonjuMNssM2D2WHWKGk,46
38
+ cadwyn-3.15.7.dist-info/RECORD,,