cadwyn 3.15.10__py3-none-any.whl → 4.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cadwyn might be problematic. Click here for more details.
- cadwyn/__init__.py +26 -8
- cadwyn/__main__.py +46 -90
- cadwyn/_asts.py +9 -133
- cadwyn/_importer.py +31 -0
- cadwyn/_render.py +152 -0
- cadwyn/_utils.py +7 -107
- cadwyn/applications.py +5 -34
- cadwyn/exceptions.py +11 -3
- cadwyn/middleware.py +4 -4
- cadwyn/route_generation.py +22 -450
- cadwyn/routing.py +2 -5
- cadwyn/schema_generation.py +946 -0
- cadwyn/structure/__init__.py +0 -2
- cadwyn/structure/schemas.py +50 -49
- cadwyn/structure/versions.py +24 -137
- {cadwyn-3.15.10.dist-info → cadwyn-4.1.0.dist-info}/METADATA +4 -5
- cadwyn-4.1.0.dist-info/RECORD +27 -0
- {cadwyn-3.15.10.dist-info → cadwyn-4.1.0.dist-info}/WHEEL +1 -1
- cadwyn/_compat.py +0 -151
- cadwyn/_package_utils.py +0 -45
- cadwyn/codegen/README.md +0 -10
- cadwyn/codegen/__init__.py +0 -10
- cadwyn/codegen/_common.py +0 -168
- cadwyn/codegen/_main.py +0 -279
- cadwyn/codegen/_plugins/__init__.py +0 -0
- cadwyn/codegen/_plugins/class_migrations.py +0 -423
- cadwyn/codegen/_plugins/class_rebuilding.py +0 -109
- cadwyn/codegen/_plugins/class_renaming.py +0 -49
- cadwyn/codegen/_plugins/import_auto_adding.py +0 -64
- cadwyn/codegen/_plugins/module_migrations.py +0 -15
- cadwyn/main.py +0 -11
- cadwyn/structure/modules.py +0 -39
- cadwyn-3.15.10.dist-info/RECORD +0 -38
- {cadwyn-3.15.10.dist-info → cadwyn-4.1.0.dist-info}/LICENSE +0 -0
- {cadwyn-3.15.10.dist-info → cadwyn-4.1.0.dist-info}/entry_points.txt +0 -0
cadwyn/_utils.py
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import importlib
|
|
3
|
-
import inspect
|
|
4
|
-
import sys
|
|
5
|
-
from collections.abc import Callable, Collection
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
from types import ModuleType
|
|
1
|
+
from collections.abc import Callable
|
|
8
2
|
from typing import Any, Generic, TypeVar, Union
|
|
9
3
|
|
|
10
|
-
from
|
|
4
|
+
from pydantic._internal._decorators import unwrap_wrapped_function
|
|
11
5
|
|
|
12
6
|
Sentinel: Any = object()
|
|
13
7
|
UnionType = type(int | str) | type(Union[int, str])
|
|
@@ -41,102 +35,8 @@ def same_definition_as_in(t: _T) -> Callable[[Callable], _T]:
|
|
|
41
35
|
return decorator
|
|
42
36
|
|
|
43
37
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
module_from_old_version = sys.modules[cls_from_old_version.__module__]
|
|
50
|
-
try:
|
|
51
|
-
module = get_another_version_of_module(module_from_old_version, new_version_dir, version_dirs)
|
|
52
|
-
except ModuleIsNotVersionedError:
|
|
53
|
-
return cls_from_old_version
|
|
54
|
-
return getattr(module, cls_from_old_version.__name__)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def get_another_version_of_module(
|
|
58
|
-
module_from_old_version: ModuleType,
|
|
59
|
-
new_version_dir: Path,
|
|
60
|
-
version_dirs: frozenset[Path] | tuple[Path, ...],
|
|
61
|
-
):
|
|
62
|
-
new_model_module_python_path = get_pythonpath_to_another_version_of_module(
|
|
63
|
-
module_from_old_version,
|
|
64
|
-
new_version_dir,
|
|
65
|
-
version_dirs,
|
|
66
|
-
)
|
|
67
|
-
return importlib.import_module(new_model_module_python_path)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def get_pythonpath_to_another_version_of_module(
|
|
71
|
-
module_from_old_version: ModuleType,
|
|
72
|
-
new_version_dir: Path,
|
|
73
|
-
version_dirs: frozenset[Path] | tuple[Path, ...],
|
|
74
|
-
) -> str:
|
|
75
|
-
# ['package', 'companies', 'head', 'schemas']
|
|
76
|
-
# ^^^^^^
|
|
77
|
-
# index = 2
|
|
78
|
-
index_of_base_schema_dir = get_index_of_head_schema_dir_in_module_python_path(
|
|
79
|
-
module_from_old_version,
|
|
80
|
-
new_version_dir,
|
|
81
|
-
version_dirs,
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
# ['package', 'companies', 'head', 'schemas']
|
|
85
|
-
model_split_python_path = module_from_old_version.__name__.split(".")
|
|
86
|
-
# ['package', 'companies', 'v2021_01_01', 'schemas']
|
|
87
|
-
model_split_python_path[index_of_base_schema_dir] = new_version_dir.name
|
|
88
|
-
# package.companies.v2021_01_01.schemas
|
|
89
|
-
return ".".join(model_split_python_path)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
@functools.cache
|
|
93
|
-
def get_index_of_head_schema_dir_in_module_python_path(
|
|
94
|
-
module_from_old_version: ModuleType,
|
|
95
|
-
parallel_dir: Path,
|
|
96
|
-
version_dirs: frozenset[Path] = frozenset(),
|
|
97
|
-
) -> int:
|
|
98
|
-
"""If version_dirs have been passed, we will check if the module is versioned and raise an exception if it isn't"""
|
|
99
|
-
file = inspect.getsourcefile(module_from_old_version)
|
|
100
|
-
# Impossible to cover
|
|
101
|
-
if file is None: # pragma: no cover
|
|
102
|
-
raise CadwynError(
|
|
103
|
-
f"Model {module_from_old_version} is not defined in a file. It is likely because it's a compiled module "
|
|
104
|
-
"which Cadwyn couldn't migrate to an older version. "
|
|
105
|
-
"If you are seeing this error -- you've encountered a bug in Cadwyn.",
|
|
106
|
-
)
|
|
107
|
-
# /home/myuser/package/companies/head/__init__.py
|
|
108
|
-
file = Path(file)
|
|
109
|
-
_validate_that_module_is_versioned(file, version_dirs)
|
|
110
|
-
is_package = file.name == "__init__.py"
|
|
111
|
-
if is_package:
|
|
112
|
-
# /home/myuser/package/companies/head/
|
|
113
|
-
file = file.parent
|
|
114
|
-
# /home/myuser/package/companies
|
|
115
|
-
root_dir = parallel_dir.parent
|
|
116
|
-
# head/schemas
|
|
117
|
-
relative_file = file.relative_to(root_dir).with_suffix("")
|
|
118
|
-
# ['head', 'schemas']
|
|
119
|
-
relative_file_parts = relative_file.parts
|
|
120
|
-
# package.companies.head.schemas.payables
|
|
121
|
-
module_python_path = module_from_old_version.__name__
|
|
122
|
-
# ['package', 'companies', 'head', 'schemas']
|
|
123
|
-
module_split_python_path = module_python_path.split(".")
|
|
124
|
-
|
|
125
|
-
index = len(module_split_python_path) - len(relative_file_parts) - int(is_package)
|
|
126
|
-
|
|
127
|
-
# When we are in latest/__init__.py, we have this special case
|
|
128
|
-
if len(relative_file_parts) == 1 and is_package:
|
|
129
|
-
index += 1
|
|
130
|
-
return index
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
def _validate_that_module_is_versioned(file: Path, version_dirs: Collection[Path]):
|
|
134
|
-
if not version_dirs:
|
|
135
|
-
return
|
|
136
|
-
for version_dir in version_dirs:
|
|
137
|
-
try:
|
|
138
|
-
file.relative_to(version_dir)
|
|
139
|
-
return
|
|
140
|
-
except ValueError:
|
|
141
|
-
pass
|
|
142
|
-
raise ModuleIsNotVersionedError(f"Module {file} is not versioned.")
|
|
38
|
+
def fully_unwrap_decorator(func: Callable, is_pydantic_v1_style_validator: Any):
|
|
39
|
+
func = unwrap_wrapped_function(func)
|
|
40
|
+
if is_pydantic_v1_style_validator and func.__closure__:
|
|
41
|
+
func = func.__closure__[0].cell_contents
|
|
42
|
+
return unwrap_wrapped_function(func)
|
cadwyn/applications.py
CHANGED
|
@@ -4,7 +4,6 @@ from collections.abc import Callable, Coroutine, Sequence
|
|
|
4
4
|
from datetime import date
|
|
5
5
|
from logging import getLogger
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from types import ModuleType
|
|
8
7
|
from typing import Any, cast
|
|
9
8
|
|
|
10
9
|
from fastapi import APIRouter, FastAPI, HTTPException, routing
|
|
@@ -24,7 +23,7 @@ from starlette.requests import Request
|
|
|
24
23
|
from starlette.responses import JSONResponse, Response
|
|
25
24
|
from starlette.routing import BaseRoute, Route
|
|
26
25
|
from starlette.types import Lifespan
|
|
27
|
-
from typing_extensions import Self
|
|
26
|
+
from typing_extensions import Self
|
|
28
27
|
|
|
29
28
|
from cadwyn.middleware import HeaderVersioningMiddleware, _get_api_version_dependency
|
|
30
29
|
from cadwyn.route_generation import generate_versioned_routers
|
|
@@ -95,9 +94,6 @@ class Cadwyn(FastAPI):
|
|
|
95
94
|
) -> None:
|
|
96
95
|
self.versions = versions
|
|
97
96
|
# TODO: Remove argument entirely in any major version.
|
|
98
|
-
latest_schemas_package = extra.pop("latest_schemas_package", None) or self.versions.head_schemas_package
|
|
99
|
-
self.versions.head_schemas_package = latest_schemas_package
|
|
100
|
-
self._latest_schemas_package = cast(ModuleType, latest_schemas_package)
|
|
101
97
|
self._dependency_overrides_provider = FakeDependencyOverridesProvider({})
|
|
102
98
|
|
|
103
99
|
super().__init__(
|
|
@@ -175,9 +171,10 @@ class Cadwyn(FastAPI):
|
|
|
175
171
|
|
|
176
172
|
@property
|
|
177
173
|
def dependency_overrides(self) -> dict[Callable[..., Any], Callable[..., Any]]:
|
|
174
|
+
# TODO: Remove this approach as it is no longer necessary
|
|
178
175
|
# This is only necessary because we cannot send self to versioned router generator
|
|
179
|
-
# because it takes a deepcopy of the router and self.versions.head_schemas_package
|
|
180
|
-
# which
|
|
176
|
+
# because it takes a deepcopy of the router and self.versions.head_schemas_package was a module
|
|
177
|
+
# which couldn't be copied.
|
|
181
178
|
return self._dependency_overrides_provider.dependency_overrides
|
|
182
179
|
|
|
183
180
|
@dependency_overrides.setter
|
|
@@ -187,16 +184,6 @@ class Cadwyn(FastAPI):
|
|
|
187
184
|
) -> None:
|
|
188
185
|
self._dependency_overrides_provider.dependency_overrides = value
|
|
189
186
|
|
|
190
|
-
@property # pragma: no cover
|
|
191
|
-
@deprecated("It is going to be deleted in the future. Use VersionBundle.head_schemas_package instead")
|
|
192
|
-
def latest_schemas_package(self):
|
|
193
|
-
return self._latest_schemas_package
|
|
194
|
-
|
|
195
|
-
@latest_schemas_package.setter # pragma: no cover
|
|
196
|
-
@deprecated("It is going to be deleted in the future. Use VersionBundle.head_schemas_package instead")
|
|
197
|
-
def latest_schemas_package(self, value: ModuleType | None):
|
|
198
|
-
self._latest_schemas_package = value
|
|
199
|
-
|
|
200
187
|
def _add_openapi_endpoints(self, unversioned_router: APIRouter):
|
|
201
188
|
if self.openapi_url is not None:
|
|
202
189
|
unversioned_router.add_route(
|
|
@@ -233,10 +220,7 @@ class Cadwyn(FastAPI):
|
|
|
233
220
|
root_router = APIRouter(dependency_overrides_provider=self._dependency_overrides_provider)
|
|
234
221
|
for router in routers:
|
|
235
222
|
root_router.include_router(router)
|
|
236
|
-
router_versions = generate_versioned_routers(
|
|
237
|
-
root_router,
|
|
238
|
-
versions=self.versions,
|
|
239
|
-
)
|
|
223
|
+
router_versions = generate_versioned_routers(root_router, versions=self.versions)
|
|
240
224
|
for version, router in router_versions.items():
|
|
241
225
|
self.add_header_versioned_routers(router, header_value=version.isoformat())
|
|
242
226
|
|
|
@@ -358,16 +342,3 @@ class Cadwyn(FastAPI):
|
|
|
358
342
|
self.router.routes.extend(added_routes)
|
|
359
343
|
|
|
360
344
|
return added_routes
|
|
361
|
-
|
|
362
|
-
@deprecated("Use builtin FastAPI methods such as include_router instead")
|
|
363
|
-
def add_unversioned_routers(self, *routers: APIRouter):
|
|
364
|
-
for router in routers:
|
|
365
|
-
self.include_router(router)
|
|
366
|
-
|
|
367
|
-
@deprecated("Use builtin FastAPI methods such as add_api_route instead")
|
|
368
|
-
def add_unversioned_routes(self, *routes: Route):
|
|
369
|
-
router = APIRouter(routes=list(routes))
|
|
370
|
-
self.include_router(router)
|
|
371
|
-
|
|
372
|
-
@deprecated("It no longer does anything")
|
|
373
|
-
def enrich_swagger(self): ...
|
cadwyn/exceptions.py
CHANGED
|
@@ -5,6 +5,10 @@ from typing import Any
|
|
|
5
5
|
from fastapi.routing import APIRoute
|
|
6
6
|
|
|
7
7
|
|
|
8
|
+
class CadwynRenderError(Exception):
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
8
12
|
class CadwynError(Exception):
|
|
9
13
|
pass
|
|
10
14
|
|
|
@@ -26,15 +30,15 @@ class LintingError(CadwynError):
|
|
|
26
30
|
pass
|
|
27
31
|
|
|
28
32
|
|
|
29
|
-
class
|
|
33
|
+
class SchemaGenerationError(CadwynError):
|
|
30
34
|
pass
|
|
31
35
|
|
|
32
36
|
|
|
33
|
-
class ModuleIsNotAvailableAsTextError(
|
|
37
|
+
class ModuleIsNotAvailableAsTextError(SchemaGenerationError):
|
|
34
38
|
pass
|
|
35
39
|
|
|
36
40
|
|
|
37
|
-
class InvalidGenerationInstructionError(
|
|
41
|
+
class InvalidGenerationInstructionError(SchemaGenerationError):
|
|
38
42
|
pass
|
|
39
43
|
|
|
40
44
|
|
|
@@ -70,3 +74,7 @@ class CadwynStructureError(CadwynError):
|
|
|
70
74
|
|
|
71
75
|
class ModuleIsNotVersionedError(ValueError):
|
|
72
76
|
pass
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class ImportFromStringError(CadwynError):
|
|
80
|
+
pass
|
cadwyn/middleware.py
CHANGED
|
@@ -64,11 +64,11 @@ class HeaderVersioningMiddleware(BaseHTTPMiddleware):
|
|
|
64
64
|
request=request,
|
|
65
65
|
dependant=self.version_header_validation_dependant,
|
|
66
66
|
async_exit_stack=async_exit_stack,
|
|
67
|
-
embed_body_fields=False,
|
|
68
67
|
)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
values, errors, *_ = solved_result
|
|
69
|
+
if errors:
|
|
70
|
+
return self.default_response_class(status_code=422, content=_normalize_errors(errors))
|
|
71
|
+
api_version = cast(date, values[self.api_version_header_name.replace("-", "_")])
|
|
72
72
|
self.api_version_var.set(api_version)
|
|
73
73
|
|
|
74
74
|
response = await call_next(request)
|