cadwyn 5.0.0__py3-none-any.whl → 5.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/_asts.py +10 -7
- cadwyn/_render.py +7 -7
- cadwyn/_utils.py +25 -2
- cadwyn/applications.py +34 -32
- cadwyn/changelogs.py +182 -181
- cadwyn/middleware.py +7 -7
- cadwyn/route_generation.py +10 -9
- cadwyn/routing.py +2 -2
- cadwyn/schema_generation.py +114 -46
- cadwyn/structure/common.py +5 -3
- cadwyn/structure/data.py +10 -7
- cadwyn/structure/endpoints.py +24 -21
- cadwyn/structure/enums.py +13 -7
- cadwyn/structure/schemas.py +63 -53
- cadwyn/structure/versions.py +50 -40
- {cadwyn-5.0.0.dist-info → cadwyn-5.1.0.dist-info}/METADATA +4 -3
- cadwyn-5.1.0.dist-info/RECORD +28 -0
- cadwyn-5.0.0.dist-info/RECORD +0 -28
- {cadwyn-5.0.0.dist-info → cadwyn-5.1.0.dist-info}/WHEEL +0 -0
- {cadwyn-5.0.0.dist-info → cadwyn-5.1.0.dist-info}/entry_points.txt +0 -0
- {cadwyn-5.0.0.dist-info → cadwyn-5.1.0.dist-info}/licenses/LICENSE +0 -0
cadwyn/changelogs.py
CHANGED
|
@@ -2,7 +2,7 @@ import copy
|
|
|
2
2
|
import sys
|
|
3
3
|
from enum import auto
|
|
4
4
|
from logging import getLogger
|
|
5
|
-
from typing import Any, Literal, TypeVar, cast, get_args
|
|
5
|
+
from typing import Any, Literal, TypeVar, Union, cast, get_args
|
|
6
6
|
|
|
7
7
|
from fastapi._compat import (
|
|
8
8
|
GenerateJsonSchema,
|
|
@@ -18,8 +18,8 @@ from fastapi.openapi.utils import (
|
|
|
18
18
|
from fastapi.routing import APIRoute
|
|
19
19
|
from pydantic import BaseModel, Field, RootModel
|
|
20
20
|
|
|
21
|
-
from cadwyn._asts import
|
|
22
|
-
from cadwyn._utils import Sentinel
|
|
21
|
+
from cadwyn._asts import GenericAliasUnionArgs
|
|
22
|
+
from cadwyn._utils import ZIP_STRICT_FALSE, Sentinel
|
|
23
23
|
from cadwyn.route_generation import _get_routes
|
|
24
24
|
from cadwyn.routing import _RootCadwynAPIRouter
|
|
25
25
|
from cadwyn.schema_generation import SchemaGenerator, _change_field_in_model, generate_versioned_models
|
|
@@ -48,12 +48,12 @@ else: # pragma: no cover
|
|
|
48
48
|
|
|
49
49
|
_logger = getLogger(__name__)
|
|
50
50
|
|
|
51
|
-
T = TypeVar("T", bound=PossibleInstructions
|
|
51
|
+
T = TypeVar("T", bound=Union[PossibleInstructions, type[VersionChange]])
|
|
52
52
|
|
|
53
53
|
|
|
54
54
|
def hidden(instruction_or_version_change: T) -> T:
|
|
55
55
|
if isinstance(
|
|
56
|
-
instruction_or_version_change, staticmethod
|
|
56
|
+
instruction_or_version_change, (staticmethod, ValidatorDidntExistInstruction, ValidatorExistedInstruction)
|
|
57
57
|
):
|
|
58
58
|
return instruction_or_version_change
|
|
59
59
|
|
|
@@ -64,7 +64,7 @@ def hidden(instruction_or_version_change: T) -> T:
|
|
|
64
64
|
def _generate_changelog(versions: VersionBundle, router: _RootCadwynAPIRouter) -> "CadwynChangelogResource":
|
|
65
65
|
changelog = CadwynChangelogResource()
|
|
66
66
|
schema_generators = generate_versioned_models(versions)
|
|
67
|
-
for version, older_version in zip(versions, versions.versions[1:],
|
|
67
|
+
for version, older_version in zip(versions, versions.versions[1:], **ZIP_STRICT_FALSE):
|
|
68
68
|
routes_from_newer_version = router.versioned_routers[version.value].routes
|
|
69
69
|
schemas_from_older_version = get_fields_from_routes(router.versioned_routers[older_version.value].routes)
|
|
70
70
|
version_changelog = CadwynVersion(value=version.value)
|
|
@@ -83,7 +83,7 @@ def _generate_changelog(versions: VersionBundle, router: _RootCadwynAPIRouter) -
|
|
|
83
83
|
*version_change.alter_schema_instructions,
|
|
84
84
|
]:
|
|
85
85
|
if (
|
|
86
|
-
isinstance(instruction, ValidatorDidntExistInstruction
|
|
86
|
+
isinstance(instruction, (ValidatorDidntExistInstruction, ValidatorExistedInstruction))
|
|
87
87
|
or instruction.is_hidden_from_changelog
|
|
88
88
|
):
|
|
89
89
|
continue
|
|
@@ -113,10 +113,12 @@ def _get_older_field_name(
|
|
|
113
113
|
|
|
114
114
|
|
|
115
115
|
def _get_affected_model_names(
|
|
116
|
-
instruction:
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
instruction: Union[
|
|
117
|
+
FieldExistedAsInstruction,
|
|
118
|
+
FieldDidntExistInstruction,
|
|
119
|
+
FieldHadInstruction,
|
|
120
|
+
FieldDidntHaveInstruction,
|
|
121
|
+
],
|
|
120
122
|
generator_from_newer_version: SchemaGenerator,
|
|
121
123
|
schemas_from_last_version: list[ModelField],
|
|
122
124
|
):
|
|
@@ -139,7 +141,7 @@ def _get_affected_model_names(
|
|
|
139
141
|
|
|
140
142
|
|
|
141
143
|
def _get_all_pydantic_models_from_generic(annotation: Any) -> list[type[BaseModel]]:
|
|
142
|
-
if not isinstance(annotation,
|
|
144
|
+
if not isinstance(annotation, GenericAliasUnionArgs):
|
|
143
145
|
if isinstance(annotation, type) and issubclass(annotation, BaseModel):
|
|
144
146
|
return [annotation]
|
|
145
147
|
else:
|
|
@@ -209,7 +211,7 @@ class CadwynFieldAttributesWereChangedChangelogEntry(BaseModel):
|
|
|
209
211
|
|
|
210
212
|
|
|
211
213
|
class CadwynModelModifiedAttributes(BaseModel):
|
|
212
|
-
name: str
|
|
214
|
+
name: Union[str, None]
|
|
213
215
|
|
|
214
216
|
|
|
215
217
|
class CadwynSchemaWasChangedChangelogEntry(BaseModel):
|
|
@@ -294,15 +296,17 @@ class CadwynVersionChange(BaseModel):
|
|
|
294
296
|
|
|
295
297
|
|
|
296
298
|
CadwynVersionChangeInstruction = RootModel[
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
299
|
+
Union[
|
|
300
|
+
CadwynEnumMembersWereAddedChangelogEntry,
|
|
301
|
+
CadwynEnumMembersWereChangedChangelogEntry,
|
|
302
|
+
CadwynEndpointWasAddedChangelogEntry,
|
|
303
|
+
CadwynEndpointWasRemovedChangelogEntry,
|
|
304
|
+
CadwynSchemaFieldWasRemovedChangelogEntry,
|
|
305
|
+
CadwynSchemaFieldWasAddedChangelogEntry,
|
|
306
|
+
CadwynFieldAttributesWereChangedChangelogEntry,
|
|
307
|
+
CadwynEndpointHadChangelogEntry,
|
|
308
|
+
CadwynSchemaWasChangedChangelogEntry,
|
|
309
|
+
]
|
|
306
310
|
]
|
|
307
311
|
|
|
308
312
|
|
|
@@ -314,185 +318,182 @@ def _convert_version_change_instruction_to_changelog_entry( # noqa: C901
|
|
|
314
318
|
schemas_from_older_version: list[ModelField],
|
|
315
319
|
routes_from_newer_version: list[APIRoute],
|
|
316
320
|
):
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
321
|
+
if isinstance(instruction, EndpointDidntExistInstruction):
|
|
322
|
+
return CadwynEndpointWasAddedChangelogEntry(
|
|
323
|
+
path=instruction.endpoint_path,
|
|
324
|
+
methods=cast(Any, instruction.endpoint_methods),
|
|
325
|
+
)
|
|
326
|
+
elif isinstance(instruction, EndpointExistedInstruction):
|
|
327
|
+
return CadwynEndpointWasRemovedChangelogEntry(
|
|
328
|
+
path=instruction.endpoint_path,
|
|
329
|
+
methods=cast(Any, instruction.endpoint_methods),
|
|
330
|
+
)
|
|
331
|
+
elif isinstance(instruction, EndpointHadInstruction):
|
|
332
|
+
if instruction.attributes.include_in_schema is not Sentinel:
|
|
324
333
|
return CadwynEndpointWasRemovedChangelogEntry(
|
|
325
334
|
path=instruction.endpoint_path,
|
|
326
335
|
methods=cast(Any, instruction.endpoint_methods),
|
|
327
336
|
)
|
|
328
|
-
case EndpointHadInstruction():
|
|
329
|
-
if instruction.attributes.include_in_schema is not Sentinel:
|
|
330
|
-
return CadwynEndpointWasRemovedChangelogEntry(
|
|
331
|
-
path=instruction.endpoint_path,
|
|
332
|
-
methods=cast(Any, instruction.endpoint_methods),
|
|
333
|
-
)
|
|
334
337
|
|
|
335
|
-
|
|
338
|
+
renaming_map = {"operation_id": "operationId"}
|
|
336
339
|
|
|
337
|
-
|
|
340
|
+
attribute_changes = []
|
|
338
341
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
attribute_changes.append(
|
|
343
|
-
CadwynEndpointAttributeChange(name=renaming_map.get(attr, attr), new_value=attr_value)
|
|
344
|
-
)
|
|
345
|
-
if instruction.attributes.name is not Sentinel and instruction.attributes.summary is Sentinel:
|
|
342
|
+
for attr in ["path", "methods", "summary", "description", "tags", "deprecated", "operation_id"]:
|
|
343
|
+
attr_value = getattr(instruction.attributes, attr)
|
|
344
|
+
if attr_value is not Sentinel:
|
|
346
345
|
attribute_changes.append(
|
|
347
|
-
CadwynEndpointAttributeChange(name=
|
|
348
|
-
)
|
|
349
|
-
|
|
350
|
-
if any(
|
|
351
|
-
getattr(instruction.attributes, attr) is not Sentinel
|
|
352
|
-
for attr in ["path", "methods", "summary", "description", "tags", "deprecated"]
|
|
353
|
-
):
|
|
354
|
-
pass
|
|
355
|
-
if any(
|
|
356
|
-
attr is not Sentinel
|
|
357
|
-
for attr in [
|
|
358
|
-
instruction.attributes.response_model,
|
|
359
|
-
instruction.attributes.response_class,
|
|
360
|
-
instruction.attributes.responses,
|
|
361
|
-
instruction.attributes.status_code,
|
|
362
|
-
]
|
|
363
|
-
):
|
|
364
|
-
newer_routes = _get_routes(
|
|
365
|
-
routes_from_newer_version,
|
|
366
|
-
instruction.endpoint_path,
|
|
367
|
-
instruction.endpoint_methods,
|
|
368
|
-
instruction.endpoint_func_name,
|
|
369
|
-
is_deleted=False,
|
|
346
|
+
CadwynEndpointAttributeChange(name=renaming_map.get(attr, attr), new_value=attr_value)
|
|
370
347
|
)
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
for method, route_openapi in newer_openapi["paths"][instruction.endpoint_path].items()
|
|
375
|
-
}
|
|
376
|
-
attribute_changes.append(CadwynEndpointAttributeChange(name="responses", new_value=changed_responses))
|
|
377
|
-
return CadwynEndpointHadChangelogEntry(
|
|
378
|
-
path=instruction.endpoint_path,
|
|
379
|
-
methods=cast(Any, instruction.endpoint_methods),
|
|
380
|
-
changes=attribute_changes,
|
|
348
|
+
if instruction.attributes.name is not Sentinel and instruction.attributes.summary is Sentinel:
|
|
349
|
+
attribute_changes.append(
|
|
350
|
+
CadwynEndpointAttributeChange(name="summary", new_value=instruction.attributes.name)
|
|
381
351
|
)
|
|
382
352
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
generator_from_newer_version.model_bundle.schemas,
|
|
404
|
-
alter_schema_instruction=instruction,
|
|
405
|
-
version_change_name=version_change.__name__,
|
|
353
|
+
if any(
|
|
354
|
+
getattr(instruction.attributes, attr) is not Sentinel
|
|
355
|
+
for attr in ["path", "methods", "summary", "description", "tags", "deprecated"]
|
|
356
|
+
):
|
|
357
|
+
pass
|
|
358
|
+
if any(
|
|
359
|
+
attr is not Sentinel
|
|
360
|
+
for attr in [
|
|
361
|
+
instruction.attributes.response_model,
|
|
362
|
+
instruction.attributes.response_class,
|
|
363
|
+
instruction.attributes.responses,
|
|
364
|
+
instruction.attributes.status_code,
|
|
365
|
+
]
|
|
366
|
+
):
|
|
367
|
+
newer_routes = _get_routes(
|
|
368
|
+
routes_from_newer_version,
|
|
369
|
+
instruction.endpoint_path,
|
|
370
|
+
instruction.endpoint_methods,
|
|
371
|
+
instruction.endpoint_func_name,
|
|
372
|
+
is_deleted=False,
|
|
406
373
|
)
|
|
374
|
+
newer_openapi = get_openapi(title="", version="", routes=newer_routes)
|
|
375
|
+
changed_responses = {
|
|
376
|
+
method: route_openapi["responses"]
|
|
377
|
+
for method, route_openapi in newer_openapi["paths"][instruction.endpoint_path].items()
|
|
378
|
+
}
|
|
379
|
+
attribute_changes.append(CadwynEndpointAttributeChange(name="responses", new_value=changed_responses))
|
|
380
|
+
return CadwynEndpointHadChangelogEntry(
|
|
381
|
+
path=instruction.endpoint_path,
|
|
382
|
+
methods=cast(Any, instruction.endpoint_methods),
|
|
383
|
+
changes=attribute_changes,
|
|
384
|
+
)
|
|
407
385
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
newer_field_openapi = _get_openapi_representation_of_a_field(newer_model, instruction.name)
|
|
412
|
-
older_field_openapi = _get_openapi_representation_of_a_field(
|
|
413
|
-
older_model, old_field_name_from_this_instruction
|
|
414
|
-
)
|
|
386
|
+
elif isinstance(instruction, (FieldHadInstruction, FieldDidntHaveInstruction)):
|
|
387
|
+
old_field_name = _get_older_field_name(instruction.schema, instruction.name, generator_from_older_version)
|
|
415
388
|
|
|
416
|
-
|
|
389
|
+
if isinstance(instruction, FieldHadInstruction) and instruction.new_name is not Sentinel:
|
|
390
|
+
old_field_name_from_this_instruction = instruction.new_name
|
|
391
|
+
attribute_changes = [
|
|
417
392
|
CadwynAttributeChange(
|
|
418
|
-
name=
|
|
419
|
-
status=CadwynAttributeChangeStatus.changed
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
old_value=old_value,
|
|
423
|
-
new_value=newer_field_openapi.get(key),
|
|
393
|
+
name="name",
|
|
394
|
+
status=CadwynAttributeChangeStatus.changed,
|
|
395
|
+
old_value=old_field_name,
|
|
396
|
+
new_value=instruction.name,
|
|
424
397
|
)
|
|
425
|
-
for key, old_value in older_field_openapi.items()
|
|
426
|
-
if old_value != newer_field_openapi.get(key)
|
|
427
|
-
]
|
|
428
|
-
attribute_changes += [
|
|
429
|
-
CadwynAttributeChange(
|
|
430
|
-
name=key,
|
|
431
|
-
status=CadwynAttributeChangeStatus.added,
|
|
432
|
-
old_value=None,
|
|
433
|
-
new_value=new_value,
|
|
434
|
-
)
|
|
435
|
-
for key, new_value in newer_field_openapi.items()
|
|
436
|
-
if key not in older_field_openapi
|
|
437
398
|
]
|
|
399
|
+
else:
|
|
400
|
+
old_field_name_from_this_instruction = instruction.name
|
|
401
|
+
attribute_changes = []
|
|
402
|
+
newer_model_wrapper = generator_from_newer_version._get_wrapper_for_model(instruction.schema)
|
|
403
|
+
newer_model_wrapper_with_migrated_field = copy.deepcopy(newer_model_wrapper)
|
|
404
|
+
_change_field_in_model(
|
|
405
|
+
newer_model_wrapper_with_migrated_field,
|
|
406
|
+
generator_from_newer_version.model_bundle.schemas,
|
|
407
|
+
alter_schema_instruction=instruction,
|
|
408
|
+
version_change_name=version_change.__name__,
|
|
409
|
+
)
|
|
438
410
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
field=old_field_name,
|
|
442
|
-
attribute_changes=attribute_changes,
|
|
443
|
-
)
|
|
444
|
-
case EnumDidntHaveMembersInstruction():
|
|
445
|
-
enum = generator_from_newer_version._get_wrapper_for_model(instruction.enum)
|
|
411
|
+
older_model = newer_model_wrapper_with_migrated_field.generate_model_copy(generator_from_newer_version)
|
|
412
|
+
newer_model = newer_model_wrapper.generate_model_copy(generator_from_newer_version)
|
|
446
413
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
members=[CadwynEnumMember(name=name, value=value) for name, value in enum.members.items()],
|
|
450
|
-
)
|
|
451
|
-
case EnumHadMembersInstruction():
|
|
452
|
-
new_enum = generator_from_newer_version[instruction.enum]
|
|
453
|
-
old_enum = generator_from_older_version[instruction.enum]
|
|
454
|
-
|
|
455
|
-
return CadwynEnumMembersWereChangedChangelogEntry(
|
|
456
|
-
enum=new_enum.__name__,
|
|
457
|
-
member_changes=[
|
|
458
|
-
CadwynAttributeChange(
|
|
459
|
-
name=name,
|
|
460
|
-
old_value=old_enum.__members__.get(name),
|
|
461
|
-
new_value=new_enum.__members__.get(name),
|
|
462
|
-
status=CadwynAttributeChangeStatus.changed
|
|
463
|
-
if name in new_enum.__members__
|
|
464
|
-
else CadwynAttributeChangeStatus.removed,
|
|
465
|
-
)
|
|
466
|
-
for name in instruction.members
|
|
467
|
-
],
|
|
468
|
-
)
|
|
469
|
-
case SchemaHadInstruction():
|
|
470
|
-
model = generator_from_newer_version._get_wrapper_for_model(instruction.schema)
|
|
414
|
+
newer_field_openapi = _get_openapi_representation_of_a_field(newer_model, instruction.name)
|
|
415
|
+
older_field_openapi = _get_openapi_representation_of_a_field(older_model, old_field_name_from_this_instruction)
|
|
471
416
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
417
|
+
attribute_changes += [
|
|
418
|
+
CadwynAttributeChange(
|
|
419
|
+
name=key,
|
|
420
|
+
status=CadwynAttributeChangeStatus.changed
|
|
421
|
+
if key in newer_field_openapi
|
|
422
|
+
else CadwynAttributeChangeStatus.removed,
|
|
423
|
+
old_value=old_value,
|
|
424
|
+
new_value=newer_field_openapi.get(key),
|
|
478
425
|
)
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
426
|
+
for key, old_value in older_field_openapi.items()
|
|
427
|
+
if old_value != newer_field_openapi.get(key)
|
|
428
|
+
]
|
|
429
|
+
attribute_changes += [
|
|
430
|
+
CadwynAttributeChange(
|
|
431
|
+
name=key,
|
|
432
|
+
status=CadwynAttributeChangeStatus.added,
|
|
433
|
+
old_value=None,
|
|
434
|
+
new_value=new_value,
|
|
484
435
|
)
|
|
436
|
+
for key, new_value in newer_field_openapi.items()
|
|
437
|
+
if key not in older_field_openapi
|
|
438
|
+
]
|
|
439
|
+
|
|
440
|
+
return CadwynFieldAttributesWereChangedChangelogEntry(
|
|
441
|
+
models=_get_affected_model_names(instruction, generator_from_newer_version, schemas_from_older_version),
|
|
442
|
+
field=old_field_name,
|
|
443
|
+
attribute_changes=attribute_changes,
|
|
444
|
+
)
|
|
445
|
+
elif isinstance(instruction, EnumDidntHaveMembersInstruction):
|
|
446
|
+
enum = generator_from_newer_version._get_wrapper_for_model(instruction.enum)
|
|
485
447
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
448
|
+
return CadwynEnumMembersWereAddedChangelogEntry(
|
|
449
|
+
enum=enum.name,
|
|
450
|
+
members=[CadwynEnumMember(name=name, value=value) for name, value in enum.members.items()],
|
|
451
|
+
)
|
|
452
|
+
elif isinstance(instruction, EnumHadMembersInstruction):
|
|
453
|
+
new_enum = generator_from_newer_version[instruction.enum]
|
|
454
|
+
old_enum = generator_from_older_version[instruction.enum]
|
|
455
|
+
|
|
456
|
+
return CadwynEnumMembersWereChangedChangelogEntry(
|
|
457
|
+
enum=new_enum.__name__,
|
|
458
|
+
member_changes=[
|
|
459
|
+
CadwynAttributeChange(
|
|
460
|
+
name=name,
|
|
461
|
+
old_value=old_enum.__members__.get(name),
|
|
462
|
+
new_value=new_enum.__members__.get(name),
|
|
463
|
+
status=CadwynAttributeChangeStatus.changed
|
|
464
|
+
if name in new_enum.__members__
|
|
465
|
+
else CadwynAttributeChangeStatus.removed,
|
|
466
|
+
)
|
|
467
|
+
for name in instruction.members
|
|
468
|
+
],
|
|
469
|
+
)
|
|
470
|
+
elif isinstance(instruction, SchemaHadInstruction):
|
|
471
|
+
model = generator_from_newer_version._get_wrapper_for_model(instruction.schema)
|
|
472
|
+
|
|
473
|
+
return CadwynSchemaWasChangedChangelogEntry(
|
|
474
|
+
model=instruction.name, modified_attributes=CadwynModelModifiedAttributes(name=model.name)
|
|
475
|
+
)
|
|
476
|
+
elif isinstance(instruction, FieldExistedAsInstruction):
|
|
477
|
+
affected_model_names = _get_affected_model_names(
|
|
478
|
+
instruction, generator_from_newer_version, schemas_from_older_version
|
|
479
|
+
)
|
|
480
|
+
return CadwynSchemaFieldWasRemovedChangelogEntry(models=affected_model_names, field=instruction.name)
|
|
481
|
+
elif isinstance(instruction, FieldDidntExistInstruction):
|
|
482
|
+
model = generator_from_newer_version[instruction.schema]
|
|
483
|
+
affected_model_names = _get_affected_model_names(
|
|
484
|
+
instruction, generator_from_newer_version, schemas_from_older_version
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
return CadwynSchemaFieldWasAddedChangelogEntry(
|
|
488
|
+
models=affected_model_names,
|
|
489
|
+
field=instruction.name,
|
|
490
|
+
field_info=_get_openapi_representation_of_a_field(model, instruction.name),
|
|
491
|
+
)
|
|
492
|
+
else: # pragma: no cover
|
|
493
|
+
_logger.warning(
|
|
494
|
+
"Encountered an unknown instruction. "
|
|
495
|
+
"This should not have happened. "
|
|
496
|
+
"Please, contact the author and show him this message and your version bundle: %s.",
|
|
497
|
+
instruction,
|
|
498
|
+
)
|
|
499
|
+
return None
|
cadwyn/middleware.py
CHANGED
|
@@ -5,7 +5,7 @@ import inspect
|
|
|
5
5
|
import re
|
|
6
6
|
from collections.abc import Awaitable, Callable
|
|
7
7
|
from contextvars import ContextVar
|
|
8
|
-
from typing import Annotated, Any, Literal, Protocol
|
|
8
|
+
from typing import Annotated, Any, Literal, Protocol, Union
|
|
9
9
|
|
|
10
10
|
from fastapi import Request
|
|
11
11
|
from starlette.middleware.base import BaseHTTPMiddleware, DispatchFunction, RequestResponseEndpoint
|
|
@@ -15,7 +15,7 @@ from cadwyn.structure.common import VersionType
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class VersionManager(Protocol):
|
|
18
|
-
def get(self, request: Request) -> str
|
|
18
|
+
def get(self, request: Request) -> Union[str, None]: ...
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
VersionValidatorC = Callable[[str], VersionType]
|
|
@@ -32,7 +32,7 @@ class HeaderVersionManager:
|
|
|
32
32
|
super().__init__()
|
|
33
33
|
self.api_version_parameter_name = api_version_parameter_name
|
|
34
34
|
|
|
35
|
-
def get(self, request: Request) -> str
|
|
35
|
+
def get(self, request: Request) -> Union[str, None]:
|
|
36
36
|
return request.headers.get(self.api_version_parameter_name)
|
|
37
37
|
|
|
38
38
|
|
|
@@ -44,7 +44,7 @@ class URLVersionManager:
|
|
|
44
44
|
self.possible_version_values = possible_version_values
|
|
45
45
|
self.url_version_regex = re.compile(f"/({'|'.join(re.escape(v) for v in possible_version_values)})/")
|
|
46
46
|
|
|
47
|
-
def get(self, request: Request) -> str
|
|
47
|
+
def get(self, request: Request) -> Union[str, None]:
|
|
48
48
|
if m := self.url_version_regex.search(request.url.path):
|
|
49
49
|
return m.group(1)
|
|
50
50
|
return None
|
|
@@ -81,10 +81,10 @@ class VersionPickingMiddleware(BaseHTTPMiddleware):
|
|
|
81
81
|
app: ASGIApp,
|
|
82
82
|
*,
|
|
83
83
|
api_version_parameter_name: str,
|
|
84
|
-
api_version_default_value: str
|
|
85
|
-
api_version_var: ContextVar[VersionType
|
|
84
|
+
api_version_default_value: Union[str, None, Callable[[Request], Awaitable[str]]],
|
|
85
|
+
api_version_var: ContextVar[Union[VersionType, None]],
|
|
86
86
|
api_version_manager: VersionManager,
|
|
87
|
-
dispatch: DispatchFunction
|
|
87
|
+
dispatch: Union[DispatchFunction, None] = None,
|
|
88
88
|
) -> None:
|
|
89
89
|
super().__init__(app, dispatch)
|
|
90
90
|
|
cadwyn/route_generation.py
CHANGED
|
@@ -7,6 +7,7 @@ from typing import (
|
|
|
7
7
|
TYPE_CHECKING,
|
|
8
8
|
Any,
|
|
9
9
|
Generic,
|
|
10
|
+
Union,
|
|
10
11
|
cast,
|
|
11
12
|
)
|
|
12
13
|
|
|
@@ -21,7 +22,7 @@ from pydantic import BaseModel
|
|
|
21
22
|
from starlette.routing import BaseRoute
|
|
22
23
|
from typing_extensions import TypeVar, assert_never
|
|
23
24
|
|
|
24
|
-
from cadwyn._utils import Sentinel
|
|
25
|
+
from cadwyn._utils import DATACLASS_SLOTS, Sentinel
|
|
25
26
|
from cadwyn.exceptions import (
|
|
26
27
|
CadwynError,
|
|
27
28
|
RouteAlreadyExistsError,
|
|
@@ -54,13 +55,13 @@ _RouteT = TypeVar("_RouteT", bound=BaseRoute)
|
|
|
54
55
|
_DELETED_ROUTE_TAG = "_CADWYN_DELETED_ROUTE"
|
|
55
56
|
|
|
56
57
|
|
|
57
|
-
@dataclass(
|
|
58
|
+
@dataclass(**DATACLASS_SLOTS, frozen=True, eq=True)
|
|
58
59
|
class _EndpointInfo:
|
|
59
60
|
endpoint_path: str
|
|
60
61
|
endpoint_methods: frozenset[str]
|
|
61
62
|
|
|
62
63
|
|
|
63
|
-
@dataclass(
|
|
64
|
+
@dataclass(**DATACLASS_SLOTS, frozen=True)
|
|
64
65
|
class GeneratedRouters(Generic[_R, _WR]):
|
|
65
66
|
endpoints: dict[VersionType, _R]
|
|
66
67
|
webhooks: dict[VersionType, _WR]
|
|
@@ -70,7 +71,7 @@ def generate_versioned_routers(
|
|
|
70
71
|
router: _R,
|
|
71
72
|
versions: VersionBundle,
|
|
72
73
|
*,
|
|
73
|
-
webhooks: _WR
|
|
74
|
+
webhooks: Union[_WR, None] = None,
|
|
74
75
|
) -> GeneratedRouters[_R, _WR]:
|
|
75
76
|
if webhooks is None:
|
|
76
77
|
webhooks = cast(_WR, APIRouter())
|
|
@@ -275,7 +276,7 @@ class _EndpointTransformer(Generic[_R, _WR]):
|
|
|
275
276
|
# TODO (https://github.com/zmievsa/cadwyn/issues/28): Simplify
|
|
276
277
|
def _apply_endpoint_changes_to_router( # noqa: C901
|
|
277
278
|
self,
|
|
278
|
-
routes: list[BaseRoute]
|
|
279
|
+
routes: Union[list[BaseRoute], list[APIRoute]],
|
|
279
280
|
version: Version,
|
|
280
281
|
):
|
|
281
282
|
for version_change in version.changes:
|
|
@@ -409,8 +410,8 @@ def _validate_no_repetitions_in_routes(routes: list[fastapi.routing.APIRoute]):
|
|
|
409
410
|
def _add_data_migrations_to_route(
|
|
410
411
|
route: APIRoute,
|
|
411
412
|
head_route: Any,
|
|
412
|
-
template_body_field: type[BaseModel]
|
|
413
|
-
template_body_field_name: str
|
|
413
|
+
template_body_field: Union[type[BaseModel], None],
|
|
414
|
+
template_body_field_name: Union[str, None],
|
|
414
415
|
dependant_for_request_migrations: "Dependant",
|
|
415
416
|
versions: VersionBundle,
|
|
416
417
|
):
|
|
@@ -469,7 +470,7 @@ def _get_routes(
|
|
|
469
470
|
routes: Sequence[BaseRoute],
|
|
470
471
|
endpoint_path: str,
|
|
471
472
|
endpoint_methods: set[str],
|
|
472
|
-
endpoint_func_name: str
|
|
473
|
+
endpoint_func_name: Union[str, None] = None,
|
|
473
474
|
*,
|
|
474
475
|
is_deleted: bool = False,
|
|
475
476
|
) -> list[fastapi.routing.APIRoute]:
|
|
@@ -490,7 +491,7 @@ def _get_routes(
|
|
|
490
491
|
def _get_route_from_func(
|
|
491
492
|
routes: Sequence[BaseRoute],
|
|
492
493
|
endpoint: Endpoint,
|
|
493
|
-
) -> fastapi.routing.APIRoute
|
|
494
|
+
) -> Union[fastapi.routing.APIRoute, None]:
|
|
494
495
|
for route in routes:
|
|
495
496
|
if isinstance(route, fastapi.routing.APIRoute) and (route.endpoint == endpoint):
|
|
496
497
|
return route
|
cadwyn/routing.py
CHANGED
|
@@ -3,7 +3,7 @@ from collections.abc import Sequence
|
|
|
3
3
|
from contextvars import ContextVar
|
|
4
4
|
from functools import cached_property
|
|
5
5
|
from logging import getLogger
|
|
6
|
-
from typing import Any
|
|
6
|
+
from typing import Any, Union
|
|
7
7
|
|
|
8
8
|
from fastapi.routing import APIRouter
|
|
9
9
|
from starlette.datastructures import URL
|
|
@@ -23,7 +23,7 @@ class _RootCadwynAPIRouter(APIRouter):
|
|
|
23
23
|
self,
|
|
24
24
|
*args: Any,
|
|
25
25
|
api_version_parameter_name: str,
|
|
26
|
-
api_version_var: ContextVar[str
|
|
26
|
+
api_version_var: ContextVar[Union[str, None]],
|
|
27
27
|
api_version_format: APIVersionFormat,
|
|
28
28
|
**kwargs: Any,
|
|
29
29
|
):
|