cadwyn 3.15.1__tar.gz → 3.15.3__tar.gz

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.

Files changed (37) hide show
  1. {cadwyn-3.15.1 → cadwyn-3.15.3}/PKG-INFO +1 -1
  2. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/applications.py +108 -13
  3. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/routing.py +37 -5
  4. {cadwyn-3.15.1 → cadwyn-3.15.3}/pyproject.toml +1 -1
  5. {cadwyn-3.15.1 → cadwyn-3.15.3}/LICENSE +0 -0
  6. {cadwyn-3.15.1 → cadwyn-3.15.3}/README.md +0 -0
  7. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/__init__.py +0 -0
  8. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/__main__.py +0 -0
  9. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/_asts.py +0 -0
  10. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/_compat.py +0 -0
  11. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/_package_utils.py +0 -0
  12. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/_utils.py +0 -0
  13. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/codegen/README.md +0 -0
  14. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/codegen/__init__.py +0 -0
  15. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/codegen/_common.py +0 -0
  16. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/codegen/_main.py +0 -0
  17. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/codegen/_plugins/__init__.py +0 -0
  18. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/codegen/_plugins/class_migrations.py +0 -0
  19. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/codegen/_plugins/class_rebuilding.py +0 -0
  20. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/codegen/_plugins/class_renaming.py +0 -0
  21. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/codegen/_plugins/import_auto_adding.py +0 -0
  22. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/codegen/_plugins/module_migrations.py +0 -0
  23. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/exceptions.py +0 -0
  24. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/main.py +0 -0
  25. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/middleware.py +0 -0
  26. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/py.typed +0 -0
  27. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/route_generation.py +0 -0
  28. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/static/__init__.py +0 -0
  29. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/static/docs.html +0 -0
  30. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/structure/__init__.py +0 -0
  31. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/structure/common.py +0 -0
  32. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/structure/data.py +0 -0
  33. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/structure/endpoints.py +0 -0
  34. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/structure/enums.py +0 -0
  35. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/structure/modules.py +0 -0
  36. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/structure/schemas.py +0 -0
  37. {cadwyn-3.15.1 → cadwyn-3.15.3}/cadwyn/structure/versions.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cadwyn
3
- Version: 3.15.1
3
+ Version: 3.15.3
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
@@ -24,6 +24,7 @@ from starlette.routing import BaseRoute, Route
24
24
  from starlette.types import Lifespan
25
25
  from typing_extensions import Self, deprecated
26
26
 
27
+ from cadwyn._utils import same_definition_as_in
27
28
  from cadwyn.middleware import HeaderVersioningMiddleware, _get_api_version_dependency
28
29
  from cadwyn.route_generation import generate_versioned_routers
29
30
  from cadwyn.routing import _RootHeaderAPIRouter
@@ -58,8 +59,13 @@ class Cadwyn(FastAPI):
58
59
  swagger_ui_oauth2_redirect_url: str | None = "/docs/oauth2-redirect",
59
60
  swagger_ui_init_oauth: dict[str, Any] | None = None,
60
61
  middleware: Sequence[Middleware] | None = None,
61
- exception_handlers: dict[int | type[Exception], Callable[[Request, Any], Coroutine[Any, Any, Response]]]
62
- | None = None,
62
+ exception_handlers: (
63
+ dict[
64
+ int | type[Exception],
65
+ Callable[[Request, Any], Coroutine[Any, Any, Response]],
66
+ ]
67
+ | None
68
+ ) = None,
63
69
  on_startup: Sequence[Callable[[], Any]] | None = None,
64
70
  on_shutdown: Sequence[Callable[[], Any]] | None = None,
65
71
  lifespan: Lifespan[Self] | None = None,
@@ -75,7 +81,9 @@ class Cadwyn(FastAPI):
75
81
  deprecated: bool | None = None,
76
82
  include_in_schema: bool = True,
77
83
  swagger_ui_parameters: dict[str, Any] | None = None,
78
- generate_unique_id_function: Callable[[routing.APIRoute], str] = Default(generate_unique_id), # noqa: B008
84
+ generate_unique_id_function: Callable[[routing.APIRoute], str] = Default( # noqa: B008
85
+ generate_unique_id
86
+ ),
79
87
  separate_input_output_schemas: bool = True,
80
88
  **extra: Any,
81
89
  ) -> None:
@@ -143,8 +151,6 @@ class Cadwyn(FastAPI):
143
151
  api_version_var=self.versions.api_version_var,
144
152
  )
145
153
 
146
- self.add_event_handler("startup", self.enrich_swagger)
147
-
148
154
  self.docs_url = docs_url
149
155
  self.redoc_url = redoc_url
150
156
  self.openapi_url = openapi_url
@@ -229,7 +235,7 @@ class Cadwyn(FastAPI):
229
235
  terms_of_service=self.terms_of_service,
230
236
  contact=self.contact,
231
237
  license_info=self.license_info,
232
- routes=self.router.routes,
238
+ routes=self.router.unversioned_routes,
233
239
  tags=self.openapi_tags,
234
240
  servers=self.servers,
