cadwyn 5.2.0__py3-none-any.whl → 5.2.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.

File without changes
@@ -0,0 +1,9 @@
1
+ from contextvars import ContextVar
2
+
3
+ from typing_extensions import Literal
4
+
5
+ DEFAULT_API_VERSION_VAR: "ContextVar[str | None]" = ContextVar("cadwyn_default_api_version")
6
+ CURRENT_DEPENDENCY_SOLVER_OPTIONS = Literal["cadwyn", "fastapi"]
7
+ CURRENT_DEPENDENCY_SOLVER_VAR: ContextVar[CURRENT_DEPENDENCY_SOLVER_OPTIONS] = ContextVar(
8
+ "cadwyn_dependencies_current_dependency_solver"
9
+ )
cadwyn/applications.py CHANGED
@@ -133,6 +133,12 @@ class Cadwyn(FastAPI):
133
133
  stacklevel=2,
134
134
  )
135
135
  api_version_parameter_name = api_version_header_name
136
+ if api_version_default_value is not None and api_version_location == "path":
137
+ raise CadwynStructureError(
138
+ "You tried to pass an api_version_default_value while putting the API version in Path. "
139
+ "This is not currently supported by Cadwyn. "
140
+ "Please, open an issue on our github if you'd like to have it."
141
+ )
136
142
 
137
143
  super().__init__(
138
144
  debug=debug,
@@ -231,8 +237,8 @@ class Cadwyn(FastAPI):
231
237
  versioning_middleware_class,
232
238
  api_version_parameter_name=api_version_parameter_name,
233
239
  api_version_manager=self._api_version_manager,
234
- api_version_default_value=api_version_default_value,
235
240
  api_version_var=self.versions.api_version_var,
241
+ api_version_default_value=api_version_default_value,
236
242
  )
237
243
  if self.api_version_format == "date" and (
238
244
  sorted(self.versions.versions, key=lambda v: v.value, reverse=True) != list(self.versions.versions)
cadwyn/dependencies.py CHANGED
@@ -1,5 +1,5 @@
1
- from cadwyn.structure.versions import _CURRENT_DEPENDENCY_SOLVER_OPTIONS, _CURRENT_DEPENDENCY_SOLVER_VAR
1
+ from cadwyn._internal.context_vars import CURRENT_DEPENDENCY_SOLVER_OPTIONS, CURRENT_DEPENDENCY_SOLVER_VAR
2
2
 
3
3
 
4
- async def current_dependency_solver() -> _CURRENT_DEPENDENCY_SOLVER_OPTIONS:
5
- return _CURRENT_DEPENDENCY_SOLVER_VAR.get("fastapi")
4
+ async def current_dependency_solver() -> CURRENT_DEPENDENCY_SOLVER_OPTIONS:
5
+ return CURRENT_DEPENDENCY_SOLVER_VAR.get("fastapi")
cadwyn/middleware.py CHANGED
@@ -7,10 +7,12 @@ from collections.abc import Awaitable, Callable
7
7
  from contextvars import ContextVar
8
8
  from typing import Annotated, Any, Literal, Protocol, Union
9
9
 
10
+ import fastapi
10
11
  from fastapi import Request
11
12
  from starlette.middleware.base import BaseHTTPMiddleware, DispatchFunction, RequestResponseEndpoint
12
13
  from starlette.types import ASGIApp
13
14
 
15
+ from cadwyn._internal.context_vars import DEFAULT_API_VERSION_VAR
14
16
  from cadwyn.structure.common import VersionType
15
17
 
16
18
 
@@ -69,6 +71,8 @@ def _generate_api_version_dependency(
69
71
  annotation=Annotated[
70
72
  validation_data_type, fastapi_depends_class(openapi_examples={"default": {"value": default_value}})
71
73
  ],
74
+ # Path-based parameters do not support a default value in FastAPI :(
75
+ default=default_value if fastapi_depends_class != fastapi.Path else inspect.Signature.empty,
72
76
  ),
73
77
  ],
74
78
  )
@@ -103,10 +107,11 @@ class VersionPickingMiddleware(BaseHTTPMiddleware):
103
107
  api_version = self._api_version_manager.get(request)
104
108
 
105
109
  if api_version is None:
106
- if callable(self.api_version_default_value): # pragma: no cover # TODO
110
+ if callable(self.api_version_default_value):
107
111
  api_version = await self.api_version_default_value(request)
108
112
  else:
109
113
  api_version = self.api_version_default_value
114
+ DEFAULT_API_VERSION_VAR.set(api_version)
110
115
 
111
116
  self.api_version_var.set(api_version)
112
117
  response = await call_next(request)
cadwyn/routing.py CHANGED
@@ -11,6 +11,7 @@ from starlette.responses import RedirectResponse
11
11
  from starlette.routing import BaseRoute, Match
12
12
  from starlette.types import Receive, Scope, Send
13
13
 
