cadwyn 4.2.2__tar.gz → 4.2.4__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 (27) hide show
  1. {cadwyn-4.2.2 → cadwyn-4.2.4}/PKG-INFO +3 -4
  2. {cadwyn-4.2.2 → cadwyn-4.2.4}/cadwyn/middleware.py +4 -4
  3. {cadwyn-4.2.2 → cadwyn-4.2.4}/cadwyn/route_generation.py +1 -0
  4. {cadwyn-4.2.2 → cadwyn-4.2.4}/cadwyn/schema_generation.py +1 -1
  5. {cadwyn-4.2.2 → cadwyn-4.2.4}/cadwyn/structure/versions.py +29 -8
  6. {cadwyn-4.2.2 → cadwyn-4.2.4}/pyproject.toml +21 -22
  7. {cadwyn-4.2.2 → cadwyn-4.2.4}/LICENSE +0 -0
  8. {cadwyn-4.2.2 → cadwyn-4.2.4}/README.md +0 -0
  9. {cadwyn-4.2.2 → cadwyn-4.2.4}/cadwyn/__init__.py +0 -0
  10. {cadwyn-4.2.2 → cadwyn-4.2.4}/cadwyn/__main__.py +0 -0
  11. {cadwyn-4.2.2 → cadwyn-4.2.4}/cadwyn/_asts.py +0 -0
  12. {cadwyn-4.2.2 → cadwyn-4.2.4}/cadwyn/_importer.py +0 -0
  13. {cadwyn-4.2.2 → cadwyn-4.2.4}/cadwyn/_render.py +0 -0
  14. {cadwyn-4.2.2 → cadwyn-4.2.4}/cadwyn/_utils.py +0 -0
  15. {cadwyn-4.2.2 → cadwyn-4.2.4}/cadwyn/applications.py +0 -0
  16. {cadwyn-4.2.2 → cadwyn-4.2.4}/cadwyn/changelogs.py +0 -0
  17. {cadwyn-4.2.2 → cadwyn-4.2.4}/cadwyn/exceptions.py +0 -0
  18. {cadwyn-4.2.2 → cadwyn-4.2.4}/cadwyn/py.typed +0 -0
  19. {cadwyn-4.2.2 → cadwyn-4.2.4}/cadwyn/routing.py +0 -0
  20. {cadwyn-4.2.2 → cadwyn-4.2.4}/cadwyn/static/__init__.py +0 -0
  21. {cadwyn-4.2.2 → cadwyn-4.2.4}/cadwyn/static/docs.html +0 -0
  22. {cadwyn-4.2.2 → cadwyn-4.2.4}/cadwyn/structure/__init__.py +0 -0
  23. {cadwyn-4.2.2 → cadwyn-4.2.4}/cadwyn/structure/common.py +0 -0
  24. {cadwyn-4.2.2 → cadwyn-4.2.4}/cadwyn/structure/data.py +0 -0
  25. {cadwyn-4.2.2 → cadwyn-4.2.4}/cadwyn/structure/endpoints.py +0 -0
  26. {cadwyn-4.2.2 → cadwyn-4.2.4}/cadwyn/structure/enums.py +0 -0
  27. {cadwyn-4.2.2 → cadwyn-4.2.4}/cadwyn/structure/schemas.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cadwyn
3
- Version: 4.2.2
3
+ Version: 4.2.4
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
@@ -31,14 +31,13 @@ Classifier: Topic :: Software Development :: Libraries
31
31
  Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
32
32
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
33
33
  Classifier: Typing :: Typed
34
- Provides-Extra: cli
35
34
  Requires-Dist: backports-strenum (>=1.3.1,<2.0.0) ; python_version < "3.11"
36
- Requires-Dist: fastapi (>=0.110.0)
35
+ Requires-Dist: fastapi[standard] (>=0.112.3)
37
36
  Requires-Dist: issubclass (>=0.1.2,<0.2.0)
38
37
  Requires-Dist: jinja2 (>=3.1.2)
39
38
  Requires-Dist: pydantic (>=2.0.0)
40
39
  Requires-Dist: starlette (>=0.30.0)
41
- Requires-Dist: typer (>=0.7.0) ; extra == "cli"
40
+ Requires-Dist: typer (>=0.7.0)
42
41
  Requires-Dist: typing-extensions
43
42
  Project-URL: Documentation, https://docs.cadwyn.dev
44
43
  Project-URL: Repository, https://github.com/zmievsa/cadwyn
@@ -64,11 +64,11 @@ class HeaderVersioningMiddleware(BaseHTTPMiddleware):
64
64
  request=request,
65
65
  dependant=self.version_header_validation_dependant,
66
66
  async_exit_stack=async_exit_stack,
67
+ embed_body_fields=False,
67
68
  )
68
- values, errors, *_ = solved_result
69
- if errors:
70
- return self.default_response_class(status_code=422, content=_normalize_errors(errors))
71
- api_version = cast(date, values[self.api_version_header_name.replace("-", "_")])
69
+ if solved_result.errors:
70
+ return self.default_response_class(status_code=422, content=_normalize_errors(solved_result.errors))
71
+ api_version = cast(date, solved_result.values[self.api_version_header_name.replace("-", "_")])
72
72
  self.api_version_var.set(api_version)
