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.
- {cadwyn-5.1.4 → cadwyn-5.2.1}/CHANGELOG.md +12 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/PKG-INFO +1 -2
- cadwyn-5.2.1/cadwyn/_internal/context_vars.py +9 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/_render.py +1 -1
- {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/applications.py +7 -1
- cadwyn-5.2.1/cadwyn/dependencies.py +5 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/middleware.py +6 -1
- {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/route_generation.py +1 -2
- {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/routing.py +10 -1
- {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/structure/schemas.py +1 -1
- {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/structure/versions.py +3 -6
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/version_changes.md +2 -4
- {cadwyn-5.1.4 → cadwyn-5.2.1}/pyproject.toml +2 -2
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_applications.py +13 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_routing.py +54 -1
- cadwyn-5.2.1/tests/versioning_styles/__init__.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/uv.lock +1 -12
- cadwyn-5.1.4/cadwyn/dependencies.py +0 -5
- {cadwyn-5.1.4 → cadwyn-5.2.1}/.github/CODE_OF_CONDUCT.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/.github/actions/setup-python-uv/action.yaml +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/.github/workflows/ci.yaml +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/.github/workflows/daily_tests.yaml +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/.github/workflows/publish_docs.yaml +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/.github/workflows/release.yaml +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/.gitignore +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/.pre-commit-config.yaml +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/LICENSE +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/Makefile +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/README.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/__init__.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/__main__.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/_asts.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/_importer.py +0 -0
- {cadwyn-5.1.4/cadwyn/static → cadwyn-5.2.1/cadwyn/_internal}/__init__.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/_utils.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/changelogs.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/exceptions.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/py.typed +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/schema_generation.py +0 -0
- {cadwyn-5.1.4/docs → cadwyn-5.2.1/cadwyn/static}/__init__.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/static/docs.html +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/structure/__init__.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/structure/common.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/structure/data.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/structure/endpoints.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/cadwyn/structure/enums.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/CNAME +0 -0
- {cadwyn-5.1.4/docs_src → cadwyn-5.2.1/docs}/__init__.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/api_version_parameter_and_context_variables.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/beware_of_data_versioning.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/changelogs.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/cli.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/endpoint_migrations.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/enum_migrations.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/index.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/main_app.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/methodology.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/schema_generation.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/schema_migrations.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/testing.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/where_to_put_the_version_and_how_to_format_it.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/home/CONTRIBUTING.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/how_to/change_business_logic/index.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/how_to/change_endpoints/index.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/how_to/change_openapi_schemas/add_field.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/how_to/change_openapi_schemas/change_field_type.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/how_to/change_openapi_schemas/change_schema_without_endpoint.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/how_to/change_openapi_schemas/changing_constraints.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/how_to/change_openapi_schemas/remove_field.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/how_to/change_openapi_schemas/rename_a_field_in_schema.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/how_to/index.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/how_to/version_with_paths_and_numbers_instead_of_headers_and_dates.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/img/dashboard_with_one_version.png +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/img/dashboard_with_two_versions.png +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/img/get_users_endpoint_from_prior_version.png +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/img/simplified_migration_model.png +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/img/sponsor_logos/monite.png +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/img/unversioned_dashboard.png +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/index.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/plugin.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/quickstart/setup.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/quickstart/tutorial.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/theory/how_to_build_versioning_framework.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/theory/how_we_got_here.md +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs/theory/literature.md +0 -0
- {cadwyn-5.1.4/docs_src/how_to → cadwyn-5.2.1/docs_src}/__init__.py +0 -0
- {cadwyn-5.1.4/docs_src/how_to/change_openapi_schemas → cadwyn-5.2.1/docs_src/how_to}/__init__.py +0 -0
- {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
- {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
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/block001.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/block002.py +0 -0
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {cadwyn-5.1.4/docs_src/quickstart/setup → cadwyn-5.2.1/docs_src/quickstart}/__init__.py +0 -0
- {cadwyn-5.1.4/docs_src/quickstart/setup/tests → cadwyn-5.2.1/docs_src/quickstart/setup}/__init__.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/quickstart/setup/block001.sh +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/quickstart/setup/block002.py +0 -0
- {cadwyn-5.1.4/docs_src/quickstart/tutorial → cadwyn-5.2.1/docs_src/quickstart/setup/tests}/__init__.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/quickstart/setup/tests/test_block002.py +0 -0
- {cadwyn-5.1.4/docs_src/quickstart/tutorial/tests → cadwyn-5.2.1/docs_src/quickstart/tutorial}/__init__.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/quickstart/tutorial/block001.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/quickstart/tutorial/block002.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/quickstart/tutorial/block003.py +0 -0
- {cadwyn-5.1.4/tests/_data → cadwyn-5.2.1/docs_src/quickstart/tutorial/tests}/__init__.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/quickstart/tutorial/tests/test_block001.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/quickstart/tutorial/tests/test_block002.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/quickstart/tutorial/tests/test_block003.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/docs_src/ruff.toml +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/mkdocs.yml +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/ruff.toml +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/scripts/fix_links.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/scripts/split_md.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/__init__.py +0 -0
- {cadwyn-5.1.4/tests/_resources → cadwyn-5.2.1/tests/_data}/__init__.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/_data/unversioned_schema_dir/__init__.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/_data/unversioned_schema_dir/unversioned_schemas.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/_data/unversioned_schemas.py +0 -0
- {cadwyn-5.1.4/tests/_resources/render → cadwyn-5.2.1/tests/_resources}/__init__.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/_resources/app_for_testing_routing.py +0 -0
- {cadwyn-5.1.4/tests/_resources/render/complex → cadwyn-5.2.1/tests/_resources/render}/__init__.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/_resources/render/classes.py +0 -0
- {cadwyn-5.1.4/tests/_resources/versioned_app → cadwyn-5.2.1/tests/_resources/render/complex}/__init__.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/_resources/render/complex/classes.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/_resources/render/complex/versions.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/_resources/render/versions.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/_resources/utils.py +0 -0
- {cadwyn-5.1.4/tests/test_schema_generation → cadwyn-5.2.1/tests/_resources/versioned_app}/__init__.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/_resources/versioned_app/app.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/_resources/versioned_app/v2021_01_01.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/_resources/versioned_app/v2022_01_02.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/_resources/versioned_app/webhooks.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/conftest.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_auth_dependencies.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_changelog.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_cli.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_data_migrations.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_render.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_router_generation.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_router_generation_with_from_future_annotations.py +0 -0
- {cadwyn-5.1.4/tests/tutorial → cadwyn-5.2.1/tests/test_schema_generation}/__init__.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_schema_generation/test_enum.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_schema_generation/test_schema.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_schema_generation/test_schema_field.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_schema_generation/test_schema_validator.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_schema_generation/test_schema_with_future_annotations.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_structure.py +0 -0
- {cadwyn-5.1.4/tests/versioning_styles → cadwyn-5.2.1/tests/tutorial}/__init__.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/tutorial/main.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/tutorial/test_example.py +0 -0
- {cadwyn-5.1.4 → cadwyn-5.2.1}/tests/versioning_styles/test_versioning_formats.py +0 -0
- {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
|
|
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)
|
|
@@ -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):
|
|
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,
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
|
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"
|
|
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
|
|
File without changes
|
{cadwyn-5.1.4 → cadwyn-5.2.1}/docs/concepts/where_to_put_the_version_and_how_to_format_it.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cadwyn-5.1.4 → cadwyn-5.2.1}/docs/how_to/change_openapi_schemas/change_schema_without_endpoint.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cadwyn-5.1.4 → cadwyn-5.2.1}/docs/how_to/change_openapi_schemas/rename_a_field_in_schema.md
RENAMED
|
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
|
{cadwyn-5.1.4/docs_src/how_to/change_openapi_schemas → cadwyn-5.2.1/docs_src/how_to}/__init__.py
RENAMED
|
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
|
{cadwyn-5.1.4/docs_src/quickstart/setup/tests → cadwyn-5.2.1/docs_src/quickstart/setup}/__init__.py
RENAMED
|
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
|
{cadwyn-5.1.4/tests/_resources/render/complex → cadwyn-5.2.1/tests/_resources/render}/__init__.py
RENAMED
|
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
|
{cadwyn-5.1.4 → cadwyn-5.2.1}/tests/test_schema_generation/test_schema_with_future_annotations.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|