cadwyn 2.3.0rc0__py3-none-any.whl → 2.3.1__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/__init__.py CHANGED
@@ -1,11 +1,13 @@
1
1
  import importlib.metadata
2
2
 
3
3
  from .codegen import generate_code_for_versioned_packages
4
+ from .main import Cadwyn
4
5
  from .routing import VersionedAPIRouter, generate_versioned_routers
5
6
  from .structure import VersionBundle, internal_body_representation_of
6
7
 
7
8
  __version__ = importlib.metadata.version("cadwyn")
8
9
  __all__ = [
10
+ "Cadwyn",
9
11
  "VersionedAPIRouter",
10
12
  "generate_code_for_versioned_packages",
11
13
  "VersionBundle",
cadwyn/main.py CHANGED
@@ -18,7 +18,7 @@ from cadwyn.routing import generate_versioned_routers
18
18
  from cadwyn.structure import VersionBundle
19
19
 
20
20
 
21
- class _Cadwyn(HeaderRoutingFastAPI):
21
+ class Cadwyn(HeaderRoutingFastAPI):
22
22
  def __init__(
23
23
  self,
24
24
  *,
@@ -37,7 +37,7 @@ class _Cadwyn(HeaderRoutingFastAPI):
37
37
  default_response_class: type[Response] = Default(JSONResponse), # noqa: B008
38
38
  redirect_slashes: bool = True,
39
39
  docs_url: str | None = "/docs",
40
- redoc_url: str | None = "/redoc",
40
+ redoc_url: None = None,
41
41
  swagger_ui_oauth2_redirect_url: str | None = "/docs/oauth2-redirect",
42
42
  swagger_ui_init_oauth: dict[str, Any] | None = None,
43
43
  middleware: Sequence[Middleware] | None = None,
@@ -77,7 +77,6 @@ class _Cadwyn(HeaderRoutingFastAPI):
77
77
  default_response_class=default_response_class,
78
78
  redirect_slashes=redirect_slashes,
79
79
  docs_url=docs_url,
80
- redoc_url=redoc_url,
81
80
  swagger_ui_oauth2_redirect_url=swagger_ui_oauth2_redirect_url,
82
81
  swagger_ui_init_oauth=swagger_ui_init_oauth,
83
82
  middleware=middleware,
cadwyn/routing.py CHANGED
@@ -41,6 +41,7 @@ from starlette.routing import (
41
41
  request_response,
42
42
  )
43
43
  from typing_extensions import assert_never
44
+ from verselect.routing import VERSION_HEADER_FORMAT
44
45
 
45
46
  from cadwyn._utils import Sentinel, UnionType, get_another_version_of_module
46
47
  from cadwyn.codegen import _get_package_path_from_module, _get_version_dir_path
@@ -55,12 +56,18 @@ from cadwyn.structure.endpoints import (
55
56
  )
56
57
  from cadwyn.structure.versions import _CADWYN_REQUEST_PARAM_NAME, _CADWYN_RESPONSE_PARAM_NAME, VersionChange
57
58
 
59
+ __all__ = [
60
+ "generate_versioned_routers",
61
+ "VersionedAPIRouter",
62
+ "VERSION_HEADER_FORMAT",
63
+ ]
64
+
58
65
  _T = TypeVar("_T", bound=Callable[..., Any])
59
66
  _R = TypeVar("_R", bound=fastapi.routing.APIRouter)
60
67
  # This is a hack we do because we can't guarantee how the user will use the router.
61
68
  _DELETED_ROUTE_TAG = "_CADWYN_DELETED_ROUTE"
62
- EndpointPath: TypeAlias = str
63
- EndpointMethod: TypeAlias = str
69
+ _EndpointPath: TypeAlias = str
70
+ _EndpointMethod: TypeAlias = str
64
71
 
65
72
 
66
73
  @dataclass(slots=True, frozen=True, eq=True)
@@ -72,7 +79,7 @@ class _EndpointInfo:
72
79
  @dataclass(slots=True)
73
80
  class _RouterInfo(Generic[_R]):
74
81
  router: _R
75
- routes_with_migrated_requests: dict[EndpointPath, set[EndpointMethod]]
82
+ routes_with_migrated_requests: dict[_EndpointPath, set[_EndpointMethod]]
76
83
  route_bodies_with_migrated_requests: set[type[BaseModel]]
77
84
 
78
85
 
@@ -447,41 +454,23 @@ class _AnnotationTransformer:
447
454
  return self._change_version_of_type(annotation, version_dir)
448
455
  elif callable(annotation):
449
456
  # TASK: https://github.com/zmievsa/cadwyn/issues/48
450
- if inspect.iscoroutinefunction(annotation):
451
-
452
- @functools.wraps(annotation)
453
- async def new_callable( # pyright: ignore[reportGeneralTypeIssues]
454
- *args: Any,
455
- **kwargs: Any,
456
- ) -> Any:
457
- return await annotation(*args, **kwargs)
458
-
459
- else:
460
-
461
- @functools.wraps(annotation)
462
- def new_callable( # pyright: ignore[reportGeneralTypeIssues]
463
- *args: Any,
464
- **kwargs: Any,
465
- ) -> Any:
466
- return annotation(*args, **kwargs)
467
-
468
- # Otherwise it will have the same signature as __wrapped__
469
- new_callable.__alt_wrapped__ = new_callable.__wrapped__ # pyright: ignore[reportGeneralTypeIssues]
470
- del new_callable.__wrapped__
457
+ annotation_modifying_decorator = _copy_function(annotation)
471
458
  old_params = inspect.signature(annotation).parameters
472
- callable_annotations = new_callable.__annotations__
459
+ callable_annotations = annotation_modifying_decorator.__annotations__
473
460
 
474
- new_callable: Any = cast(Any, new_callable)
475
- new_callable.__annotations__ = self._change_version_of_annotations(
461
+ annotation_modifying_decorator.__annotations__ = self._change_version_of_annotations(
476
462
  callable_annotations,
477
463
  version_dir,
478
464
  )
479
- new_callable.__defaults__ = self._change_version_of_annotations(
465
+ annotation_modifying_decorator.__defaults__ = self._change_version_of_annotations(
480
466
  tuple(p.default for p in old_params.values() if p.default is not inspect.Signature.empty),
481
467
  version_dir,
482
468
  )
483
- new_callable.__signature__ = _generate_signature(new_callable, old_params)
484
- return new_callable
469
+ annotation_modifying_decorator.__signature__ = _generate_signature(
470
+ annotation_modifying_decorator,
471
+ old_params,
472
+ )
473
+ return annotation_modifying_decorator
485
474
  else:
486
475
  return annotation
487
476
 
@@ -673,7 +662,7 @@ def _get_route_from_func(
673
662
  return None
674
663
 
675
664
 
676
- def _get_migrated_routes_by_path(version: Version) -> dict[EndpointPath, set[EndpointMethod]]:
665
+ def _get_migrated_routes_by_path(version: Version) -> dict[_EndpointPath, set[_EndpointMethod]]:
677
666
  request_by_path_migration_instructions = [
678
667
  version_change.alter_request_by_path_instructions for version_change in version.version_changes
679
668
  ]
@@ -683,3 +672,34 @@ def _get_migrated_routes_by_path(version: Version) -> dict[EndpointPath, set[End
683
672
  for instruction in instruction_list:
684
673
  migrated_routes[path] |= instruction.methods
685
674
  return migrated_routes
675
+
676
+
677
+ def _copy_function(function: _T) -> _T:
678
+ while hasattr(function, "__alt_wrapped__"):
679
+ function = function.__alt_wrapped__
680
+
681
+ if inspect.iscoroutinefunction(function):
682
+
683
+ @functools.wraps(function)
684
+ async def annotation_modifying_decorator( # pyright: ignore[reportGeneralTypeIssues]
685
+ *args: Any,
686
+ **kwargs: Any,
687
+ ) -> Any:
688
+ return await function(*args, **kwargs)
689
+
690
+ else:
691
+
692
+ @functools.wraps(function)
693
+ def annotation_modifying_decorator(
694
+ *args: Any,
695
+ **kwargs: Any,
696
+ ) -> Any:
697
+ return function(*args, **kwargs)
698
+
699
+ # Otherwise it will have the same signature as __wrapped__ due to how inspect module works
700
+ annotation_modifying_decorator.__alt_wrapped__ = ( # pyright: ignore[reportGeneralTypeIssues]
701
+ annotation_modifying_decorator.__wrapped__
702
+ )
703
+ del annotation_modifying_decorator.__wrapped__
704
+
705
+ return cast(_T, annotation_modifying_decorator)
@@ -207,25 +207,27 @@ class Version:
207
207
  class VersionBundle:
208
208
  def __init__(
209
209
  self,
210
- *versions: Version,
210
+ latest_version: Version,
211
+ /,
212
+ *other_versions: Version,
211
213
  api_version_var: APIVersionVarType,
212
214
  ) -> None:
213
215
  super().__init__()
214
216
 
215
- self.versions = versions
217
+ self.versions = (latest_version, *other_versions)
216
218
  self.api_version_var = api_version_var
217
- if sorted(versions, key=lambda v: v.value, reverse=True) != list(versions):
218
- raise ValueError(
219
+ if sorted(self.versions, key=lambda v: v.value, reverse=True) != list(self.versions):
220
+ raise CadwynStructureError(
219
221
  "Versions are not sorted correctly. Please sort them in descending order.",
220
222
  )
221
- if versions[-1].version_changes:
223
+ if self.versions[-1].version_changes:
222
224
  raise CadwynStructureError(
223
- f'The first version "{versions[-1].value}" cannot have any version changes. '
225
+ f'The first version "{self.versions[-1].value}" cannot have any version changes. '
224
226
  "Version changes are defined to migrate to/from a previous version so you "
225
227
  "cannot define one for the very first version.",
226
228
  )
227
229
  version_values = set()
228
- for version in versions:
230
+ for version in self.versions:
229
231
  if version.value not in version_values:
230
232
  version_values.add(version.value)
231
233
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cadwyn
3
- Version: 2.3.0rc0
3
+ Version: 2.3.1
4
4
  Summary: Modern Stripe-like API versioning in FastAPI
5
5
  Home-page: https://github.com/zmievsa/cadwyn
6
6
  License: MIT
@@ -35,7 +35,7 @@ Requires-Dist: fastapi (>=0.96.1)
35
35
  Requires-Dist: pydantic (>=1.10.0,<2.0.0)
36
36
  Requires-Dist: typer (>=0.7.0); extra == "cli"
37
37
  Requires-Dist: typing-extensions
38
- Requires-Dist: verselect-zmievsa (>=0.0.4,<0.0.5)
38
+ Requires-Dist: verselect (>=0.0.6)
39
39
  Project-URL: Repository, https://github.com/zmievsa/cadwyn
40
40
  Description-Content-Type: text/markdown
41
41
 
@@ -73,10 +73,10 @@ Its [approach](./docs/theory.md#ii-migration-based-response-building) will be us
73
73
 
74
74
  The [documentation](https://docs.cadwyn.dev) has everything you need to get started. It is recommended to read it in the following order:
75
75
 
76
- 1. [Tutorial](https://docs.cadwyn.dev/tutorial/)
77
- 2. [Recipes](https://docs.cadwyn.dev/recipes/)
78
- 3. [Reference](https://docs.cadwyn.dev/reference/)
79
- 4. [Theory](https://docs.cadwyn.dev/theory/)
76
+ 1. [Tutorial](./tutorial.md)
77
+ 2. [Recipes](./recipes.md)
78
+ 3. [Reference](./reference.md)
79
+ 4. [Theory](./theory.md) <!-- TODO: Move section about cadwyn's approach to the beginning and move other approaches and "how we got here" to another article -->
80
80
 
81
81
  ## Similar projects
82
82
 
@@ -1,20 +1,20 @@
1
- cadwyn/__init__.py,sha256=u7VCcoKnq2ETd5tO-JNtOB6ZnC4_T_M6UfkMdFT4iY0,453
1
+ cadwyn/__init__.py,sha256=95dxX1XyN-i4pPn1Ji1E5TMnWe6olqm1MYutMiWTtWw,492
2
2
  cadwyn/__main__.py,sha256=aKAwxVnhqi3ATd1UsifoLA1t3udTzz56t0BRTlktX1A,2845
3
3
  cadwyn/_utils.py,sha256=Uh6No0FNY1AR2Z2E19fMPIU9_J4lbuG8XOQU2AlDIZw,3600
4
4
  cadwyn/codegen.py,sha256=oSivxLD_DmWbO179a9n4gHIoxuBJqDhP1OapjXv9DmE,38212
5
5
  cadwyn/exceptions.py,sha256=Utb6anOzrh97nOUgqCMmZHkQg8SFafLKSKO0EUPQ0yU,624
6
- cadwyn/main.py,sha256=KOWbpufWS6q2wSNsB7SR0D2kAqlUl4lqK5xZUychhfI,4938
6
+ cadwyn/main.py,sha256=ofTKT8LvOkFh5M19UgPNWLxf-3_yVOGNTVPnoADc3Q4,4894
7
7
  cadwyn/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- cadwyn/routing.py,sha256=ukleVy52PEFTxw49zWw7p7oWGxRcKgyQV3J3P-K9jaI,32614
8
+ cadwyn/routing.py,sha256=wk60HazI0CPEjNQ1cd0D0le1nzb1tkT4eKkuIP3ImeA,33083
9
9
  cadwyn/structure/__init__.py,sha256=2c6ivh7A-MQhhvovpsgiTsuk_ovYVVMWsqMqNC5QtuE,695
10
10
  cadwyn/structure/common.py,sha256=Nc0IP4L6z_HNVFrLaeepG2NY0en2ycZndXllpgoloNg,291
11
11
  cadwyn/structure/data.py,sha256=kzm3uf7VktH8nwShUk2hdfuuz0uYt9jzkRKMbWy0NtU,6564
12
12
  cadwyn/structure/endpoints.py,sha256=VngfAydGBwekhV2tBOtNDPVgl3X1IgYxUCw--VZ5cQY,5627
13
13
  cadwyn/structure/enums.py,sha256=iMokxA2QYJ61SzyB-Pmuq3y7KL7-e6TsnjLVUaVZQnw,954
14
14
  cadwyn/structure/schemas.py,sha256=wpGFXzoq6qrOulEk7I_69UslapG5iD5crFLQp-kXEFQ,5241
15
- cadwyn/structure/versions.py,sha256=HUk-o7n6CLdaHGmrsb4xw8CF-a-uaYBEIe4Ofo4xEoA,24064
16
- cadwyn-2.3.0rc0.dist-info/entry_points.txt,sha256=eO05hLn9GoRzzpwT9GONPmXKsonjuMNssM2D2WHWKGk,46
17
- cadwyn-2.3.0rc0.dist-info/LICENSE,sha256=KeCWewiDQYpmSnzF-p_0YpoWiyDcUPaCuG8OWQs4ig4,1072
18
- cadwyn-2.3.0rc0.dist-info/WHEEL,sha256=vxFmldFsRN_Hx10GDvsdv1wroKq8r5Lzvjp6GZ4OO8c,88
19
- cadwyn-2.3.0rc0.dist-info/METADATA,sha256=pdxTG-7_SMaLfQnbowVBgi3LzS-Q4GgH9bk2WDrJCvI,3806
20
- cadwyn-2.3.0rc0.dist-info/RECORD,,
15
+ cadwyn/structure/versions.py,sha256=8HNKFVoYXgHgWmvuFVBNkk5gghxWbWlLNC40e00Ba2k,24174
16
+ cadwyn-2.3.1.dist-info/entry_points.txt,sha256=eO05hLn9GoRzzpwT9GONPmXKsonjuMNssM2D2WHWKGk,46
17
+ cadwyn-2.3.1.dist-info/LICENSE,sha256=KeCWewiDQYpmSnzF-p_0YpoWiyDcUPaCuG8OWQs4ig4,1072
18
+ cadwyn-2.3.1.dist-info/WHEEL,sha256=vxFmldFsRN_Hx10GDvsdv1wroKq8r5Lzvjp6GZ4OO8c,88
19
+ cadwyn-2.3.1.dist-info/METADATA,sha256=ggtZVFietBQ_X3eLREzDgRNav3RBrYaCPZY6-gLLVqE,3845
20
+ cadwyn-2.3.1.dist-info/RECORD,,