73
73
 
74
74
  response = await call_next(request)
@@ -376,6 +376,7 @@ def _add_data_migrations_to_route(
376
376
  head_route,
377
377
  dependant_for_request_migrations,
378
378
  request_param_name=route.dependant.request_param_name,
379
+ background_tasks_param_name=route.dependant.background_tasks_param_name,
379
380
  response_param_name=route.dependant.response_param_name,
380
381
  )(route.endpoint)
381
382
  route.dependant.call = route.endpoint
@@ -441,7 +441,7 @@ class _AnnotationTransformer:
441
441
  def migrate_route_to_version(self, route: fastapi.routing.APIRoute, *, ignore_response_model: bool = False):
442
442
  if route.response_model is not None and not ignore_response_model:
443
443
  route.response_model = self.change_version_of_annotation(route.response_model)
444
- route.response_field = fastapi.utils.create_response_field(
444
+ route.response_field = fastapi.utils.create_model_field(
445
445
  name="Response_" + route.unique_id,
446
446
  type_=route.response_model,
447
447
  mode="serialization",
@@ -10,7 +10,7 @@ from datetime import date
10
10
  from enum import Enum
11
11
  from typing import Any, ClassVar, ParamSpec, TypeAlias, TypeVar
12
12
 
13
- from fastapi import HTTPException, params
13
+ from fastapi import BackgroundTasks, HTTPException, params
14
14
  from fastapi import Request as FastapiRequest
15
15
  from fastapi import Response as FastapiResponse
16
16
  from fastapi._compat import ModelField, _normalize_errors
@@ -350,7 +350,10 @@ class VersionBundle:
350
350
  request_info: RequestInfo,
351
351
  current_version: VersionDate,
352
352
  head_route: APIRoute,
353
+ *,
353
354
  exit_stack: AsyncExitStack,
355
+ embed_body_fields: bool,
356
+ background_tasks: BackgroundTasks | None,
354
357
  ) -> dict[str, Any]:
355
358
  method = request.method
356
359
  for v in reversed(self.versions):
@@ -367,19 +370,21 @@ class VersionBundle:
367
370
  request.scope["headers"] = tuple((key.encode(), value.encode()) for key, value in request_info.headers.items())
368
371
  del request._headers
369
372
  # Remember this: if len(body_params) == 1, then route.body_schema == route.dependant.body_params[0]
370
- dependencies, errors, _, _, _ = await solve_dependencies(
373
+ result = await solve_dependencies(
371
374
  request=request,
372
375
  response=response,
373
376
  dependant=head_dependant,
374
377
  body=request_info.body,
375
378
  dependency_overrides_provider=head_route.dependency_overrides_provider,
376
379
  async_exit_stack=exit_stack,
380
+ embed_body_fields=embed_body_fields,
381
+ background_tasks=background_tasks,
377
382
  )
378
- if errors:
383
+ if result.errors:
379
384
  raise CadwynHeadRequestValidationError(
380
- _normalize_errors(errors), body=request_info.body, version=current_version
385
+ _normalize_errors(result.errors), body=request_info.body, version=current_version
381
386
  )
382
- return dependencies
387
+ return result.values
383
388
 
384
389
  def _migrate_response(
385
390
  self,
@@ -431,6 +436,7 @@ class VersionBundle:
431
436
  dependant_for_request_migrations: Dependant,
432
437
  *,
433
438
  request_param_name: str,
439
+ background_tasks_param_name: str | None,
434
440
  response_param_name: str,
435
441
  ) -> Callable[[Endpoint[_P, _R]], Endpoint[_P, _R]]:
436
442
  def wrapper(endpoint: Endpoint[_P, _R]) -> Endpoint[_P, _R]:
@@ -438,6 +444,10 @@ class VersionBundle:
438
444
  async def decorator(*args: Any, **kwargs: Any) -> _R:
439
445
  request_param: FastapiRequest = kwargs[request_param_name]
440
446
  response_param: FastapiResponse = kwargs[response_param_name]
447
+ background_tasks: BackgroundTasks | None = kwargs.get(
448
+ background_tasks_param_name, # pyright: ignore[reportArgumentType, reportCallIssue]
449
+ None,
450
+ )
441
451
  method = request_param.method
442
452
  response = Sentinel
443
453
  async with AsyncExitStack() as exit_stack:
@@ -452,7 +462,9 @@ class VersionBundle:
452
462
  response_param,
453
463
  route,
454
464
  head_route,
455
- exit_stack,
465
+ exit_stack=exit_stack,
466
+ embed_body_fields=route._embed_body_fields,
467
+ background_tasks=background_tasks,
456
468
  )
457
469
 
458
470
  response = await self._convert_endpoint_response_to_version(
@@ -524,10 +536,14 @@ class VersionBundle:
524
536
  if isinstance(response_or_response_body, StreamingResponse | FileResponse):
525
537
  body = None
526
538
  elif response_or_response_body.body:
527
- if isinstance(response_or_response_body, JSONResponse) or raised_exception is not None:
539
+ if (isinstance(response_or_response_body, JSONResponse) or raised_exception is not None) and isinstance(
540
+ response_or_response_body.body, str | bytes
541
+ ):
528
542
  body = json.loads(response_or_response_body.body)
529
- else:
543
+ elif isinstance(response_or_response_body.body, bytes):
530
544
  body = response_or_response_body.body.decode(response_or_response_body.charset)
545
+ else: # pragma: no cover # I don't see a good use case here yet
546
+ body = response_or_response_body.body
531
547
  else:
532
548
  body = None
533
549
  # TODO (https://github.com/zmievsa/cadwyn/issues/51): Only do this if there are migrations
@@ -614,7 +630,10 @@ class VersionBundle:
614
630
  response: FastapiResponse,
615
631
  route: APIRoute,
616
632
  head_route: APIRoute,
633
+ *,
617
634
  exit_stack: AsyncExitStack,
635
+ embed_body_fields: bool,
636
+ background_tasks: BackgroundTasks | None,
618
637
  ) -> dict[str, Any]:
619
638
  request: FastapiRequest = kwargs[request_param_name]
620
639
  if request_param_name == _CADWYN_REQUEST_PARAM_NAME:
@@ -655,6 +674,8 @@ class VersionBundle:
655
674
  api_version,
656
675
  head_route,
657
676
  exit_stack=exit_stack,
677
+ embed_body_fields=embed_body_fields,
678
+ background_tasks=background_tasks,
658
679
  )
