cadwyn 4.4.0__py3-none-any.whl → 4.4.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.

@@ -1,7 +1,7 @@
1
1
  import re
2
2
  from collections import defaultdict
3
3
  from collections.abc import Callable, Sequence
4
- from copy import deepcopy
4
+ from copy import copy, deepcopy
5
5
  from dataclasses import dataclass
6
6
  from typing import (
7
7
  TYPE_CHECKING,
@@ -49,6 +49,7 @@ if TYPE_CHECKING:
49
49
  _Call = TypeVar("_Call", bound=Callable[..., Any])
50
50
  _R = TypeVar("_R", bound=APIRouter)
51
51
  _WR = TypeVar("_WR", bound=APIRouter, default=APIRouter)
52
+ _RouteT = TypeVar("_RouteT", bound=BaseRoute)
52
53
  # This is a hack we do because we can't guarantee how the user will use the router.
53
54
  _DELETED_ROUTE_TAG = "_CADWYN_DELETED_ROUTE"
54
55
 
@@ -90,6 +91,25 @@ class VersionedAPIRouter(fastapi.routing.APIRouter):
90
91
  return endpoint
91
92
 
92
93
 
94
+ def copy_router(router: _R) -> _R:
95
+ router = copy(router)
96
+ router.routes = [copy_route(r) for r in router.routes]
97
+ return router
98
+
99
+
100
+ def copy_route(route: _RouteT) -> _RouteT:
101
+ if not isinstance(route, APIRoute):
102
+ return copy(route)
103
+
104
+ # This is slightly wasteful in terms of resources but it makes it easy for us
105
+ # to make sure that new versions of FastAPI are going to be supported even if
106
+ # APIRoute gets new attributes.
107
+ new_route = deepcopy(route)
108
+ new_route.dependant = copy(route.dependant)
109
+ new_route.dependencies = copy(route.dependencies)
110
+ return new_route
111
+
112
+
93
113
  class _EndpointTransformer(Generic[_R, _WR]):
94
114
  def __init__(self, parent_router: _R, versions: VersionBundle, webhooks: _WR) -> None:
95
115
  super().__init__()
@@ -103,8 +123,8 @@ class _EndpointTransformer(Generic[_R, _WR]):
103
123
  ]
104
124
 
105
125
  def transform(self) -> GeneratedRouters[_R, _WR]:
106
- router = deepcopy(self.parent_router)
107
- webhook_router = deepcopy(self.parent_webhooks_router)
126
+ router = copy_router(self.parent_router)
127
+ webhook_router = copy_router(self.parent_webhooks_router)
108
128
  routers: dict[VersionDate, _R] = {}
109
129
  webhook_routers: dict[VersionDate, _WR] = {}
110
130
 
@@ -117,8 +137,8 @@ class _EndpointTransformer(Generic[_R, _WR]):
117
137
  routers[version.value] = router
118
138
  webhook_routers[version.value] = webhook_router
119
139
  # Applying changes for the next version
120
- router = deepcopy(router)
121
- webhook_router = deepcopy(webhook_router)
140
+ router = copy_router(router)
141
+ webhook_router = copy_router(webhook_router)
122
142
  self._apply_endpoint_changes_to_router(router.routes + webhook_router.routes, version)
123
143
 
124
144
  if self.routes_that_never_existed:
@@ -134,7 +154,7 @@ class _EndpointTransformer(Generic[_R, _WR]):
134
154
  if not isinstance(head_route, APIRoute):
135
155
  continue
136
156
  _add_request_and_response_params(head_route)
137
- copy_of_dependant = deepcopy(head_route.dependant)
157
+ copy_of_dependant = copy(head_route.dependant)
138
158
 
139
159
  for older_router in list(routers.values()):
140
160
  older_route = older_router.routes[route_index]
@@ -367,6 +367,10 @@ class _PydanticModelWrapper(Generic[_T_PYDANTIC_MODEL]):
367
367
  return model_copy
368
368
 
369
369
 
370
+ def is_regular_function(call: Callable):
371
+ return isinstance(call, types.FunctionType | types.MethodType)
372
+
373
+
370
374
  class _CallableWrapper:
