cadwyn 5.4.5__tar.gz → 5.6.0__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.4.5 → cadwyn-5.6.0}/CHANGELOG.md +22 -3
  2. {cadwyn-5.4.5 → cadwyn-5.6.0}/PKG-INFO +3 -3
  3. {cadwyn-5.4.5 → cadwyn-5.6.0}/cadwyn/changelogs.py +19 -17
  4. {cadwyn-5.4.5 → cadwyn-5.6.0}/cadwyn/structure/schemas.py +4 -0
  5. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/concepts/version_changes.md +13 -13
  6. cadwyn-5.6.0/docs/home/CONTRIBUTING.md +71 -0
  7. cadwyn-5.6.0/docs/how_to/change_business_logic/index.md +28 -0
  8. {cadwyn-5.4.5 → cadwyn-5.6.0}/pyproject.toml +3 -3
  9. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/test_render.py +0 -2
  10. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/test_schema_generation/test_schema_field.py +55 -11
  11. {cadwyn-5.4.5 → cadwyn-5.6.0}/uv.lock +3 -3
  12. cadwyn-5.4.5/docs/home/CONTRIBUTING.md +0 -72
  13. cadwyn-5.4.5/docs/how_to/change_business_logic/index.md +0 -28
  14. {cadwyn-5.4.5 → cadwyn-5.6.0}/.github/CODE_OF_CONDUCT.md +0 -0
  15. {cadwyn-5.4.5 → cadwyn-5.6.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  16. {cadwyn-5.4.5 → cadwyn-5.6.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  17. {cadwyn-5.4.5 → cadwyn-5.6.0}/.github/actions/setup-python-uv/action.yaml +0 -0
  18. {cadwyn-5.4.5 → cadwyn-5.6.0}/.github/workflows/ci.yaml +0 -0
  19. {cadwyn-5.4.5 → cadwyn-5.6.0}/.github/workflows/daily_tests.yaml +0 -0
  20. {cadwyn-5.4.5 → cadwyn-5.6.0}/.github/workflows/publish_docs.yaml +0 -0
  21. {cadwyn-5.4.5 → cadwyn-5.6.0}/.github/workflows/release.yaml +0 -0
  22. {cadwyn-5.4.5 → cadwyn-5.6.0}/.gitignore +0 -0
  23. {cadwyn-5.4.5 → cadwyn-5.6.0}/.pre-commit-config.yaml +0 -0
  24. {cadwyn-5.4.5 → cadwyn-5.6.0}/LICENSE +0 -0
  25. {cadwyn-5.4.5 → cadwyn-5.6.0}/Makefile +0 -0
  26. {cadwyn-5.4.5 → cadwyn-5.6.0}/README.md +0 -0
  27. {cadwyn-5.4.5 → cadwyn-5.6.0}/cadwyn/__init__.py +0 -0
  28. {cadwyn-5.4.5 → cadwyn-5.6.0}/cadwyn/__main__.py +0 -0
  29. {cadwyn-5.4.5 → cadwyn-5.6.0}/cadwyn/_asts.py +0 -0
  30. {cadwyn-5.4.5 → cadwyn-5.6.0}/cadwyn/_importer.py +0 -0
  31. {cadwyn-5.4.5 → cadwyn-5.6.0}/cadwyn/_internal/__init__.py +0 -0
  32. {cadwyn-5.4.5 → cadwyn-5.6.0}/cadwyn/_internal/context_vars.py +0 -0
  33. {cadwyn-5.4.5 → cadwyn-5.6.0}/cadwyn/_render.py +0 -0
  34. {cadwyn-5.4.5 → cadwyn-5.6.0}/cadwyn/_utils.py +0 -0
  35. {cadwyn-5.4.5 → cadwyn-5.6.0}/cadwyn/applications.py +0 -0
  36. {cadwyn-5.4.5 → cadwyn-5.6.0}/cadwyn/dependencies.py +0 -0
  37. {cadwyn-5.4.5 → cadwyn-5.6.0}/cadwyn/exceptions.py +0 -0
  38. {cadwyn-5.4.5 → cadwyn-5.6.0}/cadwyn/middleware.py +0 -0
  39. {cadwyn-5.4.5 → cadwyn-5.6.0}/cadwyn/py.typed +0 -0
  40. {cadwyn-5.4.5 → cadwyn-5.6.0}/cadwyn/route_generation.py +0 -0
  41. {cadwyn-5.4.5 → cadwyn-5.6.0}/cadwyn/routing.py +0 -0
  42. {cadwyn-5.4.5 → cadwyn-5.6.0}/cadwyn/schema_generation.py +0 -0
  43. {cadwyn-5.4.5 → cadwyn-5.6.0}/cadwyn/static/__init__.py +0 -0
  44. {cadwyn-5.4.5 → cadwyn-5.6.0}/cadwyn/static/docs.html +0 -0
  45. {cadwyn-5.4.5 → cadwyn-5.6.0}/cadwyn/structure/__init__.py +0 -0
  46. {cadwyn-5.4.5 → cadwyn-5.6.0}/cadwyn/structure/common.py +0 -0
  47. {cadwyn-5.4.5 → cadwyn-5.6.0}/cadwyn/structure/data.py +0 -0
  48. {cadwyn-5.4.5 → cadwyn-5.6.0}/cadwyn/structure/endpoints.py +0 -0
  49. {cadwyn-5.4.5 → cadwyn-5.6.0}/cadwyn/structure/enums.py +0 -0
  50. {cadwyn-5.4.5 → cadwyn-5.6.0}/cadwyn/structure/versions.py +0 -0
  51. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/CNAME +0 -0
  52. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/__init__.py +0 -0
  53. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/concepts/api_version_parameter.md +0 -0
  54. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/concepts/beware_of_data_versioning.md +0 -0
  55. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/concepts/changelogs.md +0 -0
  56. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/concepts/cli.md +0 -0
  57. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/concepts/endpoint_migrations.md +0 -0
  58. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/concepts/enum_migrations.md +0 -0
  59. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/concepts/index.md +0 -0
  60. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/concepts/main_app.md +0 -0
  61. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/concepts/methodology.md +0 -0
  62. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/concepts/schema_generation.md +0 -0
  63. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/concepts/schema_migrations.md +0 -0
  64. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/concepts/testing.md +0 -0
  65. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/how_to/change_endpoints/index.md +0 -0
  66. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/how_to/change_openapi_schemas/add_field.md +0 -0
  67. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/how_to/change_openapi_schemas/change_field_type.md +0 -0
  68. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/how_to/change_openapi_schemas/change_schema_without_endpoint.md +0 -0
  69. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/how_to/change_openapi_schemas/changing_constraints.md +0 -0
  70. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/how_to/change_openapi_schemas/remove_field.md +0 -0
  71. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/how_to/change_openapi_schemas/rename_a_field_in_schema.md +0 -0
  72. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/how_to/index.md +0 -0
  73. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/how_to/version_with_paths_and_numbers_instead_of_headers_and_dates.md +0 -0
  74. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/img/dashboard_with_one_version.png +0 -0
  75. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/img/dashboard_with_two_versions.png +0 -0
  76. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/img/get_users_endpoint_from_prior_version.png +0 -0
  77. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/img/logos/cadwyn_icon_transparent.svg +0 -0
  78. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/img/logos/cadwyn_transparent.svg +0 -0
  79. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/img/logos/cadwyn_with_background.svg +0 -0
  80. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/img/simplified_migration_model.png +0 -0
  81. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/img/unversioned_dashboard.png +0 -0
  82. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/index.md +0 -0
  83. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/plugin.py +0 -0
  84. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/quickstart/setup.md +0 -0
  85. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/quickstart/tutorial.md +0 -0
  86. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/stylesheets/extra.css +0 -0
  87. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/theory/how_to_build_versioning_framework.md +0 -0
  88. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/theory/how_we_got_here.md +0 -0
  89. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs/theory/literature.md +0 -0
  90. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/__init__.py +0 -0
  91. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/how_to/__init__.py +0 -0
  92. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/how_to/change_openapi_schemas/__init__.py +0 -0
  93. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/__init__.py +0 -0
  94. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/block001.py +0 -0
  95. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/block002.py +0 -0
  96. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/tests/__init__.py +0 -0
  97. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/tests/test_block001.py +0 -0
  98. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/how_to/change_openapi_schemas/change_schema_without_endpoint/tests/test_block002.py +0 -0
  99. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/how_to/version_with_path_and_numbers_instead_of_headers_and_dates/__init__.py +0 -0
  100. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/how_to/version_with_path_and_numbers_instead_of_headers_and_dates/block001.py +0 -0
  101. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/how_to/version_with_path_and_numbers_instead_of_headers_and_dates/tests/__init__.py +0 -0
  102. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/how_to/version_with_path_and_numbers_instead_of_headers_and_dates/tests/test_block_001.py +0 -0
  103. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/quickstart/__init__.py +0 -0
  104. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/quickstart/setup/__init__.py +0 -0
  105. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/quickstart/setup/block001.sh +0 -0
  106. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/quickstart/setup/block002.py +0 -0
  107. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/quickstart/setup/tests/__init__.py +0 -0
  108. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/quickstart/setup/tests/test_block002.py +0 -0
  109. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/quickstart/tutorial/__init__.py +0 -0
  110. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/quickstart/tutorial/block001.py +0 -0
  111. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/quickstart/tutorial/block002.py +0 -0
  112. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/quickstart/tutorial/block003.py +0 -0
  113. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/quickstart/tutorial/tests/__init__.py +0 -0
  114. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/quickstart/tutorial/tests/test_block001.py +0 -0
  115. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/quickstart/tutorial/tests/test_block002.py +0 -0
  116. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/quickstart/tutorial/tests/test_block003.py +0 -0
  117. {cadwyn-5.4.5 → cadwyn-5.6.0}/docs_src/ruff.toml +0 -0
  118. {cadwyn-5.4.5 → cadwyn-5.6.0}/mkdocs.yml +0 -0
  119. {cadwyn-5.4.5 → cadwyn-5.6.0}/ruff.toml +0 -0
  120. {cadwyn-5.4.5 → cadwyn-5.6.0}/scripts/fix_links.py +0 -0
  121. {cadwyn-5.4.5 → cadwyn-5.6.0}/scripts/split_md.py +0 -0
  122. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/__init__.py +0 -0
  123. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/_data/__init__.py +0 -0
  124. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/_data/unversioned_schema_dir/__init__.py +0 -0
  125. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/_data/unversioned_schema_dir/unversioned_schemas.py +0 -0
  126. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/_data/unversioned_schemas.py +0 -0
  127. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/_resources/__init__.py +0 -0
  128. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/_resources/app_for_testing_routing.py +0 -0
  129. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/_resources/render/__init__.py +0 -0
  130. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/_resources/render/classes.py +0 -0
  131. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/_resources/render/complex/__init__.py +0 -0
  132. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/_resources/render/complex/classes.py +0 -0
  133. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/_resources/render/complex/versions.py +0 -0
  134. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/_resources/render/versions.py +0 -0
  135. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/_resources/utils.py +0 -0
  136. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/_resources/versioned_app/__init__.py +0 -0
  137. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/_resources/versioned_app/app.py +0 -0
  138. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/_resources/versioned_app/v2021_01_01.py +0 -0
  139. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/_resources/versioned_app/v2022_01_02.py +0 -0
  140. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/_resources/versioned_app/webhooks.py +0 -0
  141. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/conftest.py +0 -0
  142. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/test_applications.py +0 -0
  143. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/test_auth_dependencies.py +0 -0
  144. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/test_changelog.py +0 -0
  145. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/test_cli.py +0 -0
  146. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/test_data_migrations.py +0 -0
  147. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/test_router_generation.py +0 -0
  148. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/test_router_generation_with_from_future_annotations.py +0 -0
  149. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/test_routing.py +0 -0
  150. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/test_schema_generation/__init__.py +0 -0
  151. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/test_schema_generation/test_enum.py +0 -0
  152. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/test_schema_generation/test_schema.py +0 -0
  153. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/test_schema_generation/test_schema_validator.py +0 -0
  154. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/test_schema_generation/test_schema_with_future_annotations.py +0 -0
  155. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/test_structure.py +0 -0
  156. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/tutorial/__init__.py +0 -0
  157. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/tutorial/main.py +0 -0
  158. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/tutorial/test_example.py +0 -0
  159. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/versioning_styles/__init__.py +0 -0
  160. {cadwyn-5.4.5 → cadwyn-5.6.0}/tests/versioning_styles/test_versioning_formats.py +0 -0
  161. {cadwyn-5.4.5 → cadwyn-5.6.0}/tox.ini +0 -0
@@ -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.6.0]
9
+
10
+ ### Added
11
+
12
+ * `schema(...).had(json_schema_extra=...)` support by @csemanish12
13
+
14
+ ## [5.5.0]
15
+
16
+ * Fix the rest of the issues from fastapi==0.119.0
17
+
18
+ ## [5.4.5]
19
+
20
+ ### Fixed
21
+
22
+ * Many issues after GenerateJsonSchema getting removed in fastapi==0.119.0
23
+
24
+ ### Changed
25
+
26
+ * A lot of documentation improvements by @Shigerman
27
+
8
28
  ## [5.4.4]
9
29
 
10
30
  ### Fixed
@@ -33,9 +53,8 @@ Please follow [the Keep a Changelog standard](https://keepachangelog.com/en/1.0.
33
53
 
34
54
  ### Added
35
55
 
36
- - `typing_inspection` dependency from pydantic team for complex isinstance checks that must be the same between `typing` and `typing_extensions`
37
- - Support for `pydantic>=2.12.0` FieldInfo refactoring from https://github.com/pydantic/pydantic/pull/11898
38
-
56
+ * `typing_inspection` dependency from pydantic team for complex isinstance checks that must be the same between `typing` and `typing_extensions`
57
+ * Support for `pydantic>=2.12.0` FieldInfo refactoring from <https://github.com/pydantic/pydantic/pull/11898>
39
58
 
40
59
  ## [5.3.3]
41
60
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cadwyn
3
- Version: 5.4.5
3
+ Version: 5.6.0
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
@@ -35,14 +35,14 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
35
35
  Classifier: Typing :: Typed
36
36
  Requires-Python: >=3.9
37
37
  Requires-Dist: backports-strenum<2,>=1.3.1; python_version < '3.11'
38
- Requires-Dist: fastapi>=0.112.4
38
+ Requires-Dist: fastapi>=0.119.0
39
39
  Requires-Dist: jinja2>=3.1.2
40
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
43
  Requires-Dist: typing-inspection>=0.4.0
44
44
  Provides-Extra: standard
45
- Requires-Dist: fastapi[standard]>=0.112.3; extra == 'standard'
45
+ Requires-Dist: fastapi[standard]>=0.119.0; extra == 'standard'
46
46
  Requires-Dist: typer>=0.7.0; extra == 'standard'
47
47
  Description-Content-Type: text/markdown
48
48
 
@@ -2,9 +2,13 @@ import copy
2
2
  import sys
3
3
  from enum import auto
4
4
  from logging import getLogger
5
- from typing import TYPE_CHECKING, Any, Literal, TypeVar, Union, cast, get_args
5
+ from typing import Any, Literal, TypeVar, Union, cast, get_args
6
6
 
7
- from fastapi.openapi.constants import REF_TEMPLATE
7
+ from fastapi._compat import (
8
+ get_compat_model_name_map,
9
+ get_definitions,
10
+ )
11
+ from fastapi._compat.v2 import ModelField
8
12
  from fastapi.openapi.utils import (
9
13
  get_fields_from_routes,
10
14
  get_openapi,
@@ -35,9 +39,6 @@ from .structure.schemas import (
35
39
  ValidatorExistedInstruction,
36
40
  )
37
41
 
38
- if TYPE_CHECKING:
39
- from fastapi._compat import ModelField
40
-
41
42
  if sys.version_info >= (3, 11): # pragma: no cover
42
43
  from enum import StrEnum
43
44
  else: # pragma: no cover
@@ -89,7 +90,7 @@ def _generate_changelog(versions: VersionBundle, router: _RootCadwynAPIRouter) -
89
90
  version_change,
90
91
  generator_from_newer_version,
91
92
  generator_from_older_version,
92
- schemas_from_older_version,
93
+ schemas_from_older_version, # pyright: ignore[reportArgumentType]
93
94
  cast("list[APIRoute]", routes_from_newer_version),
94
95
  )
95
96
  if changelog_entry is not None: # pragma: no branch # This should never happen
@@ -153,21 +154,22 @@ def _get_all_pydantic_models_from_generic(annotation: Any) -> list[type[BaseMode
153
154
 
154
155
 
155
156
  def _get_openapi_representation_of_a_field(model: type[BaseModel], field_name: str) -> dict:
156
- from fastapi._compat import (
157
- GenerateJsonSchema,
158
- ModelField,
159
- get_compat_model_name_map,
160
- get_definitions,
161
- )
162
-
163
157
  class CadwynDummyModelForRepresentation(BaseModel):
164
158
  my_field: model
165
159
 
166
- model_name_map = get_compat_model_name_map([CadwynDummyModelForRepresentation.model_fields["my_field"]])
167
- schema_generator = GenerateJsonSchema(ref_template=REF_TEMPLATE)
160
+ model_name_map = get_compat_model_name_map(
161
+ [
162
+ CadwynDummyModelForRepresentation.model_fields["my_field"], # pyright: ignore[reportArgumentType]
163
+ ]
164
+ )
165
+
168
166
  _, definitions = get_definitions(
169
- fields=[ModelField(CadwynDummyModelForRepresentation.model_fields["my_field"], "my_field")],
170
- schema_generator=schema_generator,
167
+ fields=[
168
+ ModelField(
169
+ CadwynDummyModelForRepresentation.model_fields["my_field"],
170
+ "my_field",
171
+ ), # pyright: ignore[reportArgumentType]
172
+ ],
171
173
  model_name_map=model_name_map,
172
174
  separate_input_output_schemas=False,
173
175
  )
@@ -58,6 +58,7 @@ PossibleFieldAttributes = Literal[
58
58
  "allow_mutation",
59
59
  "pattern",
60
60
  "discriminator",
61
+ "json_schema_extra",
61
62
  ]
62
63
 
63
64
 
@@ -100,6 +101,7 @@ class FieldChanges:
100
101
  allow_mutation: bool
101
102
  pattern: str
102
103
  discriminator: str
104
+ json_schema_extra: Union[dict[str, Any], Callable[[dict[str, Any]], None], None]
103
105
 
104
106
 
105
107
  @dataclass(**DATACLASS_SLOTS)
@@ -178,6 +180,7 @@ class AlterFieldInstructionFactory:
178
180
  allow_mutation: bool = Sentinel,
179
181
  pattern: str = Sentinel,
180
182
  discriminator: str = Sentinel,
183
+ json_schema_extra: Union[dict[str, Any], Callable[[dict[str, Any]], None], None] = Sentinel,
181
184
  ) -> FieldHadInstruction:
182
185
  return FieldHadInstruction(
183
186
  is_hidden_from_changelog=False,
@@ -222,6 +225,7 @@ class AlterFieldInstructionFactory:
222
225
  allow_mutation=allow_mutation,
223
226
  pattern=pattern,
224
227
  discriminator=discriminator,
228
+ json_schema_extra=json_schema_extra,
225
229
  ),
226
230
  )
227
231
 
@@ -132,12 +132,12 @@ In Cadwyn, you use the latest version. This attribute is a way for you to descri
132
132
 
133
133
  This approach of *maintaining the present and describing the past* might appear weird. You just need to form the correct mindset which is counter-intuitive at first but after just one or two attempts at versioning you will see how much sense this approach makes.
134
134
 
135
- Imagine you needed to know what your code looked like two weeks ago. You would use `git checkout` or `git reset` with an older commit because `git` stores the latest version of your code (which is also called HEAD) and the diffs between it and each previous version as a chain of changes. This is exactly how Cadwyn works! We store the latest version and use the diffs to regenerate the older versions.
135
+ Imagine you need to know what your code looked like two weeks ago. You would use `git checkout` or `git reset` with an older commit because `git` stores the latest version of your code (which is also called HEAD) and the diffs between it and each previous version as a chain of changes. This is exactly how Cadwyn works! We store the latest version and use the diffs to regenerate the older versions.
136
136
 
137
137
  <details>
138
138
  <summary>Note to curious readers</summary>
139
139
 
140
- Git doesn't actually work this way internally. My description is closer to how SVN works. It is just a really simplistic metaphor to explain a concept.
140
+ Git doesn't actually work this way internally. My description is closer to how SVN works. It is just a really simple metaphor to explain a concept.
141
141
  </details>
142
142
 
143
143
  ### Data migrations
@@ -167,7 +167,7 @@ class RemoveTaxIdEndpoints(VersionChange):
167
167
  request.body["created_at"] = request.body.pop("creation_date")
168
168
  ```
169
169
 
170
- Did you notice how the schema for `InvoiceCreateRequest` is specified in our migration? This will signal to Cadwyn to apply it to all routes that have this schema as their body.
170
+ Did you notice how the schema for `InvoiceCreateRequest` is specified in our migration? This signals Cadwyn to apply it to all routes with this schema as their body.
171
171
 
172
172
  Now we have not only described how schemas changed but we have also described how to migrate a request of the old version to the new version. When Cadwyn receives a request targeting a particular version, the request is first validated against the schema of that particular version. Then Cadwyn applies all request migrations until the latest version to migrate the request to latest. So now your business logic receives the latest version of the request yet for your clients you have two versions of your API -- you have added variability without introducing any complexity into your business logic.
173
173
 
@@ -204,13 +204,13 @@ class RemoveTaxIdEndpoints(VersionChange):
204
204
  response.body["creation_date"] = response.body.pop("created_at")
205
205
  ```
206
206
 
207
- Did you notice how the schema for `InvoiceResource` is specified in our migration? This will signal to Cadwyn to apply it to all routes that have this schema as their `response_model`. Notice also that we now use `BaseInvoice` in our instructions -- let's imagine that it is the parent of both `InvoiceCreateRequest` and `InvoiceResource` so renaming it there will rename it in these schemas as well. You can, however, apply the instructions to both individual schemas instead of their parent if you want to.
207
+ Did you notice how the schema for `InvoiceResource` is specified in our migration? This signals Cadwyn to apply it to all routes with this schema as their `response_model`. Notice also that we now use `BaseInvoice` in our instructions -- imagine it is the parent of both `InvoiceCreateRequest` and `InvoiceResource` so renaming it there will rename it in these schemas as well. You can, however, apply the instructions to both individual schemas instead of their parent if you want to.
208
208
 
209
- Now our request comes, Cadwyn migrates it to the latest version using our request migration, then we do our business logic, return the latest response from it, and Cadwyn migrates it back to the request version. Does our business logic or database know about the fact that we have two versions? No, not at all! It is zero-cost. Imagine how beneficial it is when you support not two but two hundred versions.
209
+ Now our request comes, Cadwyn migrates it to the latest version using our request migration, then we do our business logic, return the latest response from it, and Cadwyn migrates it back to the request version. Does our business logic or database know about the fact that we have two versions? No, not at all! It is zero-cost. Consider the benefits of supporting not just two, but two hundred versions.
210
210
 
211
211
  ![The diagram showing how it works](../img/simplified_migration_model.png)
212
212
 
213
- **Notice** how we used the **latest** versions of our schemas in our migration -- this pattern can be found everywhere in Cadwyn. The latest version of your schemas is used to describe what happened to all other versions because other versions might not exist when you are defining migrations for them.
213
+ **Notice** how the **latest** versions of our schemas are used in our migration -- this pattern can be found everywhere in Cadwyn. The latest version of your schemas is used to describe what happened to all other versions because other versions might not exist when you are defining migrations for them.
214
214
 
215
215
  #### Path-based migration specification
216
216
 
@@ -260,13 +260,13 @@ from cadwyn import (
260
260
 
261
261
 
262
262
  class RemoveTaxIdEndpoints(VersionChange):
263
- description = "Change status code in 'GET /v1/invoices' when invoice was not found from 400 to 404"
263
+ description = "Replace status code 400 with 404 in 'GET /v1/invoices' if invoice is not found"
264
264
  instructions_to_migrate_to_previous_version = ()
265
265
 
266
266
  @convert_response_to_previous_version_for(
267
267
  "/v1/invoices", ["GET"], migrate_http_errors=True
268
268
  )
269
- def change_400_to_404(response: ResponseInfo):
269
+ def replace_400_with_404(response: ResponseInfo):
270
270
  if response.status_code == 400:
271
271
  response.status_code = 404
272
272
  ```
@@ -292,9 +292,9 @@ Cadwyn can migrate more than just request bodies.
292
292
 
293
293
  #### Internal representations
294
294
 
295
- We have only reviewed simplistic cases so far. But what happens when you cannot just migrate your data that easily? It can happen because your earlier versions had **more data** than your newer versions. Or that data had more formats.
295
+ We have only reviewed simple cases so far. But what happens when you cannot just migrate your data that easily? It can happen because your earlier versions had **more data** than your newer versions. Or that data had more formats.
296
296
 
297
- Let's imagine that previously the `User` schema had a list of addresses but now we want to make a breaking change and turn them into a single address. The naive migration will just take the first address from the list for requests and turn that one address into a list for responses like so:
297
+ Imagine that previously the `User` schema had a list of addresses but now we want to make a breaking change and turn them into a single address. The naive migration will just take the first address from the list for requests and turn that address into a list for responses like so:
298
298
 
299
299
  ```python
300
300
  from cadwyn import (
@@ -328,7 +328,7 @@ class RemoveTaxIdEndpoints(VersionChange):
328
328
  response.body["addresses"] = [response.body.pop("address")]
329
329
  ```
330
330
 
331
- But this will not work. Now when the user from the old version asks us to save three addresses, we will in fact save only one. Old data is also going to be affected -- if old users had multiple addresses, we will only be able to return one of them. This is bad -- we have made a breaking change!
331
+ But this will not work. If the user from the old version requests to save three addresses, only one will actually be saved. Old data is also going to be affected -- if old users had multiple addresses, we will only be able to return one of them. This is bad -- we have made a breaking change!
332
332
 
333
333
  In order to solve this issue, Cadwyn uses a concept of **internal representations**. An internal representation of your data is like a database entry of your data -- it is its **latest** version plus all the fields that are incompatible with the latest API version. If we were talking about classes, then internal representation would be a child of your latest schemas -- it has all the same data and a little more, it expands its functionality. Essentially your internal representation of user object can contain much more data than your latest schemas.
334
334
 
@@ -357,11 +357,11 @@ class RemoveTaxIdEndpoints(VersionChange):
357
357
  )
358
358
  ```
359
359
 
360
- Yes, we do not need any of the migrations anymore because responses are handled automatically. See how-to section for an example of how we would achieve the same feat for requests.
360
+ Yes, we do not need any of the migrations anymore because responses are handled automatically. See the how-to section for an example of achieving the same result with requests.
361
361
 
362
362
  #### Manual body migrations
363
363
 
364
- Oftentimes you will have a need to migrate your data outside of routing, manually. For example, when you need to send a versioned response to your client via webhook or inside a worker/cronjob. In these instances, you can use `cadwyn.VersionBundle.migrate_response_body`:
364
+ Oftentimes you will need to migrate your data outside of routing, manually. For example, when you need to send a versioned response to your client via webhook or inside a worker/cronjob. In these instances, you can use `cadwyn.VersionBundle.migrate_response_body`:
365
365
 
366
366
  ```python
367
367
  from users import UserResource
@@ -0,0 +1,71 @@
1
+ # Contribution Guide
2
+
3
+ ## Setting up the environment
4
+
5
+ * The minimum supported version is Python 3.10. It is recommended to manage multiple Python versions on your system with [uv](https://docs.astral.sh/uv/)
6
+ * We maintain a Makefile with several commands to help with common tasks
7
+
8
+ 1. Install [uv](https://docs.astral.sh/uv/)
9
+ 2. Run `uv sync` to create a virtual environment and install the dependencies
10
+ 3. Install [pre-commit](https://pre-commit.com/) using uv: `uv tool install pre-commit`
11
+ 4. Run `pre-commit install --install-hooks` to install pre-commit hooks
12
+
13
+ ## Code contributions
14
+
15
+ ### Workflow
16
+
17
+ 1. [Fork](https://github.com/zmievsa/cadwyn/fork) the [Cadwyn repository](https://github.com/zmievsa/cadwyn)
18
+ 2. Clone your fork locally with git
19
+ 3. [Set up the environment](#setting-up-the-environment)
20
+ 4. Make your changes
21
+ 5. Commit your changes to git
22
+ 6. Push the changes
23
+ 7. Open a [pull request](https://docs.github.com/en/pull-requests). Give the pull request a descriptive title indicating what was changed
24
+
25
+ ## Guidelines for writing code
26
+
27
+ * Code should be [Pythonic and zen](https://peps.python.org/pep-0020/)
28
+ * All code should be fully [typed](https://peps.python.org/pep-0484/). This is enforced via [ruff](https://github.com/astral-sh/ruff) but the type hinting itself will be enforced by [pyright](https://github.com/microsoft/pyright/) in the future
29
+ * When complex types are required, use [type aliases](https://docs.python.org/3/library/typing.html#type-aliases)
30
+ * If something cannot be typed correctly due to the limitations of the type checkers, use [typing.cast](https://docs.python.org/3/library/typing.html#typing.cast) to resolve the issue. However, use `typing.cast` only as a last resort, after exhausting all other options of [type narrowing](https://mypy.readthedocs.io/en/stable/type_narrowing.html), such as [isinstance()](https://docs.python.org/3/library/functions.html#isinstance) checks and [type guards](https://docs.python.org/3/library/typing.html#typing.TypeGuard)
31
+ * Use `pyright: ignore` once you have verified that the line is correct, but pyright has issues with it
32
+ * If you are adding or modifying existing code, make sure that it's fully tested. 100% test coverage is mandatory, and will be checked on the PR using [Github Actions](https://github.com/features/actions)
33
+ * When adding a new public interface, make sure you have included it in the concept documentation located in `docs/concepts.md`. If applicable, add or modify examples in the docs related to the new functionality
34
+
35
+ ### Writing and running tests
36
+
37
+ Tests are contained within the `tests` directory, and follow roughly the same
38
+ directory structure as the `cadwyn` module. If you are adding a test
39
+ case, it should be located within the correct submodule of `tests`. E.g.
40
+ tests for `cadwyn/codegen.py` reside in `tests/codegen`.
41
+
42
+ `make test` to run tests located in `tests`
43
+
44
+ ### Running type checkers
45
+
46
+ We use [pyright](https://github.com/microsoft/pyright/) to enforce type safety.
47
+ You can run it with:
48
+
49
+ `uv run pyright .`
50
+
51
+ ## Project documentation
52
+
53
+ The documentation is located in the `/docs` directory and uses
54
+ [Markdown](https://www.markdownguide.org/).
55
+
56
+ ### Docs theme and appearance
57
+
58
+ We welcome contributions that improve the appearance and usability of the docs. We use [mkdocs-material](https://squidfunk.github.io/mkdocs-material/) If you wish to contribute to the docs style / setup, or static site generation, consult the theme docs as a first step.
59
+
60
+ ### Running the docs locally
61
+
62
+ After improving the docs, serve the documentation with `mkdocs serve`
63
+
64
+ ### Writing and editing docs
65
+
66
+ We welcome contributions that improve the content of the docs. Feel free to add examples, clarify text, restructure the docs, etc., but make sure to follow these guidelines:
67
+
68
+ * Write text in idiomatic, simple English
69
+ * Opt for [Oxford commas](https://en.wikipedia.org/wiki/Serial_comma) when listing a series of terms
70
+ * Keep examples simple and self contained
71
+ * Provide links where applicable
@@ -0,0 +1,28 @@
1
+
2
+ # Change the business logic in a new version
3
+
4
+ First, ask yourself: are you sure a behavioral change is really necessary? Are you sure it is not possible to keep the same logic for both versions? Or at least make the behavior depend on the received data? Behavioral changes (or **side effects**) are the least maintainable part of almost any versioning approach. They produce the largest footprint on your code. So if you are not careful, your logic will be littered with version checks.
5
+
6
+ But if you are certain that you need to make a breaking behavioral change, Cadwyn has all the tools to minimize its impact as much as possible.
7
+
8
+ ## Calling endpoint causes unexpected data modifications
9
+
10
+ Use an `if statement` with a [side effect](../../concepts/version_changes.md#version-changes-with-side-effects).
11
+
12
+ ## Calling endpoint doesn't cause expected data modifications
13
+
14
+ Use an `if statement` with a [side effect](../../concepts/version_changes.md#version-changes-with-side-effects).
15
+
16
+ ## Calling endpoint doesn't cause expected additional actions (e.g. Webhooks)
17
+
18
+ Use an `if statement` with a [side effect](../../concepts/version_changes.md#version-changes-with-side-effects).
19
+
20
+ ## Errors
21
+
22
+ ### Change the status code or a message in an HTTP error
23
+
24
+ You can [migrate anything about the error](../../concepts/version_changes.md#migration-of-http-errors) in a version change.
25
+
26
+ ### Introduce a new error or remove an old error
27
+
28
+ Use an `if statement` with a [side effect](../../concepts/version_changes.md#version-changes-with-side-effects).
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cadwyn"
3
- version = "5.4.5"
3
+ version = "5.6.0"
4
4
  description = "Production-ready community-driven modern Stripe-like API versioning in FastAPI"
5
5
  authors = [{ name = "Stanislav Zmiev", email = "zmievsa@gmail.com" }]
6
6
  license = "MIT"
@@ -51,7 +51,7 @@ classifiers = [
51
51
  "Topic :: Internet :: WWW/HTTP",
52
52
  ]
53
53
  dependencies = [
54
- "fastapi >=0.112.4",
54
+ "fastapi >=0.119.0",
55
55
  "starlette >=0.30.0",
56
56
  "pydantic >=2.11.0",
57
57
  "jinja2 >=3.1.2",
@@ -62,7 +62,7 @@ dependencies = [
62
62
 
63
63
 
64
64
  [project.optional-dependencies]
65
- standard = ["fastapi[standard]>=0.112.3", "typer>=0.7.0"]
65
+ standard = ["fastapi[standard]>=0.119.0", "typer>=0.7.0"]
66
66
 
67
67
  [tool.uv]
68
68
  dev-dependencies = [
@@ -10,8 +10,6 @@ from cadwyn.structure.versions import Version, VersionBundle
10
10
  from tests.test_cli import code
11
11
 
12
12
 
13
- # TODO: Return this test once https://github.com/pydantic/pydantic/pull/11898 is merged
14
- @pytest.mark.xfail
15
13
  def test__render_model__with_weird_types():
16
14
  result = render_model_by_path(
17
15
  "tests._resources.render.complex.classes:ModelWithWeirdFields",
@@ -10,6 +10,7 @@ from cadwyn.exceptions import (
10
10
  CadwynStructureError,
11
11
  InvalidGenerationInstructionError,
12
12
  )
13
+ from cadwyn.schema_generation import SchemaGenerator
13
14
  from cadwyn.structure import schema
14
15
  from tests.conftest import (
15
16
  CreateRuntimeSchemas,
@@ -68,16 +69,17 @@ def test__field_existed_as__original_schema_has_a_field(create_runtime_schemas:
68
69
 
69
70
 
70
71
  def test__field_existed_as__extras_are_added(create_runtime_schemas: CreateRuntimeSchemas):
71
- schemas = create_runtime_schemas(
72
- version_change(
73
- schema(EmptySchema)
74
- .field("foo")
75
- .existed_as(
76
- type=int,
77
- info=Field(deflolbtt="hewwo"), # pyright: ignore[reportCallIssue]
78
- ),
72
+ with pytest.warns(DeprecationWarning):
73
+ schemas = create_runtime_schemas(
74
+ version_change(
75
+ schema(EmptySchema)
76
+ .field("foo")
77
+ .existed_as(
78
+ type=int,
79
+ info=Field(deflolbtt="hewwo"), # pyright: ignore[reportCallIssue]
80
+ ),
81
+ )
79
82
  )
80
- )
81
83
 
82
84
  class ExpectedSchema(BaseModel):
83
85
  foo: int = Field(json_schema_extra={"deflolbtt": "hewwo"})
@@ -180,7 +182,7 @@ def assert_field_had_changes_apply(
180
182
  attr: str,
181
183
  attr_value: Any,
182
184
  create_runtime_schemas: CreateRuntimeSchemas,
183
- ):
185
+ ) -> dict[str, SchemaGenerator]:
184
186
  schemas = create_runtime_schemas(version_change(schema(model).field("foo").had(**{attr: attr_value})))
185
187
 
186
188
  field_info = schemas["2000-01-01"][model].model_fields["foo"]
@@ -190,6 +192,8 @@ def assert_field_had_changes_apply(
190
192
  else:
191
193
  assert getattr(field_info, attr) == attr_value
192
194
 
195
+ return schemas
196
+
193
197
 
194
198
  @pytest.mark.parametrize(
195
199
  ("attr", "attr_value"),
@@ -266,7 +270,7 @@ def test__schema_field_had__decimal_field(attr: str, attr_value: Any, create_run
266
270
 
267
271
  @pytest.mark.parametrize(
268
272
  ("attr", "attr_value"),
269
- [("exclude", [16, 17, 18])],
273
+ [("exclude", True)],
270
274
  )
271
275
  def test__schema_field_had__list_of_int_field(attr: str, attr_value: Any, create_runtime_schemas: CreateRuntimeSchemas):
272
276
  class SchemaWithOneListOfIntField(BaseModel):
@@ -294,6 +298,46 @@ def test__schema_field_had__float_field(
294
298
  )
295
299
 
296
300
 
301
+ def test_schema_field_had__json_schema_extra_as_dict(create_runtime_schemas: CreateRuntimeSchemas):
302
+ class SchemaWithFooHadDictJsonSchemaExtra(BaseModel):
303
+ foo: str
304
+
305
+ schemas = assert_field_had_changes_apply(
306
+ SchemaWithFooHadDictJsonSchemaExtra,
307
+ "json_schema_extra",
308
+ {"example": "bar"},
309
+ create_runtime_schemas,
310
+ )
311
+
312
+ # Validate the JSON Schema produced by Pydantic contains the extra
313
+ model_cls = schemas["2000-01-01"][SchemaWithFooHadDictJsonSchemaExtra] # pyright: ignore
314
+ json_schema = model_cls.model_json_schema()
315
+ assert json_schema["properties"]["foo"]["example"] == "bar"
316
+
317
+
318
+ def test__schema_field_had__json_schema_extra_as_callable(create_runtime_schemas: CreateRuntimeSchemas):
319
+ def modify_schema(schema_dict: dict[str, Any]):
320
+ schema_dict["example"] = "bar"
321
+ schema_dict["custom"] = 42
322
+
323
+ class SchemaWithFooHadCallableJsonSchemaExtra(BaseModel):
324
+ foo: str
325
+
326
+ schemas = assert_field_had_changes_apply(
327
+ SchemaWithFooHadCallableJsonSchemaExtra,
328
+ "json_schema_extra",
329
+ modify_schema,
330
+ create_runtime_schemas,
331
+ )
332
+
333
+ # Validate the JSON Schema produced by Pydantic contains changes from the callable
334
+ model_cls = schemas["2000-01-01"][SchemaWithFooHadCallableJsonSchemaExtra] # pyright: ignore
335
+ json_schema = model_cls.model_json_schema()
336
+ props = json_schema["properties"]["foo"]
337
+ assert props["example"] == "bar"
338
+ assert props["custom"] == 42
339
+
340
+
297
341
  def test__schema_field_didnt_have__removing_default(create_runtime_schemas: CreateRuntimeSchemas):
298
342
  class SchemaWithDefaults(BaseModel):
299
343
  foo: str = "hewwo"
@@ -99,7 +99,7 @@ wheels = [
99
99
 
100
100
  [[package]]
101
101
  name = "cadwyn"
102
- version = "5.4.5"
102
+ version = "5.6.0"
103
103
  source = { editable = "." }
104
104
  dependencies = [
105
105
  { name = "backports-strenum", marker = "python_full_version < '3.11'" },
@@ -144,8 +144,8 @@ dev = [
144
144
  [package.metadata]
145
145
  requires-dist = [
146
146
  { name = "backports-strenum", marker = "python_full_version < '3.11'", specifier = ">=1.3.1,<2" },
147
- { name = "fastapi", specifier = ">=0.112.4" },
148
- { name = "fastapi", extras = ["standard"], marker = "extra == 'standard'", specifier = ">=0.112.3" },
147
+ { name = "fastapi", specifier = ">=0.119.0" },
148
+ { name = "fastapi", extras = ["standard"], marker = "extra == 'standard'", specifier = ">=0.119.0" },
149
149
  { name = "jinja2", specifier = ">=3.1.2" },
150
150
  { name = "pydantic", specifier = ">=2.11.0" },
151
151
  { name = "starlette", specifier = ">=0.30.0" },
@@ -1,72 +0,0 @@
1
- # Contribution Guide
2
-
3
- ## Setting up the environment
4
-
5
- * The lowest currently supported version is Python 3.10. You should use
6
- [uv](https://docs.astral.sh/uv/) to manage multiple Python
7
- versions on your system.
8
- * We maintain a Makefile with several commands to help with common tasks.
9
-
10
- 1. Install [uv](https://docs.astral.sh/uv/)
11
- 2. Run `uv sync` to create a virtual environment and install the required development dependencies
12
- 3. Install [pre-commit](https://pre-commit.com/) using uv: `uv tool install pre-commit`
13
- 4. Run `pre-commit install --install-hooks` to install pre-commit hooks
14
-
15
- ## Code contributions
16
-
17
- ### Workflow
18
-
19
- 1. [Fork](https://github.com/zmievsa/cadwyn/fork) the [Cadwyn repository](https://github.com/zmievsa/cadwyn)
20
- 2. Clone your fork locally with git
21
- 3. [Set up the environment](#setting-up-the-environment)
22
- 4. Make your changes
23
- 5. Commit your changes to git.
24
- 6. Open a [pull request](https://docs.github.com/en/pull-requests). Give the pull request a descriptive title indicating what it changes.
25
-
26
- ## Guidelines for writing code
27
-
28
- * Code should be [Pythonic and zen](https://peps.python.org/pep-0020/)
29
- * All code should be fully [typed](https://peps.python.org/pep-0484/). This is enforced via [ruff](https://github.com/astral-sh/ruff) but the type hinting itself will be enforced by [pyright](https://github.com/microsoft/pyright/) in the future.
30
- * When requiring complex types, use a [type alias](https://docs.python.org/3/library/typing.html#type-aliases).
31
- * If something cannot be typed correctly due to a limitation of the type checkers, you may use [typing.cast](https://docs.python.org/3/library/typing.html#typing.cast) to rectify the situation. However, you should only use as a last resort if you've exhausted all other options of [type narrowing](https://mypy.readthedocs.io/en/stable/type_narrowing.html), such as [isinstance()](https://docs.python.org/3/library/functions.html#isinstance) checks and [type guards](https://docs.python.org/3/library/typing.html#typing.TypeGuard)
32
- * You may use `pyright: ignore` if you ensured that a line is correct, but pyright has issues with it.
33
- * If you are adding or modifying existing code, ensure that it's fully tested. 100% test coverage is mandatory, and will be checked on the PR using [Github Actions](https://github.com/features/actions)
34
- * When adding a new public interface, it has to be included in the concept documentation located in `docs/concepts.md`. If applicable, add or modify examples in the docs related to the new functionality implemented.
35
-
36
- ### Writing and running tests
37
-
38
- Tests are contained within the `tests` directory, and follow roughly the same
39
- directory structure as the `cadwyn` module. If you are adding a test
40
- case, it should be located within the correct submodule of `tests`. E.g.
41
- tests for `cadwyn/codegen.py` reside in `tests/codegen`.
42
-
43
- `make test` to run tests located in `tests`
44
-
45
- ### Running type checkers
46
-
47
- We use [pyright](https://github.com/microsoft/pyright/) to enforce type safety.
48
- You can run it with:
49
-
50
- `uv run pyright .`
51
-
52
- ## Project documentation
53
-
54
- The documentation is located in the `/docs` directory and is all in
55
- [Markdown](https://www.markdownguide.org/).
56
-
57
- ### Docs theme and appearance
58
-
59
- We welcome contributions that enhance / improve the appearance and usability of the docs. We use [mkdocs-material](https://squidfunk.github.io/mkdocs-material/) If you wish to contribute to the docs style / setup, or static site generation, you should consult the theme docs as a first step.
60
-
61
- ### Running the docs locally
62
-
63
- Then you can serve the documentation with `mkdocs serve`
64
-
65
- ### Writing and editing docs
66
-
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
-
69
- * Write text in idiomatic English, using simple language
70
- * Opt for [Oxford commas](https://en.wikipedia.org/wiki/Serial_comma) when listing a series of terms
71
- * Keep examples simple and self contained
72
- * Provide links where applicable
@@ -1,28 +0,0 @@
1
-
2
- # Change the business logic in a new version
3
-
4
- First, ask yourself: are you sure there really needs to be a behavioral change? Are you sure it is not possible to keep the same logic for both versions? Or at least make the behavior depend on the received data? Behavioral changes (or **side effects**) are the least maintainable part of almost any versioning approach. They produce the largest footprint on your code so if you are not careful -- your logic will be littered with version checks.
5
-
6
- But if you are certain that you need to make a breaking behavioral change, Cadwyn has all the tools to minimize its impact as much as possible.
7
-
8
- ## Calling endpoint causes unexpected data modifications
9
-
10
- You'd use an `if statement` with a [side effect](../../concepts/version_changes.md#version-changes-with-side-effects).
11
-
12
- ## Calling endpoint doesn't cause expected data modifications
13
-
14
- You'd use an `if statement` with a [side effect](../../concepts/version_changes.md#version-changes-with-side-effects).
15
-
16
- ## Calling endpoint doesn't cause expected additional actions (e.g. Webhooks)
17
-
18
- You'd use an `if statement` with a [side effect](../../concepts/version_changes.md#version-changes-with-side-effects).
19
-
20
- ## Errors
21
-
22
- ### Change the status code or a message in an HTTP error
23
-
24
- You can [migrate anything about the error](../../concepts/version_changes.md#migration-of-http-errors) in a version change.
25
-
26
- ### Introduce a new error or remove an old error
27
-
28
- You'd use an `if statement` with a [side effect](../../concepts/version_changes.md#version-changes-with-side-effects).
File without changes
File without changes