cadwyn 5.3.2__tar.gz → 5.4.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.3.2 → cadwyn-5.4.1}/.pre-commit-config.yaml +17 -9
- {cadwyn-5.3.2 → cadwyn-5.4.1}/CHANGELOG.md +20 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/PKG-INFO +8 -9
- {cadwyn-5.3.2 → cadwyn-5.4.1}/README.md +5 -7
- {cadwyn-5.3.2 → cadwyn-5.4.1}/cadwyn/__main__.py +1 -1
- {cadwyn-5.3.2 → cadwyn-5.4.1}/cadwyn/_asts.py +1 -1
- {cadwyn-5.3.2 → cadwyn-5.4.1}/cadwyn/applications.py +2 -2
- {cadwyn-5.3.2 → cadwyn-5.4.1}/cadwyn/changelogs.py +5 -5
- {cadwyn-5.3.2 → cadwyn-5.4.1}/cadwyn/route_generation.py +2 -2
- {cadwyn-5.3.2 → cadwyn-5.4.1}/cadwyn/schema_generation.py +32 -29
- {cadwyn-5.3.2 → cadwyn-5.4.1}/cadwyn/structure/data.py +2 -2
- {cadwyn-5.3.2 → cadwyn-5.4.1}/cadwyn/structure/schemas.py +2 -2
- {cadwyn-5.3.2 → cadwyn-5.4.1}/cadwyn/structure/versions.py +1 -1
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/concepts/version_changes.md +25 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/how_to/change_openapi_schemas/remove_field.md +2 -1
- cadwyn-5.4.1/docs/img/logos/cadwyn_icon_transparent.svg +1 -0
- cadwyn-5.4.1/docs/img/logos/cadwyn_transparent.svg +1 -0
- cadwyn-5.4.1/docs/img/logos/cadwyn_with_background.svg +1 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/index.md +0 -6
- cadwyn-5.4.1/docs/stylesheets/extra.css +12 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/mkdocs.yml +7 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/pyproject.toml +4 -3
- {cadwyn-5.3.2 → cadwyn-5.4.1}/ruff.toml +21 -13
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/conftest.py +1 -1
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/test_applications.py +14 -12
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/test_cli.py +0 -15
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/test_render.py +4 -2
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/test_router_generation.py +11 -11
- cadwyn-5.4.1/uv.lock +1792 -0
- cadwyn-5.3.2/docs/img/sponsor_logos/monite.png +0 -0
- cadwyn-5.3.2/uv.lock +0 -1737
- {cadwyn-5.3.2 → cadwyn-5.4.1}/.github/CODE_OF_CONDUCT.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/.github/actions/setup-python-uv/action.yaml +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/.github/workflows/ci.yaml +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/.github/workflows/daily_tests.yaml +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/.github/workflows/publish_docs.yaml +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/.github/workflows/release.yaml +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/.gitignore +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/LICENSE +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/Makefile +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/cadwyn/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/cadwyn/_importer.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/cadwyn/_internal/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/cadwyn/_internal/context_vars.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/cadwyn/_render.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/cadwyn/_utils.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/cadwyn/dependencies.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/cadwyn/exceptions.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/cadwyn/middleware.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/cadwyn/py.typed +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/cadwyn/routing.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/cadwyn/static/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/cadwyn/static/docs.html +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/cadwyn/structure/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/cadwyn/structure/common.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/cadwyn/structure/endpoints.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/cadwyn/structure/enums.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/CNAME +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/concepts/api_version_parameter.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/concepts/beware_of_data_versioning.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/concepts/changelogs.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/concepts/cli.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/concepts/endpoint_migrations.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/concepts/enum_migrations.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/concepts/index.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/concepts/main_app.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/concepts/methodology.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/concepts/schema_generation.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/concepts/schema_migrations.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/concepts/testing.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/home/CONTRIBUTING.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/how_to/change_business_logic/index.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/how_to/change_endpoints/index.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/how_to/change_openapi_schemas/add_field.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/how_to/change_openapi_schemas/change_field_type.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/how_to/change_openapi_schemas/change_schema_without_endpoint.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/how_to/change_openapi_schemas/changing_constraints.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/how_to/change_openapi_schemas/rename_a_field_in_schema.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/how_to/index.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/how_to/version_with_paths_and_numbers_instead_of_headers_and_dates.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/img/dashboard_with_one_version.png +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/img/dashboard_with_two_versions.png +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/img/get_users_endpoint_from_prior_version.png +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/img/simplified_migration_model.png +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/img/unversioned_dashboard.png +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/plugin.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/quickstart/setup.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/quickstart/tutorial.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/theory/how_to_build_versioning_framework.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/theory/how_we_got_here.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs/theory/literature.md +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/how_to/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/how_to/change_openapi_schemas/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/block001.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/block002.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/tests/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/tests/test_block001.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/tests/test_block002.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/how_to/version_with_path_and_numbers_instead_of_headers_and_dates/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/how_to/version_with_path_and_numbers_instead_of_headers_and_dates/block001.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/how_to/version_with_path_and_numbers_instead_of_headers_and_dates/tests/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/how_to/version_with_path_and_numbers_instead_of_headers_and_dates/tests/test_block_001.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/quickstart/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/quickstart/setup/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/quickstart/setup/block001.sh +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/quickstart/setup/block002.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/quickstart/setup/tests/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/quickstart/setup/tests/test_block002.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/quickstart/tutorial/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/quickstart/tutorial/block001.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/quickstart/tutorial/block002.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/quickstart/tutorial/block003.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/quickstart/tutorial/tests/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/quickstart/tutorial/tests/test_block001.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/quickstart/tutorial/tests/test_block002.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/quickstart/tutorial/tests/test_block003.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/docs_src/ruff.toml +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/scripts/fix_links.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/scripts/split_md.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/_data/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/_data/unversioned_schema_dir/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/_data/unversioned_schema_dir/unversioned_schemas.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/_data/unversioned_schemas.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/_resources/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/_resources/app_for_testing_routing.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/_resources/render/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/_resources/render/classes.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/_resources/render/complex/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/_resources/render/complex/classes.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/_resources/render/complex/versions.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/_resources/render/versions.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/_resources/utils.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/_resources/versioned_app/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/_resources/versioned_app/app.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/_resources/versioned_app/v2021_01_01.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/_resources/versioned_app/v2022_01_02.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/_resources/versioned_app/webhooks.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/test_auth_dependencies.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/test_changelog.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/test_data_migrations.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/test_router_generation_with_from_future_annotations.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/test_routing.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/test_schema_generation/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/test_schema_generation/test_enum.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/test_schema_generation/test_schema.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/test_schema_generation/test_schema_field.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/test_schema_generation/test_schema_validator.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/test_schema_generation/test_schema_with_future_annotations.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/test_structure.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/tutorial/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/tutorial/main.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/tutorial/test_example.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/versioning_styles/__init__.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tests/versioning_styles/test_versioning_formats.py +0 -0
- {cadwyn-5.3.2 → cadwyn-5.4.1}/tox.ini +0 -0
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
default_language_version:
|
|
2
|
-
python: python3
|
|
2
|
+
python: python3
|
|
3
3
|
repos:
|
|
4
4
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
5
5
|
rev: v5.0.0
|
|
6
6
|
hooks:
|
|
7
7
|
- id: check-added-large-files
|
|
8
8
|
- id: check-yaml
|
|
9
|
-
args:
|
|
9
|
+
args:
|
|
10
|
+
- '--unsafe'
|
|
10
11
|
- id: check-toml
|
|
11
12
|
- id: end-of-file-fixer
|
|
12
13
|
- id: trailing-whitespace
|
|
@@ -18,27 +19,34 @@ repos:
|
|
|
18
19
|
- id: python-check-blanket-noqa
|
|
19
20
|
|
|
20
21
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
21
|
-
rev: v0.
|
|
22
|
+
rev: v0.11.10
|
|
22
23
|
hooks:
|
|
23
24
|
- id: ruff
|
|
24
|
-
args:
|
|
25
|
+
args:
|
|
26
|
+
- '--fix'
|
|
27
|
+
- '--exit-non-zero-on-fix'
|
|
25
28
|
- id: ruff-format
|
|
26
29
|
|
|
27
30
|
- repo: https://github.com/adamchainz/blacken-docs
|
|
28
|
-
rev:
|
|
31
|
+
rev: 1.19.1 # replace with latest tag on GitHub
|
|
29
32
|
hooks:
|
|
30
33
|
- id: blacken-docs
|
|
31
34
|
additional_dependencies:
|
|
32
35
|
- black==22.12.0
|
|
33
|
-
args:
|
|
36
|
+
args:
|
|
37
|
+
- '--line-length=80'
|
|
38
|
+
- '--target-version=py310'
|
|
39
|
+
- '--skip-errors'
|
|
34
40
|
|
|
35
41
|
- repo: https://github.com/igorshubovych/markdownlint-cli
|
|
36
|
-
rev: v0.
|
|
42
|
+
rev: v0.44.0
|
|
37
43
|
hooks:
|
|
38
44
|
- id: markdownlint
|
|
39
|
-
args:
|
|
45
|
+
args:
|
|
46
|
+
- '--disable'
|
|
47
|
+
- MD013
|
|
40
48
|
|
|
41
49
|
- repo: https://github.com/astral-sh/uv-pre-commit
|
|
42
|
-
rev: 0.
|
|
50
|
+
rev: 0.7.5
|
|
43
51
|
hooks:
|
|
44
52
|
- id: uv-lock
|
|
@@ -5,6 +5,26 @@ Please follow [the Keep a Changelog standard](https://keepachangelog.com/en/1.0.
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [5.4.1]
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
* Fixed import error in python 3.9 when using typing_extensions==3.14.0
|
|
13
|
+
|
|
14
|
+
## [5.4.0]
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- `typing_inspection` dependency from pydantic team for complex isinstance checks that must be the same between `typing` and `typing_extensions`
|
|
19
|
+
- Support for `pydantic>=2.12.0` FieldInfo refactoring from https://github.com/pydantic/pydantic/pull/11898
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## [5.3.3]
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
* Fixed pypi badge link in README
|
|
27
|
+
|
|
8
28
|
## [5.3.2]
|
|
9
29
|
|
|
10
30
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cadwyn
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.4.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
|
|
@@ -37,9 +37,10 @@ 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
39
|
Requires-Dist: jinja2>=3.1.2
|
|
40
|
-
Requires-Dist: pydantic>=2.
|
|
40
|
+
Requires-Dist: pydantic>=2.11.0
|
|
41
41
|
Requires-Dist: starlette>=0.30.0
|
|
42
42
|
Requires-Dist: typing-extensions>=4.8.0
|
|
43
|
+
Requires-Dist: typing-inspection>=0.4.0
|
|
43
44
|
Provides-Extra: standard
|
|
44
45
|
Requires-Dist: fastapi[standard]>=0.112.3; extra == 'standard'
|
|
45
46
|
Requires-Dist: typer>=0.7.0; extra == 'standard'
|
|
@@ -59,7 +60,7 @@ Production-ready community-driven modern [Stripe-like](https://stripe.com/blog/a
|
|
|
59
60
|
<img src="https://img.shields.io/codecov/c/github/zmievsa/cadwyn?color=%2334D058&logo=codecov" alt="Coverage">
|
|
60
61
|
</a>
|
|
61
62
|
<a href="https://pypi.org/project/cadwyn/" target="_blank">
|
|
62
|
-
<img alt="PyPI" src="https://img.shields.io/pypi/v/cadwyn?color=%2334D058&logo=pypi&label=PyPI
|
|
63
|
+
<img alt="PyPI" src="https://img.shields.io/pypi/v/cadwyn?color=%2334D058&logo=pypi&label=PyPI" alt="Package version">
|
|
63
64
|
</a>
|
|
64
65
|
<a href="https://pypi.org/project/cadwyn/" target="_blank">
|
|
65
66
|
<img src="https://img.shields.io/pypi/pyversions/cadwyn?color=%2334D058&logo=python" alt="Supported Python versions">
|
|
@@ -71,12 +72,12 @@ Production-ready community-driven modern [Stripe-like](https://stripe.com/blog/a
|
|
|
71
72
|
|
|
72
73
|
## Who is this for?
|
|
73
74
|
|
|
74
|
-
Cadwyn allows you to maintain the implementation just for your newest API version and get all the older versions generated automatically. You keep API
|
|
75
|
+
Cadwyn allows you to maintain the implementation just for your newest API version and get all the older versions generated automatically. You keep API backward compatibility encapsulated in small and independent "version change" modules while your business logic stays simple and knows nothing about versioning.
|
|
75
76
|
|
|
76
|
-
|
|
77
|
+
This [approach](https://docs.cadwyn.dev/theory/how_we_got_here/#ii-migration-based-response-building) may be useful if you want to:
|
|
77
78
|
|
|
78
79
|
1. Support many API versions for a long time
|
|
79
|
-
2.
|
|
80
|
+
2. Have features and bugfixes automatically backported to older API versions
|
|
80
81
|
|
|
81
82
|
Whether you are a newbie in API versioning, a pro looking for a sophisticated tool, an experimenter looking to build a similar framework, or even someone who just wants to learn about all approaches to API versioning -- Cadwyn has the functionality, theory, and documentation to cover all the mentioned use cases.
|
|
82
83
|
|
|
@@ -86,6 +87,4 @@ The [documentation](https://docs.cadwyn.dev) has everything you need to succeed.
|
|
|
86
87
|
|
|
87
88
|
## Sponsors
|
|
88
89
|
|
|
89
|
-
These are our gorgeous sponsors. They are using Cadwyn and are sponsoring it through various means. Contact [me](https://github.com/zmievsa) if you would like to become one too!
|
|
90
|
-
|
|
91
|
-
[](https://docs.monite.com/)
|
|
90
|
+
These are our gorgeous sponsors. They are using Cadwyn and are sponsoring it through various means. Contact [me](https://github.com/zmievsa) if you would like to become one, too!
|
|
@@ -12,7 +12,7 @@ Production-ready community-driven modern [Stripe-like](https://stripe.com/blog/a
|
|
|
12
12
|
<img src="https://img.shields.io/codecov/c/github/zmievsa/cadwyn?color=%2334D058&logo=codecov" alt="Coverage">
|
|
13
13
|
</a>
|
|
14
14
|
<a href="https://pypi.org/project/cadwyn/" target="_blank">
|
|
15
|
-
<img alt="PyPI" src="https://img.shields.io/pypi/v/cadwyn?color=%2334D058&logo=pypi&label=PyPI
|
|
15
|
+
<img alt="PyPI" src="https://img.shields.io/pypi/v/cadwyn?color=%2334D058&logo=pypi&label=PyPI" alt="Package version">
|
|
16
16
|
</a>
|
|
17
17
|
<a href="https://pypi.org/project/cadwyn/" target="_blank">
|
|
18
18
|
<img src="https://img.shields.io/pypi/pyversions/cadwyn?color=%2334D058&logo=python" alt="Supported Python versions">
|
|
@@ -24,12 +24,12 @@ Production-ready community-driven modern [Stripe-like](https://stripe.com/blog/a
|
|
|
24
24
|
|
|
25
25
|
## Who is this for?
|
|
26
26
|
|
|
27
|
-
Cadwyn allows you to maintain the implementation just for your newest API version and get all the older versions generated automatically. You keep API
|
|
27
|
+
Cadwyn allows you to maintain the implementation just for your newest API version and get all the older versions generated automatically. You keep API backward compatibility encapsulated in small and independent "version change" modules while your business logic stays simple and knows nothing about versioning.
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
This [approach](https://docs.cadwyn.dev/theory/how_we_got_here/#ii-migration-based-response-building) may be useful if you want to:
|
|
30
30
|
|
|
31
31
|
1. Support many API versions for a long time
|
|
32
|
-
2.
|
|
32
|
+
2. Have features and bugfixes automatically backported to older API versions
|
|
33
33
|
|
|
34
34
|
Whether you are a newbie in API versioning, a pro looking for a sophisticated tool, an experimenter looking to build a similar framework, or even someone who just wants to learn about all approaches to API versioning -- Cadwyn has the functionality, theory, and documentation to cover all the mentioned use cases.
|
|
35
35
|
|
|
@@ -39,6 +39,4 @@ The [documentation](https://docs.cadwyn.dev) has everything you need to succeed.
|
|
|
39
39
|
|
|
40
40
|
## Sponsors
|
|
41
41
|
|
|
42
|
-
These are our gorgeous sponsors. They are using Cadwyn and are sponsoring it through various means. Contact [me](https://github.com/zmievsa) if you would like to become one too!
|
|
43
|
-
|
|
44
|
-
[](https://docs.monite.com/)
|
|
42
|
+
These are our gorgeous sponsors. They are using Cadwyn and are sponsoring it through various means. Contact [me](https://github.com/zmievsa) if you would like to become one, too!
|
|
@@ -22,7 +22,7 @@ NoneType = type(None)
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
# A parent type of typing._GenericAlias
|
|
25
|
-
_BaseGenericAlias = cast(type, type(List[int])).mro()[1] # noqa: UP006
|
|
25
|
+
_BaseGenericAlias = cast("type", type(List[int])).mro()[1] # noqa: UP006
|
|
26
26
|
|
|
27
27
|
# type(list[int]) and type(List[int]) are different which is why we have to do this.
|
|
28
28
|
# Please note that this problem is much wider than just lists which is why we use typing._BaseGenericAlias
|
|
@@ -400,7 +400,7 @@ class Cadwyn(FastAPI):
|
|
|
400
400
|
init_oauth=self.swagger_ui_init_oauth,
|
|
401
401
|
swagger_ui_parameters=self.swagger_ui_parameters,
|
|
402
402
|
)
|
|
403
|
-
return self._render_docs_dashboard(req, cast(str, self.docs_url))
|
|
403
|
+
return self._render_docs_dashboard(req, cast("str", self.docs_url))
|
|
404
404
|
|
|
405
405
|
async def redoc_dashboard(self, req: Request) -> Response:
|
|
406
406
|
version = req.query_params.get("version")
|
|
@@ -410,7 +410,7 @@ class Cadwyn(FastAPI):
|
|
|
410
410
|
openapi_url = root_path + f"{self.openapi_url}?version={version}"
|
|
411
411
|
return get_redoc_html(openapi_url=openapi_url, title=f"{self.title} - ReDoc")
|
|
412
412
|
|
|
413
|
-
return self._render_docs_dashboard(req, docs_url=cast(str, self.redoc_url))
|
|
413
|
+
return self._render_docs_dashboard(req, docs_url=cast("str", self.redoc_url))
|
|
414
414
|
|
|
415
415
|
def _extract_root_path(self, req: Request):
|
|
416
416
|
return req.scope.get("root_path", "").rstrip("/")
|
|
@@ -93,7 +93,7 @@ def _generate_changelog(versions: VersionBundle, router: _RootCadwynAPIRouter) -
|
|
|
93
93
|
generator_from_newer_version,
|
|
94
94
|
generator_from_older_version,
|
|
95
95
|
schemas_from_older_version,
|
|
96
|
-
cast(list[APIRoute], routes_from_newer_version),
|
|
96
|
+
cast("list[APIRoute]", routes_from_newer_version),
|
|
97
97
|
)
|
|
98
98
|
if changelog_entry is not None: # pragma: no branch # This should never happen
|
|
99
99
|
version_change_changelog.instructions.append(CadwynVersionChangeInstruction(changelog_entry))
|
|
@@ -321,18 +321,18 @@ def _convert_version_change_instruction_to_changelog_entry( # noqa: C901
|
|
|
321
321
|
if isinstance(instruction, EndpointDidntExistInstruction):
|
|
322
322
|
return CadwynEndpointWasAddedChangelogEntry(
|
|
323
323
|
path=instruction.endpoint_path,
|
|
324
|
-
methods=cast(Any, instruction.endpoint_methods),
|
|
324
|
+
methods=cast("Any", instruction.endpoint_methods),
|
|
325
325
|
)
|
|
326
326
|
elif isinstance(instruction, EndpointExistedInstruction):
|
|
327
327
|
return CadwynEndpointWasRemovedChangelogEntry(
|
|
328
328
|
path=instruction.endpoint_path,
|
|
329
|
-
methods=cast(Any, instruction.endpoint_methods),
|
|
329
|
+
methods=cast("Any", instruction.endpoint_methods),
|
|
330
330
|
)
|
|
331
331
|
elif isinstance(instruction, EndpointHadInstruction):
|
|
332
332
|
if instruction.attributes.include_in_schema is not Sentinel:
|
|
333
333
|
return CadwynEndpointWasRemovedChangelogEntry(
|
|
334
334
|
path=instruction.endpoint_path,
|
|
335
|
-
methods=cast(Any, instruction.endpoint_methods),
|
|
335
|
+
methods=cast("Any", instruction.endpoint_methods),
|
|
336
336
|
)
|
|
337
337
|
|
|
338
338
|
renaming_map = {"operation_id": "operationId"}
|
|
@@ -379,7 +379,7 @@ def _convert_version_change_instruction_to_changelog_entry( # noqa: C901
|
|
|
379
379
|
attribute_changes.append(CadwynEndpointAttributeChange(name="responses", new_value=changed_responses))
|
|
380
380
|
return CadwynEndpointHadChangelogEntry(
|
|
381
381
|
path=instruction.endpoint_path,
|
|
382
|
-
methods=cast(Any, instruction.endpoint_methods),
|
|
382
|
+
methods=cast("Any", instruction.endpoint_methods),
|
|
383
383
|
changes=attribute_changes,
|
|
384
384
|
)
|
|
385
385
|
|
|
@@ -78,7 +78,7 @@ def generate_versioned_routers(
|
|
|
78
78
|
webhooks: Union[_WR, None] = None,
|
|
79
79
|
) -> GeneratedRouters[_R, _WR]:
|
|
80
80
|
if webhooks is None:
|
|
81
|
-
webhooks = cast(_WR, APIRouter())
|
|
81
|
+
webhooks = cast("_WR", APIRouter())
|
|
82
82
|
return _EndpointTransformer(router, versions, webhooks).transform()
|
|
83
83
|
|
|
84
84
|
|
|
@@ -167,7 +167,7 @@ class _EndpointTransformer(Generic[_R, _WR]):
|
|
|
167
167
|
|
|
168
168
|
# We know they are APIRoutes because of the check at the very beginning of the top loop.
|
|
169
169
|
# I.e. Because head_route is an APIRoute, both routes are APIRoutes too
|
|
170
|
-
older_route = cast(APIRoute, older_route)
|
|
170
|
+
older_route = cast("APIRoute", older_route)
|
|
171
171
|
# Wait.. Why do we need this code again?
|
|
172
172
|
if older_route.body_field is not None and _route_has_a_simple_body_schema(older_route):
|
|
173
173
|
if hasattr(older_route.body_field.type_, "__cadwyn_original_model__"):
|
|
@@ -11,14 +11,20 @@ from collections.abc import Callable, Sequence
|
|
|
11
11
|
from datetime import date
|
|
12
12
|
from enum import Enum
|
|
13
13
|
from functools import cache
|
|
14
|
-
from typing import
|
|
14
|
+
from typing import (
|
|
15
|
+
TYPE_CHECKING,
|
|
16
|
+
Annotated,
|
|
17
|
+
Generic,
|
|
18
|
+
Union,
|
|
19
|
+
_BaseGenericAlias, # pyright: ignore[reportAttributeAccessIssue]
|
|
20
|
+
cast,
|
|
21
|
+
)
|
|
15
22
|
|
|
16
23
|
import fastapi.params
|
|
17
24
|
import fastapi.security.base
|
|
18
25
|
import fastapi.utils
|
|
19
26
|
import pydantic
|
|
20
27
|
import pydantic._internal._decorators
|
|
21
|
-
import typing_extensions
|
|
22
28
|
from fastapi import Response
|
|
23
29
|
from fastapi.dependencies.utils import is_async_gen_callable, is_coroutine_callable, is_gen_callable
|
|
24
30
|
from fastapi.routing import APIRoute
|
|
@@ -32,12 +38,12 @@ from pydantic._internal._decorators import (
|
|
|
32
38
|
RootValidatorDecoratorInfo,
|
|
33
39
|
ValidatorDecoratorInfo,
|
|
34
40
|
)
|
|
41
|
+
from pydantic._internal._known_annotated_metadata import collect_known_metadata
|
|
35
42
|
from pydantic._internal._typing_extra import try_eval_type as pydantic_try_eval_type
|
|
36
43
|
from pydantic.fields import ComputedFieldInfo, FieldInfo
|
|
37
44
|
from typing_extensions import (
|
|
38
45
|
Any,
|
|
39
46
|
Doc,
|
|
40
|
-
NewType,
|
|
41
47
|
Self,
|
|
42
48
|
TypeAlias,
|
|
43
49
|
TypeAliasType,
|
|
@@ -80,11 +86,6 @@ if TYPE_CHECKING:
|
|
|
80
86
|
from cadwyn.structure.versions import HeadVersion, Version, VersionBundle
|
|
81
87
|
|
|
82
88
|
|
|
83
|
-
if sys.version_info >= (3, 10):
|
|
84
|
-
from typing import _BaseGenericAlias # pyright: ignore[reportAttributeAccessIssue]
|
|
85
|
-
else:
|
|
86
|
-
from typing_extensions import _BaseGenericAlias # pyright: ignore[reportAttributeAccessIssue]
|
|
87
|
-
|
|
88
89
|
_Call = TypeVar("_Call", bound=Callable[..., Any])
|
|
89
90
|
|
|
90
91
|
_FieldName: TypeAlias = str
|
|
@@ -153,16 +154,12 @@ class PydanticFieldWrapper:
|
|
|
153
154
|
)
|
|
154
155
|
|
|
155
156
|
|
|
156
|
-
def _extract_passed_field_attributes(field_info: FieldInfo):
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
for
|
|
160
|
-
if
|
|
157
|
+
def _extract_passed_field_attributes(field_info: FieldInfo) -> dict[str, object]:
|
|
158
|
+
return {
|
|
159
|
+
k: v
|
|
160
|
+
for k, v in (field_info._attributes_set | collect_known_metadata(field_info.metadata)[0]).items()
|
|
161
|
+
if k in _all_field_arg_names and not (k == "frozen" and v is None)
|
|
161
162
|
}
|
|
162
|
-
# PydanticV2 always adds frozen to _attributes_set but we don't want it if it wasn't explicitly set
|
|
163
|
-
if attributes.get("frozen", ...) is None:
|
|
164
|
-
attributes.pop("frozen")
|
|
165
|
-
return attributes
|
|
166
163
|
|
|
167
164
|
|
|
168
165
|
@dataclasses.dataclass(**DATACLASS_SLOTS)
|
|
@@ -265,7 +262,7 @@ def _wrap_pydantic_model(model: type[_T_PYDANTIC_MODEL]) -> "_PydanticModelWrapp
|
|
|
265
262
|
# For example, when "from __future__ import annotations" is used in the file with the schema
|
|
266
263
|
if model is not BaseModel:
|
|
267
264
|
model.model_rebuild(raise_errors=False)
|
|
268
|
-
model = cast(type[_T_PYDANTIC_MODEL], model)
|
|
265
|
+
model = cast("type[_T_PYDANTIC_MODEL]", model)
|
|
269
266
|
|
|
270
267
|
decorators = _get_model_decorators(model)
|
|
271
268
|
validators = {}
|
|
@@ -345,7 +342,7 @@ def _wrap_pydantic_model(model: type[_T_PYDANTIC_MODEL]) -> "_PydanticModelWrapp
|
|
|
345
342
|
def _get_field_and_validator_names_from_model(cls: type) -> tuple[set[_FieldName], set[str]]:
|
|
346
343
|
fields = cls.model_fields
|
|
347
344
|
source = inspect.getsource(cls)
|
|
348
|
-
cls_ast = cast(ast.ClassDef, ast.parse(textwrap.dedent(source)).body[0])
|
|
345
|
+
cls_ast = cast("ast.ClassDef", ast.parse(textwrap.dedent(source)).body[0])
|
|
349
346
|
validator_names = (
|
|
350
347
|
_get_validator_info_or_none(node)
|
|
351
348
|
for node in cls_ast.body
|
|
@@ -468,7 +465,7 @@ class _PydanticModelWrapper(Generic[_T_PYDANTIC_MODEL]):
|
|
|
468
465
|
fields = {name: field.generate_field_copy(generator) for name, field in self.fields.items()}
|
|
469
466
|
model_copy = type(self.cls)(
|
|
470
467
|
self.name,
|
|
471
|
-
tuple(generator[cast(type[BaseModel], base)] for base in self.cls.__bases__),
|
|
468
|
+
tuple(generator[cast("type[BaseModel]", base)] for base in self.cls.__bases__),
|
|
472
469
|
self.other_attributes
|
|
473
470
|
| per_field_validators
|
|
474
471
|
| root_validators
|
|
@@ -514,7 +511,7 @@ class _CallableWrapper:
|
|
|
514
511
|
return hash(self._original_callable)
|
|
515
512
|
|
|
516
513
|
def __eq__(self, value: object) -> bool:
|
|
517
|
-
return self._original_callable == value
|
|
514
|
+
return self._original_callable == value
|
|
518
515
|
|
|
519
516
|
|
|
520
517
|
class _AsyncCallableWrapper(_CallableWrapper):
|
|
@@ -587,9 +584,11 @@ class _AnnotationTransformer:
|
|
|
587
584
|
self._remake_endpoint_dependencies(route)
|
|
588
585
|
|
|
589
586
|
def _change_version_of_a_non_container_annotation(self, annotation: Any) -> Any:
|
|
590
|
-
|
|
587
|
+
from typing_inspection.typing_objects import is_any, is_newtype, is_typealiastype
|
|
588
|
+
|
|
589
|
+
if isinstance(annotation, (types.GenericAlias, _BaseGenericAlias)):
|
|
591
590
|
return get_origin(annotation)[tuple(self.change_version_of_annotation(arg) for arg in get_args(annotation))]
|
|
592
|
-
elif
|
|
591
|
+
elif is_typealiastype(annotation):
|
|
593
592
|
if (
|
|
594
593
|
annotation.__module__ is not None and (annotation.__module__.startswith("pydantic."))
|
|
595
594
|
) or annotation.__name__ in _PYDANTIC_ALL_EXPORTED_NAMES:
|
|
@@ -616,7 +615,7 @@ class _AnnotationTransformer:
|
|
|
616
615
|
return getitem(
|
|
617
616
|
tuple(self.change_version_of_annotation(a) for a in get_args(annotation)),
|
|
618
617
|
)
|
|
619
|
-
elif annotation
|
|
618
|
+
elif is_any(annotation) or is_newtype(annotation):
|
|
620
619
|
return annotation
|
|
621
620
|
elif isinstance(annotation, type):
|
|
622
621
|
return self._change_version_of_type(annotation)
|
|
@@ -776,7 +775,7 @@ class SchemaGenerator:
|
|
|
776
775
|
wrapper = self._get_wrapper_for_model(model)
|
|
777
776
|
model_copy = wrapper.generate_model_copy(self)
|
|
778
777
|
self.concrete_models[model] = model_copy
|
|
779
|
-
return cast(type[_T_ANY_MODEL], model_copy)
|
|
778
|
+
return cast("type[_T_ANY_MODEL]", model_copy)
|
|
780
779
|
|
|
781
780
|
@overload
|
|
782
781
|
def _get_wrapper_for_model(self, model: type[BaseModel]) -> "_PydanticModelWrapper[BaseModel]": ...
|
|
@@ -865,7 +864,7 @@ def _apply_alter_schema_instructions(
|
|
|
865
864
|
elif isinstance(alter_schema_instruction, ValidatorExistedInstruction):
|
|
866
865
|
validator_name = get_name_of_function_wrapped_in_pydantic_validator(alter_schema_instruction.validator)
|
|
867
866
|
raw_validator = cast(
|
|
868
|
-
pydantic._internal._decorators.PydanticDescriptorProxy, alter_schema_instruction.validator
|
|
867
|
+
"pydantic._internal._decorators.PydanticDescriptorProxy", alter_schema_instruction.validator
|
|
869
868
|
)
|
|
870
869
|
schema_info.validators[validator_name] = _wrap_validator(
|
|
871
870
|
raw_validator.wrapped,
|
|
@@ -1041,15 +1040,19 @@ def _delete_field_attributes(
|
|
|
1041
1040
|
annotation: Any,
|
|
1042
1041
|
) -> None:
|
|
1043
1042
|
for attr_name in alter_schema_instruction.attributes:
|
|
1043
|
+
deleted = False
|
|
1044
|
+
|
|
1044
1045
|
if attr_name in field.passed_field_attributes:
|
|
1045
1046
|
field.delete_attribute(name=attr_name)
|
|
1046
|
-
|
|
1047
|
+
deleted = True
|
|
1048
|
+
if get_origin(annotation) == Annotated and any( # pragma: no branch
|
|
1047
1049
|
hasattr(sub_ann, attr_name) for sub_ann in get_args(annotation)
|
|
1048
1050
|
):
|
|
1049
1051
|
for sub_ann in get_args(annotation):
|
|
1050
1052
|
if hasattr(sub_ann, attr_name):
|
|
1051
1053
|
object.__setattr__(sub_ann, attr_name, None)
|
|
1052
|
-
|
|
1054
|
+
deleted = True
|
|
1055
|
+
if not deleted:
|
|
1053
1056
|
raise InvalidGenerationInstructionError(
|
|
1054
1057
|
f'You tried to delete the attribute "{attr_name}" of field "{alter_schema_instruction.name}" '
|
|
1055
1058
|
f'from "{model.name}" in "{version_change_name}" '
|
|
@@ -1100,7 +1103,7 @@ class _EnumWrapper(Generic[_T_ENUM]):
|
|
|
1100
1103
|
for attr_name, attr in initialization_namespace.items():
|
|
1101
1104
|
enum_dict[attr_name] = attr
|
|
1102
1105
|
enum_dict["__doc__"] = self.cls.__doc__
|
|
1103
|
-
model_copy = cast(type[_T_ENUM], type(self.name, self.cls.__bases__, enum_dict))
|
|
1106
|
+
model_copy = cast("type[_T_ENUM]", type(self.name, self.cls.__bases__, enum_dict))
|
|
1104
1107
|
model_copy.__cadwyn_original_model__ = self.cls # pyright: ignore[reportAttributeAccessIssue]
|
|
1105
1108
|
return model_copy
|
|
1106
1109
|
|
|
@@ -140,7 +140,7 @@ def convert_request_to_next_version_for(
|
|
|
140
140
|
if isinstance(schema_or_path, str):
|
|
141
141
|
return _AlterRequestByPathInstruction(
|
|
142
142
|
path=schema_or_path,
|
|
143
|
-
methods=set(cast(list, methods_or_second_schema)),
|
|
143
|
+
methods=set(cast("list", methods_or_second_schema)),
|
|
144
144
|
transformer=transformer,
|
|
145
145
|
)
|
|
146
146
|
else:
|
|
@@ -214,7 +214,7 @@ def convert_response_to_previous_version_for(
|
|
|
214
214
|
# The validation above checks that methods is not None
|
|
215
215
|
return _AlterResponseByPathInstruction(
|
|
216
216
|
path=schema_or_path,
|
|
217
|
-
methods=set(cast(list, methods_or_second_schema)),
|
|
217
|
+
methods=set(cast("list", methods_or_second_schema)),
|
|
218
218
|
transformer=transformer,
|
|
219
219
|
migrate_http_errors=migrate_http_errors,
|
|
220
220
|
)
|
|
@@ -246,7 +246,7 @@ class AlterFieldInstructionFactory:
|
|
|
246
246
|
info: Union[FieldInfo, Any, None] = None,
|
|
247
247
|
) -> FieldExistedAsInstruction:
|
|
248
248
|
if info is None:
|
|
249
|
-
info = cast(FieldInfo, Field())
|
|
249
|
+
info = cast("FieldInfo", Field())
|
|
250
250
|
info.annotation = type
|
|
251
251
|
return FieldExistedAsInstruction(is_hidden_from_changelog=False, schema=self.schema, name=self.name, field=info)
|
|
252
252
|
|
|
@@ -317,7 +317,7 @@ class AlterSchemaInstructionFactory:
|
|
|
317
317
|
def validator(
|
|
318
318
|
self, func: "Union[Callable[..., Any], classmethod[Any, Any, Any], PydanticDescriptorProxy]", /
|
|
319
319
|
) -> AlterValidatorInstructionFactory:
|
|
320
|
-
func = cast(Union[Callable[..., Any], PydanticDescriptorProxy], unwrap_wrapped_function(func))
|
|
320
|
+
func = cast("Union[Callable[..., Any], PydanticDescriptorProxy]", unwrap_wrapped_function(func))
|
|
321
321
|
|
|
322
322
|
if not isinstance(func, PydanticDescriptorProxy):
|
|
323
323
|
if hasattr(func, "__self__"):
|
|
@@ -620,7 +620,7 @@ class VersionBundle:
|
|
|
620
620
|
detail = response_info.body
|
|
621
621
|
if detail is None:
|
|
622
622
|
detail = http.HTTPStatus(response_info.status_code).phrase
|
|
623
|
-
raised_exception.detail = cast(str, detail)
|
|
623
|
+
raised_exception.detail = cast("str", detail)
|
|
624
624
|
raised_exception.headers = dict(response_info.headers)
|
|
625
625
|
raised_exception.status_code = response_info.status_code
|
|
626
626
|
|
|
@@ -408,6 +408,31 @@ print(BulkCreateUsersRequestBody is BulkCreateUsersResponseBody) # False
|
|
|
408
408
|
|
|
409
409
|
or to specify migrations using [endpoint path](#path-based-migration-specification) instead of a schema.
|
|
410
410
|
|
|
411
|
+
## Dependency re-execution warning
|
|
412
|
+
|
|
413
|
+
Notice that whenever a request comes to Cadwyn, we first validate it against the request's version of the schema, then we migrate it to the latest version, and then validate again to prevent migrations from creating invalid requests.
|
|
414
|
+
|
|
415
|
+
This means that if you have a dependency that is executed during the request validation, **it will be executed twice**. For example, if you have a dependency that checks whether a user exists in the database, it will be executed twice. This is not a problem if the dependency is idempotent but it is a problem if it is not.
|
|
416
|
+
|
|
417
|
+
To solve this problem, you can use the `cadwyn.current_dependency_solver` dependency which tell you whether your dependency is getting called before or after the request is migrated. If you want to run it once we migrated the request to the latest version, you should only run it when `current_dependency_solver` is `"cadwyn"`. If you want your dependency to run at the very beginning of handling the request, you should only run it when `current_dependency_solver` is `"fastapi"`.
|
|
418
|
+
|
|
419
|
+
```python
|
|
420
|
+
from cadwyn import current_dependency_solver
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def my_dependency(
|
|
424
|
+
dependency_solver: Annotated[
|
|
425
|
+
Literal["fastapi", "cadwyn"], Depends(current_dependency_solver)
|
|
426
|
+
]
|
|
427
|
+
):
|
|
428
|
+
if dependency_solver == "fastapi": # Before migration
|
|
429
|
+
...
|
|
430
|
+
else: # After migration
|
|
431
|
+
...
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
but the majority of your dependencies will not need this as most dependencies should not have side effects or network calls within them.
|
|
435
|
+
|
|
411
436
|
## Version changes with side effects
|
|
412
437
|
|
|
413
438
|
Sometimes you will use API versioning to handle a breaking change in your **business logic**, not in the schemas themselves. In such cases, it is tempting to add a version check and just follow the new business logic such as:
|
|
@@ -83,7 +83,8 @@ Let's say that we had a nullable `middle_name` field but we decided that it does
|
|
|
83
83
|
schema(BaseUser)
|
|
84
84
|
.field("middle_name")
|
|
85
85
|
.existed_as(
|
|
86
|
-
type=str | None,
|
|
86
|
+
type=str | None,
|
|
87
|
+
info=Field(description="User's Middle Name", default=None),
|
|
87
88
|
),
|
|
88
89
|
)
|
|
89
90
|
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg clip-rule="evenodd" fill-rule="evenodd" height="104.996mm" image-rendering="optimizeQuality" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" viewBox="0 0 2410.37 2560.05" width="108.373mm" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="objectBoundingBox" x1="50%" x2="50%" y1="100%" y2="0%"><stop offset="0" stop-color="#5a1ee2"/><stop offset="1" stop-color="#9f52ee"/></linearGradient><linearGradient id="b" gradientUnits="userSpaceOnUse" x1="1279.45" x2="1826.35" y1="1276.73" y2="1018.32"><stop offset="0" stop-color="#8740ea"/><stop offset="1" stop-color="#e791ff"/></linearGradient><path d="m0 0h2410.38v2560.05h-2410.38z" fill="none"/><g fill-rule="nonzero"><path d="m1246.08 741.76c-188.54 0-359.22 76.41-482.76 199.96-123.54 123.54-199.96 294.22-199.96 482.76s76.41 359.23 199.96 482.77c123.54 123.54 294.22 199.96 482.76 199.96s359.23-76.41 482.77-199.96c19.07-19.07 37.03-39.27 53.75-60.49 9.08-11.53 25.65-13.82 37.52-5.19l88.47 64.28c6.22 4.52 9.92 10.68 11 18.29s-.78 14.56-5.51 20.62c-21.48 27.55-44.64 53.74-69.31 78.41-153.22 153.22-364.9 247.99-598.68 247.99-233.79 0-445.46-94.77-598.68-247.99s-247.99-364.9-247.99-598.68c0-233.79 94.77-445.46 247.99-598.68s364.89-247.99 598.68-247.99c233.02 0 444.07 94.16 597.18 246.5l-141.94 91.37c-120.79-108.14-280.33-173.91-455.23-173.91z" fill="url(#a)"/><path d="m609.17 397.48 169.83 432.05-50.81 19.85-169.83-432.05z" fill="#974ced"/><path d="m1241.57 1320.13 556.05-431.46 66.61 86.25-556.05 431.47z" fill="url(#b)"/><path d="m463.33 1349.37 993.64 598.21-56.35 93.51-993.64-598.21z" fill="url(#a)"/><path d="m1407.66 1977.15c97.66-120.17 197.41-214.12 299.23-281.82 102.94-68.46 208.08-110.16 315.38-125.11l7.48 54.01c-99.04 13.8-196.64 52.66-292.75 116.58-97.25 64.66-192.95 154.92-287.06 270.72l-42.27-34.38z" fill="#884bd4"/></g><path d="m583.76 288.89c65.46 0 118.52 53.06 118.52 118.52s-53.06 118.52-118.52 118.52-118.52-53.06-118.52-118.52 53.06-118.52 118.52-118.52z" fill="#974ced"/><path d="m1830.93 669.87c144.66 0 261.92 117.27 261.92 261.92 0 144.66-117.27 261.92-261.92 261.92-144.66 0-261.92-117.27-261.92-261.92 0-144.66 117.27-261.92 261.92-261.92z" fill="#e791ff"/><path d="m1428.79 1846.58c81.61 0 147.76 66.16 147.76 147.76 0 81.61-66.16 147.76-147.76 147.76-81.61 0-147.76-66.16-147.76-147.76 0-81.61 66.16-147.76 147.76-147.76z" fill="#e791ff"/><path d="m2026.01 1484.86c62.06 0 112.36 50.31 112.36 112.36 0 62.06-50.31 112.36-112.36 112.36-62.06 0-112.36-50.31-112.36-112.36 0-62.06 50.31-112.36 112.36-112.36z" fill="#e791ff"/><path d="m435.16 1232.96c90.11 0 163.16 73.05 163.16 163.16s-73.05 163.16-163.16 163.16-163.16-73.05-163.16-163.16 73.05-163.16 163.16-163.16z" fill="#e791ff"/><path d="m1274.87 1267.83c52.7 0 95.43 42.73 95.43 95.43s-42.73 95.43-95.43 95.43-95.43-42.73-95.43-95.43 42.73-95.43 95.43-95.43z" fill="#8740ea"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg clip-rule="evenodd" fill-rule="evenodd" height="104.996mm" image-rendering="optimizeQuality" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" viewBox="0 0 14313.61 3866.19" width="108.373mm" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="objectBoundingBox" x1="50%" x2="50%" y1="100%" y2="0%"><stop offset="0" stop-color="#5a1ee2"/><stop offset="1" stop-color="#9f52ee"/></linearGradient><linearGradient id="b" gradientUnits="userSpaceOnUse" x1="1903.41" x2="2936.67" y1="1866.35" y2="1378.14"><stop offset="0" stop-color="#8740ea"/><stop offset="1" stop-color="#e791ff"/></linearGradient><g fill-rule="nonzero"><path d="m1840.35 855.62c-356.21 0-678.69 144.37-912.09 377.78-233.41 233.41-377.78 555.88-377.78 912.09s144.37 678.7 377.78 912.11 555.88 377.78 912.09 377.78 678.7-144.37 912.11-377.78c36.04-36.04 69.97-74.2 101.55-114.28 17.16-21.78 48.47-26.11 70.9-9.81l167.14 121.44c11.75 8.54 18.75 20.18 20.77 34.56 2.03 14.38-1.48 27.51-10.41 38.96-40.59 52.05-84.34 101.53-130.95 148.14-289.49 289.49-689.41 468.54-1131.11 468.54-441.71 0-841.61-179.05-1131.1-468.54s-468.54-689.41-468.54-1131.11c0-441.71 179.05-841.61 468.54-1131.1s689.39-468.54 1131.1-468.54c440.25 0 839 177.9 1128.26 465.72l-268.17 172.62c-228.21-204.31-529.64-328.58-860.09-328.58z" fill="url(#a)"/><path d="m637.02 205.17 320.87 816.28-95.99 37.5-320.86-816.28z" fill="#974ced"/><path d="m1831.83 1948.35 1050.57-815.18 125.85 162.96-1050.57 815.18z" fill="url(#b)"/><path d="m361.5 2003.59 1877.3 1130.22-106.48 176.67-1877.3-1130.22z" fill="url(#a)"/><path d="m2145.63 3189.67c184.51-227.04 372.98-404.54 565.34-532.45 194.49-129.34 393.12-208.13 595.86-236.38l14.13 102.04c-187.12 26.07-371.51 99.5-553.11 220.25-183.73 122.16-364.54 292.69-542.36 511.49z" fill="#884bd4"/></g><path d="m589.03 0c123.67 0 223.92 100.25 223.92 223.92s-100.25 223.92-223.92 223.92-223.92-100.25-223.92-223.92 100.25-223.92 223.92-223.92z" fill="#974ced"/><g fill="#e791ff"><path d="m2945.33 719.79c273.31 0 494.86 221.55 494.86 494.86s-221.55 494.86-494.86 494.86-494.86-221.55-494.86-494.86 221.55-494.86 494.86-494.86z"/><path d="m2185.56 2942.97c154.18 0 279.17 124.99 279.17 279.17s-124.99 279.17-279.17 279.17-279.17-124.99-279.17-279.17 124.99-279.17 279.17-279.17z"/><path d="m3313.89 2259.57c117.25 0 212.29 95.05 212.29 212.29 0 117.25-95.05 212.29-212.29 212.29-117.25 0-212.29-95.05-212.29-212.29 0-117.25 95.05-212.29 212.29-212.29z"/><path d="m308.26 1783.66c170.25 0 308.26 138.01 308.26 308.26s-138.01 308.26-308.26 308.26-308.26-138.01-308.26-308.26 138.01-308.26 308.26-308.26z"/></g><path d="m1894.75 1849.54c99.57 0 180.29 80.72 180.29 180.29s-80.72 180.29-180.29 180.29-180.29-80.72-180.29-180.29 80.72-180.29 180.29-180.29z" fill="#8740ea"/><path d="m4640.58 3233.75c173.84 0 329.7-59.95 443.61-155.86v125.89h341.69v-1519.65h-341.69v125.89c-113.9-95.91-269.76-155.86-443.61-155.86-452.6 0-785.31 344.7-785.31 791.3 0 443.61 332.71 788.3 785.31 788.3zm0-338.7c-263.77 0-443.61-191.83-443.61-449.6 0-260.77 179.84-452.6 443.61-452.6s443.61 191.83 443.61 452.6c0 257.78-179.84 449.6-443.61 449.6zm1182.6-452.6c0 446.6 332.71 791.3 785.31 791.3 173.84 0 329.7-59.95 443.61-155.86v125.89h341.69v-2098.14h-341.69v704.38c-113.9-95.91-269.76-155.86-443.61-155.86-452.6 0-785.31 344.7-785.31 788.3zm338.7 0c0-260.77 179.84-449.6 446.61-449.6 266.76 0 443.61 188.83 443.61 449.6s-179.84 452.6-443.61 452.6-446.61-191.83-446.61-452.6zm2903.08 155.86 287.74 605.46h257.78l725.35-1519.65h-374.66l-482.57 1001.11-284.75-608.46h-260.76l-284.75 608.46-479.58-1001.11h-377.67l728.35 1519.65h254.78zm3382.65-914.19h-377.67l-479.57 1013.1-479.58-1013.1h-377.67l668.41 1405.75-368.67 776.31h374.66l1040.08-2182.06zm735.99 761.32c0-275.75 143.88-452.6 395.65-452.6 251.78 0 392.65 177.05 392.65 452.6v758.33h341.7v-758.33c0-461.59-290.75-791.3-734.35-791.3-164.86 0-296.73 53.95-395.65 134.88v-104.91h-338.7v1519.65h338.7v-758.33z" fill="#fff" fill-rule="nonzero"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg clip-rule="evenodd" fill-rule="evenodd" height="104.996mm" image-rendering="optimizeQuality" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" viewBox="0 0 13833 4217.33" width="108.373mm" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="objectBoundingBox" x1="50%" x2="50%" y1="100%" y2="0%"><stop offset="0" stop-color="#5a1ee2"/><stop offset="1" stop-color="#9f52ee"/></linearGradient><linearGradient id="b" gradientUnits="userSpaceOnUse" x1="2346.93" x2="3249.83" y1="2002.05" y2="1575.43"><stop offset="0" stop-color="#8740ea"/><stop offset="1" stop-color="#e791ff"/></linearGradient><path d="m0-.01h13833.01v4217.33h-13833.01z" fill="#060320"/><g fill-rule="nonzero"><path d="m2291.83 1118.85c-311.27 0-593.06 126.16-797.01 330.11-203.96 203.96-330.11 485.75-330.11 797.01 0 311.27 126.16 593.06 330.11 797.02 203.96 203.96 485.75 330.11 797.01 330.11 311.27 0 593.06-126.16 797.02-330.11 31.49-31.49 61.14-64.84 88.74-99.86 15-19.03 42.35-22.82 61.95-8.57l146.05 106.12c10.27 7.46 16.39 17.63 18.15 30.2 1.78 12.57-1.29 24.04-9.09 34.04-35.47 45.48-73.7 88.72-114.43 129.45-252.96 252.96-602.43 409.42-988.4 409.42s-735.43-156.46-988.39-409.42-409.42-602.43-409.42-988.4 156.46-735.43 409.42-988.39 602.41-409.42 988.39-409.42c384.7 0 733.14 155.45 985.91 406.96l-234.34 150.84c-199.42-178.53-462.81-287.12-751.57-287.12z" fill="url(#a)"/><path d="m1240.33 550.46 280.38 713.29-83.87 32.77-280.39-713.29z" fill="#974ced"/><path d="m2284.39 2073.71 918.01-712.33 109.97 142.39-918.01 712.33z" fill="url(#b)"/><path d="m999.57 2121.97 1640.44 987.62-93.04 154.38-1640.45-987.62z" fill="url(#a)"/><path d="m2558.6 3158.4c161.23-198.39 325.92-353.5 494.01-465.27 169.95-113.02 343.52-181.87 520.68-206.56l12.34 89.16c-163.51 22.78-324.64 86.94-483.32 192.46-160.55 106.75-318.55 255.76-473.93 446.95l-69.78-56.75z" fill="#884bd4"/></g><path d="m1198.39 371.18c108.07 0 195.67 87.6 195.67 195.67s-87.6 195.67-195.67 195.67-195.67-87.6-195.67-195.67 87.6-195.67 195.67-195.67z" fill="#974ced"/><g fill="#e791ff"><path d="m3257.39 1000.15c238.82 0 432.42 193.6 432.42 432.42s-193.6 432.42-432.42 432.42-432.42-193.6-432.42-432.42 193.6-432.42 432.42-432.42z"/><path d="m2593.49 2942.83c134.73 0 243.95 109.22 243.95 243.95s-109.22 243.95-243.95 243.95-243.95-109.22-243.95-243.95 109.22-243.95 243.95-243.95z"/><path d="m3579.46 2345.65c102.45 0 185.51 83.05 185.51 185.51 0 102.45-83.05 185.51-185.51 185.51-102.45 0-185.51-83.05-185.51-185.51 0-102.45 83.05-185.51 185.51-185.51z"/><path d="m953.04 1929.79c148.77 0 269.36 120.6 269.36 269.36 0 148.77-120.6 269.36-269.36 269.36-148.77 0-269.36-120.6-269.36-269.36 0-148.77 120.6-269.36 269.36-269.36z"/></g><path d="m2339.37 1987.36c87.01 0 157.55 70.54 157.55 157.55s-70.54 157.55-157.55 157.55-157.55-70.54-157.55-157.55 70.54-157.55 157.55-157.55z" fill="#8740ea"/><path d="m4738.75 3196.92c151.91 0 288.11-52.39 387.63-136.2v110.01h298.58v-1327.91h-298.58v110.01c-99.53-83.81-235.73-136.2-387.63-136.2-395.49 0-686.22 301.21-686.22 691.46 0 387.63 290.73 688.84 686.22 688.84zm0-295.96c-230.49 0-387.63-167.62-387.63-392.88 0-227.87 157.15-395.49 387.63-395.49 230.49 0 387.63 167.62 387.63 395.49 0 225.25-157.15 392.88-387.63 392.88zm1033.39-395.49c0 390.25 290.73 691.46 686.22 691.46 151.91 0 288.11-52.39 387.63-136.2v110.01h298.58v-1833.41h-298.58v615.5c-99.53-83.81-235.73-136.2-387.63-136.2-395.49 0-686.22 301.21-686.22 688.84zm295.96 0c0-227.87 157.15-392.88 390.26-392.88 233.1 0 387.63 165.01 387.63 392.88s-157.15 395.49-387.63 395.49c-230.49 0-390.26-167.62-390.26-395.49zm2536.8 136.2 251.44 529.07h225.25l633.83-1327.91h-327.39l-421.69 874.8-248.82-531.69h-227.86l-248.82 531.69-419.07-874.8h-330.02l636.45 1327.91h222.64l254.05-529.07zm2955.86-798.85h-330.02l-419.06 885.28-419.07-885.28h-330.02l584.08 1228.38-322.16 678.37h327.39l908.85-1906.75zm643.13 665.26c0-240.96 125.72-395.49 345.73-395.49s343.11 154.71 343.11 395.49v662.65h298.59v-662.65c0-403.35-254.06-691.46-641.7-691.46-144.06 0-259.3 47.14-345.73 117.86v-91.67h-295.96v1327.91h295.96v-662.65z" fill="#fff" fill-rule="nonzero"/></svg>
|
|
@@ -36,9 +36,3 @@ Whether you are a newbie in API versioning, a pro looking for a sophisticated to
|
|
|
36
36
|
## Get started
|
|
37
37
|
|
|
38
38
|
It is recommended to read the [quickstart tutorial](https://docs.cadwyn.dev/quickstart/setup/) first to get your feet wet with Cadwyn's approach
|
|
39
|
-
|
|
40
|
-
## Sponsors
|
|
41
|
-
|
|
42
|
-
These are our gorgeous sponsors. They are using Cadwyn and are sponsoring it through various means. Contact [me](https://github.com/zmievsa) if you would like to become one too!
|
|
43
|
-
|
|
44
|
-
[](https://docs.monite.com/)
|
|
@@ -11,6 +11,9 @@ plugins:
|
|
|
11
11
|
hooks:
|
|
12
12
|
on_pre_build: "docs.plugin:on_pre_build"
|
|
13
13
|
|
|
14
|
+
extra_css:
|
|
15
|
+
- stylesheets/extra.css
|
|
16
|
+
|
|
14
17
|
markdown_extensions:
|
|
15
18
|
toc:
|
|
16
19
|
permalink: true
|
|
@@ -41,8 +44,12 @@ markdown_extensions:
|
|
|
41
44
|
md_in_html: null
|
|
42
45
|
mdx_include:
|
|
43
46
|
markdown_include_variants:
|
|
47
|
+
|
|
44
48
|
theme:
|
|
45
49
|
name: "material"
|
|
50
|
+
logo: img/logos/cadwyn_icon_transparent.svg
|
|
51
|
+
favicon: img/logos/cadwyn_icon_transparent.svg
|
|
52
|
+
|
|
46
53
|
features:
|
|
47
54
|
- content.code.annotate
|
|
48
55
|
- content.code.copy
|