cadwyn 3.15.10__py3-none-any.whl → 4.0.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.

@@ -1,28 +1,14 @@
1
- import functools
2
- import inspect
3
1
  import re
4
- import types
5
- import typing
6
- import warnings
7
2
  from collections import defaultdict
8
3
  from collections.abc import Callable, Sequence
9
4
  from copy import deepcopy
10
5
  from dataclasses import dataclass
11
- from enum import Enum
12
- from pathlib import Path
13
- from types import GenericAlias, MappingProxyType, ModuleType
14
6
  from typing import (
15
7
  TYPE_CHECKING,
16
- Annotated,
17
8
  Any,
18
9
  Generic,
19
10
  TypeVar,
20
- _BaseGenericAlias, # pyright: ignore[reportAttributeAccessIssue]
21
11
  cast,
22
- final,
23
- get_args,
24
- get_origin,
25
- overload,
26
12
  )
27
13
 
28
14
  import fastapi.params
@@ -30,18 +16,13 @@ import fastapi.routing
30
16
  import fastapi.security.base
31
17
  import fastapi.utils
32
18
  from fastapi import APIRouter
33
- from fastapi._compat import ModelField as FastAPIModelField
34
- from fastapi._compat import create_body_model
35
- from fastapi.params import Depends
36
19
  from fastapi.routing import APIRoute
37
20
  from issubclass import issubclass as lenient_issubclass
38
21
  from pydantic import BaseModel
39
22
  from starlette.routing import BaseRoute
40
- from typing_extensions import Self, assert_never, deprecated
23
+ from typing_extensions import assert_never
41
24
 