659
680
  # Because we re-added it into our kwargs when we did solve_dependencies
660
681
  if _CADWYN_REQUEST_PARAM_NAME in new_kwargs:
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "cadwyn"
3
- version = "4.2.2"
3
+ version = "4.2.4"
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"
@@ -50,40 +50,45 @@ classifiers = [
50
50
 
51
51
 
52
52
  [tool.poetry.dependencies]
53
+ fastapi = { version = ">=0.112.3", extras = ["standard"] }
53
54
  python = "^3.10"
54
55
  typing-extensions = "*"
55
- fastapi = ">=0.110.0"
56
- starlette = ">=0.30.0" # Because of this: https://github.com/encode/starlette/pull/2191
56
+ starlette = ">=0.30.0"
57
57
  pydantic = ">=2.0.0"
58
- typer = { version = ">=0.7.0", optional = true }
58
+ typer = ">=0.7.0"
59
59
  jinja2 = ">=3.1.2"
60
60
  issubclass = "^0.1.2"
61
61
  backports-strenum = { version = "^1.3.1", python = "<3.11" }
62
62
 
63
- [tool.poetry.extras]
64
- cli = ["typer"]
65
-
66
-
67
63
  [tool.poetry.group.dev.dependencies]
68
- pytest = ">=7.2.1"
69
- pytest-cov = ">=4.0.0"
70
- uvicorn = "*"
71
64
  pdbpp = "^0.10.3"
65
+ python-multipart = ">=0.0.6"
66
+ better-devtools = "^0.13.3"
67
+ pytest-sugar = "^1.0.0"
68
+
69
+ [tool.poetry.group.tests.dependencies]
70
+ svcs = "^24.1.0"
72
71
  httpx = "*"
73
72
  pytest-fixture-classes = ">=1.0.3"
73
+ pytest = ">=7.2.1"
74
+ pytest-cov = ">=4.0.0"
74
75
  dirty-equals = ">=0.6.0"
76
+
77
+ [tool.poetry.group.docs.dependencies]
75
78
  mkdocs = ">=1.5.2"
76
79
  mkdocs-material = ">=9.3.1"
77
- python-multipart = ">=0.0.6"
78
80
  mkdocs-simple-hooks = ">=0.1.5"
79
- pytest-sugar = "^1.0.0"
80
- better-devtools = "^0.13.3"
81
- svcs = "^24.1.0"
81
+ mdx-include = "^1.4.2"
82
82
  mike = "^2.1.2"
83
83
 
84
84
  [tool.poetry.scripts]
85
85
  cadwyn = "cadwyn.__main__:app"
86
86
 
87
+ [tool.coverage.run]
88
+ data_file = "coverage/coverage"
89
+ parallel = true
90
+ branch = true
91
+
87
92
  [tool.coverage.report]
88
93
  skip_covered = true
89
94
  skip_empty = true
@@ -106,13 +111,7 @@ exclude_lines = [
106
111
  "__rich_repr__",
107
112
  "__repr__",
108
113
  ]
109
- omit = [
110
- "./docs/plugin.py",
111
- "./site/plugin.py",
112
- "./tests/_data/_temp/**/*",
113
- "tests/tutorial/data/**/*",
114
- "scripts/*.py",
115
- ]
114
+ omit = ["./docs/plugin.py", "./site/plugin.py", "scripts/*.py"]
116
115
 
117
116
  [tool.pyright]
118
117
  reportMissingImports = true
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
File without changes
File without changes
File without changes
File without changes