14
+ from cadwyn._internal.context_vars import DEFAULT_API_VERSION_VAR
14
15
  from cadwyn._utils import same_definition_as_in
15
16
  from cadwyn.middleware import APIVersionFormat
16
17
  from cadwyn.structure.common import VersionType
@@ -70,8 +71,8 @@ class _RootCadwynAPIRouter(APIRouter):
70
71
  if scope["type"] == "lifespan":
71
72
  await self.lifespan(scope, receive, send)
72
73
  return
73
-
74
74
  version = self.api_version_var.get(None)
75
+ default_version_that_was_picked = DEFAULT_API_VERSION_VAR.get(None)
75
76
 
76
77
  # if version is None, then it's an unversioned request and we need to use the unversioned routes
77
78
  # if there will be a value, we search for the most suitable version
@@ -81,6 +82,14 @@ class _RootCadwynAPIRouter(APIRouter):
81
82
  routes = self.versioned_routers[version].routes
82
83
  else:
83
84
  routes = await self._get_routes_from_closest_suitable_version(version)
85
+ if default_version_that_was_picked:
86
+ # We add unversioned routes to versioned routes because otherwise unversioned routes
87
+ # will be completely unavailable when a default version is passed. So routes such as
88
+ # /docs will not be accessible at all.
89
+
90
+ # We use this order because if versioned routes go first and there is a versioned route that is
91
+ # the same as an unversioned route -- the unversioned one becomes impossible to match.
92
+ routes = self.unversioned_routes + routes
84
93
  await self.process_request(scope=scope, receive=receive, send=send, routes=routes)
85
94
 
86
95
  @cached_property
@@ -24,8 +24,9 @@ from fastapi.routing import APIRoute, _prepare_response_content
24
24
  from pydantic import BaseModel
25
25
  from pydantic_core import PydanticUndefined
26
26
  from starlette._utils import is_async_callable
27
- from typing_extensions import Any, Literal, ParamSpec, TypeAlias, TypeVar, assert_never, deprecated, get_args
27
+ from typing_extensions import Any, ParamSpec, TypeAlias, TypeVar, assert_never, deprecated, get_args
28
28
 
29
+ from cadwyn._internal.context_vars import CURRENT_DEPENDENCY_SOLVER_VAR
29
30
  from cadwyn._utils import classproperty