235
241
  )
@@ -317,29 +323,118 @@ class Cadwyn(FastAPI):
317
323
  except ValueError as e:
318
324
  raise ValueError("header_value should be in ISO 8601 format") from e
319
325
 
326
+ added_routes: list[BaseRoute] = []
320
327
  if header_value_as_dt not in self.router.versioned_routers: # pragma: no branch
321
328
  self.router.versioned_routers[header_value_as_dt] = APIRouter(**self._kwargs_to_router)
322
- if self.openapi_url is not None: # pragma: no branch
323
- self.router.versioned_routers[header_value_as_dt].add_route(
324
- path=self.openapi_url,
325
- endpoint=self.openapi_jsons,
326
- include_in_schema=False,
327
- )
328
329
 
329
- added_routes: list[BaseRoute] = []
330
+ versioned_router = self.router.versioned_routers[header_value_as_dt]
331
+ if self.openapi_url is not None: # pragma: no branch
332
+ versioned_router.add_route(
333
+ path=self.openapi_url,
334
+ endpoint=self.openapi_jsons,
335
+ include_in_schema=False,
336
+ )
337
+ added_routes.append(versioned_router.routes[-1])
338
+
339
+ added_route_count = 0
330
340
  for router in (first_router, *other_routers):
331
341
  self.router.versioned_routers[header_value_as_dt].include_router(
332
342
  router,
333
343
  dependencies=[Depends(_get_api_version_dependency(self.router.api_version_header_name, header_value))],
334
344
  )
345
+ added_route_count += len(router.routes)
346
+
347
+ added_routes.extend(versioned_router.routes[-added_route_count:])
348
+ self.router.routes.extend(added_routes)
335
349
 
350
+ self.enrich_swagger()
336
351
  return added_routes
337
352
 
338
353
  def add_unversioned_routers(self, *routers: APIRouter):
339
354
  for router in routers:
340
355
  self.router.include_router(router)
356
+ self.enrich_swagger()
341
357
 
342
358
  @deprecated("Use add add_unversioned_routers instead")
343
359
  def add_unversioned_routes(self, *routes: Route):
344
360
  router = APIRouter(routes=list(routes))
345
361
  self.include_router(router)
362
+ self.enrich_swagger()
363
+
364
+ @same_definition_as_in(FastAPI.include_router)
365
+ def include_router(self, *args: Any, **kwargs: Any):
366
+ route = super().include_router(*args, **kwargs)
367
+ self.enrich_swagger()
368
+ return route
369
+
370
+ @same_definition_as_in(FastAPI.post)
371
+ def post(self, *args: Any, **kwargs: Any):
372
+ route = super().post(*args, **kwargs)
373
+ self.enrich_swagger()
374
+ return route
375
+
376
+ @same_definition_as_in(FastAPI.get)
377
+ def get(self, *args: Any, **kwargs: Any):
378
+ route = super().get(*args, **kwargs)
379
+ self.enrich_swagger()
380
+ return route
381
+
382
+ @same_definition_as_in(FastAPI.patch)
383
+ def patch(self, *args: Any, **kwargs: Any):
384
+ route = super().patch(*args, **kwargs)
385
+ self.enrich_swagger()
386
+ return route
387
+
388
+ @same_definition_as_in(FastAPI.delete)
389
+ def delete(self, *args: Any, **kwargs: Any):
390
+ route = super().delete(*args, **kwargs)
391
+ self.enrich_swagger()
392
+ return route
393
+
394
+ @same_definition_as_in(FastAPI.put)
395
+ def put(self, *args: Any, **kwargs: Any):
396
+ route = super().put(*args, **kwargs)
397
+ self.enrich_swagger()
398
+ return route
399
+
400
+ @same_definition_as_in(FastAPI.trace)
401
+ def trace(self, *args: Any, **kwargs: Any): # pragma: no cover
402
+ route = super().trace(*args, **kwargs)
403
+ self.enrich_swagger()
404
+ return route
405
+
406
+ @same_definition_as_in(FastAPI.options)
407
+ def options(self, *args: Any, **kwargs: Any):
408
+ route = super().options(*args, **kwargs)
409
+ self.enrich_swagger()
410
+ return route
411
+
412
+ @same_definition_as_in(FastAPI.head)
413
+ def head(self, *args: Any, **kwargs: Any):
414
+ route = super().head(*args, **kwargs)
415
+ self.enrich_swagger()
416
+ return route
417
+
418
+ @same_definition_as_in(FastAPI.add_api_route)
419
+ def add_api_route(self, *args: Any, **kwargs: Any):
420
+ route = super().add_api_route(*args, **kwargs)
421
+ self.enrich_swagger()
422
+ return route
423
+
424
+ @same_definition_as_in(FastAPI.api_route)
425
+ def api_route(self, *args: Any, **kwargs: Any):
426
+ route = super().api_route(*args, **kwargs)
427
+ self.enrich_swagger()
428
+ return route
429
+
430
+ @same_definition_as_in(FastAPI.add_api_websocket_route)
431
+ def add_api_websocket_route(self, *args: Any, **kwargs: Any): # pragma: no cover
432
+ route = super().add_api_websocket_route(*args, **kwargs)
433
+ self.enrich_swagger()
434
+ return route
435
+
436
+ @same_definition_as_in(FastAPI.websocket)
437
+ def websocket(self, *args: Any, **kwargs: Any): # pragma: no cover
438
+ route = super().websocket(*args, **kwargs)
439
+ self.enrich_swagger()
440
+ return route
@@ -9,10 +9,15 @@ from typing import Any
9
9
  from fastapi.routing import APIRouter