42
- from cadwyn._compat import get_annotation_from_model_field, model_fields, rebuild_fastapi_body_param
43
- from cadwyn._package_utils import get_version_dir_path
44
- from cadwyn._utils import Sentinel, UnionType, get_another_version_of_cls
25
+ from cadwyn._utils import Sentinel
45
26
  from cadwyn.exceptions import (
46
27
  CadwynError,
47
28
  RouteAlreadyExistsError,
@@ -51,6 +32,10 @@ from cadwyn.exceptions import (
51
32
  RouterGenerationError,
52
33
  RouterPathParamsModifiedError,
53
34
  )
35
+ from cadwyn.schema_generation import (
36
+ _add_request_and_response_params,
37
+ _generate_versioned_models,
38
+ )
54
39
  from cadwyn.structure import Version, VersionBundle
55
40
  from cadwyn.structure.common import Endpoint, VersionDate
56
41
  from cadwyn.structure.endpoints import (
@@ -58,7 +43,9 @@ from cadwyn.structure.endpoints import (
58
43
  EndpointExistedInstruction,
59
44
  EndpointHadInstruction,
60
45
  )
61
- from cadwyn.structure.versions import _CADWYN_REQUEST_PARAM_NAME, _CADWYN_RESPONSE_PARAM_NAME, VersionChange
46
+ from cadwyn.structure.versions import (
47
+ VersionChange,
48
+ )
62
49
 
63
50
  if TYPE_CHECKING:
64
51
  from fastapi.dependencies.models import Dependant
@@ -75,45 +62,8 @@ class _EndpointInfo:
75
62
  endpoint_methods: frozenset[str]
76
63
 
77
64
 
78
- @deprecated("It will soon be deleted. Use HeadVersion version changes instead.")
79
- class InternalRepresentationOf:
80
- def __class_getitem__(cls, original_schema: type, /) -> type[Self]:
81
- return cast(Any, type("InternalRepresentationOf", (cls, original_schema), {}))
82
-
83
-
84
- @overload
85
- def generate_versioned_routers(
86
- router: _R,
87
- versions: VersionBundle,
88
- ) -> dict[VersionDate, _R]: ...
89
-
90
-
91
- @overload
92
- @deprecated("Do not use the latest_schemas_package argument. Put head_schemas_package into your VersionBundle instead")
93
- def generate_versioned_routers(
94
- router: _R,
95
- versions: VersionBundle,
96
- latest_schemas_package: ModuleType | None,
97
- ) -> dict[VersionDate, _R]: ...
98
-
99
-
100
- def generate_versioned_routers(
101
- router: _R,
102
- versions: VersionBundle,
103
- latest_schemas_package: ModuleType | None = None,
104
- ) -> dict[VersionDate, _R]:
105
- if versions.head_schemas_package is not None:
106
- head_schemas_package = versions.head_schemas_package
107
- elif latest_schemas_package is not None: # pragma: no cover
108
- head_schemas_package = latest_schemas_package
109
- else: # pragma: no cover
110
- raise TypeError(
111
- "TypeError: generate_versioned_routers() must be called with a VersionBundle "
112
- "that contains a non-null head_schemas_package."
113
- )
114
- versions.head_schemas_package = head_schemas_package
115
- versions._validate_head_schemas_package_structure()
116
- return _EndpointTransformer(router, versions, head_schemas_package).transform()
65
+ def generate_versioned_routers(router: _R, versions: VersionBundle) -> dict[VersionDate, _R]:
66
+ return _EndpointTransformer(router, versions).transform()
117
67
 
118
68
 
119
69
  class VersionedAPIRouter(fastapi.routing.APIRouter):
@@ -131,30 +81,22 @@ class VersionedAPIRouter(fastapi.routing.APIRouter):
131
81
 
132
82
 
133
83
  class _EndpointTransformer(Generic[_R]):
134
- def __init__(
135
- self,
136
- parent_router: _R,
137
- versions: VersionBundle,
138
- head_schemas_package: ModuleType,
139
- ) -> None:
84
+ def __init__(self, parent_router: _R, versions: VersionBundle) -> None:
140
85
  super().__init__()
141
86
  self.parent_router = parent_router
142
87
  self.versions = versions
143
- self.annotation_transformer = _AnnotationTransformer(head_schemas_package, versions)
88
+ self.schema_generators = _generate_versioned_models(versions)
144
89
 
145
90
  self.routes_that_never_existed = [
146
91
  route for route in parent_router.routes if isinstance(route, APIRoute) and _DELETED_ROUTE_TAG in route.tags
147
92
  ]
148
93
 
149
94
  def transform(self) -> dict[VersionDate, _R]:
150
- schema_to_internal_request_body_representation = _extract_internal_request_schemas_from_router(
151
- self.parent_router
152
- )
153
95
  router = deepcopy(self.parent_router)
154
96
  routers: dict[VersionDate, _R] = {}
155
97
 
156
98
  for version in self.versions:
157
- self.annotation_transformer.migrate_router_to_version(router, version)
99
+ self.schema_generators[str(version.value)].annotation_transformer.migrate_router_to_version(router)
158
100
 
159
101
  self._validate_all_data_converters_are_applied(router, version)
160
102
 
@@ -178,12 +120,6 @@ class _EndpointTransformer(Generic[_R]):
178
120
  _add_request_and_response_params(head_route)
179
121
  copy_of_dependant = deepcopy(head_route.dependant)
180
122
 
181
- if _route_has_a_simple_body_schema(head_route):
182
- self._replace_internal_representation_with_the_versioned_schema(
183
- copy_of_dependant,
184
- schema_to_internal_request_body_representation,
185
- )
186
-
187
123
  for older_router in list(routers.values()):
188
124
  older_route = older_router.routes[route_index]
189
125
 
@@ -192,10 +128,10 @@ class _EndpointTransformer(Generic[_R]):
192
128
  older_route = cast(APIRoute, older_route)
193
129
  # Wait.. Why do we need this code again?
194
130
  if older_route.body_field is not None and _route_has_a_simple_body_schema(older_route):
195
- template_older_body_model = self.annotation_transformer._change_version_of_annotations(
196
- older_route.body_field.type_,
197
- self.annotation_transformer.head_version_dir,
198
- )
131
+ if hasattr(older_route.body_field.type_, "__cadwyn_original_model__"):
132
+ template_older_body_model = older_route.body_field.type_.__cadwyn_original_model__
133
+ else:
134
+ template_older_body_model = older_route.body_field.type_
199
135
  else:
200
136
  template_older_body_model = None
201
137
  _add_data_migrations_to_route(
@@ -274,44 +210,19 @@ class _EndpointTransformer(Generic[_R]):
274
210
  for route in router.routes:
275
211
  if isinstance(route, APIRoute):
276
212
  if route.response_model is not None and lenient_issubclass(route.response_model, BaseModel):
277
- # FIXME: This is going to fail on Pydantic 1
278
213
  response_models.add(route.response_model)
279
214
  # Not sure if it can ever be None when it's a simple schema. Eh, I would rather be safe than sorry
280
215
  if _route_has_a_simple_body_schema(route) and route.body_field is not None:
281
- annotation = get_annotation_from_model_field(route.body_field)
282
- if lenient_issubclass(annotation, BaseModel):
283
- # FIXME: This is going to fail on Pydantic 1
216
+ annotation = route.body_field.field_info.annotation
217
+ if annotation is not None and lenient_issubclass(annotation, BaseModel):
284
218
  request_bodies.add(annotation)
285
219
  path_to_route_methods_mapping[route.path] |= route.methods
286
220
 
287
- head_response_models = {
288
- self.annotation_transformer._change_version_of_annotations(
289
- model,
290
- self.versions.versioned_directories_with_head[0],
291
- )
292
- for model in response_models
293
- }
294
- head_request_bodies = {
295
- self.annotation_transformer._change_version_of_annotations(
296
- body,
297
- self.versions.versioned_directories_with_head[0],
298
- )
299
- for body in request_bodies
300
- }
221
+ head_response_models = {model.__cadwyn_original_model__ for model in response_models}
222
+ head_request_bodies = {getattr(body, "__cadwyn_original_model__", body) for body in request_bodies}
301
223
 
302
224
  return path_to_route_methods_mapping, head_response_models, head_request_bodies
303
225
 
304
- def _replace_internal_representation_with_the_versioned_schema(
305
- self,
306
- copy_of_dependant: "Dependant",
307
- schema_to_internal_request_body_representation: dict[type[BaseModel], type[BaseModel]],
308
- ):
309
- body_param: FastAPIModelField = copy_of_dependant.body_params[0]
310
- body_schema = body_param.type_
311
- new_type = schema_to_internal_request_body_representation.get(body_schema, body_schema)
312
- new_body_param = rebuild_fastapi_body_param(body_param, new_type)
313
- copy_of_dependant.body_params = [new_body_param]
314
-
315
226
  # TODO (https://github.com/zmievsa/cadwyn/issues/28): Simplify
316
227
  def _apply_endpoint_changes_to_router( # noqa: C901
317
228
  self,
@@ -437,40 +348,6 @@ class _EndpointTransformer(Generic[_R]):
437
348
  )
438
349
 
439
350
 
440
- def _extract_internal_request_schemas_from_router(
441
- router: fastapi.routing.APIRouter,
442
- ) -> dict[type[BaseModel], type[BaseModel]]:
443
- """Please note that this functon replaces internal bodies with original bodies in the router"""
444
- schema_to_internal_request_body_representation = {}
445
-
446
- def _extract_internal_request_schemas_from_annotations(annotations: dict[str, Any]):
447
- for key, annotation in annotations.items():
448
- if isinstance(annotation, type(Annotated[int, int])): # pyright: ignore[reportArgumentType]
449
- args = get_args(annotation)
450
- if isinstance(args[1], type) and issubclass( # pragma: no branch
451
- args[1],
452
- InternalRepresentationOf, # pyright: ignore[reportDeprecated]
453
- ):
454
- internal_schema = args[0]
455
- original_schema = args[1].mro()[2]
456
- schema_to_internal_request_body_representation[original_schema] = internal_schema
457
- if len(args[2:]) != 0:
458
- annotations[key] = Annotated[(original_schema, *args[2:])]
459
- else:
460
- annotations[key] = original_schema
461
- return annotations
462
-
463
- for route in router.routes:
464
- if isinstance(route, APIRoute): # pragma: no branch
465
- route.endpoint = _modify_callable_annotations(
466
- route.endpoint,
467
- modify_annotations=_extract_internal_request_schemas_from_annotations,
468
- annotation_modifying_wrapper_factory=_copy_endpoint,
469
- )
470
- _remake_endpoint_dependencies(route)
471
- return schema_to_internal_request_body_representation
472
-
473
-
474
351
  def _validate_no_repetitions_in_routes(routes: list[fastapi.routing.APIRoute]):
475
352
  route_map = {}
476
353
 
@@ -481,201 +358,6 @@ def _validate_no_repetitions_in_routes(routes: list[fastapi.routing.APIRoute]):
481
358
  route_map[route_info] = route
482
359
 
483
360
 
484
- @final
485
- class _AnnotationTransformer:
486
- __slots__ = (
487
- "versions",
488
- "head_schemas_package",
489
- "head_version_dir",
490
- "latest_version_dir",
491
- "change_versions_of_a_non_container_annotation",
492
- )
493
-
494
- def __init__(self, head_schemas_package: ModuleType, versions: VersionBundle) -> None:
495
- self.versions = versions
496
- self.versions.head_schemas_package = head_schemas_package
497
- self.head_schemas_package = head_schemas_package
498
- self.head_version_dir = min(versions.versioned_directories_with_head) # "head" < "v0000_00_00"
499
- self.latest_version_dir = max(versions.versioned_directories_with_head) # "v2005_11_11" > "v2000_11_11"
500
-
501
- # This cache is not here for speeding things up. It's for preventing the creation of copies of the same object
502
- # because such copies could produce weird behaviors at runtime, especially if you/fastapi do any comparisons.
503
- # It's defined here and not on the method because of this: https://youtu.be/sVjtp6tGo0g
504
- self.change_versions_of_a_non_container_annotation = functools.cache(
505
- self._change_versions_of_a_non_container_annotation,
506
- )
507
-
508
- def migrate_router_to_version(self, router: fastapi.routing.APIRouter, version: Version):
509
- version_dir = get_version_dir_path(self.head_schemas_package, version.value)
510
- if not version_dir.is_dir():
511
- raise RouterGenerationError(
512
- f"Versioned schema directory '{version_dir}' does not exist.",
513
- )
514
- for route in router.routes:
515
- if not isinstance(route, fastapi.routing.APIRoute):
516
- continue
517
- self.migrate_route_to_version(route, version_dir)
518
-
519
- def migrate_route_to_version(
520
- self,
521
- route: fastapi.routing.APIRoute,
522
- version_dir: Path,
523
- *,
524
- ignore_response_model: bool = False,
525
- ):
526
- if route.response_model is not None and not ignore_response_model:
527
- route.response_model = self._change_version_of_annotations(route.response_model, version_dir)
528
- route.response_field = fastapi.utils.create_model_field(
529
- name="Response_" + route.unique_id,
530
- type_=route.response_model,
531
- mode="serialization",
532
- )
533
- route.secure_cloned_response_field = fastapi.utils.create_cloned_field(route.response_field)
534
- route.dependencies = self._change_version_of_annotations(route.dependencies, version_dir)
535
- route.endpoint = self._change_version_of_annotations(route.endpoint, version_dir)
536
- for callback in route.callbacks or []:
537
- if not isinstance(callback, APIRoute):
538
- continue
539
- self.migrate_route_to_version(callback, version_dir, ignore_response_model=ignore_response_model)
540
- _remake_endpoint_dependencies(route)
541
-
542
- def _change_versions_of_a_non_container_annotation(self, annotation: Any, version_dir: Path) -> Any:
543
- if isinstance(annotation, _BaseGenericAlias | GenericAlias):
544
- return get_origin(annotation)[
545
- tuple(self._change_version_of_annotations(arg, version_dir) for arg in get_args(annotation))
546
- ]
547
- elif isinstance(annotation, Depends):
548
- return Depends(
549
- self._change_version_of_annotations(annotation.dependency, version_dir),
550
- use_cache=annotation.use_cache,
551
- )
552
- elif isinstance(annotation, UnionType):
553
- getitem = typing.Union.__getitem__ # pyright: ignore[reportAttributeAccessIssue]
554
- return getitem(
555
- tuple(self._change_version_of_annotations(a, version_dir) for a in get_args(annotation)),
556
- )
557
- elif annotation is typing.Any or isinstance(annotation, typing.NewType):
558
- return annotation
559
- elif isinstance(annotation, type):
560
- if annotation.__module__ == "pydantic.main" and issubclass(annotation, BaseModel):
561
- return create_body_model(
562
- fields=self._change_version_of_annotations(model_fields(annotation), version_dir).values(),
563
- model_name=annotation.__name__,
564
- )
565
- return self._change_version_of_type(annotation, version_dir)
566
- elif callable(annotation):
567
- if type(annotation).__module__.startswith(
568
- ("fastapi.", "pydantic.", "pydantic_core.", "starlette.")
569
- ) or isinstance(annotation, fastapi.params.Security | fastapi.security.base.SecurityBase):
570
- return annotation
571
-
572
- def modifier(annotation: Any):
573
- return self._change_version_of_annotations(annotation, version_dir)
574
-
575
- return _modify_callable_annotations(
576
- annotation,
577
- modifier,
578
- modifier,
579
- annotation_modifying_wrapper_factory=_copy_function_through_class_based_wrapper,
580
- )
581
- else:
582
- return annotation
583
-
584
- def _change_version_of_annotations(self, annotation: Any, version_dir: Path) -> Any:
585
- """Recursively go through all annotations and if they were taken from any versioned package, change them to the
586
- annotations corresponding to the version_dir passed.
587
-
588
- So if we had a annotation "UserResponse" from "head" version, and we passed version_dir of "v1_0_1", it would
589
- replace "UserResponse" with the the same class but from the "v1_0_1" version.
590
-
591
- """
592
- if isinstance(annotation, dict):
593
- return {
594
- self._change_version_of_annotations(key, version_dir): self._change_version_of_annotations(
595
- value,
596
- version_dir,
597
- )
598
- for key, value in annotation.items()
599
- }
600
-
601
- elif isinstance(annotation, list | tuple):
602
- return type(annotation)(self._change_version_of_annotations(v, version_dir) for v in annotation)
603
- else:
604
- return self.change_versions_of_a_non_container_annotation(annotation, version_dir)
605
-
606
- def _change_version_of_type(self, annotation: type, version_dir: Path):
607
- if issubclass(annotation, BaseModel | Enum):
608
- if version_dir == self.latest_version_dir:
609
- source_file = inspect.getsourcefile(annotation)
610
- if source_file is None: # pragma: no cover # I am not even sure how to cover this
611
- warnings.warn(
612
- f'Failed to find where the type annotation "{annotation}" is located.'
613
- "Please, double check that it's located in the right directory",
614
- stacklevel=7,
615
- )
616
- else:
617
- self._validate_source_file_is_located_in_template_dir(annotation, source_file)
618
- return get_another_version_of_cls(annotation, version_dir, self.versions.versioned_directories_with_head)
619
- else:
620
- return annotation
621
-
622
- def _validate_source_file_is_located_in_template_dir(self, annotation: type, source_file: str):
623
- template_dir = str(self.head_version_dir)
624
- dir_with_versions = str(self.head_version_dir.parent)
625
- # So if it is somewhere close to version dirs (either within them or next to them),
626
- # but not located in "head",
627
- # but also not located in any other version dir
628
- if (
629
- source_file.startswith(dir_with_versions)
630
- and not source_file.startswith(template_dir)
631
- and any(source_file.startswith(str(d)) for d in self.versions.versioned_directories_with_head)
632
- ):
633
- raise RouterGenerationError(
634
- f'"{annotation}" is not defined in "{self.head_version_dir}" even though it must be. '
635
- f'It is defined in "{Path(source_file).parent}". '
636
- "It probably means that you used a specific version of the class in fastapi dependencies "
637
- 'or pydantic schemas instead of "head".',
638
- )
639
-
640
-
641
- def _modify_callable_annotations(
642
- call: _Call,
643
- modify_annotations: Callable[[dict[str, Any]], dict[str, Any]] = lambda a: a,
644
- modify_defaults: Callable[[tuple[Any, ...]], tuple[Any, ...]] = lambda a: a,
645
- *,
646
- annotation_modifying_wrapper_factory: Callable[[_Call], _Call],
647
- ) -> _Call:
648
- annotation_modifying_wrapper = annotation_modifying_wrapper_factory(call)
649
- old_params = inspect.signature(call).parameters
650
- callable_annotations = annotation_modifying_wrapper.__annotations__
651
- annotation_modifying_wrapper.__annotations__ = modify_annotations(callable_annotations)
652
- annotation_modifying_wrapper.__defaults__ = modify_defaults(
653
- tuple(p.default for p in old_params.values() if p.default is not inspect.Signature.empty),
654
- )
655
- annotation_modifying_wrapper.__signature__ = _generate_signature(
656
- annotation_modifying_wrapper,
657
- old_params,
658
- )
659
-
660
- return annotation_modifying_wrapper
661
-
662
-
663
- def _remake_endpoint_dependencies(route: fastapi.routing.APIRoute):
664
- # Unlike get_dependant, APIRoute is the public API of FastAPI and it's (almost) guaranteed to be stable.
665
-
666
- route_copy = fastapi.routing.APIRoute(route.path, route.endpoint, dependencies=route.dependencies)
667
- route.dependant = route_copy.dependant
668
- route.body_field = route_copy.body_field
669
- _add_request_and_response_params(route)
670
-
671
-
672
- def _add_request_and_response_params(route: APIRoute):
673
- if not route.dependant.request_param_name:
674
- route.dependant.request_param_name = _CADWYN_REQUEST_PARAM_NAME
675
- if not route.dependant.response_param_name:
676
- route.dependant.response_param_name = _CADWYN_RESPONSE_PARAM_NAME
677
-
678
-
679
361
  def _add_data_migrations_to_route(
680
362
  route: APIRoute,
681
363
  head_route: Any,
@@ -734,42 +416,6 @@ def _apply_endpoint_had_instruction(
734
416
  setattr(original_route, attr_name, attr)
735
417
 
736
418
 
737
- def _generate_signature(
738
- new_callable: Callable,
739
- old_params: MappingProxyType[str, inspect.Parameter],
740
- ):
741
- parameters = []
742
- default_counter = 0
743
- for param in old_params.values():
744
- if param.default is not inspect.Signature.empty:
745
- assert new_callable.__defaults__ is not None, ( # noqa: S101
746
- "Defaults cannot be None here. If it is, you have found a bug in Cadwyn. "
747
- "Please, report it in our issue tracker."
748
- )
749
- default = new_callable.__defaults__[default_counter]
750
- default_counter += 1
751
- else:
752
- default = inspect.Signature.empty
753
- parameters.append(
754
- inspect.Parameter(
755
- param.name,
756
- param.kind,
757
- default=default,
758
- annotation=new_callable.__annotations__.get(
759
- param.name,
760
- inspect.Signature.empty,
761
- ),
762
- ),
763
- )
764
- return inspect.Signature(
765
- parameters=parameters,
766
- return_annotation=new_callable.__annotations__.get(
767
- "return",
768
- inspect.Signature.empty,
769
- ),
770
- )
771
-
772
-
773
419
  def _get_routes(
774
420
  routes: Sequence[BaseRoute],
775
421
  endpoint_path: str,
@@ -802,80 +448,6 @@ def _get_route_from_func(
802
448
  return None
803
449
 
804
450
 
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
822
-
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)
865
- else:
866
- return _CallableWrapper(call)
867
-
868
-
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__
875
-
876
- return call
877
-
878
-
879
451
  def _route_has_a_simple_body_schema(route: APIRoute) -> bool:
880
452
  # Remember this: if len(body_params) == 1, then route.body_schema == route.dependant.body_params[0]
881
453
  return len(route.dependant.body_params) == 1
cadwyn/routing.py CHANGED
@@ -14,13 +14,10 @@ from starlette.types import Receive, Scope, Send
14
14
 
15
15
  from cadwyn._utils import same_definition_as_in
16
16
 
17
- from .route_generation import (
18
- InternalRepresentationOf, # pyright: ignore[reportDeprecated]
19
- generate_versioned_routers,
20
- )
17
+ from .route_generation import generate_versioned_routers
21
18
 
22
19
  # TODO: Remove this in a major version. This is only here for backwards compatibility
23
- __all__ = ["InternalRepresentationOf", "generate_versioned_routers"]
20
+ __all__ = ["generate_versioned_routers"]
24
21
 
25
22
  _logger = getLogger(__name__)
26
23