30
31
  from cadwyn.exceptions import (
31
32
  CadwynError,
@@ -52,10 +53,6 @@ _CADWYN_REQUEST_PARAM_NAME = "cadwyn_request_param"
52
53
  _CADWYN_RESPONSE_PARAM_NAME = "cadwyn_response_param"
53
54
  _P = ParamSpec("_P")
54
55
  _R = TypeVar("_R")
55
- _CURRENT_DEPENDENCY_SOLVER_OPTIONS = Literal["cadwyn", "fastapi"]
56
- _CURRENT_DEPENDENCY_SOLVER_VAR: ContextVar[_CURRENT_DEPENDENCY_SOLVER_OPTIONS] = ContextVar(
57
- "cadwyn_dependencies_dry_run"
58
- )
59
56
 
60
57
  PossibleInstructions: TypeAlias = Union[
61
58
  AlterSchemaSubInstruction, AlterEndpointSubInstruction, AlterEnumSubInstruction, SchemaHadInstruction, staticmethod
@@ -387,7 +384,7 @@ class VersionBundle:
387
384
  request.scope["headers"] = tuple((key.encode(), value.encode()) for key, value in request_info.headers.items())
388
385
  del request._headers
389
386
  # This gives us the ability to tell the user whether cadwyn is running its dependencies or FastAPI
390
- _CURRENT_DEPENDENCY_SOLVER_VAR.set("cadwyn")
387
+ CURRENT_DEPENDENCY_SOLVER_VAR.set("cadwyn")
391
388
  # Remember this: if len(body_params) == 1, then route.body_schema == route.dependant.body_params[0]
392
389
  result = await solve_dependencies(
393
390
  request=request,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cadwyn
3
- Version: 5.2.0
3
+ Version: 5.2.1
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
@@ -4,15 +4,17 @@ cadwyn/_asts.py,sha256=QvqZmDdwH8U-Ocpj9vYR6MRrIANF8ugk9oZgAo76qLs,5223
4
4
  cadwyn/_importer.py,sha256=QV6HqODCG9K2oL4Vc15fAqL2-plMvUWw_cgaj4Ln4C8,1075
5
5
  cadwyn/_render.py,sha256=VAY2Twd_MfKaC8_X22AMIQd72_UHy87icdAbKL8hd90,5526
6
6
  cadwyn/_utils.py,sha256=q_mTtMKTNTDzqCza67XST-jaPSfuTgnFLmOe0dlGeYY,2295
7
- cadwyn/applications.py,sha256=YyESoMwQMq9jxqai6lNrsL0BAKxjTKW5lm3Se_U81j4,20391
7
+ cadwyn/applications.py,sha256=xE-r96qSIWji6-mrpRhQVGTLHEvayfMDi0cRaIVPkds,20777
8
8
  cadwyn/changelogs.py,sha256=aBTlsZ8PQpw9t4sSyezNTYDs6CMPtzIGulgAHA1ELPs,19622
9
- cadwyn/dependencies.py,sha256=9JihGg7MPbP0oBFRWebvIzhh6B-oYqd1EYNjB-OZqpw,241
9
+ cadwyn/dependencies.py,sha256=phUJ4Fy3UTegpOfDfHMjxsvASWo-1NQgwqphNnPdoVQ,241
10
10
  cadwyn/exceptions.py,sha256=8D1G7ewLrMF5jq7leald1g0ulcH9zQl8ufEK3b7HHAE,1749
11
- cadwyn/middleware.py,sha256=jtcysj66Fck_3EteK0zLJCOTMms3g6avi3U8lV-roQI,4316
11
+ cadwyn/middleware.py,sha256=jWhuISMEMo7kYLMJ8y4wzso1SNSpaW7Zkxocqi9MLEA,4617
12
12
  cadwyn/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  cadwyn/route_generation.py,sha256=Po9wC4IvLBgX6EodWH3JFzEO7j3ckoXmIKMO-ubCGcA,25194
14
- cadwyn/routing.py,sha256=EkV38cDQFAtR1M_fGWeq81lYaSPuDK4Pr8fjTTJVZvY,6912
14
+ cadwyn/routing.py,sha256=Ii6Qbgm9lGks1IAk-kDuIu_dX3FXsA77KSfGOFmLbgc,7604
15
15
  cadwyn/schema_generation.py,sha256=QK_G-G56S1brGTSpEGEE77tbifSi7IzwNK1oiVfMkPA,46850
16
+ cadwyn/_internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ cadwyn/_internal/context_vars.py,sha256=VcM8eAoSlvrIMFQhjZmjflV5o1yrPSEZZGkwOK4OSf4,378
16
18
  cadwyn/static/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
19
  cadwyn/static/docs.html,sha256=WNm5ANJVy51TcIUFOaqKf1Z8eF86CC85TTHPxACtkzw,3455
18
20
  cadwyn/structure/__init__.py,sha256=Wgvjdq3vfl9Yhe-BkcFGAMi_Co11YOfTmJQqgF5Gzx4,655
@@ -21,9 +23,9 @@ cadwyn/structure/data.py,sha256=NWURVnP_84VI2ugp9ppo0Ofyve3pVYjymF9K82Jh-SA,7791
21
23
  cadwyn/structure/endpoints.py,sha256=zUgzglNhBPnmWdJ03A8pFT4zPs_lj8nQ7c7Uo2d-ejU,6246
22
24
  cadwyn/structure/enums.py,sha256=4FCc9aniLE3VuWAVIacrNP_FWxTIUm9JkeeHA_zZdwQ,1254
23
25
  cadwyn/structure/schemas.py,sha256=O9yNw1OB0Qz-PvNB0FYlQm34gQsjyG2l9gg9x-RnJIY,10781
24
- cadwyn/structure/versions.py,sha256=eEF5FUrao3bYd-OiZeyG0fkozuThRqhW7sEzRNhdsfQ,34440
25
- cadwyn-5.2.0.dist-info/METADATA,sha256=Z5KXqBdEg4NRb7oopNkvUP7bDpHC_Y-UYEXnvydFhBU,4529
26
- cadwyn-5.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
27
- cadwyn-5.2.0.dist-info/entry_points.txt,sha256=mGX8wl-Xfhpr5M93SUmkykaqinUaYAvW9rtDSX54gx0,47
28
- cadwyn-5.2.0.dist-info/licenses/LICENSE,sha256=KeCWewiDQYpmSnzF-p_0YpoWiyDcUPaCuG8OWQs4ig4,1072
29
- cadwyn-5.2.0.dist-info/RECORD,,
26
+ cadwyn/structure/versions.py,sha256=XeaZo6fUjQ_u32JBSLDKcuHkWm_z3z2mB_4yTe_Hr84,34307
27
+ cadwyn-5.2.1.dist-info/METADATA,sha256=HE7pZOYLsS3B8aW5VA-e5_-W1OxAE0T933Zy_0kDWco,4529
28
+ cadwyn-5.2.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
29
+ cadwyn-5.2.1.dist-info/entry_points.txt,sha256=mGX8wl-Xfhpr5M93SUmkykaqinUaYAvW9rtDSX54gx0,47
30
+ cadwyn-5.2.1.dist-info/licenses/LICENSE,sha256=KeCWewiDQYpmSnzF-p_0YpoWiyDcUPaCuG8OWQs4ig4,1072
31
+ cadwyn-5.2.1.dist-info/RECORD,,
File without changes