cadwyn 5.3.3__tar.gz → 5.4.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of cadwyn might be problematic. Click here for more details.

Files changed (161) hide show
  1. {cadwyn-5.3.3 → cadwyn-5.4.2}/.pre-commit-config.yaml +17 -9
  2. {cadwyn-5.3.3 → cadwyn-5.4.2}/CHANGELOG.md +20 -0
  3. {cadwyn-5.3.3 → cadwyn-5.4.2}/PKG-INFO +7 -8
  4. {cadwyn-5.3.3 → cadwyn-5.4.2}/README.md +4 -6
  5. {cadwyn-5.3.3 → cadwyn-5.4.2}/cadwyn/__main__.py +1 -1
  6. {cadwyn-5.3.3 → cadwyn-5.4.2}/cadwyn/_asts.py +1 -1
  7. {cadwyn-5.3.3 → cadwyn-5.4.2}/cadwyn/applications.py +2 -2
  8. {cadwyn-5.3.3 → cadwyn-5.4.2}/cadwyn/changelogs.py +5 -5
  9. {cadwyn-5.3.3 → cadwyn-5.4.2}/cadwyn/route_generation.py +2 -2
  10. {cadwyn-5.3.3 → cadwyn-5.4.2}/cadwyn/schema_generation.py +56 -37
  11. {cadwyn-5.3.3 → cadwyn-5.4.2}/cadwyn/structure/data.py +2 -2
  12. {cadwyn-5.3.3 → cadwyn-5.4.2}/cadwyn/structure/schemas.py +2 -2
  13. {cadwyn-5.3.3 → cadwyn-5.4.2}/cadwyn/structure/versions.py +1 -1
  14. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/concepts/version_changes.md +25 -0
  15. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/home/CONTRIBUTING.md +1 -1
  16. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/how_to/change_openapi_schemas/remove_field.md +2 -1
  17. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/index.md +4 -10
  18. cadwyn-5.4.2/docs/theory/how_to_build_versioning_framework.md +24 -0
  19. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/theory/literature.md +1 -1
  20. {cadwyn-5.3.3 → cadwyn-5.4.2}/pyproject.toml +4 -3
  21. {cadwyn-5.3.3 → cadwyn-5.4.2}/ruff.toml +21 -13
  22. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/conftest.py +1 -1
  23. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/test_applications.py +14 -12
  24. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/test_cli.py +0 -15
  25. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/test_render.py +4 -2
  26. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/test_router_generation.py +11 -11
  27. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/test_schema_generation/test_schema_field.py +46 -1
  28. {cadwyn-5.3.3 → cadwyn-5.4.2}/uv.lock +867 -838
  29. cadwyn-5.3.3/docs/img/sponsor_logos/monite.png +0 -0
  30. cadwyn-5.3.3/docs/theory/how_to_build_versioning_framework.md +0 -23
  31. {cadwyn-5.3.3 → cadwyn-5.4.2}/.github/CODE_OF_CONDUCT.md +0 -0
  32. {cadwyn-5.3.3 → cadwyn-5.4.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  33. {cadwyn-5.3.3 → cadwyn-5.4.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  34. {cadwyn-5.3.3 → cadwyn-5.4.2}/.github/actions/setup-python-uv/action.yaml +0 -0
  35. {cadwyn-5.3.3 → cadwyn-5.4.2}/.github/workflows/ci.yaml +0 -0
  36. {cadwyn-5.3.3 → cadwyn-5.4.2}/.github/workflows/daily_tests.yaml +0 -0
  37. {cadwyn-5.3.3 → cadwyn-5.4.2}/.github/workflows/publish_docs.yaml +0 -0
  38. {cadwyn-5.3.3 → cadwyn-5.4.2}/.github/workflows/release.yaml +0 -0
  39. {cadwyn-5.3.3 → cadwyn-5.4.2}/.gitignore +0 -0
  40. {cadwyn-5.3.3 → cadwyn-5.4.2}/LICENSE +0 -0
  41. {cadwyn-5.3.3 → cadwyn-5.4.2}/Makefile +0 -0
  42. {cadwyn-5.3.3 → cadwyn-5.4.2}/cadwyn/__init__.py +0 -0
  43. {cadwyn-5.3.3 → cadwyn-5.4.2}/cadwyn/_importer.py +0 -0
  44. {cadwyn-5.3.3 → cadwyn-5.4.2}/cadwyn/_internal/__init__.py +0 -0
  45. {cadwyn-5.3.3 → cadwyn-5.4.2}/cadwyn/_internal/context_vars.py +0 -0
  46. {cadwyn-5.3.3 → cadwyn-5.4.2}/cadwyn/_render.py +0 -0
  47. {cadwyn-5.3.3 → cadwyn-5.4.2}/cadwyn/_utils.py +0 -0
  48. {cadwyn-5.3.3 → cadwyn-5.4.2}/cadwyn/dependencies.py +0 -0
  49. {cadwyn-5.3.3 → cadwyn-5.4.2}/cadwyn/exceptions.py +0 -0
  50. {cadwyn-5.3.3 → cadwyn-5.4.2}/cadwyn/middleware.py +0 -0
  51. {cadwyn-5.3.3 → cadwyn-5.4.2}/cadwyn/py.typed +0 -0
  52. {cadwyn-5.3.3 → cadwyn-5.4.2}/cadwyn/routing.py +0 -0
  53. {cadwyn-5.3.3 → cadwyn-5.4.2}/cadwyn/static/__init__.py +0 -0
  54. {cadwyn-5.3.3 → cadwyn-5.4.2}/cadwyn/static/docs.html +0 -0
  55. {cadwyn-5.3.3 → cadwyn-5.4.2}/cadwyn/structure/__init__.py +0 -0
  56. {cadwyn-5.3.3 → cadwyn-5.4.2}/cadwyn/structure/common.py +0 -0
  57. {cadwyn-5.3.3 → cadwyn-5.4.2}/cadwyn/structure/endpoints.py +0 -0
  58. {cadwyn-5.3.3 → cadwyn-5.4.2}/cadwyn/structure/enums.py +0 -0
  59. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/CNAME +0 -0
  60. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/__init__.py +0 -0
  61. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/concepts/api_version_parameter.md +0 -0
  62. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/concepts/beware_of_data_versioning.md +0 -0
  63. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/concepts/changelogs.md +0 -0
  64. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/concepts/cli.md +0 -0
  65. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/concepts/endpoint_migrations.md +0 -0
  66. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/concepts/enum_migrations.md +0 -0
  67. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/concepts/index.md +0 -0
  68. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/concepts/main_app.md +0 -0
  69. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/concepts/methodology.md +0 -0
  70. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/concepts/schema_generation.md +0 -0
  71. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/concepts/schema_migrations.md +0 -0
  72. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/concepts/testing.md +0 -0
  73. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/how_to/change_business_logic/index.md +0 -0
  74. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/how_to/change_endpoints/index.md +0 -0
  75. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/how_to/change_openapi_schemas/add_field.md +0 -0
  76. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/how_to/change_openapi_schemas/change_field_type.md +0 -0
  77. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/how_to/change_openapi_schemas/change_schema_without_endpoint.md +0 -0
  78. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/how_to/change_openapi_schemas/changing_constraints.md +0 -0
  79. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/how_to/change_openapi_schemas/rename_a_field_in_schema.md +0 -0
  80. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/how_to/index.md +0 -0
  81. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/how_to/version_with_paths_and_numbers_instead_of_headers_and_dates.md +0 -0
  82. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/img/dashboard_with_one_version.png +0 -0
  83. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/img/dashboard_with_two_versions.png +0 -0
  84. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/img/get_users_endpoint_from_prior_version.png +0 -0
  85. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/img/logos/cadwyn_icon_transparent.svg +0 -0
  86. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/img/logos/cadwyn_transparent.svg +0 -0
  87. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/img/logos/cadwyn_with_background.svg +0 -0
  88. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/img/simplified_migration_model.png +0 -0
  89. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/img/unversioned_dashboard.png +0 -0
  90. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/plugin.py +0 -0
  91. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/quickstart/setup.md +0 -0
  92. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/quickstart/tutorial.md +0 -0
  93. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/stylesheets/extra.css +0 -0
  94. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs/theory/how_we_got_here.md +0 -0
  95. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/__init__.py +0 -0
  96. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/how_to/__init__.py +0 -0
  97. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/how_to/change_openapi_schemas/__init__.py +0 -0
  98. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/__init__.py +0 -0
  99. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/block001.py +0 -0
  100. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/block002.py +0 -0
  101. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/tests/__init__.py +0 -0
  102. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/tests/test_block001.py +0 -0
  103. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/tests/test_block002.py +0 -0
  104. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/how_to/version_with_path_and_numbers_instead_of_headers_and_dates/__init__.py +0 -0
  105. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/how_to/version_with_path_and_numbers_instead_of_headers_and_dates/block001.py +0 -0
  106. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/how_to/version_with_path_and_numbers_instead_of_headers_and_dates/tests/__init__.py +0 -0
  107. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/how_to/version_with_path_and_numbers_instead_of_headers_and_dates/tests/test_block_001.py +0 -0
  108. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/quickstart/__init__.py +0 -0
  109. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/quickstart/setup/__init__.py +0 -0
  110. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/quickstart/setup/block001.sh +0 -0
  111. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/quickstart/setup/block002.py +0 -0
  112. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/quickstart/setup/tests/__init__.py +0 -0
  113. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/quickstart/setup/tests/test_block002.py +0 -0
  114. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/quickstart/tutorial/__init__.py +0 -0
  115. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/quickstart/tutorial/block001.py +0 -0
  116. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/quickstart/tutorial/block002.py +0 -0
  117. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/quickstart/tutorial/block003.py +0 -0
  118. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/quickstart/tutorial/tests/__init__.py +0 -0
  119. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/quickstart/tutorial/tests/test_block001.py +0 -0
  120. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/quickstart/tutorial/tests/test_block002.py +0 -0
  121. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/quickstart/tutorial/tests/test_block003.py +0 -0
  122. {cadwyn-5.3.3 → cadwyn-5.4.2}/docs_src/ruff.toml +0 -0
  123. {cadwyn-5.3.3 → cadwyn-5.4.2}/mkdocs.yml +0 -0
  124. {cadwyn-5.3.3 → cadwyn-5.4.2}/scripts/fix_links.py +0 -0
  125. {cadwyn-5.3.3 → cadwyn-5.4.2}/scripts/split_md.py +0 -0
  126. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/__init__.py +0 -0
  127. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/_data/__init__.py +0 -0
  128. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/_data/unversioned_schema_dir/__init__.py +0 -0
  129. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/_data/unversioned_schema_dir/unversioned_schemas.py +0 -0
  130. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/_data/unversioned_schemas.py +0 -0
  131. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/_resources/__init__.py +0 -0
  132. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/_resources/app_for_testing_routing.py +0 -0
  133. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/_resources/render/__init__.py +0 -0
  134. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/_resources/render/classes.py +0 -0
  135. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/_resources/render/complex/__init__.py +0 -0
  136. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/_resources/render/complex/classes.py +0 -0
  137. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/_resources/render/complex/versions.py +0 -0
  138. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/_resources/render/versions.py +0 -0
  139. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/_resources/utils.py +0 -0
  140. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/_resources/versioned_app/__init__.py +0 -0
  141. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/_resources/versioned_app/app.py +0 -0
  142. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/_resources/versioned_app/v2021_01_01.py +0 -0
  143. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/_resources/versioned_app/v2022_01_02.py +0 -0
  144. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/_resources/versioned_app/webhooks.py +0 -0
  145. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/test_auth_dependencies.py +0 -0
  146. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/test_changelog.py +0 -0
  147. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/test_data_migrations.py +0 -0
  148. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/test_router_generation_with_from_future_annotations.py +0 -0
  149. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/test_routing.py +0 -0
  150. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/test_schema_generation/__init__.py +0 -0
  151. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/test_schema_generation/test_enum.py +0 -0
  152. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/test_schema_generation/test_schema.py +0 -0
  153. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/test_schema_generation/test_schema_validator.py +0 -0
  154. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/test_schema_generation/test_schema_with_future_annotations.py +0 -0
  155. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/test_structure.py +0 -0
  156. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/tutorial/__init__.py +0 -0
  157. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/tutorial/main.py +0 -0
  158. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/tutorial/test_example.py +0 -0
  159. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/versioning_styles/__init__.py +0 -0
  160. {cadwyn-5.3.3 → cadwyn-5.4.2}/tests/versioning_styles/test_versioning_formats.py +0 -0
  161. {cadwyn-5.3.3 → cadwyn-5.4.2}/tox.ini +0 -0
@@ -1,12 +1,13 @@
1
1
  default_language_version:
2
- python: python3.10
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: ["--unsafe"]
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.8.1
22
+ rev: v0.11.10
22
23
  hooks:
23
24
  - id: ruff
24
- args: [--fix, --exit-non-zero-on-fix]
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: "1.19.1" # replace with latest tag on GitHub
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: ["--line-length=80", "--target-version=py310", "--skip-errors"]
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.43.0
42
+ rev: v0.44.0
37
43
  hooks:
38
44
  - id: markdownlint
39
- args: ["--disable", "MD013"]
45
+ args:
46
+ - '--disable'
47
+ - MD013
40
48
 
41
49
  - repo: https://github.com/astral-sh/uv-pre-commit
42
- rev: 0.6.5
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.2]
9
+
10
+ ### Fixed
11
+
12
+ * Added support for computed fields
13
+
14
+ ## [5.4.1]
15
+
16
+ ### Fixed
17
+
18
+ * Fixed import error in python 3.9 when using typing_extensions==3.14.0
19
+
20
+ ## [5.4.0]
21
+
22
+ ### Added
23
+
24
+ - `typing_inspection` dependency from pydantic team for complex isinstance checks that must be the same between `typing` and `typing_extensions`
25
+ - Support for `pydantic>=2.12.0` FieldInfo refactoring from https://github.com/pydantic/pydantic/pull/11898
26
+
27
+
8
28
  ## [5.3.3]
9
29
 
10
30
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cadwyn
3
- Version: 5.3.3
3
+ Version: 5.4.2
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.0.0
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'
@@ -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 versioning encapsulated in small and independent "version change" modules while your business logic stays simple and knows nothing about versioning.
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
- Its [approach](https://docs.cadwyn.dev/theory/how_we_got_here/#ii-migration-based-response-building) will be useful if you want to:
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. Effortlessly backport features and bugfixes to older API versions
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
- [![Monite](https://docs.cadwyn.dev/img/sponsor_logos/monite.png)](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!
@@ -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 versioning encapsulated in small and independent "version change" modules while your business logic stays simple and knows nothing about versioning.
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
- Its [approach](https://docs.cadwyn.dev/theory/how_we_got_here/#ii-migration-based-response-building) will be useful if you want to:
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. Effortlessly backport features and bugfixes to older API versions
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
- [![Monite](https://docs.cadwyn.dev/img/sponsor_logos/monite.png)](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!
@@ -36,7 +36,7 @@ def version_callback(value: bool):
36
36
  def output_code(code: str, raw: bool):
37
37
  if raw:
38
38
  typer.echo(code)
39
- else:
39
+ else: # pragma: no cover
40
40
  _CONSOLE.print(Syntax(code, "python", line_numbers=True))
41
41
 
42
42
 
@@ -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 TYPE_CHECKING, Annotated, Generic, Union, cast
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
- attributes = {
158
- attr_name: field_info._attributes_set[attr_name]
159
- for attr_name in _all_field_arg_names
160
- if attr_name in field_info._attributes_set
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)
@@ -239,6 +236,11 @@ def _wrap_validator(func: Callable, is_pydantic_v1_style_validator: Any, decorat
239
236
  func = func.__func__
240
237
  kwargs = dataclasses.asdict(decorator_info)
241
238
  decorator_fields = kwargs.pop("fields", None)
239
+
240
+ # wrapped_property is not accepted by computed_field()
241
+ if isinstance(decorator_info, ComputedFieldInfo):
242
+ kwargs.pop("wrapped_property", None)
243
+
242
244
  actual_decorator = PYDANTIC_DECORATOR_TYPE_TO_DECORATOR_MAP[type(decorator_info)]
243
245
  if is_pydantic_v1_style_validator:
244
246
  # There's an inconsistency in their interfaces so we gotta resort to this
@@ -265,7 +267,7 @@ def _wrap_pydantic_model(model: type[_T_PYDANTIC_MODEL]) -> "_PydanticModelWrapp
265
267
  # For example, when "from __future__ import annotations" is used in the file with the schema
266
268
  if model is not BaseModel:
267
269
  model.model_rebuild(raise_errors=False)
268
- model = cast(type[_T_PYDANTIC_MODEL], model)
270
+ model = cast("type[_T_PYDANTIC_MODEL]", model)
269
271
 
270
272
  decorators = _get_model_decorators(model)
271
273
  validators = {}
@@ -345,7 +347,7 @@ def _wrap_pydantic_model(model: type[_T_PYDANTIC_MODEL]) -> "_PydanticModelWrapp
345
347
  def _get_field_and_validator_names_from_model(cls: type) -> tuple[set[_FieldName], set[str]]:
346
348
  fields = cls.model_fields
347
349
  source = inspect.getsource(cls)
348
- cls_ast = cast(ast.ClassDef, ast.parse(textwrap.dedent(source)).body[0])
350
+ cls_ast = cast("ast.ClassDef", ast.parse(textwrap.dedent(source)).body[0])
349
351
  validator_names = (
350
352
  _get_validator_info_or_none(node)
351
353
  for node in cls_ast.body
@@ -468,7 +470,7 @@ class _PydanticModelWrapper(Generic[_T_PYDANTIC_MODEL]):
468
470
  fields = {name: field.generate_field_copy(generator) for name, field in self.fields.items()}
469
471
  model_copy = type(self.cls)(
470
472
  self.name,
471
- tuple(generator[cast(type[BaseModel], base)] for base in self.cls.__bases__),
473
+ tuple(generator[cast("type[BaseModel]", base)] for base in self.cls.__bases__),
472
474
  self.other_attributes
473
475
  | per_field_validators
474
476
  | root_validators
@@ -587,9 +589,11 @@ class _AnnotationTransformer:
587
589
  self._remake_endpoint_dependencies(route)
588
590
 
589
591
  def _change_version_of_a_non_container_annotation(self, annotation: Any) -> Any:
590
- if isinstance(annotation, (_BaseGenericAlias, types.GenericAlias)):
592
+ from typing_inspection.typing_objects import is_any, is_newtype, is_typealiastype
593
+
594
+ if isinstance(annotation, (types.GenericAlias, _BaseGenericAlias)):
591
595
  return get_origin(annotation)[tuple(self.change_version_of_annotation(arg) for arg in get_args(annotation))]
592
- elif isinstance(annotation, TypeAliasType):
596
+ elif is_typealiastype(annotation):
593
597
  if (
594
598
  annotation.__module__ is not None and (annotation.__module__.startswith("pydantic."))
595
599
  ) or annotation.__name__ in _PYDANTIC_ALL_EXPORTED_NAMES:
@@ -616,7 +620,7 @@ class _AnnotationTransformer:
616
620
  return getitem(
617
621
  tuple(self.change_version_of_annotation(a) for a in get_args(annotation)),
618
622
  )
619
- elif annotation is typing.Any or annotation is typing_extensions.Any or isinstance(annotation, NewType):
623
+ elif is_any(annotation) or is_newtype(annotation):
620
624
  return annotation
621
625
  elif isinstance(annotation, type):
622
626
  return self._change_version_of_type(annotation)
@@ -776,7 +780,7 @@ class SchemaGenerator:
776
780
  wrapper = self._get_wrapper_for_model(model)
777
781
  model_copy = wrapper.generate_model_copy(self)
778
782
  self.concrete_models[model] = model_copy
779
- return cast(type[_T_ANY_MODEL], model_copy)
783
+ return cast("type[_T_ANY_MODEL]", model_copy)
780
784
 
781
785
  @overload
782
786
  def _get_wrapper_for_model(self, model: type[BaseModel]) -> "_PydanticModelWrapper[BaseModel]": ...
@@ -865,7 +869,7 @@ def _apply_alter_schema_instructions(
865
869
  elif isinstance(alter_schema_instruction, ValidatorExistedInstruction):
866
870
  validator_name = get_name_of_function_wrapped_in_pydantic_validator(alter_schema_instruction.validator)
867
871
  raw_validator = cast(
868
- pydantic._internal._decorators.PydanticDescriptorProxy, alter_schema_instruction.validator
872
+ "pydantic._internal._decorators.PydanticDescriptorProxy", alter_schema_instruction.validator
869
873
  )
870
874
  schema_info.validators[validator_name] = _wrap_validator(
871
875
  raw_validator.wrapped,
@@ -1041,15 +1045,19 @@ def _delete_field_attributes(
1041
1045
  annotation: Any,
1042
1046
  ) -> None:
1043
1047
  for attr_name in alter_schema_instruction.attributes:
1048
+ deleted = False
1049
+
1044
1050
  if attr_name in field.passed_field_attributes:
1045
1051
  field.delete_attribute(name=attr_name)
1046
- elif get_origin(annotation) == Annotated and any( # pragma: no branch
1052
+ deleted = True
1053
+ if get_origin(annotation) == Annotated and any( # pragma: no branch
1047
1054
  hasattr(sub_ann, attr_name) for sub_ann in get_args(annotation)
1048
1055
  ):
1049
1056
  for sub_ann in get_args(annotation):
1050
1057
  if hasattr(sub_ann, attr_name):
1051
1058
  object.__setattr__(sub_ann, attr_name, None)
1052
- else:
1059
+ deleted = True
1060
+ if not deleted:
1053
1061
  raise InvalidGenerationInstructionError(
1054
1062
  f'You tried to delete the attribute "{attr_name}" of field "{alter_schema_instruction.name}" '
1055
1063
  f'from "{model.name}" in "{version_change_name}" '
@@ -1058,19 +1066,30 @@ def _delete_field_attributes(
1058
1066
 
1059
1067
 
1060
1068
  def _delete_field_from_model(model: _PydanticModelWrapper, field_name: str, version_change_name: str):
1061
- if field_name not in model.fields:
1069
+ if field_name in model.fields:
1070
+ model.fields.pop(field_name)
1071
+ model.annotations.pop(field_name)
1072
+ for validator_name, validator in model.validators.copy().items():
1073
+ if isinstance(validator, _PerFieldValidatorWrapper) and field_name in validator.fields:
1074
+ validator.fields.remove(field_name)
1075
+ # TODO: This behavior doesn't feel natural
1076
+ if not validator.fields:
1077
+ model.validators[validator_name].is_deleted = True
1078
+
1079
+ elif (
1080
+ field_name in model.validators
1081
+ and isinstance(model.validators[field_name], _ValidatorWrapper)
1082
+ and hasattr(model.validators[field_name], "decorator")
1083
+ and model.validators[field_name].decorator == pydantic.computed_field
1084
+ ):
1085
+ validator = model.validators[field_name]
1086
+ model.validators[field_name].is_deleted = True
1087
+ model.annotations.pop(field_name, None)
1088
+ else:
1062
1089
  raise InvalidGenerationInstructionError(
1063
1090
  f'You tried to delete a field "{field_name}" from "{model.name}" '
1064
1091
  f'in "{version_change_name}" but it doesn\'t have such a field.',
1065
1092
  )
1066
- model.fields.pop(field_name)
1067
- model.annotations.pop(field_name)
1068
- for validator_name, validator in model.validators.copy().items():
1069
- if isinstance(validator, _PerFieldValidatorWrapper) and field_name in validator.fields:
1070
- validator.fields.remove(field_name)
1071
- # TODO: This behavior doesn't feel natural
1072
- if not validator.fields:
1073
- model.validators[validator_name].is_deleted = True
1074
1093
 
1075
1094
 
1076
1095
  class _DummyEnum(Enum):
@@ -1100,7 +1119,7 @@ class _EnumWrapper(Generic[_T_ENUM]):
1100
1119
  for attr_name, attr in initialization_namespace.items():
1101
1120
  enum_dict[attr_name] = attr
1102
1121
  enum_dict["__doc__"] = self.cls.__doc__
1103
- model_copy = cast(type[_T_ENUM], type(self.name, self.cls.__bases__, enum_dict))
1122
+ model_copy = cast("type[_T_ENUM]", type(self.name, self.cls.__bases__, enum_dict))
1104
1123
  model_copy.__cadwyn_original_model__ = self.cls # pyright: ignore[reportAttributeAccessIssue]
1105
1124
  return model_copy
1106
1125
 
@@ -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:
@@ -66,7 +66,7 @@ Then you can serve the documentation with `mkdocs serve`
66
66
 
67
67
  We welcome contributions that enhance / improve the content of the docs. Feel free to add examples, clarify text, restructure the docs, etc., but make sure to follow these guidelines:
68
68
 
69
- * Write text in idiomatic english, using simple language
69
+ * Write text in idiomatic English, using simple language
70
70
  * Opt for [Oxford commas](https://en.wikipedia.org/wiki/Serial_comma) when listing a series of terms
71
71
  * Keep examples simple and self contained
72
72
  * Provide links where applicable
@@ -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, description="User's Middle Name", default=None
86
+ type=str | None,
87
+ info=Field(description="User's Middle Name", default=None),
87
88
  ),
88
89
  )
89
90
  ```
@@ -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 package" alt="Package version">
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,21 +24,15 @@ 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 support a single version of your code while auto-generating the schemas and routes for older versions. You keep API versioning encapsulated in small and independent "version change" modules while your business logic stays simple and knows nothing about versioning.
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
- Its [approach](https://docs.cadwyn.dev/theory/how_we_got_here/#ii-migration-based-response-building) will be useful if you want to:
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. Effortlessly backport features and bugfixes to older API versions
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
 
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
- [![Monite](./img/sponsor_logos/monite.png)](https://docs.monite.com/)
@@ -0,0 +1,24 @@
1
+ # How to build a versioning framework
2
+
3
+ ## Questions to ask yourself when rating your framework
4
+
5
+ ### How easy is it to create a version?
6
+
7
+ If it is too easy, it is probably a trap. The framework is probably hiding too much complexity from you and will shoot you in the back later. For example, early on we tried a simple "copy the entire business logic into a separate directory" approach which made it so simple to add new versions. We added too many of them in the end, thus it got hellishly hard to maintain or get rid of these versions.
8
+
9
+ ### How easy is it to delete an old version?
10
+
11
+ Your framework must be able to let you clean up versions as simply as possible and cheaply whenever you need to. For example, if your framework tries to minimize the amount of code duplication in your repository by having new routes include old routes within them and new business logic inherit from classes from old business logic, then deleting an old version is going to be painful; oftentimes even dangerous as versions can quickly start interacting with each other in all sorts of ways, turning a single small application into a set of interconnected applications.
12
+
13
+ ### How easy is it to see the differences between versions?
14
+
15
+ The easier it is, the better off our users are.
16
+
17
+ ### What exactly do you need to duplicate to create a new version?
18
+
19
+ The less we duplicate and maintain by hand, the easier it is to support. However, the less we duplicate, the more chance there is to break the old versions with new releases.
20
+
21
+ ### How easy is it to notice accidental data versioning?
22
+
23
+ Data versioning is an incredibly big issue when versioning your API.
24
+ So if your framework makes it hard to version data -- it's really good!
@@ -1,6 +1,6 @@
1
1
  # Literature
2
2
 
3
- During Cadwyn's development, I went through countless resources on API Versioning. The following are the most unique and effective ones I could find.
3
+ During Cadwyn's development, I went through countless resources on API Versioning. The following are the most unique and effective ones I managed to find.
4
4
 
5
5
  ## Cadwyn-like API Versioning
6
6