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/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 GenericAliasUnion
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 | type[VersionChange])
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 | ValidatorDidntExistInstruction | ValidatorExistedInstruction
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:], strict=False):
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 | ValidatorExistedInstruction)
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: FieldExistedAsInstruction
117
- | FieldDidntExistInstruction
118
- | FieldHadInstruction
119
- | FieldDidntHaveInstruction,
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, GenericAliasUnion):
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 | None
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
- CadwynEnumMembersWereAddedChangelogEntry
298
- | CadwynEnumMembersWereChangedChangelogEntry
299
- | CadwynEndpointWasAddedChangelogEntry
300
- | CadwynEndpointWasRemovedChangelogEntry
301
- | CadwynSchemaFieldWasRemovedChangelogEntry
302
- | CadwynSchemaFieldWasAddedChangelogEntry
303
- | CadwynFieldAttributesWereChangedChangelogEntry
304
- | CadwynEndpointHadChangelogEntry
305
- | CadwynSchemaWasChangedChangelogEntry
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
- match instruction:
318
- case EndpointDidntExistInstruction():
319
- return CadwynEndpointWasAddedChangelogEntry(
320
- path=instruction.endpoint_path,
321
- methods=cast(Any, instruction.endpoint_methods),
322
- )
323
- case EndpointExistedInstruction():
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
- renaming_map = {"operation_id": "operationId"}
338
+ renaming_map = {"operation_id": "operationId"}
336
339
 
337
- attribute_changes = []
340
+ attribute_changes = []
338
341
 
339
- for attr in ["path", "methods", "summary", "description", "tags", "deprecated", "operation_id"]:
340
- attr_value = getattr(instruction.attributes, attr)
341
- if attr_value is not Sentinel:
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="summary", new_value=instruction.attributes.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
- newer_openapi = get_openapi(title="", version="", routes=newer_routes)
372
- changed_responses = {
373
- method: route_openapi["responses"]
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
- case FieldHadInstruction() | FieldDidntHaveInstruction():
384
- old_field_name = _get_older_field_name(instruction.schema, instruction.name, generator_from_older_version)
385
-
386
- if isinstance(instruction, FieldHadInstruction) and instruction.new_name is not Sentinel:
387
- old_field_name_from_this_instruction = instruction.new_name
388
- attribute_changes = [
389
- CadwynAttributeChange(
390
- name="name",
391
- status=CadwynAttributeChangeStatus.changed,
392
- old_value=old_field_name,
393
- new_value=instruction.name,
394
- )
395
- ]
396
- else:
397
- old_field_name_from_this_instruction = instruction.name
398
- attribute_changes = []
399
- newer_model_wrapper = generator_from_newer_version._get_wrapper_for_model(instruction.schema)
400
- newer_model_wrapper_with_migrated_field = copy.deepcopy(newer_model_wrapper)
401
- _change_field_in_model(
402
- newer_model_wrapper_with_migrated_field,
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
- older_model = newer_model_wrapper_with_migrated_field.generate_model_copy(generator_from_newer_version)
409
- newer_model = newer_model_wrapper.generate_model_copy(generator_from_newer_version)
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
- attribute_changes += [
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=key,
419
- status=CadwynAttributeChangeStatus.changed
420
- if key in newer_field_openapi
421
- else CadwynAttributeChangeStatus.removed,
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
- return CadwynFieldAttributesWereChangedChangelogEntry(
440
- models=_get_affected_model_names(instruction, generator_from_newer_version, schemas_from_older_version),
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
- return CadwynEnumMembersWereAddedChangelogEntry(
448
- enum=enum.name,
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
- return CadwynSchemaWasChangedChangelogEntry(
473
- model=instruction.name, modified_attributes=CadwynModelModifiedAttributes(name=model.name)
474
- )
475
- case FieldExistedAsInstruction():
476
- affected_model_names = _get_affected_model_names(
477
- instruction, generator_from_newer_version, schemas_from_older_version
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
- return CadwynSchemaFieldWasRemovedChangelogEntry(models=affected_model_names, field=instruction.name)
480
- case FieldDidntExistInstruction():
481
- model = generator_from_newer_version[instruction.schema]
482
- affected_model_names = _get_affected_model_names(
483
- instruction, generator_from_newer_version, schemas_from_older_version
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
- return CadwynSchemaFieldWasAddedChangelogEntry(
487
- models=affected_model_names,
488
- field=instruction.name,
489
- field_info=_get_openapi_representation_of_a_field(model, instruction.name),
490
- )
491
- case _: # pragma: no cover
492
- _logger.warning(
493
- "Encountered an unknown instruction. "
494
- "This should not have happened. "
495
- "Please, contact the author and show him this message and your version bundle: %s.",
496
- instruction,
497
- )
498
- return None
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 | None: ...
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 | None:
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 | None:
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 | None | Callable[[Request], Awaitable[str]],
85
- api_version_var: ContextVar[VersionType | None],
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 | None = None,
87
+ dispatch: Union[DispatchFunction, None] = None,
88
88
  ) -> None:
89
89
  super().__init__(app, dispatch)
90
90
 
@@ -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(slots=True, frozen=True, eq=True)
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(slots=True, frozen=True)
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 | None = None,
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] | list[APIRoute],
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] | None,
413
- template_body_field_name: str | None,
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 | None = None,
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 | None:
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 | None],
26
+ api_version_var: ContextVar[Union[str, None]],
27
27
  api_version_format: APIVersionFormat,
28
28
  **kwargs: Any,
29
29
  ):