cadwyn 5.1.4__tar.gz → 5.2.1__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 (158) hide show
  1. {cadwyn-5.1.4 → cadwyn-5.2.1}/CHANGELOG.md +12 -0
  2. {cadwyn-5.1.4 → cadwyn-5.2.1}/PKG-INFO +1 -2
  3. cadwyn-5.2.1/cadwyn/_internal/context_vars.py +9 -0
  4. {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/_render.py +1 -1
  5. {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/applications.py +7 -1
  6. cadwyn-5.2.1/cadwyn/dependencies.py +5 -0
  7. {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/middleware.py +6 -1
  8. {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/route_generation.py +1 -2
  9. {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/routing.py +10 -1
  10. {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/structure/schemas.py +1 -1
  11. {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/structure/versions.py +3 -6
  12. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/version_changes.md +2 -4
  13. {cadwyn-5.1.4 → cadwyn-5.2.1}/pyproject.toml +2 -2
  14. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_applications.py +13 -0
  15. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_routing.py +54 -1
  16. cadwyn-5.2.1/tests/versioning_styles/__init__.py +0 -0
  17. {cadwyn-5.1.4 → cadwyn-5.2.1}/uv.lock +1 -12
  18. cadwyn-5.1.4/cadwyn/dependencies.py +0 -5
  19. {cadwyn-5.1.4 → cadwyn-5.2.1}/.github/CODE_OF_CONDUCT.md +0 -0
  20. {cadwyn-5.1.4 → cadwyn-5.2.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  21. {cadwyn-5.1.4 → cadwyn-5.2.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  22. {cadwyn-5.1.4 → cadwyn-5.2.1}/.github/actions/setup-python-uv/action.yaml +0 -0
  23. {cadwyn-5.1.4 → cadwyn-5.2.1}/.github/workflows/ci.yaml +0 -0
  24. {cadwyn-5.1.4 → cadwyn-5.2.1}/.github/workflows/daily_tests.yaml +0 -0
  25. {cadwyn-5.1.4 → cadwyn-5.2.1}/.github/workflows/publish_docs.yaml +0 -0
  26. {cadwyn-5.1.4 → cadwyn-5.2.1}/.github/workflows/release.yaml +0 -0
  27. {cadwyn-5.1.4 → cadwyn-5.2.1}/.gitignore +0 -0
  28. {cadwyn-5.1.4 → cadwyn-5.2.1}/.pre-commit-config.yaml +0 -0
  29. {cadwyn-5.1.4 → cadwyn-5.2.1}/LICENSE +0 -0
  30. {cadwyn-5.1.4 → cadwyn-5.2.1}/Makefile +0 -0
  31. {cadwyn-5.1.4 → cadwyn-5.2.1}/README.md +0 -0
  32. {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/__init__.py +0 -0
  33. {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/__main__.py +0 -0
  34. {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/_asts.py +0 -0
  35. {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/_importer.py +0 -0
  36. {cadwyn-5.1.4/cadwyn/static → cadwyn-5.2.1/cadwyn/_internal}/__init__.py +0 -0
  37. {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/_utils.py +0 -0
  38. {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/changelogs.py +0 -0
  39. {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/exceptions.py +0 -0
  40. {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/py.typed +0 -0
  41. {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/schema_generation.py +0 -0
  42. {cadwyn-5.1.4/docs → cadwyn-5.2.1/cadwyn/static}/__init__.py +0 -0
  43. {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/static/docs.html +0 -0
  44. {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/structure/__init__.py +0 -0
  45. {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/structure/common.py +0 -0
  46. {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/structure/data.py +0 -0
  47. {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/structure/endpoints.py +0 -0
  48. {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/structure/enums.py +0 -0
  49. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/CNAME +0 -0
  50. {cadwyn-5.1.4/docs_src → cadwyn-5.2.1/docs}/__init__.py +0 -0
  51. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/api_version_parameter_and_context_variables.md +0 -0
  52. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/beware_of_data_versioning.md +0 -0
  53. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/changelogs.md +0 -0
  54. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/cli.md +0 -0
  55. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/endpoint_migrations.md +0 -0
  56. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/enum_migrations.md +0 -0
  57. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/index.md +0 -0
  58. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/main_app.md +0 -0
  59. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/methodology.md +0 -0
  60. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/schema_generation.md +0 -0
  61. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/schema_migrations.md +0 -0
  62. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/testing.md +0 -0
  63. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/where_to_put_the_version_and_how_to_format_it.md +0 -0
  64. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/home/CONTRIBUTING.md +0 -0
  65. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/how_to/change_business_logic/index.md +0 -0
  66. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/how_to/change_endpoints/index.md +0 -0
  67. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/how_to/change_openapi_schemas/add_field.md +0 -0
  68. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/how_to/change_openapi_schemas/change_field_type.md +0 -0
  69. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/how_to/change_openapi_schemas/change_schema_without_endpoint.md +0 -0
  70. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/how_to/change_openapi_schemas/changing_constraints.md +0 -0
  71. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/how_to/change_openapi_schemas/remove_field.md +0 -0
  72. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/how_to/change_openapi_schemas/rename_a_field_in_schema.md +0 -0
  73. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/how_to/index.md +0 -0
  74. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/how_to/version_with_paths_and_numbers_instead_of_headers_and_dates.md +0 -0
  75. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/img/dashboard_with_one_version.png +0 -0
  76. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/img/dashboard_with_two_versions.png +0 -0
  77. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/img/get_users_endpoint_from_prior_version.png +0 -0
  78. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/img/simplified_migration_model.png +0 -0
  79. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/img/sponsor_logos/monite.png +0 -0
  80. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/img/unversioned_dashboard.png +0 -0
  81. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/index.md +0 -0
  82. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/plugin.py +0 -0
  83. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/quickstart/setup.md +0 -0
  84. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/quickstart/tutorial.md +0 -0
  85. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/theory/how_to_build_versioning_framework.md +0 -0
  86. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/theory/how_we_got_here.md +0 -0
  87. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/theory/literature.md +0 -0
  88. {cadwyn-5.1.4/docs_src/how_to → cadwyn-5.2.1/docs_src}/__init__.py +0 -0
  89. {cadwyn-5.1.4/docs_src/how_to/change_openapi_schemas → cadwyn-5.2.1/docs_src/how_to}/__init__.py +0 -0
  90. {cadwyn-5.1.4/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint → cadwyn-5.2.1/docs_src/how_to/change_openapi_schemas}/__init__.py +0 -0
  91. {cadwyn-5.1.4/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/tests → cadwyn-5.2.1/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint}/__init__.py +0 -0
  92. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/block001.py +0 -0
  93. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/block002.py +0 -0
  94. {cadwyn-5.1.4/docs_src/how_to/version_with_path_and_numbers_instead_of_headers_and_dates → cadwyn-5.2.1/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/tests}/__init__.py +0 -0
  95. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/tests/test_block001.py +0 -0
  96. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/tests/test_block002.py +0 -0
  97. {cadwyn-5.1.4/docs_src/how_to/version_with_path_and_numbers_instead_of_headers_and_dates/tests → cadwyn-5.2.1/docs_src/how_to/version_with_path_and_numbers_instead_of_headers_and_dates}/__init__.py +0 -0
  98. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/how_to/version_with_path_and_numbers_instead_of_headers_and_dates/block001.py +0 -0
  99. {cadwyn-5.1.4/docs_src/quickstart → cadwyn-5.2.1/docs_src/how_to/version_with_path_and_numbers_instead_of_headers_and_dates/tests}/__init__.py +0 -0
  100. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/how_to/version_with_path_and_numbers_instead_of_headers_and_dates/tests/test_block_001.py +0 -0
  101. {cadwyn-5.1.4/docs_src/quickstart/setup → cadwyn-5.2.1/docs_src/quickstart}/__init__.py +0 -0
  102. {cadwyn-5.1.4/docs_src/quickstart/setup/tests → cadwyn-5.2.1/docs_src/quickstart/setup}/__init__.py +0 -0
  103. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/quickstart/setup/block001.sh +0 -0
  104. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/quickstart/setup/block002.py +0 -0
  105. {cadwyn-5.1.4/docs_src/quickstart/tutorial → cadwyn-5.2.1/docs_src/quickstart/setup/tests}/__init__.py +0 -0
  106. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/quickstart/setup/tests/test_block002.py +0 -0
  107. {cadwyn-5.1.4/docs_src/quickstart/tutorial/tests → cadwyn-5.2.1/docs_src/quickstart/tutorial}/__init__.py +0 -0
  108. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/quickstart/tutorial/block001.py +0 -0
  109. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/quickstart/tutorial/block002.py +0 -0
  110. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/quickstart/tutorial/block003.py +0 -0
  111. {cadwyn-5.1.4/tests/_data → cadwyn-5.2.1/docs_src/quickstart/tutorial/tests}/__init__.py +0 -0
  112. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/quickstart/tutorial/tests/test_block001.py +0 -0
  113. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/quickstart/tutorial/tests/test_block002.py +0 -0
  114. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/quickstart/tutorial/tests/test_block003.py +0 -0
  115. {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/ruff.toml +0 -0
  116. {cadwyn-5.1.4 → cadwyn-5.2.1}/mkdocs.yml +0 -0
  117. {cadwyn-5.1.4 → cadwyn-5.2.1}/ruff.toml +0 -0
  118. {cadwyn-5.1.4 → cadwyn-5.2.1}/scripts/fix_links.py +0 -0
  119. {cadwyn-5.1.4 → cadwyn-5.2.1}/scripts/split_md.py +0 -0
  120. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/__init__.py +0 -0
  121. {cadwyn-5.1.4/tests/_resources → cadwyn-5.2.1/tests/_data}/__init__.py +0 -0
  122. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/_data/unversioned_schema_dir/__init__.py +0 -0
  123. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/_data/unversioned_schema_dir/unversioned_schemas.py +0 -0
  124. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/_data/unversioned_schemas.py +0 -0
  125. {cadwyn-5.1.4/tests/_resources/render → cadwyn-5.2.1/tests/_resources}/__init__.py +0 -0
  126. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/_resources/app_for_testing_routing.py +0 -0
  127. {cadwyn-5.1.4/tests/_resources/render/complex → cadwyn-5.2.1/tests/_resources/render}/__init__.py +0 -0
  128. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/_resources/render/classes.py +0 -0
  129. {cadwyn-5.1.4/tests/_resources/versioned_app → cadwyn-5.2.1/tests/_resources/render/complex}/__init__.py +0 -0
  130. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/_resources/render/complex/classes.py +0 -0
  131. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/_resources/render/complex/versions.py +0 -0
  132. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/_resources/render/versions.py +0 -0
  133. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/_resources/utils.py +0 -0
  134. {cadwyn-5.1.4/tests/test_schema_generation → cadwyn-5.2.1/tests/_resources/versioned_app}/__init__.py +0 -0
  135. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/_resources/versioned_app/app.py +0 -0
  136. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/_resources/versioned_app/v2021_01_01.py +0 -0
  137. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/_resources/versioned_app/v2022_01_02.py +0 -0
  138. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/_resources/versioned_app/webhooks.py +0 -0
  139. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/conftest.py +0 -0
  140. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_auth_dependencies.py +0 -0
  141. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_changelog.py +0 -0
  142. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_cli.py +0 -0
  143. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_data_migrations.py +0 -0
  144. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_render.py +0 -0
  145. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_router_generation.py +0 -0
  146. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_router_generation_with_from_future_annotations.py +0 -0
  147. {cadwyn-5.1.4/tests/tutorial → cadwyn-5.2.1/tests/test_schema_generation}/__init__.py +0 -0
  148. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_schema_generation/test_enum.py +0 -0
  149. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_schema_generation/test_schema.py +0 -0
  150. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_schema_generation/test_schema_field.py +0 -0
  151. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_schema_generation/test_schema_validator.py +0 -0
  152. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_schema_generation/test_schema_with_future_annotations.py +0 -0
  153. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_structure.py +0 -0
  154. {cadwyn-5.1.4/tests/versioning_styles → cadwyn-5.2.1/tests/tutorial}/__init__.py +0 -0
  155. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/tutorial/main.py +0 -0
  156. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/tutorial/test_example.py +0 -0
  157. {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/versioning_styles/test_versioning_formats.py +0 -0
  158. {cadwyn-5.1.4 → cadwyn-5.2.1}/tox.ini +0 -0
@@ -5,6 +5,18 @@ Please follow [the Keep a Changelog standard](https://keepachangelog.com/en/1.0.
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [5.2.1]
9
+
10
+ ### Fixed
11
+
12
+ * [#268](https://github.com/zmievsa/cadwyn/issues/268) A bug where we received 404 for all unversioned routes when a default api version was passed to Cadwyn at initialization
13
+
14
+ ## [5.2.0]
15
+
16
+ ### Removed
17
+
18
+ * `issubclass` dependency
19
+
8
20
  ## [5.1.4]
9
21
 
10
22
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cadwyn
3
- Version: 5.1.4
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
@@ -36,7 +36,6 @@ Classifier: Typing :: Typed
36
36
  Requires-Python: >=3.9
37
37
  Requires-Dist: backports-strenum<2,>=1.3.1; python_version < '3.11'
38
38
  Requires-Dist: fastapi>=0.112.4
39
- Requires-Dist: issubclass>=0.1.2
40
39
  Requires-Dist: jinja2>=3.1.2
41
40
  Requires-Dist: pydantic>=2.0.0
42
41
  Requires-Dist: starlette>=0.30.0
@@ -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
+ )
@@ -5,10 +5,10 @@ from enum import Enum
5
5
  from typing import TYPE_CHECKING, Union
6
6
 
7
7
  import typer
8
- from issubclass import issubclass as lenient_issubclass
9
8
  from pydantic import BaseModel
10
9
 
11
10
  from cadwyn._asts import get_fancy_repr, pop_docstring_from_cls_body
11
+ from cadwyn._utils import lenient_issubclass
12
12
  from cadwyn.exceptions import CadwynRenderError
13
13
  from cadwyn.schema_generation import (
14
14
  PydanticFieldWrapper,
@@ -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)
@@ -0,0 +1,5 @@
1
+ from cadwyn._internal.context_vars import CURRENT_DEPENDENCY_SOLVER_OPTIONS, CURRENT_DEPENDENCY_SOLVER_VAR
2
+
3
+
4
+ async def current_dependency_solver() -> CURRENT_DEPENDENCY_SOLVER_OPTIONS:
5
+ return CURRENT_DEPENDENCY_SOLVER_VAR.get("fastapi")
@@ -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)
@@ -17,12 +17,11 @@ import fastapi.security.base
17
17
  import fastapi.utils
18
18
  from fastapi import APIRouter
19
19
  from fastapi.routing import APIRoute
20
- from issubclass import issubclass as lenient_issubclass
21
20
  from pydantic import BaseModel
22
21
  from starlette.routing import BaseRoute
23
22
  from typing_extensions import TypeVar, assert_never
24
23
 
25
- from cadwyn._utils import DATACLASS_SLOTS, Sentinel
24
+ from cadwyn._utils import DATACLASS_SLOTS, Sentinel, lenient_issubclass
26
25
  from cadwyn.exceptions import (
27
26
  CadwynError,
28
27
  RouteAlreadyExistsError,
@@ -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
@@ -2,7 +2,6 @@ from collections.abc import Callable
2
2
  from dataclasses import dataclass
3
3
  from typing import TYPE_CHECKING, Any, Literal, Union, cast
4
4
 
5
- from issubclass import issubclass as lenient_issubclass
6
5
  from pydantic import AliasChoices, AliasPath, BaseModel, Field
7
6
  from pydantic._internal._decorators import PydanticDescriptorProxy, unwrap_wrapped_function
8
7
  from pydantic.fields import FieldInfo
@@ -12,6 +11,7 @@ from cadwyn._utils import (
12
11
  Sentinel,
13
12
  fully_unwrap_decorator,
14
13
  get_name_of_function_wrapped_in_pydantic_validator,
14
+ lenient_issubclass,
15
15
  )
16
16
  from cadwyn.exceptions import CadwynStructureError
17
17
 
@@ -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,
@@ -39,8 +39,7 @@ from .v2023_02_10 import RemoveTaxIDEndpoints
39
39
 
40
40
  versions = VersionBundle(
41
41
  HeadVersion(),
42
- Version("2023-02-10"),
43
- RemoveTaxIDEndpoints,
42
+ Version("2023-02-10", RemoveTaxIDEndpoints),
44
43
  Version("2022-11-16"),
45
44
  )
46
45
  ```
@@ -64,8 +63,7 @@ from .v2023_02_10 import RemoveTaxIDEndpoints
64
63
 
65
64
  versions = VersionBundle(
66
65
  HeadVersion(),
67
- Version("2023-02-10"),
68
- RemoveTaxIDEndpoints,
66
+ Version("2023-02-10", RemoveTaxIDEndpoints),
69
67
  Version("2022-11-16"),
70
68
  )
71
69
  ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cadwyn"
3
- version = "5.1.4"
3
+ version = "5.2.1"
4
4
  description = "Production-ready community-driven modern Stripe-like API versioning in FastAPI"
5
5
  authors = [{ name = "Stanislav Zmiev", email = "zmievsa@gmail.com" }]
6
6
  license = "MIT"
@@ -55,7 +55,6 @@ dependencies = [
55
55
  "starlette >=0.30.0",
56
56
  "pydantic >=2.0.0",
57
57
  "jinja2 >=3.1.2",
58
- "issubclass >=0.1.2",
59
58
  "backports-strenum >=1.3.1,<2; python_version < '3.11'",
60
59
  "typing-extensions>=4.8.0",
61
60
  ]
@@ -154,6 +153,7 @@ reportMissingSuperCall = true
154
153
  reportFunctionMemberAccess = false
155
154
  reportCircularImports = true
156
155
  reportInvalidTypeForm = false
156
+ reportPrivateImportUsage = false
157
157
 
158
158
  [tool.pytest.ini_options]
159
159
  filterwarnings = [
@@ -8,6 +8,7 @@ from fastapi.testclient import TestClient
8
8
  from pydantic import BaseModel
9
9
 
10
10
  from cadwyn import Cadwyn
11
+ from cadwyn.exceptions import CadwynStructureError
11
12
  from cadwyn.route_generation import VersionedAPIRouter
12
13
  from cadwyn.structure.endpoints import endpoint
13
14
  from cadwyn.structure.schemas import schema
@@ -405,3 +406,15 @@ def test__api_version_header_name_is_deprecated_and_translates_to_api_version_pa
405
406
  with pytest.warns(DeprecationWarning):
406
407
  cadwyn = Cadwyn(api_version_header_name="x-api-version", versions=VersionBundle(Version("2022-11-16")))
407
408
  assert cadwyn.api_version_parameter_name == "x-api-version"
409
+
410
+
411
+ def test__api_version_default_value_with_path_location__should_raise_error():
412
+ with pytest.raises(
413
+ CadwynStructureError,
414
+ match="You tried to pass an api_version_default_value while putting the API version in Path",
415
+ ):
416
+ Cadwyn(
417
+ versions=VersionBundle(HeadVersion(), Version("2022-11-16")),
418
+ api_version_default_value="2022-11-16",
419
+ api_version_location="path",
420
+ )
@@ -1,3 +1,5 @@
1
+ from typing import Callable
2
+
1
3
  import pytest
2
4
  from starlette.requests import Request
3
5
  from starlette.responses import PlainTextResponse
@@ -5,7 +7,8 @@ from starlette.routing import Match, NoMatchFound
5
7
  from starlette.testclient import TestClient
6
8
 
7
9
  from cadwyn import Cadwyn
8
- from cadwyn.structure.versions import Version, VersionBundle
10
+ from cadwyn.route_generation import VersionedAPIRouter
11
+ from cadwyn.structure.versions import HeadVersion, Version, VersionBundle
9
12
  from tests._resources.app_for_testing_routing import mixed_hosts_app
10
13
 
11
14
 
@@ -153,3 +156,53 @@ def test__host_routing__partial_match__404():
153
156
 
154
157
  response = client.get("/v1/doggies/tom")
155
158
  assert response.status_code == 200
159
+
160
+
161
+ async def get_default_version(req: Request):
162
+ return "2023-04-14"
163
+
164
+
165
+ @pytest.mark.parametrize("default_version", ["2023-04-14", get_default_version])
166
+ def test__get_unversioned_endpoints__with_default_version(default_version: "str | Callable"):
167
+ app = Cadwyn(
168
+ versions=VersionBundle(HeadVersion(), Version("2023-04-14"), Version("2022-11-16")),
169
+ api_version_default_value=default_version,
170
+ )
171
+
172
+ router = VersionedAPIRouter()
173
+
174
+ @app.get("/my_duplicated_route")
175
+ def get_my_unversioned_number():
176
+ return 11
177
+
178
+ @router.get("/my_duplicated_route")
179
+ def get_my_versioned_number():
180
+ return 83
181
+
182
+ @router.get("/my_single_route")
183
+ def get_my_versioned_number_2():
184
+ return 52
185
+
186
+ app.generate_and_include_versioned_routers(router)
187
+
188
+ with TestClient(app) as client:
189
+ resp = client.get("/docs")
190
+ assert resp.status_code == 200, resp.json()
191
+
192
+ resp = client.get("/docs?version=2023-04-14")
193
+ assert resp.status_code == 200, resp.json()
194
+
195
+ resp = client.get("/docs?version=2022-11-16")
196
+ assert resp.status_code == 200, resp.json()
197
+
198
+ resp = client.get("/my_duplicated_route")
199
+ assert resp.status_code == 200, resp.json()
200
+ assert resp.json() == 11
201
+
202
+ resp = client.get("/my_duplicated_route", headers={"X-API-VERSION": "2023-04-14"})
203
+ assert resp.status_code == 200, resp.json()
204
+ assert resp.json() == 83
205
+
206
+ resp = client.get("/my_single_route")
207
+ assert resp.status_code == 200, resp.json()
208
+ assert resp.json() == 52
File without changes
@@ -94,12 +94,11 @@ wheels = [
94
94
 
95
95
  [[package]]
96
96
  name = "cadwyn"
97
- version = "5.1.4"
97
+ version = "5.2.1"
98
98
  source = { editable = "." }
99
99
  dependencies = [
100
100
  { name = "backports-strenum", marker = "python_full_version < '3.11'" },
101
101
  { name = "fastapi" },
102
- { name = "issubclass" },
103
102
  { name = "jinja2" },
104
103
  { name = "pydantic" },
105
104
  { name = "starlette" },
@@ -140,7 +139,6 @@ requires-dist = [
140
139
  { name = "backports-strenum", marker = "python_full_version < '3.11'", specifier = ">=1.3.1,<2" },
141
140
  { name = "fastapi", specifier = ">=0.112.4" },
142
141
  { name = "fastapi", extras = ["standard"], marker = "extra == 'standard'", specifier = ">=0.112.3" },
143
- { name = "issubclass", specifier = ">=0.1.2" },
144
142
  { name = "jinja2", specifier = ">=3.1.2" },
145
143
  { name = "pydantic", specifier = ">=2.0.0" },
146
144
  { name = "starlette", specifier = ">=0.30.0" },
@@ -614,15 +612,6 @@ wheels = [
614
612
  { url = "https://files.pythonhosted.org/packages/76/74/5222a632fd8d3202ddef383b71c8b6c31a9d77989030efba5be561163d41/inline_snapshot-0.20.8-py3-none-any.whl", hash = "sha256:bded4e142b8817930e4df428b88c462308a8f01ad699852e7574a54bad7ea9f2", size = 48157 },
615
613
  ]
616
614
 
617
- [[package]]
618
- name = "issubclass"
619
- version = "0.1.2"
620
- source = { registry = "https://pypi.org/simple" }
621
- sdist = { url = "https://files.pythonhosted.org/packages/7d/89/f9a05e3bbc7cc2a5806138e62f2f94504e491411c8d0c994296dcfd0cfaf/issubclass-0.1.2.tar.gz", hash = "sha256:740dabca95adbd25442d1dd616ed455046ab9551c38514758dc291f431fded35", size = 1850 }
622
- wheels = [
623
- { url = "https://files.pythonhosted.org/packages/b1/69/3135026fbb9d4bcdc1e76a9e4c18fc4c6161a6eea6905b76d27eeaa00940/issubclass-0.1.2-py3-none-any.whl", hash = "sha256:ea54b6b27526cf1be49a4bc15d713bef0b480e0230482626a37cf21529da3864", size = 2164 },
624
- ]
625
-
626
615
  [[package]]
627
616
  name = "jinja2"
628
617
  version = "3.1.6"
@@ -1,5 +0,0 @@
1
- from cadwyn.structure.versions import _CURRENT_DEPENDENCY_SOLVER_OPTIONS, _CURRENT_DEPENDENCY_SOLVER_VAR
2
-
3
-
4
- async def current_dependency_solver() -> _CURRENT_DEPENDENCY_SOLVER_OPTIONS:
5
- return _CURRENT_DEPENDENCY_SOLVER_VAR.get("fastapi")
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
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
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