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.

@@ -1,13 +1,18 @@
1
1
  from collections.abc import Callable
2
2
  from dataclasses import dataclass
3
- from typing import TYPE_CHECKING, Any, Literal, cast
3
+ from typing import TYPE_CHECKING, Any, Literal, Union, cast
4
4
 
5
5
  from issubclass import issubclass as lenient_issubclass
6
6
  from pydantic import AliasChoices, AliasPath, BaseModel, Field
7
7
  from pydantic._internal._decorators import PydanticDescriptorProxy, unwrap_wrapped_function
8
8
  from pydantic.fields import FieldInfo
9
9
 
10
- from cadwyn._utils import Sentinel, fully_unwrap_decorator
10
+ from cadwyn._utils import (
11
+ DATACLASS_SLOTS,
12
+ Sentinel,
13
+ fully_unwrap_decorator,
14
+ get_name_of_function_wrapped_in_pydantic_validator,
15
+ )
11
16
  from cadwyn.exceptions import CadwynStructureError
12
17
 
13
18
  from .common import _HiddenAttributeMixin
@@ -57,34 +62,34 @@ PossibleFieldAttributes = Literal[
57
62
 
58
63
 
59
64
  # TODO: Add json_schema_extra as a breaking change in a major version
60
- @dataclass(slots=True)
65
+ @dataclass(**DATACLASS_SLOTS)
61
66
  class FieldChanges:
62
67
  default: Any
63
- alias: str | None
68
+ alias: Union[str, None]
64
69
  default_factory: Any
65
- alias_priority: int | None
66
- validation_alias: str | AliasPath | AliasChoices | None
67
- serialization_alias: str | None
68
- title: str | None
69
- field_title_generator: Callable[[str, FieldInfo], str] | None
70
+ alias_priority: Union[int, None]
71
+ validation_alias: Union[str, AliasPath, AliasChoices, None]
72
+ serialization_alias: Union[str, None]
73
+ title: Union[str, None]
74
+ field_title_generator: Union[Callable[[str, FieldInfo], str], None]
70
75
  description: str
71
- examples: list[Any] | None
76
+ examples: Union[list[Any], None]
72
77
  exclude: "AbstractSetIntStr | MappingIntStrAny | Any"
73
78
  const: bool
74
79
  deprecated: bool
75
- frozen: bool | None
76
- validate_default: bool | None
80
+ frozen: Union[bool, None]
81
+ validate_default: Union[bool, None]
77
82
  repr: bool
78
- init: bool | None
79
- init_var: bool | None
80
- kw_only: bool | None
83
+ init: Union[bool, None]
84
+ init_var: Union[bool, None]
85
+ kw_only: Union[bool, None]
81
86
  fail_fast: bool
82
87
  gt: float
83
88
  ge: float
84
89
  lt: float
85
90
  le: float
86
91
  strict: bool
87
- coerce_numbers_to_str: bool | None
92
+ coerce_numbers_to_str: Union[bool, None]
88
93
  multiple_of: float
89
94
  allow_inf_nan: bool
90
95
  max_digits: int
@@ -97,7 +102,7 @@ class FieldChanges:
97
102
  discriminator: str
98
103
 
99
104
 
100
- @dataclass(slots=True)
105
+ @dataclass(**DATACLASS_SLOTS)
101
106
  class FieldHadInstruction(_HiddenAttributeMixin):
102
107
  schema: type[BaseModel]
103
108
  name: str
@@ -106,20 +111,20 @@ class FieldHadInstruction(_HiddenAttributeMixin):
106
111
  new_name: str
107
112
 
108
113
 
109
- @dataclass(slots=True)
114
+ @dataclass(**DATACLASS_SLOTS)
110
115
  class FieldDidntHaveInstruction(_HiddenAttributeMixin):
111
116
  schema: type[BaseModel]
112
117
  name: str
113
118
  attributes: tuple[str, ...]
114
119
 
115
120
 
116
- @dataclass(slots=True)
121
+ @dataclass(**DATACLASS_SLOTS)
117
122
  class FieldDidntExistInstruction(_HiddenAttributeMixin):
118
123
  schema: type[BaseModel]
119
124
  name: str
120
125
 
121
126
 
122
- @dataclass(slots=True)
127
+ @dataclass(**DATACLASS_SLOTS)
123
128
  class FieldExistedAsInstruction(_HiddenAttributeMixin):
124
129
  schema: type[BaseModel]
125
130
  name: str
@@ -127,7 +132,7 @@ class FieldExistedAsInstruction(_HiddenAttributeMixin):
127
132
 
128
133
 
129
134
  # TODO (https://github.com/zmievsa/cadwyn/issues/112): Add an ability to add extras
130
- @dataclass(slots=True)
135
+ @dataclass(**DATACLASS_SLOTS)
131
136
  class AlterFieldInstructionFactory:
132
137
  schema: type[BaseModel]
133
138
  name: str
@@ -138,20 +143,20 @@ class AlterFieldInstructionFactory:
138
143
  name: str = Sentinel,
139
144
  type: Any = Sentinel,
140
145
  default: Any = Sentinel,
141
- alias: str | None = Sentinel,
146
+ alias: Union[str, None] = Sentinel,
142
147
  default_factory: Callable = Sentinel,
143
- alias_priority: int = Sentinel,
144
- validation_alias: str = Sentinel,
145
- serialization_alias: str = Sentinel,
146
- title: str = Sentinel,
147
- field_title_generator: Callable[[str, FieldInfo], str] = Sentinel,
148
+ alias_priority: Union[int, None] = Sentinel,
149
+ validation_alias: Union[str, AliasPath, AliasChoices, None] = Sentinel,
150
+ serialization_alias: Union[str, None] = Sentinel,
151
+ title: Union[str, None] = Sentinel,
152
+ field_title_generator: Union[Callable[[str, FieldInfo], str], None] = Sentinel,
148
153
  description: str = Sentinel,
149
- examples: list[Any] = Sentinel,
154
+ examples: Union[list[Any], None] = Sentinel,
150
155
  exclude: "AbstractSetIntStr | MappingIntStrAny | Any" = Sentinel,
151
156
  const: bool = Sentinel,
152
157
  deprecated: bool = Sentinel,
153
- frozen: bool = Sentinel,
154
- validate_default: bool = Sentinel,
158
+ frozen: Union[bool, None] = Sentinel,
159
+ validate_default: Union[bool, None] = Sentinel,
155
160
  repr: bool = Sentinel,
156
161
  init: bool = Sentinel,
157
162
  init_var: bool = Sentinel,
@@ -175,6 +180,7 @@ class AlterFieldInstructionFactory:
175
180
  discriminator: str = Sentinel,
176
181
  ) -> FieldHadInstruction:
