cadwyn 4.4.5__tar.gz → 4.6.0a1__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-4.4.5 → cadwyn-4.6.0a1}/.github/actions/setup-python-uv/action.yaml +3 -3
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/.github/workflows/ci.yaml +41 -15
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/.github/workflows/daily_tests.yaml +2 -4
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/CHANGELOG.md +12 -0
- cadwyn-4.6.0a1/Makefile +11 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/PKG-INFO +1 -1
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/cadwyn/applications.py +2 -2
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/cadwyn/route_generation.py +7 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/cadwyn/schema_generation.py +37 -5
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/cadwyn/structure/data.py +17 -5
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/cadwyn/structure/schemas.py +64 -15
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/how_to/change_openapi_schemas/change_field_type.md +0 -1
- cadwyn-4.6.0a1/docs/how_to/change_openapi_schemas/change_schema_without_endpoint.md +17 -0
- cadwyn-4.6.0a1/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/block001.py +22 -0
- cadwyn-4.6.0a1/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/block002.py +22 -0
- cadwyn-4.6.0a1/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/tests/test_block001.py +15 -0
- cadwyn-4.6.0a1/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/tests/test_block002.py +14 -0
- cadwyn-4.6.0a1/docs_src/quickstart/tutorial/tests/test_block003.py +36 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/mkdocs.yml +1 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/pyproject.toml +6 -2
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/ruff.toml +3 -0
- cadwyn-4.6.0a1/tests/_resources/render/complex/__init__.py +0 -0
- cadwyn-4.6.0a1/tests/_resources/versioned_app/__init__.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/test_cli.py +2 -10
- cadwyn-4.6.0a1/tests/test_router_generation_with_from_future_annotations.py +62 -0
- cadwyn-4.6.0a1/tests/test_schema_generation/__init__.py +0 -0
- cadwyn-4.6.0a1/tests/tutorial/__init__.py +0 -0
- cadwyn-4.6.0a1/tox.ini +61 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/uv.lock +409 -377
- cadwyn-4.4.5/.github/workflows/validate_links.yaml +0 -41
- cadwyn-4.4.5/Makefile +0 -14
- cadwyn-4.4.5/docs_src/quickstart/tutorial/tests/test_block003.py +0 -21
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/.github/CODE_OF_CONDUCT.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/.github/workflows/publish_docs.yaml +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/.github/workflows/release.yaml +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/.gitignore +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/.pre-commit-config.yaml +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/LICENSE +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/README.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/cadwyn/__init__.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/cadwyn/__main__.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/cadwyn/_asts.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/cadwyn/_importer.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/cadwyn/_render.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/cadwyn/_utils.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/cadwyn/changelogs.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/cadwyn/exceptions.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/cadwyn/middleware.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/cadwyn/py.typed +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/cadwyn/routing.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/cadwyn/static/__init__.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/cadwyn/static/docs.html +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/cadwyn/structure/__init__.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/cadwyn/structure/common.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/cadwyn/structure/endpoints.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/cadwyn/structure/enums.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/cadwyn/structure/versions.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/CNAME +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/__init__.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/concepts/api_version_header_and_context_variables.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/concepts/beware_of_data_versioning.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/concepts/changelogs.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/concepts/cli.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/concepts/endpoint_migrations.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/concepts/enum_migrations.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/concepts/index.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/concepts/main_app.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/concepts/methodology.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/concepts/schema_generation.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/concepts/schema_migrations.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/concepts/testing.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/concepts/version_changes.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/home/CONTRIBUTING.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/how_to/change_business_logic/index.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/how_to/change_endpoints/index.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/how_to/change_openapi_schemas/add_field.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/how_to/change_openapi_schemas/changing_constraints.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/how_to/change_openapi_schemas/remove_field.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/how_to/change_openapi_schemas/rename_a_field_in_schema.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/how_to/index.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/img/dashboard_with_one_version.png +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/img/dashboard_with_two_versions.png +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/img/get_users_endpoint_from_prior_version.png +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/img/simplified_migration_model.png +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/img/sponsor_logos/monite.png +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/img/unversioned_dashboard.png +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/index.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/plugin.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/quickstart/setup.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/quickstart/tutorial.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/theory/how_to_build_versioning_framework.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/theory/how_we_got_here.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs/theory/literature.md +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs_src/__init__.py +0 -0
- {cadwyn-4.4.5/docs_src/quickstart → cadwyn-4.6.0a1/docs_src/how_to}/__init__.py +0 -0
- {cadwyn-4.4.5/docs_src/quickstart/setup → cadwyn-4.6.0a1/docs_src/how_to/change_openapi_schemas}/__init__.py +0 -0
- {cadwyn-4.4.5/docs_src/quickstart/setup/tests → cadwyn-4.6.0a1/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint}/__init__.py +0 -0
- {cadwyn-4.4.5/docs_src/quickstart/tutorial → cadwyn-4.6.0a1/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/tests}/__init__.py +0 -0
- {cadwyn-4.4.5/docs_src/quickstart/tutorial/tests → cadwyn-4.6.0a1/docs_src/quickstart}/__init__.py +0 -0
- {cadwyn-4.4.5/tests/_data → cadwyn-4.6.0a1/docs_src/quickstart/setup}/__init__.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs_src/quickstart/setup/block001.sh +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs_src/quickstart/setup/block002.py +0 -0
- {cadwyn-4.4.5/tests/_resources → cadwyn-4.6.0a1/docs_src/quickstart/setup/tests}/__init__.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs_src/quickstart/setup/tests/test_block002.py +0 -0
- {cadwyn-4.4.5/tests/_resources/render → cadwyn-4.6.0a1/docs_src/quickstart/tutorial}/__init__.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs_src/quickstart/tutorial/block001.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs_src/quickstart/tutorial/block002.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs_src/quickstart/tutorial/block003.py +0 -0
- {cadwyn-4.4.5/tests/_resources/render/complex → cadwyn-4.6.0a1/docs_src/quickstart/tutorial/tests}/__init__.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs_src/quickstart/tutorial/tests/test_block001.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/docs_src/quickstart/tutorial/tests/test_block002.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/scripts/fix_links.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/scripts/split_md.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/__init__.py +0 -0
- {cadwyn-4.4.5/tests/_resources/versioned_app → cadwyn-4.6.0a1/tests/_data}/__init__.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/_data/unversioned_schema_dir/__init__.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/_data/unversioned_schema_dir/unversioned_schemas.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/_data/unversioned_schemas.py +0 -0
- {cadwyn-4.4.5/tests/test_schema_generation → cadwyn-4.6.0a1/tests/_resources}/__init__.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/_resources/app_for_testing_routing.py +0 -0
- {cadwyn-4.4.5/tests/tutorial → cadwyn-4.6.0a1/tests/_resources/render}/__init__.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/_resources/render/classes.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/_resources/render/complex/classes.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/_resources/render/complex/versions.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/_resources/render/versions.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/_resources/utils.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/_resources/versioned_app/app.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/_resources/versioned_app/v2021_01_01.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/_resources/versioned_app/v2022_01_02.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/_resources/versioned_app/webhooks.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/conftest.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/test_applications.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/test_auth_dependencies.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/test_changelog.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/test_data_migrations.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/test_render.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/test_router_generation.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/test_routing.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/test_schema_generation/test_enum.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/test_schema_generation/test_schema.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/test_schema_generation/test_schema_field.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/test_schema_generation/test_schema_validator.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/test_structure.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/tutorial/main.py +0 -0
- {cadwyn-4.4.5 → cadwyn-4.6.0a1}/tests/tutorial/test_example.py +0 -0
|
@@ -8,7 +8,7 @@ inputs:
|
|
|
8
8
|
uv-version:
|
|
9
9
|
description: "The uv version to set up"
|
|
10
10
|
required: true
|
|
11
|
-
default: "0.
|
|
11
|
+
default: "0.5.8"
|
|
12
12
|
|
|
13
13
|
runs:
|
|
14
14
|
using: "composite"
|
|
@@ -16,12 +16,12 @@ runs:
|
|
|
16
16
|
- uses: actions/setup-python@v5
|
|
17
17
|
with:
|
|
18
18
|
python-version: ${{ inputs.python-version }}
|
|
19
|
-
- uses: astral-sh/setup-uv@
|
|
19
|
+
- uses: astral-sh/setup-uv@v5
|
|
20
20
|
with:
|
|
21
21
|
version: ${{ inputs.uv-version }}
|
|
22
22
|
enable-cache: true
|
|
23
23
|
cache-dependency-glob: "uv.lock"
|
|
24
24
|
- run: |
|
|
25
25
|
uv sync --frozen --all-extras --dev
|
|
26
|
-
|
|
26
|
+
uv pip install --system tox tox-uv
|
|
27
27
|
shell: bash
|
|
@@ -10,7 +10,7 @@ on:
|
|
|
10
10
|
branches: [main, 3.x.x]
|
|
11
11
|
types: [opened, synchronize]
|
|
12
12
|
paths:
|
|
13
|
-
- ".github/workflows/ci.yaml"
|
|
13
|
+
- ".github/workflows/ci.yaml" # self
|
|
14
14
|
- "**.py"
|
|
15
15
|
- "**.toml"
|
|
16
16
|
- "**.lock"
|
|
@@ -41,13 +41,13 @@ jobs:
|
|
|
41
41
|
- uses: ./.github/actions/setup-python-uv
|
|
42
42
|
with:
|
|
43
43
|
python-version: ${{ matrix.python-version }}
|
|
44
|
-
- run:
|
|
44
|
+
- run: tox run -e py
|
|
45
45
|
- name: Upload coverage results
|
|
46
46
|
uses: actions/upload-artifact@v4
|
|
47
47
|
if: matrix.os == 'ubuntu-latest' # Cross-platform coverage combination doesn't work
|
|
48
48
|
with:
|
|
49
49
|
name: coverage-results-${{ matrix.python-version }}
|
|
50
|
-
path: coverage
|
|
50
|
+
path: .coverage*
|
|
51
51
|
Tutorial-tests:
|
|
52
52
|
runs-on: ubuntu-latest
|
|
53
53
|
name: Tutorial tests
|
|
@@ -58,13 +58,15 @@ jobs:
|
|
|
58
58
|
python-version: "3.10"
|
|
59
59
|
- name: Install cadwyn with instructions from docs
|
|
60
60
|
run: sh docs_src/quickstart/setup/block001.sh
|
|
61
|
-
- run:
|
|
62
|
-
|
|
61
|
+
- run: |
|
|
62
|
+
pip install uv
|
|
63
|
+
uv pip install --system pytest coverage dirty-equals
|
|
64
|
+
coverage run -m pytest docs_src
|
|
63
65
|
- name: Upload coverage results
|
|
64
66
|
uses: actions/upload-artifact@v4
|
|
65
67
|
with:
|
|
66
68
|
name: coverage-results-docs
|
|
67
|
-
path: coverage
|
|
69
|
+
path: .coverage*
|
|
68
70
|
Coverage:
|
|
69
71
|
needs: [Tests, Tutorial-tests]
|
|
70
72
|
runs-on: ubuntu-latest
|
|
@@ -75,32 +77,56 @@ jobs:
|
|
|
75
77
|
with:
|
|
76
78
|
pattern: coverage-results-*
|
|
77
79
|
merge-multiple: true
|
|
78
|
-
path:
|
|
80
|
+
path: .
|
|
79
81
|
- uses: actions/setup-python@v5
|
|
80
82
|
with:
|
|
81
83
|
python-version: "3.10"
|
|
82
|
-
- run:
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
- run: |
|
|
85
|
+
pip install uv
|
|
86
|
+
uv pip install --system tox tox-uv
|
|
87
|
+
tox run -e coverage_report-ci
|
|
85
88
|
- name: Upload to Codecov
|
|
86
89
|
uses: codecov/codecov-action@v4
|
|
87
90
|
env:
|
|
88
91
|
fail_ci_if_error: true
|
|
89
92
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
|
90
|
-
- run: coverage report --fail-under=100 --show-missing
|
|
91
93
|
|
|
92
94
|
Lint:
|
|
93
95
|
runs-on: ubuntu-latest
|
|
94
96
|
steps:
|
|
95
97
|
- uses: actions/checkout@v4
|
|
96
|
-
- uses:
|
|
98
|
+
- uses: actions/setup-python@v5
|
|
99
|
+
with:
|
|
100
|
+
python-version: "3.10"
|
|
101
|
+
- run: |
|
|
102
|
+
python -m pip install uv
|
|
103
|
+
uv pip install --system pre-commit pre-commit-uv
|
|
104
|
+
pre-commit run --all-files
|
|
97
105
|
|
|
98
|
-
|
|
106
|
+
Validate-links:
|
|
99
107
|
runs-on: ubuntu-latest
|
|
100
108
|
steps:
|
|
101
109
|
- uses: actions/checkout@v4
|
|
102
110
|
- uses: ./.github/actions/setup-python-uv
|
|
111
|
+
with:
|
|
112
|
+
# When this version is updated,
|
|
113
|
+
# update the pyright `base_python` version in `tox.ini`, too.
|
|
114
|
+
python-version: "3.10"
|
|
115
|
+
- run: tox run -e docs
|
|
116
|
+
- name: Validate links
|
|
117
|
+
uses: umbrelladocs/action-linkspector@v1
|
|
118
|
+
with:
|
|
119
|
+
reporter: github-pr-review
|
|
120
|
+
filter_mode: diff_context
|
|
121
|
+
fail_level: any
|
|
103
122
|
|
|
104
|
-
|
|
123
|
+
Typecheck:
|
|
124
|
+
runs-on: ubuntu-latest
|
|
125
|
+
steps:
|
|
126
|
+
- uses: actions/checkout@v4
|
|
127
|
+
- uses: ./.github/actions/setup-python-uv
|
|
105
128
|
with:
|
|
106
|
-
|
|
129
|
+
# When this version is updated,
|
|
130
|
+
# update the pyright `base_python` version in `tox.ini`, too.
|
|
131
|
+
python-version: "3.10"
|
|
132
|
+
- run: tox run -e pyright
|
|
@@ -21,10 +21,8 @@ jobs:
|
|
|
21
21
|
with:
|
|
22
22
|
python-version: ${{ matrix.python-version }}
|
|
23
23
|
- run: uv sync --refresh --all-extras --dev --upgrade
|
|
24
|
-
- run: pytest .
|
|
25
|
-
-
|
|
26
|
-
with:
|
|
27
|
-
pylance-version: latest-release
|
|
24
|
+
- run: uv run pytest . # We intentionally don't use tox here to run tests "as is"
|
|
25
|
+
- run: uv run pyright --version && uv run pyright .
|
|
28
26
|
|
|
29
27
|
notify-on-failure:
|
|
30
28
|
name: Notify on failure
|
|
@@ -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
|
+
### Added
|
|
9
|
+
|
|
10
|
+
* Added support for more field attributes in `schema.had()` and `schema.didnt_have()`: `field_title_generator`, `fail_fast`, `coerce_numbers_to_str`, `union_mode`, `allow_mutation`, `pattern`, `discriminator`
|
|
11
|
+
* Added support for forwardrefs in body fields (for example, when you use `from __future__ import annotations` in the file with your routes)
|
|
12
|
+
* Added support for forwardrefs in route dependencies
|
|
13
|
+
|
|
14
|
+
## [4.5.0]
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
* `check_usage` argument to request/response by schema converters. Cadwyn always checks whether a schema mentioned in a converter applies to one or more endpoints to guarantee that the converter will apply to at least one endpoint. Sometimes, however, you do not need this validation. For example, when you use these converters for converting webhook bodies. Setting `check_usage=False` makes it possible to skip the validation
|
|
19
|
+
|
|
8
20
|
## [4.4.5]
|
|
9
21
|
|
|
10
22
|
### Fixed
|
cadwyn-4.6.0a1/Makefile
ADDED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cadwyn
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.6.0a1
|
|
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
|
|
@@ -279,7 +279,7 @@ class Cadwyn(FastAPI):
|
|
|
279
279
|
except (ValueError, TypeError):
|
|
280
280
|
version = raw_version
|
|
281
281
|
|
|
282
|
-
if version in self.router.versioned_routers:
|
|
282
|
+
if isinstance(version, date) and version in self.router.versioned_routers:
|
|
283
283
|
routes = self.router.versioned_routers[version].routes
|
|
284
284
|
formatted_version = version.isoformat()
|
|
285
285
|
elif version == "unversioned" and self._there_are_public_unversioned_routes():
|
|
@@ -296,7 +296,7 @@ class Cadwyn(FastAPI):
|
|
|
296
296
|
self.servers.insert(0, {"url": root_path})
|
|
297
297
|
|
|
298
298
|
webhook_routes = None
|
|
299
|
-
if version in self._versioned_webhook_routers:
|
|
299
|
+
if isinstance(version, date) and version in self._versioned_webhook_routers:
|
|
300
300
|
webhook_routes = self._versioned_webhook_routers[version].routes
|
|
301
301
|
|
|
302
302
|
return JSONResponse(
|
|
@@ -221,6 +221,8 @@ class _EndpointTransformer(Generic[_R, _WR]):
|
|
|
221
221
|
|
|
222
222
|
for by_schema_converters in version_change.alter_request_by_schema_instructions.values():
|
|
223
223
|
for by_schema_converter in by_schema_converters:
|
|
224
|
+
if not by_schema_converter.check_usage: # pragma: no cover
|
|
225
|
+
continue
|
|
224
226
|
missing_models = set(by_schema_converter.schemas) - head_request_bodies
|
|
225
227
|
if missing_models:
|
|
226
228
|
raise RouteRequestBySchemaConverterDoesNotApplyToAnythingError(
|
|
@@ -232,6 +234,8 @@ class _EndpointTransformer(Generic[_R, _WR]):
|
|
|
232
234
|
)
|
|
233
235
|
for by_schema_converters in version_change.alter_response_by_schema_instructions.values():
|
|
234
236
|
for by_schema_converter in by_schema_converters:
|
|
237
|
+
if not by_schema_converter.check_usage: # pragma: no cover
|
|
238
|
+
continue
|
|
235
239
|
missing_models = set(by_schema_converter.schemas) - head_response_models
|
|
236
240
|
if missing_models:
|
|
237
241
|
raise RouteResponseBySchemaConverterDoesNotApplyToAnythingError(
|
|
@@ -240,6 +244,9 @@ class _EndpointTransformer(Generic[_R, _WR]):
|
|
|
240
244
|
f"failed to find routes with the following response models: "
|
|
241
245
|
f"{[m.__name__ for m in missing_models]}. "
|
|
242
246
|
f"This means that you are trying to apply this converter to non-existing endpoint(s). "
|
|
247
|
+
"If this is intentional and this converter really does not apply to any endpoints, then "
|
|
248
|
+
"pass check_usage=False argument to "
|
|
249
|
+
f"{version_change.__name__}.{by_schema_converter.transformer.__name__}"
|
|
243
250
|
)
|
|
244
251
|
|
|
245
252
|
def _extract_all_routes_identifiers(
|
|
@@ -40,6 +40,7 @@ from pydantic._internal._decorators import (
|
|
|
40
40
|
RootValidatorDecoratorInfo,
|
|
41
41
|
ValidatorDecoratorInfo,
|
|
42
42
|
)
|
|
43
|
+
from pydantic._internal._typing_extra import try_eval_type as pydantic_try_eval_type
|
|
43
44
|
from pydantic.fields import ComputedFieldInfo, FieldInfo
|
|
44
45
|
from typing_extensions import Doc, Self, _AnnotatedAlias, assert_never
|
|
45
46
|
|
|
@@ -158,7 +159,7 @@ def migrate_response_body(
|
|
|
158
159
|
*,
|
|
159
160
|
latest_body: Any,
|
|
160
161
|
version: VersionDate | str,
|
|
161
|
-
):
|
|
162
|
+
) -> Any:
|
|
162
163
|
"""Convert the data to a specific version
|
|
163
164
|
|
|
164
165
|
Apply all version changes from latest until the passed version in reverse order
|
|
@@ -231,6 +232,12 @@ def _is_dunder(attr_name: str):
|
|
|
231
232
|
|
|
232
233
|
|
|
233
234
|
def _wrap_pydantic_model(model: type[_T_PYDANTIC_MODEL]) -> "_PydanticModelWrapper[_T_PYDANTIC_MODEL]":
|
|
235
|
+
# In case we have a forwardref within one of the fields
|
|
236
|
+
# For example, when "from __future__ import annotations" is used in the file with the schema
|
|
237
|
+
if model is not BaseModel:
|
|
238
|
+
model.model_rebuild(raise_errors=False)
|
|
239
|
+
model = cast(type[_T_PYDANTIC_MODEL], model)
|
|
240
|
+
|
|
234
241
|
decorators = _get_model_decorators(model)
|
|
235
242
|
validators = {}
|
|
236
243
|
for decorator_wrapper in decorators:
|
|
@@ -239,8 +246,20 @@ def _wrap_pydantic_model(model: type[_T_PYDANTIC_MODEL]) -> "_PydanticModelWrapp
|
|
|
239
246
|
|
|
240
247
|
wrapped_validator = _wrap_validator(decorator_wrapper.func, decorator_wrapper.shim, decorator_wrapper.info)
|
|
241
248
|
validators[decorator_wrapper.cls_var_name] = wrapped_validator
|
|
249
|
+
|
|
250
|
+
annotations = {
|
|
251
|
+
name: value
|
|
252
|
+
if not isinstance(value, str)
|
|
253
|
+
else model.model_fields[name].annotation or model.__annotations__[name]
|
|
254
|
+
for name, value in model.__annotations__.items()
|
|
255
|
+
}
|
|
256
|
+
|
|
242
257
|
fields = {
|
|
243
|
-
field_name: PydanticFieldWrapper(
|
|
258
|
+
field_name: PydanticFieldWrapper(
|
|
259
|
+
model.model_fields[field_name],
|
|
260
|
+
annotations[field_name],
|
|
261
|
+
field_name,
|
|
262
|
+
)
|
|
244
263
|
for field_name in model.__annotations__
|
|
245
264
|
}
|
|
246
265
|
|
|
@@ -263,7 +282,7 @@ def _wrap_pydantic_model(model: type[_T_PYDANTIC_MODEL]) -> "_PydanticModelWrapp
|
|
|
263
282
|
fields=fields,
|
|
264
283
|
other_attributes=other_attributes,
|
|
265
284
|
validators=validators,
|
|
266
|
-
annotations=
|
|
285
|
+
annotations=annotations,
|
|
267
286
|
)
|
|
268
287
|
|
|
269
288
|
|
|
@@ -355,6 +374,7 @@ class _PydanticModelWrapper(Generic[_T_PYDANTIC_MODEL]):
|
|
|
355
374
|
if not validator.is_deleted and type(validator) == _ValidatorWrapper # noqa: E721
|
|
356
375
|
}
|
|
357
376
|
fields = {name: field.generate_field_copy(generator) for name, field in self.fields.items()}
|
|
377
|
+
|
|
358
378
|
model_copy = type(self.cls)(
|
|
359
379
|
self.name,
|
|
360
380
|
tuple(generator[cast(type[BaseModel], base)] for base in self.cls.__bases__),
|
|
@@ -418,6 +438,7 @@ class _AnnotationTransformer:
|
|
|
418
438
|
# because such copies could produce weird behaviors at runtime, especially if you/fastapi do any comparisons.
|
|
419
439
|
# It's defined here and not on the method because of this: https://youtu.be/sVjtp6tGo0g
|
|
420
440
|
self.generator = generator
|
|
441
|
+
# TODO: Rewrite this to memoize
|
|
421
442
|
self.change_versions_of_a_non_container_annotation = functools.cache(
|
|
422
443
|
self._change_version_of_a_non_container_annotation
|
|
423
444
|
)
|
|
@@ -531,6 +552,9 @@ class _AnnotationTransformer:
|
|
|
531
552
|
annotation_modifying_wrapper = annotation_modifying_wrapper_factory(call)
|
|
532
553
|
old_params = inspect.signature(call).parameters
|
|
533
554
|
callable_annotations = annotation_modifying_wrapper.__annotations__
|
|
555
|
+
callable_annotations = {
|
|
556
|
+
k: v if type(v) is not str else _try_eval_type(v, call.__globals__) for k, v in callable_annotations.items()
|
|
557
|
+
}
|
|
534
558
|
annotation_modifying_wrapper.__annotations__ = modify_annotations(callable_annotations)
|
|
535
559
|
annotation_modifying_wrapper.__defaults__ = modify_defaults(
|
|
536
560
|
tuple(p.default for p in old_params.values() if p.default is not inspect.Signature.empty),
|
|
@@ -631,8 +655,6 @@ class SchemaGenerator:
|
|
|
631
655
|
|
|
632
656
|
if model in self.concrete_models:
|
|
633
657
|
return self.concrete_models[model]
|
|
634
|
-
else:
|
|
635
|
-
wrapper = self._get_wrapper_for_model(model)
|
|
636
658
|
|
|
637
659
|
wrapper = self._get_wrapper_for_model(model)
|
|
638
660
|
model_copy = wrapper.generate_model_copy(self)
|
|
@@ -655,6 +677,8 @@ class SchemaGenerator:
|
|
|
655
677
|
return self.model_bundle.enums[model]
|
|
656
678
|
|
|
657
679
|
if lenient_issubclass(model, BaseModel):
|
|
680
|
+
# TODO: My god, what if one of its fields is in our concrete schemas and we don't use it? :O
|
|
681
|
+
# TODO: Add an argument with our concrete schemas for _wrap_pydantic_model
|
|
658
682
|
wrapper = _wrap_pydantic_model(model)
|
|
659
683
|
self.model_bundle.schemas[model] = wrapper
|
|
660
684
|
elif lenient_issubclass(model, Enum):
|
|
@@ -977,3 +1001,11 @@ class _EnumWrapper(Generic[_T_ENUM]):
|
|
|
977
1001
|
and k not in _DummyEnum.__dict__
|
|
978
1002
|
and (k not in mro_dict or mro_dict[k] is not v)
|
|
979
1003
|
}
|
|
1004
|
+
|
|
1005
|
+
|
|
1006
|
+
def _try_eval_type(value: Any, globals: dict[str, Any]) -> Any:
|
|
1007
|
+
new_value, success = pydantic_try_eval_type(value, globals)
|
|
1008
|
+
if success:
|
|
1009
|
+
return new_value
|
|
1010
|
+
else: # pragma: no cover # Can't imagine when this would happen
|
|
1011
|
+
return value
|
|
@@ -86,6 +86,12 @@ class _AlterDataInstruction:
|
|
|
86
86
|
return self.transformer(__request_or_response)
|
|
87
87
|
|
|
88
88
|
|
|
89
|
+
@dataclass
|
|
90
|
+
class _BaseAlterBySchemaInstruction:
|
|
91
|
+
schemas: tuple[Any, ...]
|
|
92
|
+
check_usage: bool = True
|
|
93
|
+
|
|
94
|
+
|
|
89
95
|
##########
|
|
90
96
|
# Requests
|
|
91
97
|
##########
|
|
@@ -97,8 +103,7 @@ class _BaseAlterRequestInstruction(_AlterDataInstruction):
|
|
|
97
103
|
|
|
98
104
|
|
|
99
105
|
@dataclass
|
|
100
|
-
class _AlterRequestBySchemaInstruction(_BaseAlterRequestInstruction):
|
|
101
|
-
schemas: tuple[Any, ...]
|
|
106
|
+
class _AlterRequestBySchemaInstruction(_BaseAlterBySchemaInstruction, _BaseAlterRequestInstruction): ...
|
|
102
107
|
|
|
103
108
|
|
|
104
109
|
@dataclass
|
|
@@ -110,7 +115,10 @@ class _AlterRequestByPathInstruction(_BaseAlterRequestInstruction):
|
|
|
110
115
|
|
|
111
116
|
@overload
|
|
112
117
|
def convert_request_to_next_version_for(
|
|
113
|
-
first_schema: type,
|
|
118
|
+
first_schema: type,
|
|
119
|
+
/,
|
|
120
|
+
*additional_schemas: type,
|
|
121
|
+
check_usage: bool = True,
|
|
114
122
|
) -> "type[staticmethod[_P, None]]": ...
|
|
115
123
|
|
|
116
124
|
|
|
@@ -123,6 +131,7 @@ def convert_request_to_next_version_for(
|
|
|
123
131
|
methods_or_second_schema: list[str] | None | type = None,
|
|
124
132
|
/,
|
|
125
133
|
*additional_schemas: type,
|
|
134
|
+
check_usage: bool = True,
|
|
126
135
|
) -> "type[staticmethod[_P, None]]":
|
|
127
136
|
_validate_decorator_args(schema_or_path, methods_or_second_schema, additional_schemas)
|
|
128
137
|
|
|
@@ -141,6 +150,7 @@ def convert_request_to_next_version_for(
|
|
|
141
150
|
return _AlterRequestBySchemaInstruction(
|
|
142
151
|
schemas=schemas,
|
|
143
152
|
transformer=transformer,
|
|
153
|
+
check_usage=check_usage,
|
|
144
154
|
)
|
|
145
155
|
|
|
146
156
|
return decorator # pyright: ignore[reportReturnType]
|
|
@@ -158,8 +168,7 @@ class _BaseAlterResponseInstruction(_AlterDataInstruction):
|
|
|
158
168
|
|
|
159
169
|
|
|
160
170
|
@dataclass
|
|
161
|
-
class _AlterResponseBySchemaInstruction(_BaseAlterResponseInstruction):
|
|
162
|
-
schemas: tuple[Any, ...]
|
|
171
|
+
class _AlterResponseBySchemaInstruction(_BaseAlterBySchemaInstruction, _BaseAlterResponseInstruction): ...
|
|
163
172
|
|
|
164
173
|
|
|
165
174
|
@dataclass
|
|
@@ -175,6 +184,7 @@ def convert_response_to_previous_version_for(
|
|
|
175
184
|
/,
|
|
176
185
|
*schemas: type,
|
|
177
186
|
migrate_http_errors: bool = False,
|
|
187
|
+
check_usage: bool = True,
|
|
178
188
|
) -> "type[staticmethod[_P, None]]": ...
|
|
179
189
|
|
|
180
190
|
|
|
@@ -194,6 +204,7 @@ def convert_response_to_previous_version_for(
|
|
|
194
204
|
/,
|
|
195
205
|
*additional_schemas: type,
|
|
196
206
|
migrate_http_errors: bool = False,
|
|
207
|
+
check_usage: bool = True,
|
|
197
208
|
) -> "type[staticmethod[_P, None]]":
|
|
198
209
|
_validate_decorator_args(schema_or_path, methods_or_second_schema, additional_schemas)
|
|
199
210
|
|
|
@@ -215,6 +226,7 @@ def convert_response_to_previous_version_for(
|
|
|
215
226
|
schemas=schemas,
|
|
216
227
|
transformer=transformer,
|
|
217
228
|
migrate_http_errors=migrate_http_errors,
|
|
229
|
+
check_usage=check_usage,
|
|
218
230
|
)
|
|
219
231
|
|
|
220
232
|
return decorator # pyright: ignore[reportReturnType]
|
|
@@ -3,7 +3,7 @@ from dataclasses import dataclass
|
|
|
3
3
|
from typing import TYPE_CHECKING, Any, Literal, cast
|
|
4
4
|
|
|
5
5
|
from issubclass import issubclass as lenient_issubclass
|
|
6
|
-
from pydantic import BaseModel, Field
|
|
6
|
+
from pydantic import AliasChoices, AliasPath, BaseModel, Field
|
|
7
7
|
from pydantic._internal._decorators import PydanticDescriptorProxy, unwrap_wrapped_function
|
|
8
8
|
from pydantic.fields import FieldInfo
|
|
9
9
|
|
|
@@ -18,58 +18,83 @@ if TYPE_CHECKING:
|
|
|
18
18
|
|
|
19
19
|
PossibleFieldAttributes = Literal[
|
|
20
20
|
"default",
|
|
21
|
-
"default_factory",
|
|
22
21
|
"alias",
|
|
22
|
+
"alias_priority",
|
|
23
|
+
"default_factory",
|
|
24
|
+
"validation_alias",
|
|
25
|
+
"serialization_alias",
|
|
23
26
|
"title",
|
|
27
|
+
"field_title_generator",
|
|
24
28
|
"description",
|
|
29
|
+
"examples",
|
|
25
30
|
"exclude",
|
|
26
31
|
"const",
|
|
32
|
+
"deprecated",
|
|
33
|
+
"frozen",
|
|
34
|
+
"validate_default",
|
|
35
|
+
"repr",
|
|
36
|
+
"init",
|
|
37
|
+
"init_var",
|
|
38
|
+
"kw_only",
|
|
39
|
+
"fail_fast",
|
|
27
40
|
"gt",
|
|
28
41
|
"ge",
|
|
29
42
|
"lt",
|
|
30
43
|
"le",
|
|
31
|
-
"deprecated",
|
|
32
|
-
"fail_fast",
|
|
33
44
|
"strict",
|
|
45
|
+
"coerce_numbers_to_str",
|
|
34
46
|
"multiple_of",
|
|
35
47
|
"allow_inf_nan",
|
|
36
48
|
"max_digits",
|
|
37
49
|
"decimal_places",
|
|
38
50
|
"min_length",
|
|
39
51
|
"max_length",
|
|
52
|
+
"union_mode",
|
|
40
53
|
"allow_mutation",
|
|
41
54
|
"pattern",
|
|
42
55
|
"discriminator",
|
|
43
|
-
"repr",
|
|
44
56
|
]
|
|
45
57
|
|
|
46
58
|
|
|
59
|
+
# TODO: Add json_schema_extra as a breaking change in a major version
|
|
47
60
|
@dataclass(slots=True)
|
|
48
61
|
class FieldChanges:
|
|
49
62
|
default: Any
|
|
63
|
+
alias: str | None
|
|
50
64
|
default_factory: Any
|
|
51
|
-
|
|
52
|
-
|
|
65
|
+
alias_priority: int | None
|
|
66
|
+
validation_alias: str | AliasPath | AliasChoices | None
|
|
67
|
+
serialization_alias: str | None
|
|
68
|
+
title: str | None
|
|
69
|
+
field_title_generator: Callable[[str, FieldInfo], str] | None
|
|
53
70
|
description: str
|
|
71
|
+
examples: list[Any] | None
|
|
54
72
|
exclude: "AbstractSetIntStr | MappingIntStrAny | Any"
|
|
55
73
|
const: bool
|
|
56
74
|
deprecated: bool
|
|
75
|
+
frozen: bool | None
|
|
76
|
+
validate_default: bool | None
|
|
77
|
+
repr: bool
|
|
78
|
+
init: bool | None
|
|
79
|
+
init_var: bool | None
|
|
80
|
+
kw_only: bool | None
|
|
57
81
|
fail_fast: bool
|
|
58
82
|
gt: float
|
|
59
83
|
ge: float
|
|
60
84
|
lt: float
|
|
61
85
|
le: float
|
|
62
86
|
strict: bool
|
|
87
|
+
coerce_numbers_to_str: bool | None
|
|
63
88
|
multiple_of: float
|
|
64
89
|
allow_inf_nan: bool
|
|
65
90
|
max_digits: int
|
|
66
91
|
decimal_places: int
|
|
67
92
|
min_length: int
|
|
68
93
|
max_length: int
|
|
94
|
+
union_mode: Literal["smart", "left_to_right"]
|
|
69
95
|
allow_mutation: bool
|
|
70
96
|
pattern: str
|
|
71
97
|
discriminator: str
|
|
72
|
-
repr: bool
|
|
73
98
|
|
|
74
99
|
|
|
75
100
|
@dataclass(slots=True)
|
|
@@ -113,29 +138,41 @@ class AlterFieldInstructionFactory:
|
|
|
113
138
|
name: str = Sentinel,
|
|
114
139
|
type: Any = Sentinel,
|
|
115
140
|
default: Any = Sentinel,
|
|
141
|
+
alias: str | None = Sentinel,
|
|
116
142
|
default_factory: Callable = Sentinel,
|
|
117
|
-
|
|
143
|
+
alias_priority: int = Sentinel,
|
|
144
|
+
validation_alias: str = Sentinel,
|
|
145
|
+
serialization_alias: str = Sentinel,
|
|
118
146
|
title: str = Sentinel,
|
|
147
|
+
field_title_generator: Callable[[str, FieldInfo], str] = Sentinel,
|
|
119
148
|
description: str = Sentinel,
|
|
149
|
+
examples: list[Any] = Sentinel,
|
|
120
150
|
exclude: "AbstractSetIntStr | MappingIntStrAny | Any" = Sentinel,
|
|
121
151
|
const: bool = Sentinel,
|
|
152
|
+
deprecated: bool = Sentinel,
|
|
153
|
+
frozen: bool = Sentinel,
|
|
154
|
+
validate_default: bool = Sentinel,
|
|
155
|
+
repr: bool = Sentinel,
|
|
156
|
+
init: bool = Sentinel,
|
|
157
|
+
init_var: bool = Sentinel,
|
|
158
|
+
kw_only: bool = Sentinel,
|
|
159
|
+
fail_fast: bool = Sentinel,
|
|
122
160
|
gt: float = Sentinel,
|
|
123
161
|
ge: float = Sentinel,
|
|
124
162
|
lt: float = Sentinel,
|
|
125
163
|
le: float = Sentinel,
|
|
126
164
|
strict: bool = Sentinel,
|
|
127
|
-
|
|
165
|
+
coerce_numbers_to_str: bool = Sentinel,
|
|
128
166
|
multiple_of: float = Sentinel,
|
|
129
167
|
allow_inf_nan: bool = Sentinel,
|
|
130
168
|
max_digits: int = Sentinel,
|
|
131
169
|
decimal_places: int = Sentinel,
|
|
132
170
|
min_length: int = Sentinel,
|
|
133
171
|
max_length: int = Sentinel,
|
|
172
|
+
union_mode: Literal["smart", "left_to_right"] = Sentinel,
|
|
134
173
|
allow_mutation: bool = Sentinel,
|
|
135
174
|
pattern: str = Sentinel,
|
|
136
175
|
discriminator: str = Sentinel,
|
|
137
|
-
repr: bool = Sentinel,
|
|
138
|
-
fail_fast: bool = Sentinel,
|
|
139
176
|
) -> FieldHadInstruction:
|
|
140
177
|
return FieldHadInstruction(
|
|
141
178
|
schema=self.schema,
|
|
@@ -145,28 +182,40 @@ class AlterFieldInstructionFactory:
|
|
|
145
182
|
field_changes=FieldChanges(
|
|
146
183
|
default=default,
|
|
147
184
|
default_factory=default_factory,
|
|
185
|
+
alias_priority=alias_priority,
|
|
148
186
|
alias=alias,
|
|
187
|
+
validation_alias=validation_alias,
|
|
188
|
+
serialization_alias=serialization_alias,
|
|
149
189
|
title=title,
|
|
190
|
+
field_title_generator=field_title_generator,
|
|
150
191
|
description=description,
|
|
192
|
+
examples=examples,
|
|
151
193
|
exclude=exclude,
|
|
152
194
|
const=const,
|
|
195
|
+
deprecated=deprecated,
|
|
196
|
+
frozen=frozen,
|
|
197
|
+
validate_default=validate_default,
|
|
198
|
+
repr=repr,
|
|
199
|
+
init=init,
|
|
200
|
+
init_var=init_var,
|
|
201
|
+
kw_only=kw_only,
|
|
202
|
+
fail_fast=fail_fast,
|
|
153
203
|
gt=gt,
|
|
154
204
|
ge=ge,
|
|
155
205
|
lt=lt,
|
|
156
206
|
le=le,
|
|
157
|
-
deprecated=deprecated,
|
|
158
207
|
strict=strict,
|
|
208
|
+
coerce_numbers_to_str=coerce_numbers_to_str,
|
|
159
209
|
multiple_of=multiple_of,
|
|
160
210
|
allow_inf_nan=allow_inf_nan,
|
|
161
211
|
max_digits=max_digits,
|
|
162
212
|
decimal_places=decimal_places,
|
|
163
213
|
min_length=min_length,
|
|
164
214
|
max_length=max_length,
|
|
215
|
+
union_mode=union_mode,
|
|
165
216
|
allow_mutation=allow_mutation,
|
|
166
217
|
pattern=pattern,
|
|
167
218
|
discriminator=discriminator,
|
|
168
|
-
repr=repr,
|
|
169
|
-
fail_fast=fail_fast,
|
|
170
219
|
),
|
|
171
220
|
)
|
|
172
221
|
|
|
@@ -58,7 +58,6 @@ Additional resources:
|
|
|
58
58
|
* <https://github.com/OAI/OpenAPI-Specification/issues/1552>
|
|
59
59
|
* <https://users.rust-lang.org/t/solved-is-adding-an-enum-variant-a-breaking-change/26721/5>
|
|
60
60
|
* <https://github.com/graphql/graphql-js/issues/968>
|
|
61
|
-
* <https://medium.com/@jakob.fiegerl/java-jackson-enum-de-serialization-with-rest-backward-compatibility-9c3ec85ac13d>
|
|
62
61
|
|
|
63
62
|
In these sections, we'll be working with our user's response model: `users.UserResource`. Note that the main theme here is "Will I be able to serialize this change to any of my versions?" as any change to responses can make them incompatible with the data in your database.
|
|
64
63
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Change a schema that is not used in any endpoint
|
|
2
|
+
|
|
3
|
+
In some situations, we may want to use versioning not just for our openapi schemas and endpoints but also within our code such as when we want to send versioned webhooks to our clients.
|
|
4
|
+
|
|
5
|
+
For example, let's say we want to change the type of an "id" field from integer to string:
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
{!> ../docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/block001.py !}
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Unless there is an endpoint that has `User` as its response_model, this code will end up causing an error when we run our Cadwyn app. This is because Cadwyn tries to make sure that all of your converters apply to at least one endpoint. Otherwise, it would be too easy for you to make a mistake when writing converters for the wrong schemas.
|
|
12
|
+
|
|
13
|
+
To avoid it, set `check_usage=False`:
|
|
14
|
+
|
|
15
|
+
```python hl_lines="21"
|
|
16
|
+
{!> ../docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/block002.py !}
|
|
17
|
+
```
|
cadwyn-4.6.0a1/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/block001.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
|
|
3
|
+
from cadwyn import ResponseInfo, VersionChange, convert_response_to_previous_version_for, schema
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# User from latest version
|
|
7
|
+
class User(BaseModel):
|
|
8
|
+
id: str
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ChangeUserIDToString(VersionChange):
|
|
12
|
+
description = (
|
|
13
|
+
"Change users' ID field to a string to support any kind of ID. "
|
|
14
|
+
"Be careful: if you use a non-integer ID in a new version and "
|
|
15
|
+
"try to get it from the old version, the ID will be zero in response"
|
|
16
|
+
)
|
|
17
|
+
instructions_to_migrate_to_previous_version = [
|
|
18
|
+
schema(User).field("id").had(type=int),
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
@convert_response_to_previous_version_for(User)
|
|
22
|
+
def change_id_to_int(response: ResponseInfo): ...
|