371
375
  """__eq__ and __hash__ are needed to make sure that dependency overrides work correctly.
372
376
  They are based on putting dependencies (functions) as keys for the dictionary so if we want to be able to
@@ -376,6 +380,8 @@ class _CallableWrapper:
376
380
  def __init__(self, original_callable: Callable) -> None:
377
381
  super().__init__()
378
382
  self._original_callable = original_callable
383
+ if not is_regular_function(original_callable):
384
+ original_callable = original_callable.__call__
379
385
  functools.update_wrapper(self, original_callable)
380
386
 
381
387
  @property
@@ -458,6 +464,12 @@ class _AnnotationTransformer:
458
464
  def _change_version_of_a_non_container_annotation(self, annotation: Any) -> Any:
459
465
  if isinstance(annotation, _BaseGenericAlias | types.GenericAlias):
460
466
  return get_origin(annotation)[tuple(self.change_version_of_annotation(arg) for arg in get_args(annotation))]
467
+ elif isinstance(annotation, fastapi.params.Security):
468
+ return fastapi.params.Security(
469
+ self.change_version_of_annotation(annotation.dependency),
470
+ scopes=annotation.scopes,
471
+ use_cache=annotation.use_cache,
472
+ )
461
473
  elif isinstance(annotation, fastapi.params.Depends):
462
474
  return fastapi.params.Depends(
463
475
  self.change_version_of_annotation(annotation.dependency),
@@ -475,7 +487,7 @@ class _AnnotationTransformer:
475
487
  elif callable(annotation):
476
488
  if type(annotation).__module__.startswith(
477
489
  ("fastapi.", "pydantic.", "pydantic_core.", "starlette.")
478
- ) or isinstance(annotation, fastapi.params.Security | fastapi.security.base.SecurityBase):
490
+ ) or isinstance(annotation, fastapi.security.base.SecurityBase):
479
491
  return annotation
480
492
 
481
493
  def modifier(annotation: Any):
@@ -568,8 +580,12 @@ class _AnnotationTransformer:
568
580
  def _copy_function_through_class_based_wrapper(cls, call: Any):
569
581
  """Separate from copy_endpoint because endpoints MUST be functions in FastAPI, they cannot be cls instances"""
570
582
  call = cls._unwrap_callable(call)
571
-
572
- if inspect.iscoroutinefunction(call):
583
+ if not is_regular_function(call):
584
+ # This means that the callable is actually an instance of a regular class
585
+ actual_call = call.__call__
586
+ else:
587
+ actual_call = call
588
+ if inspect.iscoroutinefunction(actual_call):
573
589
  return _AsyncCallableWrapper(call)
574
590
  else:
575
591
  return _CallableWrapper(call)
@@ -578,9 +594,6 @@ class _AnnotationTransformer:
578
594
  def _unwrap_callable(call: Any) -> Any:
579
595
  while hasattr(call, "_original_callable"):
580
596
  call = call._original_callable
581
- if not isinstance(call, types.FunctionType | types.MethodType):
582
- # This means that the callable is actually an instance of a regular class
583
- call = call.__call__
584
597
 
585
598
  return call
586
599
 
@@ -607,7 +620,7 @@ class SchemaGenerator:
607
620
 
608
621
  def __getitem__(self, model: type[_T_ANY_MODEL], /) -> type[_T_ANY_MODEL]:
609
622
  if not isinstance(model, type) or not issubclass(model, BaseModel | Enum) or model in (BaseModel, RootModel):
610
- return model # pyright: ignore[reportReturnType]
623
+ return model
611
624
  model = _unwrap_model(model)
612
625
 
613
626
  if model in self.concrete_models:
@@ -156,6 +156,8 @@ class VersionChange:
156
156
  "instructions_to_migrate_to_previous_version",
157
157
  "__module__",
158
158
  "__doc__",
159
+ "__firstlineno__",
160
+ "__static_attributes__",
159
161
  }:
160
162
  raise CadwynStructureError(
161
163
  f"Found: '{attr_name}' attribute of type '{type(attr_value)}' in '{cls.__name__}'."
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: cadwyn
3
- Version: 4.4.0
3
+ Version: 4.4.2
4
4
  Summary: Production-ready community-driven modern Stripe-like API versioning in FastAPI
5
5
  Project-URL: Source code, https://github.com/zmievsa/cadwyn
6
6
  Project-URL: Documentation, https://docs.cadwyn.dev
7
7
  Author-email: Stanislav Zmiev <zmievsa@gmail.com>
8
8
  License-Expression: MIT
9
9
  License-File: LICENSE
10
- Keywords: api,api-versioning,code-generation,fastapi,hints,json-schema,pydantic,python,python310,python311,python312,stripe,versioning
10
+ Keywords: api,api-versioning,code-generation,fastapi,hints,json-schema,pydantic,python,python310,python311,python312,python313,stripe,versioning
11
11
  Classifier: Development Status :: 5 - Production/Stable
12
12
  Classifier: Environment :: Web Environment
13
13
  Classifier: Framework :: AsyncIO
@@ -23,6 +23,7 @@ Classifier: Programming Language :: Python :: 3
23
23
  Classifier: Programming Language :: Python :: 3.10
24
24
  Classifier: Programming Language :: Python :: 3.11
25
25
  Classifier: Programming Language :: Python :: 3.12
26
+ Classifier: Programming Language :: Python :: 3.13
26
27
  Classifier: Topic :: Internet
27
28
  Classifier: Topic :: Internet :: WWW/HTTP
28
29
  Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
@@ -9,9 +9,9 @@ cadwyn/changelogs.py,sha256=SdrdAKQ01mpzs-EN_zg-D0TY7wxsibjRjLMhGcI4q80,20066
9
9
  cadwyn/exceptions.py,sha256=VlJKRmEGfFTDtHbOWc8kXK4yMi2N172K684Y2UIV8rI,1832
10
10
  cadwyn/middleware.py,sha256=kUZK2dmoricMbv6knPCIHpXEInX2670XIwAj0v_XQxk,3408
11
11
  cadwyn/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- cadwyn/route_generation.py,sha256=870pZK-Gs_pUuRRj9jcJm3AOc-Ua2jtNRoJcwUutLA4,24028
12
+ cadwyn/route_generation.py,sha256=2qUTPHsG6u6yXUA5pLa3ubfZOc9FrISDMELmAFhDSUY,24696
13
13
  cadwyn/routing.py,sha256=9AHSojmuLgUAQlLMIqXz-ViZ9n-fljgOsn7oxha7PjM,7341
14
- cadwyn/schema_generation.py,sha256=1eJ--nyG8onUROWegCmQQWJ4iJEu1MrTt-8bnLIQyeQ,39992
14
+ cadwyn/schema_generation.py,sha256=JuWuBPZTEC_K8BEd67jaXFqD-Y4r40njJLQFNBrU77Y,40465
15
15
  cadwyn/static/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  cadwyn/static/docs.html,sha256=WNm5ANJVy51TcIUFOaqKf1Z8eF86CC85TTHPxACtkzw,3455
17
17
  cadwyn/structure/__init__.py,sha256=vej7TdTMSOg8U8Wk7GTNdA4rc6loA9083FWaTg4jAaY,655
@@ -20,9 +20,9 @@ cadwyn/structure/data.py,sha256=1ALPhBBCE_t4GrxM0Fa3hQ-jkORJgeWNySnZ42bsi0g,7382
20
20
  cadwyn/structure/endpoints.py,sha256=9FFnbqPM9v0CP6J6tGhMNKVvWqA9u3ZjI2Fannr2Rl0,5933
21
21
  cadwyn/structure/enums.py,sha256=bZL-iUOUFi9ZYlMZJw-tAix2yrgCp3gH3N2gwO44LUU,1043
22
22
  cadwyn/structure/schemas.py,sha256=D0BD1D3v9MRdVWchU9JM2zHd0dvB0UgXHDGFCe5aQZc,8209
23
- cadwyn/structure/versions.py,sha256=2qe7xFYNdzdWmCZhkf4_zJ7lF0XdtyM1wq6xQ9omb_c,33655
24
- cadwyn-4.4.0.dist-info/METADATA,sha256=YcNWXemwnjoR8H2ODF7yFTWRBOjbPaxar1vKXe9UmOs,4443
25
- cadwyn-4.4.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
26
- cadwyn-4.4.0.dist-info/entry_points.txt,sha256=mGX8wl-Xfhpr5M93SUmkykaqinUaYAvW9rtDSX54gx0,47
27
- cadwyn-4.4.0.dist-info/licenses/LICENSE,sha256=KeCWewiDQYpmSnzF-p_0YpoWiyDcUPaCuG8OWQs4ig4,1072
28
- cadwyn-4.4.0.dist-info/RECORD,,
23
+ cadwyn/structure/versions.py,sha256=7vbKSj0XwYNDbDXRAhB4iHT1K1YCRAcvXsQrC6rZn7w,33731
24
+ cadwyn-4.4.2.dist-info/METADATA,sha256=jXeXnBO2beIecKbyGh1RGv_Fq1TQpgc46_dXHIGLjcs,4504
25
+ cadwyn-4.4.2.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
26
+ cadwyn-4.4.2.dist-info/entry_points.txt,sha256=mGX8wl-Xfhpr5M93SUmkykaqinUaYAvW9rtDSX54gx0,47
27
+ cadwyn-4.4.2.dist-info/licenses/LICENSE,sha256=KeCWewiDQYpmSnzF-p_0YpoWiyDcUPaCuG8OWQs4ig4,1072
28
+ cadwyn-4.4.2.dist-info/RECORD,,
File without changes