177
182
  return FieldHadInstruction(
183
+ is_hidden_from_changelog=False,
178
184
  schema=self.schema,
179
185
  name=self.name,
180
186
  type=type,
@@ -225,22 +231,24 @@ class AlterFieldInstructionFactory:
225
231
  raise CadwynStructureError(
226
232
  f"Unknown attribute {attribute!r}. Are you sure it's a valid field attribute?"
227
233
  )
228
- return FieldDidntHaveInstruction(self.schema, self.name, attributes)
234
+ return FieldDidntHaveInstruction(
235
+ is_hidden_from_changelog=False, schema=self.schema, name=self.name, attributes=attributes
236
+ )
229
237
 
230
238
  @property
231
239
  def didnt_exist(self) -> FieldDidntExistInstruction:
232
- return FieldDidntExistInstruction(self.schema, name=self.name)
240
+ return FieldDidntExistInstruction(is_hidden_from_changelog=False, schema=self.schema, name=self.name)
233
241
 
234
242
  def existed_as(
235
243
  self,
236
244
  *,
237
245
  type: Any,
238
- info: FieldInfo | Any | None = None,
246
+ info: Union[FieldInfo, Any, None] = None,
239
247
  ) -> FieldExistedAsInstruction:
240
248
  if info is None:
241
249
  info = cast(FieldInfo, Field())
242
250
  info.annotation = type
243
- return FieldExistedAsInstruction(self.schema, name=self.name, field=info)
251
+ return FieldExistedAsInstruction(is_hidden_from_changelog=False, schema=self.schema, name=self.name, field=info)
244
252
 
245
253
 
246
254
  def _get_model_decorators(model: type[BaseModel]):
@@ -255,22 +263,22 @@ def _get_model_decorators(model: type[BaseModel]):
255
263
  ]
256
264
 
257
265
 
258
- @dataclass(slots=True)
266
+ @dataclass(**DATACLASS_SLOTS)
259
267
  class ValidatorExistedInstruction:
260
268
  schema: type[BaseModel]
261
- validator: Callable[..., Any] | PydanticDescriptorProxy
269
+ validator: Union[Callable[..., Any], PydanticDescriptorProxy]
262
270
 
263
271
 
264
- @dataclass(slots=True)
272
+ @dataclass(**DATACLASS_SLOTS)
265
273
  class ValidatorDidntExistInstruction:
266
274
  schema: type[BaseModel]
267
275
  name: str
268
276
 
269
277
 
270
- @dataclass(slots=True)
278
+ @dataclass(**DATACLASS_SLOTS)
271
279
  class AlterValidatorInstructionFactory:
272
280
  schema: type[BaseModel]
273
- func: Callable[..., Any] | PydanticDescriptorProxy
281
+ func: Union[Callable[..., Any], PydanticDescriptorProxy]
274
282
 
275
283
  @property
276
284
  def existed(self) -> ValidatorExistedInstruction:
@@ -278,26 +286,28 @@ class AlterValidatorInstructionFactory:
278
286
 
279
287
  @property
280
288
  def didnt_exist(self) -> ValidatorDidntExistInstruction:
281
- return ValidatorDidntExistInstruction(self.schema, self.func.__name__)
289
+ return ValidatorDidntExistInstruction(
290
+ self.schema, get_name_of_function_wrapped_in_pydantic_validator(self.func)
291
+ )
282
292
 
283
293
 
284
- AlterSchemaSubInstruction = (
285
- FieldHadInstruction
286
- | FieldDidntHaveInstruction
287
- | FieldDidntExistInstruction
288
- | FieldExistedAsInstruction
289
- | ValidatorExistedInstruction
290
- | ValidatorDidntExistInstruction
291
- )
294
+ AlterSchemaSubInstruction = Union[
295
+ FieldHadInstruction,
296
+ FieldDidntHaveInstruction,
297
+ FieldDidntExistInstruction,
298
+ FieldExistedAsInstruction,
299
+ ValidatorExistedInstruction,
300
+ ValidatorDidntExistInstruction,
301
+ ]
292
302
 
293
303
 
294
- @dataclass(slots=True)
304
+ @dataclass(**DATACLASS_SLOTS)
295
305
  class SchemaHadInstruction(_HiddenAttributeMixin):
296
306
  schema: type[BaseModel]
297
307
  name: str
298
308
 
299
309
 
300
- @dataclass(slots=True)
310
+ @dataclass(**DATACLASS_SLOTS)
301
311
  class AlterSchemaInstructionFactory:
302
312
  schema: type[BaseModel]
303
313
 
@@ -305,9 +315,9 @@ class AlterSchemaInstructionFactory:
305
315
  return AlterFieldInstructionFactory(self.schema, name)
306
316
 
307
317
  def validator(
308
- self, func: "Callable[..., Any] | classmethod[Any, Any, Any] | PydanticDescriptorProxy", /
318
+ self, func: "Union[Callable[..., Any], classmethod[Any, Any, Any], PydanticDescriptorProxy]", /
309
319
  ) -> AlterValidatorInstructionFactory:
310
- func = cast(Callable | PydanticDescriptorProxy, unwrap_wrapped_function(func))
320
+ func = cast(Union[Callable[..., Any], PydanticDescriptorProxy], unwrap_wrapped_function(func))
311
321
 
312
322
  if not isinstance(func, PydanticDescriptorProxy):
313
323
  if hasattr(func, "__self__"):
@@ -321,7 +331,7 @@ class AlterSchemaInstructionFactory:
321
331
  return AlterValidatorInstructionFactory(self.schema, func)
322
332
 
323
333
  def had(self, *, name: str) -> SchemaHadInstruction:
324
- return SchemaHadInstruction(self.schema, name)
334
+ return SchemaHadInstruction(is_hidden_from_changelog=False, schema=self.schema, name=name)
325
335
 
326
336
 
327
337
  def schema(model: type[BaseModel], /) -> AlterSchemaInstructionFactory:
@@ -8,7 +8,7 @@ from contextlib import AsyncExitStack
8
8
  from contextvars import ContextVar
9
9
  from datetime import date
10
10
  from enum import Enum
11
- from typing import Any, ClassVar, ParamSpec, TypeAlias, TypeVar
11
+ from typing import TYPE_CHECKING, ClassVar, Union
12
12
 
13
13
  from fastapi import BackgroundTasks, HTTPException, params
14
14
  from fastapi import Request as FastapiRequest
@@ -23,7 +23,7 @@ from fastapi.routing import APIRoute, _prepare_response_content
23
23
  from pydantic import BaseModel
24
24
  from pydantic_core import PydanticUndefined
25
25
  from starlette._utils import is_async_callable
26
- from typing_extensions import assert_never, deprecated
26
+ from typing_extensions import Any, ParamSpec, TypeAlias, TypeVar, assert_never, deprecated, get_args
27
27
 
28
28
  from cadwyn._utils import classproperty
29
29
  from cadwyn.exceptions import (
@@ -51,22 +51,28 @@ _CADWYN_REQUEST_PARAM_NAME = "cadwyn_request_param"
51
51
  _CADWYN_RESPONSE_PARAM_NAME = "cadwyn_response_param"
52
52
  _P = ParamSpec("_P")
53
53
  _R = TypeVar("_R")
54
- PossibleInstructions: TypeAlias = (
55
- AlterSchemaSubInstruction
56
- | AlterEndpointSubInstruction
57
- | AlterEnumSubInstruction
58
- | SchemaHadInstruction
59
- | staticmethod
60
- )
61
- APIVersionVarType: TypeAlias = ContextVar[VersionType | None] | ContextVar[VersionType]
54
+ PossibleInstructions: TypeAlias = Union[
55
+ AlterSchemaSubInstruction, AlterEndpointSubInstruction, AlterEnumSubInstruction, SchemaHadInstruction, staticmethod
56
+ ]
57
+ APIVersionVarType: TypeAlias = Union[ContextVar[Union[VersionType, None]], ContextVar[VersionType]]
62
58
  IdentifierPythonPath = str
63
59
 
64
60
 
61
+ if TYPE_CHECKING:
62
+ _AlterSchemaSubInstructionArgs = AlterSchemaSubInstruction
63
+ _AlterEnumSubInstructionArgs = AlterEnumSubInstruction
64
+ _AlterEndpointSubInstructionArgs = AlterEndpointSubInstruction
65
+ else:
66
+ _AlterSchemaSubInstructionArgs = get_args(AlterSchemaSubInstruction)
67
+ _AlterEnumSubInstructionArgs = get_args(AlterEnumSubInstruction)
68
+ _AlterEndpointSubInstructionArgs = get_args(AlterEndpointSubInstruction)
69
+
70
+
65
71
  class VersionChange:
66
72
  description: ClassVar[str] = Sentinel
67
73
  is_hidden_from_changelog: bool = False
68
74
  instructions_to_migrate_to_previous_version: ClassVar[Sequence[PossibleInstructions]] = Sentinel
69
- alter_schema_instructions: ClassVar[list[AlterSchemaSubInstruction | SchemaHadInstruction]] = Sentinel
75
+ alter_schema_instructions: ClassVar[list[Union[AlterSchemaSubInstruction, SchemaHadInstruction]]] = Sentinel
70
76
  alter_enum_instructions: ClassVar[list[AlterEnumSubInstruction]] = Sentinel
71
77
  alter_endpoint_instructions: ClassVar[list[AlterEndpointSubInstruction]] = Sentinel
72
78
  alter_request_by_schema_instructions: ClassVar[dict[type[BaseModel], list[_AlterRequestBySchemaInstruction]]] = (
@@ -75,7 +81,7 @@ class VersionChange:
75
81
  alter_request_by_path_instructions: ClassVar[dict[str, list[_AlterRequestByPathInstruction]]] = Sentinel
76
82
  alter_response_by_schema_instructions: ClassVar[dict[type, list[_AlterResponseBySchemaInstruction]]] = Sentinel
77
83
  alter_response_by_path_instructions: ClassVar[dict[str, list[_AlterResponseByPathInstruction]]] = Sentinel
78
- _bound_version_bundle: "VersionBundle | None"
84
+ _bound_version_bundle: "Union[VersionBundle, None]"
79
85
 
80
86
  def __init_subclass__(cls, _abstract: bool = False) -> None:
81
87
  super().__init_subclass__()
@@ -112,11 +118,13 @@ class VersionChange:
112
118
  cls.alter_response_by_schema_instructions = defaultdict(list)
113
119
  cls.alter_response_by_path_instructions = defaultdict(list)
114
120
  for alter_instruction in cls.instructions_to_migrate_to_previous_version:
115
- if isinstance(alter_instruction, SchemaHadInstruction | AlterSchemaSubInstruction):
121
+ if isinstance(alter_instruction, SchemaHadInstruction) or isinstance( # noqa: SIM101
122
+ alter_instruction, _AlterSchemaSubInstructionArgs
123
+ ):
116
124
  cls.alter_schema_instructions.append(alter_instruction)
117
- elif isinstance(alter_instruction, AlterEnumSubInstruction):
125
+ elif isinstance(alter_instruction, _AlterEnumSubInstructionArgs):
118
126
  cls.alter_enum_instructions.append(alter_instruction)
119
- elif isinstance(alter_instruction, AlterEndpointSubInstruction):
127
+ elif isinstance(alter_instruction, _AlterEndpointSubInstructionArgs):
120
128
  cls.alter_endpoint_instructions.append(alter_instruction)
121
129
  elif isinstance(alter_instruction, staticmethod): # pragma: no cover
122
130
  raise NotImplementedError(f'"{alter_instruction}" is an unacceptable version change instruction')
@@ -139,17 +147,19 @@ class VersionChange:
139
147
  f"Attribute 'instructions_to_migrate_to_previous_version' must be a sequence in '{cls.__name__}'.",
140
148
  )
141
149
  for instruction in cls.instructions_to_migrate_to_previous_version:
142
- if not isinstance(instruction, PossibleInstructions):
150
+ if not isinstance(instruction, get_args(PossibleInstructions)):
143
151
  raise CadwynStructureError(
144
152
  f"Instruction '{instruction}' is not allowed. Please, use the correct instruction types",
145
153
  )
146
154
  for attr_name, attr_value in cls.__dict__.items():
147
155
  if not isinstance(
148
156
  attr_value,
149
- _AlterRequestBySchemaInstruction
150
- | _AlterRequestByPathInstruction
151
- | _AlterResponseBySchemaInstruction
152
- | _AlterResponseByPathInstruction,
157
+ (
158
+ _AlterRequestBySchemaInstruction,
159
+ _AlterRequestByPathInstruction,
160
+ _AlterResponseBySchemaInstruction,
161
+ _AlterResponseByPathInstruction,
162
+ ),
153
163
  ) and attr_name not in {
154
164
  "description",
155
165
  "side_effects",
@@ -201,7 +211,7 @@ class VersionChangeWithSideEffects(VersionChange, _abstract=True):
201
211
 
202
212
 
203
213
  class Version:
204
- def __init__(self, value: str | date, *changes: type[VersionChange]) -> None:
214
+ def __init__(self, value: Union[str, date], *changes: type[VersionChange]) -> None:
205
215
  super().__init__()
206
216
 
207
217
  if isinstance(value, date):
@@ -249,10 +259,10 @@ def get_cls_pythonpath(cls: type) -> IdentifierPythonPath:
249
259
  class VersionBundle:
250
260
  def __init__(
251
261
  self,
252
- latest_version_or_head_version: Version | HeadVersion,
262
+ latest_version_or_head_version: Union[Version, HeadVersion],
253
263
  /,
254
264
  *other_versions: Version,
255
- api_version_var: ContextVar[VersionType | None] | None = None,
265
+ api_version_var: Union[ContextVar[Union[VersionType, None]], None] = None,
256
266
  ) -> None:
257
267
  super().__init__()
258
268
 
@@ -342,7 +352,7 @@ class VersionBundle:
342
352
 
343
353
  async def _migrate_request(
344
354
  self,
345
- body_type: type[BaseModel] | None,
355
+ body_type: Union[type[BaseModel], None],
346
356
  head_dependant: Dependant,
347
357
  path: str,
348
358
  request: FastapiRequest,
@@ -353,7 +363,7 @@ class VersionBundle:
353
363
  *,
354
364
  exit_stack: AsyncExitStack,
355
365
  embed_body_fields: bool,
356
- background_tasks: BackgroundTasks | None,
366
+ background_tasks: Union[BackgroundTasks, None],
357
367
  ) -> dict[str, Any]:
358
368
  method = request.method
359
369
 
@@ -417,22 +427,22 @@ class VersionBundle:
417
427
  # TODO (https://github.com/zmievsa/cadwyn/issues/113): Refactor this function and all functions it calls.
418
428
  def _versioned(
419
429
  self,
420
- head_body_field: type[BaseModel] | None,
421
- module_body_field_name: str | None,
430
+ head_body_field: Union[type[BaseModel], None],
431
+ module_body_field_name: Union[str, None],
422
432
  route: APIRoute,
423
433
  head_route: APIRoute,
424
434
  dependant_for_request_migrations: Dependant,
425
435
  *,
426
436
  request_param_name: str,
427
- background_tasks_param_name: str | None,
437
+ background_tasks_param_name: Union[str, None],
428
438
  response_param_name: str,
429
- ) -> Callable[[Endpoint[_P, _R]], Endpoint[_P, _R]]:
430
- def wrapper(endpoint: Endpoint[_P, _R]) -> Endpoint[_P, _R]:
439
+ ) -> "Callable[[Endpoint[_P, _R]], Endpoint[_P, _R]]":
440
+ def wrapper(endpoint: "Endpoint[_P, _R]") -> "Endpoint[_P, _R]":
431
441
  @functools.wraps(endpoint)
432
442
  async def decorator(*args: Any, **kwargs: Any) -> _R:
433
443
  request_param: FastapiRequest = kwargs[request_param_name]
434
444
  response_param: FastapiResponse = kwargs[response_param_name]
435
- background_tasks: BackgroundTasks | None = kwargs.get(
445
+ background_tasks: Union[BackgroundTasks, None] = kwargs.get(
436
446
  background_tasks_param_name, # pyright: ignore[reportArgumentType]
437
447
  )
438
448
  method = request_param.method
@@ -498,9 +508,9 @@ class VersionBundle:
498
508
  kwargs.pop(response_param_name)
499
509
  try:
500
510
  if is_async_callable(func_to_get_response_from):
501
- response_or_response_body: FastapiResponse | object = await func_to_get_response_from(**kwargs)
511
+ response_or_response_body: Union[FastapiResponse, object] = await func_to_get_response_from(**kwargs)
502
512
  else:
503
- response_or_response_body: FastapiResponse | object = await run_in_threadpool(
513
+ response_or_response_body: Union[FastapiResponse, object] = await run_in_threadpool(
504
514
  func_to_get_response_from,
505
515
  **kwargs,
506
516
  )
@@ -520,11 +530,11 @@ class VersionBundle:
520
530
  # TODO (https://github.com/zmievsa/cadwyn/issues/126): Add support for migrating `FileResponse`
521
531
  # Starlette breaks Liskov Substitution principle and
522
532
  # doesn't define `body` for `StreamingResponse` and `FileResponse`
523
- if isinstance(response_or_response_body, StreamingResponse | FileResponse):
533
+ if isinstance(response_or_response_body, (StreamingResponse, FileResponse)):
524
534
  body = None
525
535
  elif response_or_response_body.body:
526
536
  if (isinstance(response_or_response_body, JSONResponse) or raised_exception is not None) and isinstance(
527
- response_or_response_body.body, str | bytes
537
+ response_or_response_body.body, (str, bytes)
528
538
  ):
529
539
  body = json.loads(response_or_response_body.body)
530
540
  elif isinstance(response_or_response_body.body, bytes):
@@ -609,8 +619,8 @@ class VersionBundle:
609
619
 
610
620
  async def _convert_endpoint_kwargs_to_version(
611
621
  self,
612
- head_body_field: type[BaseModel] | None,
613
- body_field_alias: str | None,
622
+ head_body_field: Union[type[BaseModel], None],
623
+ body_field_alias: Union[str, None],
614
624
  head_dependant: Dependant,
615
625
  request_param_name: str,
616
626
  kwargs: dict[str, Any],
@@ -620,7 +630,7 @@ class VersionBundle:
620
630
  *,
621
631
  exit_stack: AsyncExitStack,
622
632
  embed_body_fields: bool,
623
- background_tasks: BackgroundTasks | None,
633
+ background_tasks: Union[BackgroundTasks, None],
624
634
  ) -> dict[str, Any]:
625
635
  request: FastapiRequest = kwargs[request_param_name]
626
636
  if request_param_name == _CADWYN_REQUEST_PARAM_NAME:
@@ -637,7 +647,7 @@ class VersionBundle:
637
647
  and body_field_alias is not None
638
648
  and body_field_alias in kwargs
639
649
  ):
640
- raw_body: BaseModel | None = kwargs.get(body_field_alias)
650
+ raw_body: Union[BaseModel, None] = kwargs.get(body_field_alias)
641
651
  if raw_body is None: # pragma: no cover # This is likely an impossible case but we would like to be safe
642
652
  body = None
643
653
  # It means we have a dict or a list instead of a full model.
@@ -673,7 +683,7 @@ class VersionBundle:
673
683
 
674
684
  # We use this instead of `.body()` to automatically guess body type and load the correct body, even if it's a form
675
685
  async def _get_body(
676
- request: FastapiRequest, body_field: ModelField | None, exit_stack: AsyncExitStack
686
+ request: FastapiRequest, body_field: Union[ModelField, None], exit_stack: AsyncExitStack
677
687
  ): # pragma: no cover # This is from fastapi
678
688
  is_body_form = body_field and isinstance(body_field.field_info, params.Form)
679
689
  try:
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cadwyn
3
- Version: 5.0.0
3
+ Version: 5.1.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
7
7
  Author-email: Stanislav Zmiev <zmievsa@gmail.com>
8
8
  License-Expression: MIT
9
9
  License-File: LICENSE
10
- Keywords: api,api-versioning,code-generation,fastapi,hints,json-schema,pydantic,python,python310,python311,python312,python313,stripe,versioning
10
+ Keywords: api,api-versioning,code-generation,fastapi,hints,json-schema,pydantic,python,python310,python311,python312,python313,python39,stripe,versioning
11
11
  Classifier: Development Status :: 5 - Production/Stable
12
12
  Classifier: Environment :: Web Environment
13
13
  Classifier: Framework :: AsyncIO
@@ -20,6 +20,7 @@ Classifier: License :: OSI Approved :: MIT License
20
20
  Classifier: Operating System :: OS Independent
21
21
  Classifier: Programming Language :: Python
22
22
  Classifier: Programming Language :: Python :: 3
23
+ Classifier: Programming Language :: Python :: 3.9
23
24
  Classifier: Programming Language :: Python :: 3.10
24
25
  Classifier: Programming Language :: Python :: 3.11
25
26
  Classifier: Programming Language :: Python :: 3.12
@@ -32,7 +33,7 @@ Classifier: Topic :: Software Development :: Libraries
32
33
  Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
33
34
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
34
35
  Classifier: Typing :: Typed
35
- Requires-Python: >=3.10
36
+ Requires-Python: >=3.9
36
37
  Requires-Dist: backports-strenum<2,>=1.3.1; python_version < '3.11'
37
38
  Requires-Dist: fastapi>=0.112.3
38
39
  Requires-Dist: issubclass>=0.1.2
@@ -0,0 +1,28 @@
1
+ cadwyn/__init__.py,sha256=gWsp-oNP5LhBqWF_rsrv6sEYP9KchTu8xhxAR8uaPm0,1035
2
+ cadwyn/__main__.py,sha256=fGoKJPNVueqqXW4rqwmRUBBMoDGeLEyATdT-rel5Pvs,2449
3
+ cadwyn/_asts.py,sha256=QvqZmDdwH8U-Ocpj9vYR6MRrIANF8ugk9oZgAo76qLs,5223
4
+ cadwyn/_importer.py,sha256=QV6HqODCG9K2oL4Vc15fAqL2-plMvUWw_cgaj4Ln4C8,1075
5
+ cadwyn/_render.py,sha256=7jb32Cnbf6xJicYBGRuhGz4U5LUzo8InZbf-gENgnEw,5537
6
+ cadwyn/_utils.py,sha256=q_mTtMKTNTDzqCza67XST-jaPSfuTgnFLmOe0dlGeYY,2295
7
+ cadwyn/applications.py,sha256=YyESoMwQMq9jxqai6lNrsL0BAKxjTKW5lm3Se_U81j4,20391
8
+ cadwyn/changelogs.py,sha256=aBTlsZ8PQpw9t4sSyezNTYDs6CMPtzIGulgAHA1ELPs,19622
9
+ cadwyn/exceptions.py,sha256=gLCikeUPeLJwVjM8_DoSTIFHwmNI7n7vw1atpgHvbMU,1803
10
+ cadwyn/middleware.py,sha256=jtcysj66Fck_3EteK0zLJCOTMms3g6avi3U8lV-roQI,4316
11
+ cadwyn/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ cadwyn/route_generation.py,sha256=e7mnVqtvTx1Oh_khmrM0dAowhVUOO1K4vaVVEGgXgrY,25230
13
+ cadwyn/routing.py,sha256=EkV38cDQFAtR1M_fGWeq81lYaSPuDK4Pr8fjTTJVZvY,6912
14
+ cadwyn/schema_generation.py,sha256=J9HWrxwmsozR6RwhLDhL5cdy_hnwJ0HmusIKhQ7yt8I,44912
15
+ cadwyn/static/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ cadwyn/static/docs.html,sha256=WNm5ANJVy51TcIUFOaqKf1Z8eF86CC85TTHPxACtkzw,3455
17
+ cadwyn/structure/__init__.py,sha256=Wgvjdq3vfl9Yhe-BkcFGAMi_Co11YOfTmJQqgF5Gzx4,655
18
+ cadwyn/structure/common.py,sha256=YuyfYMxkJcj2c5SFh9teBoEC2xLO5_2QjPzYjwdZcTs,478
19
+ cadwyn/structure/data.py,sha256=NWURVnP_84VI2ugp9ppo0Ofyve3pVYjymF9K82Jh-SA,7791
20
+ cadwyn/structure/endpoints.py,sha256=zUgzglNhBPnmWdJ03A8pFT4zPs_lj8nQ7c7Uo2d-ejU,6246
21
+ cadwyn/structure/enums.py,sha256=4FCc9aniLE3VuWAVIacrNP_FWxTIUm9JkeeHA_zZdwQ,1254
22
+ cadwyn/structure/schemas.py,sha256=80AUbko1cksXh7qPBmnknDFLK52xr4kw9HfHeXqcw1I,10813
23
+ cadwyn/structure/versions.py,sha256=9ihqt27upAOWXCGYou2rCJ7ZqgxjJshKOFpx2LuLpBE,33905
24
+ cadwyn-5.1.0.dist-info/METADATA,sha256=VJxbSg9GgUty0MNmjf9gkvLnMrx5R5UFq-FseQ3yWOk,4562
25
+ cadwyn-5.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
26
+ cadwyn-5.1.0.dist-info/entry_points.txt,sha256=mGX8wl-Xfhpr5M93SUmkykaqinUaYAvW9rtDSX54gx0,47
27
+ cadwyn-5.1.0.dist-info/licenses/LICENSE,sha256=KeCWewiDQYpmSnzF-p_0YpoWiyDcUPaCuG8OWQs4ig4,1072
28
+ cadwyn-5.1.0.dist-info/RECORD,,
@@ -1,28 +0,0 @@
1
- cadwyn/__init__.py,sha256=gWsp-oNP5LhBqWF_rsrv6sEYP9KchTu8xhxAR8uaPm0,1035
2
- cadwyn/__main__.py,sha256=fGoKJPNVueqqXW4rqwmRUBBMoDGeLEyATdT-rel5Pvs,2449
3
- cadwyn/_asts.py,sha256=kd6Ngwr8c-uTNx-R-pP48Scf0OdjrTyEeSYe5DLoGqo,5095
4
- cadwyn/_importer.py,sha256=QV6HqODCG9K2oL4Vc15fAqL2-plMvUWw_cgaj4Ln4C8,1075
5
- cadwyn/_render.py,sha256=VArS68879hXRntMKCQJ7LUpCv8aToavHZV1JuBFmtfY,5513
6
- cadwyn/_utils.py,sha256=rlD1SkswtZ1bWgKj6PLYbVaHYkD-NzY4iUcHdPd2Y68,1475
7
- cadwyn/applications.py,sha256=bBO5kK-lDbjk3Rrbx8TdSY5EHDq4BkwlOzQruZ4Vj00,20128
8
- cadwyn/changelogs.py,sha256=-ft0Rpx_xDoVWNuAH8NYmUpE9UnSZfreyL-Qa2fP78Y,20004
9
- cadwyn/exceptions.py,sha256=gLCikeUPeLJwVjM8_DoSTIFHwmNI7n7vw1atpgHvbMU,1803
10
- cadwyn/middleware.py,sha256=lP8bFHHhXEMjOyDdljxiC2fH5K826qPe07qERq1nnG8,4274
11
- cadwyn/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- cadwyn/route_generation.py,sha256=EclCM9CI05ZRQW1qa052cBnF3Zb8Qj4lbIvHQSy0g_8,25152
13
- cadwyn/routing.py,sha256=cG3ieQ9NlF-PvslT9fy5AHBNco4lMgDoqQu42klnCLk,6899
14
- cadwyn/schema_generation.py,sha256=pM5bM0Tfi10ahSWRaD4h3zjzH-ERRkySh1iVURwf010,42089
15
- cadwyn/static/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- cadwyn/static/docs.html,sha256=WNm5ANJVy51TcIUFOaqKf1Z8eF86CC85TTHPxACtkzw,3455
17
- cadwyn/structure/__init__.py,sha256=Wgvjdq3vfl9Yhe-BkcFGAMi_Co11YOfTmJQqgF5Gzx4,655
18
- cadwyn/structure/common.py,sha256=3UEbxzAjDfHT2GPMQ3tNA3pEqJd9ZnDclqsj2ZDpzv4,399
19
- cadwyn/structure/data.py,sha256=uViRW4uOOonXZj90hOlPNk02AIwp0fvDNoF8M5_CEes,7707
20
- cadwyn/structure/endpoints.py,sha256=8lrc4xanCt7gat106yYRIQC0TNxzFkLF-urIml_d_X0,5934
21
- cadwyn/structure/enums.py,sha256=bZL-iUOUFi9ZYlMZJw-tAix2yrgCp3gH3N2gwO44LUU,1043
22
- cadwyn/structure/schemas.py,sha256=v_wDTn84SgHVDFDlTgoalUzBXpDbT5Hl73Skp0UyGAM,10081
23
- cadwyn/structure/versions.py,sha256=p0Ui00RX8DW9M-wndiaXBK2t85Bd4ypGigPCfVZ9PiE,33167
24
- cadwyn-5.0.0.dist-info/METADATA,sha256=SNnhEGuX_BVE8C5IM-PDBTyrfBvRb2aR2S_DrwxCoJ4,4504
25
- cadwyn-5.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
26
- cadwyn-5.0.0.dist-info/entry_points.txt,sha256=mGX8wl-Xfhpr5M93SUmkykaqinUaYAvW9rtDSX54gx0,47
27
- cadwyn-5.0.0.dist-info/licenses/LICENSE,sha256=KeCWewiDQYpmSnzF-p_0YpoWiyDcUPaCuG8OWQs4ig4,1072
28
- cadwyn-5.0.0.dist-info/RECORD,,
File without changes