10
10
  from starlette.datastructures import URL
11
11
  from starlette.responses import RedirectResponse
12
- from starlette.routing import BaseRoute, Match
12
+ from starlette.routing import BaseRoute, Match, Route
13
13
  from starlette.types import Receive, Scope, Send
14
14
 
15
- from .route_generation import InternalRepresentationOf, generate_versioned_routers # pyright: ignore[reportDeprecated]
15
+ from cadwyn._utils import same_definition_as_in
16
+
17
+ from .route_generation import (
18
+ InternalRepresentationOf,
19
+ generate_versioned_routers,
20
+ )
16
21
 
17
22
  # TODO: Remove this in a major version. This is only here for backwards compatibility
18
23
  __all__ = ["InternalRepresentationOf", "generate_versioned_routers"]
@@ -46,6 +51,7 @@ class _RootHeaderAPIRouter(APIRouter):
46
51
  self.versioned_routers: dict[date, APIRouter] = {}
47
52
  self.api_version_header_name = api_version_header_name.lower()
48
53
  self.api_version_var = api_version_var
54
+ self.unversioned_routes: list[Route] = []
49
55
 
50
56
  @cached_property
51
57
  def sorted_versions(self):
@@ -68,13 +74,19 @@ class _RootHeaderAPIRouter(APIRouter):
68
74
  # then the request version is older that the oldest route we have
69
75
  _logger.info(
70
76
  "Request version is older than the oldest version. No route can match this version",
71
- extra={"oldest_version": self.min_routes_version.isoformat(), "request_version": request_version},
77
+ extra={
78
+ "oldest_version": self.min_routes_version.isoformat(),
79
+ "request_version": request_version,
80
+ },
72
81
  )
73
82
  return []
74
83
  version_chosen = self.find_closest_date_but_not_new(request_header_value)
75
84
  _logger.info(
76
85
  "Partial match. The endpoint with a lower version was selected for the API call",
77
- extra={"version_chosen": version_chosen, "request_version": request_version},
86
+ extra={
87
+ "version_chosen": version_chosen,
88
+ "request_version": request_version,
89
+ },
78
90
  )
79
91
  return self.versioned_routers[version_chosen].routes
80
92
 
@@ -94,13 +106,33 @@ class _RootHeaderAPIRouter(APIRouter):
94
106
  # if header_value is None, then it's an unversioned request and we need to use the unversioned routes
95
107
  # if there will be a value, we search for the most suitable version
96
108
  if not header_value:
97
- routes = self.routes
109
+ routes = self.unversioned_routes
98
110
  elif header_value in self.versioned_routers:
99
111
  routes = self.versioned_routers[header_value].routes
100
112
  else:
101
113
  routes = self.pick_version(request_header_value=header_value)
102
114
  await self.process_request(scope=scope, receive=receive, send=send, routes=routes)
103
115
 
116
+ @same_definition_as_in(APIRouter.add_api_route)
117
+ def add_api_route(self, *args: Any, **kwargs: Any):
118
+ super().add_api_route(*args, **kwargs)
119
+ self.unversioned_routes.append(self.routes[-1])
120
+
121
+ @same_definition_as_in(APIRouter.add_route)
122
+ def add_route(self, *args: Any, **kwargs: Any):
123
+ super().add_route(*args, **kwargs)
124
+ self.unversioned_routes.append(self.routes[-1])
125
+
126
+ @same_definition_as_in(APIRouter.add_api_websocket_route)
127
+ def add_api_websocket_route(self, *args: Any, **kwargs: Any): # pragma: no cover
128
+ super().add_api_websocket_route(*args, **kwargs)
129
+ self.unversioned_routes.append(self.routes[-1])
130
+
131
+ @same_definition_as_in(APIRouter.add_websocket_route)
132
+ def add_websocket_route(self, *args: Any, **kwargs: Any): # pragma: no cover
133
+ super().add_websocket_route(*args, **kwargs)
134
+ self.unversioned_routes.append(self.routes[-1])
135
+
104
136
  async def process_request(self, scope: Scope, receive: Receive, send: Send, routes: Sequence[BaseRoute]) -> None:
105
137
  """
106
138
  its a copy-paste from starlette.routing.Router
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "cadwyn"
3
- version = "3.15.1"
3
+ version = "3.15.3"
4
4
  description = "Production-ready community-driven modern Stripe-like API versioning in FastAPI"
5
5
  authors = ["Stanislav Zmiev <zmievsa@gmail.com>"]
6
6
  license = "MIT"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes