starmallow 0.7.0__py3-none-any.whl → 0.9.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.
starmallow/utils.py CHANGED
@@ -3,8 +3,10 @@ import datetime as dt
3
3
  import inspect
4
4
  import logging
5
5
  import re
6
+ import sys
6
7
  import uuid
7
8
  import warnings
9
+ from collections.abc import Callable, Mapping, Sequence
8
10
  from contextlib import AsyncExitStack, asynccontextmanager, contextmanager
9
11
  from dataclasses import is_dataclass
10
12
  from decimal import Decimal
@@ -13,42 +15,42 @@ from types import NoneType, UnionType
13
15
  from typing import (
14
16
  TYPE_CHECKING,
15
17
  Any,
16
- Callable,
17
- Dict,
18
18
  ForwardRef,
19
- FrozenSet,
20
- List,
21
- Mapping,
22
- Sequence,
23
- Set,
24
- Tuple,
25
- Type,
19
+ Protocol,
20
+ TypeGuard,
21
+ TypeVar,
26
22
  Union,
27
- _eval_type,
28
- _GenericAlias,
23
+ _eval_type, # type: ignore
29
24
  get_args,
30
25
  get_origin,
31
26
  )
32
27
 
33
- import dpath.util
28
+ import dpath
34
29
  import marshmallow as ma
35
30
  import marshmallow.fields as mf
36
- import marshmallow_dataclass.collection_field as collection_field
31
+ import marshmallow_dataclass2.collection_field as collection_field
37
32
  import typing_inspect
38
33
  from marshmallow.validate import Equal, OneOf
34
+ from marshmallow_dataclass2 import class_schema, is_generic_alias_of_dataclass
35
+ from marshmallow_dataclass2.union_field import Union as UnionField
39
36
  from starlette.responses import Response
40
37
  from typing_inspect import is_final_type, is_generic_type, is_literal_type
41
38
 
42
39
  from starmallow.concurrency import contextmanager_in_threadpool
43
40
  from starmallow.datastructures import DefaultPlaceholder, DefaultType
44
- from starmallow.union_field import Union as UnionField
41
+
42
+ if sys.version_info >= (3, 11):
43
+ from typing import NotRequired
44
+ else:
45
+ # Python 3.10 and below
46
+ from typing_extensions import NotRequired
45
47
 
46
48
  if TYPE_CHECKING: # pragma: nocover
47
49
  from starmallow.routing import APIRoute
48
50
 
49
51
  logger = logging.getLogger(__name__)
50
52
 
51
- status_code_ranges: Dict[str, str] = {
53
+ status_code_ranges: dict[str, str] = {
52
54
  "1XX": "Information",
53
55
  "2XX": "Success",
54
56
  "3XX": "Redirection",
@@ -57,7 +59,7 @@ status_code_ranges: Dict[str, str] = {
57
59
  "DEFAULT": "Default Response",
58
60
  }
59
61
 
60
- MARSHMALLOW_ITERABLES: Tuple[mf.Field] = (
62
+ MARSHMALLOW_ITERABLES: tuple[type[mf.Field], ...] = (
61
63
  mf.Dict,
62
64
  mf.List,
63
65
  mf.Mapping,
@@ -82,33 +84,38 @@ PY_TO_MF_MAPPING = {
82
84
 
83
85
  PY_ITERABLES = [
84
86
  list,
85
- List,
87
+ list,
86
88
  collections.abc.Sequence,
87
89
  Sequence,
88
90
  tuple,
89
- Tuple,
91
+ tuple,
92
+ set,
90
93
  set,
91
- Set,
92
94
  frozenset,
93
- FrozenSet,
95
+ frozenset,
96
+ dict,
94
97
  dict,
95
- Dict,
96
98
  collections.abc.Mapping,
97
99
  Mapping,
98
100
  ]
99
101
 
102
+ T = TypeVar("T")
103
+
100
104
 
101
- def get_model_field(model: Any, **kwargs) -> mf.Field:
105
+ def get_model_field(model: Any, **kwargs) -> mf.Field | None:
102
106
  if model == inspect._empty:
103
107
  return None
104
108
 
105
109
  if is_marshmallow_dataclass(model):
106
110
  model = model.Schema
107
111
 
112
+ if is_generic_alias_of_dataclass(model):
113
+ model = class_schema(model)
114
+
108
115
  if is_marshmallow_schema(model):
109
116
  return mf.Nested(model if isinstance(model, ma.Schema) else model())
110
117
 
111
- if is_marshmallow_field(model):
118
+ if is_marshmallow_field_or_generic(model):
112
119
  return model if isinstance(model, mf.Field) else model()
113
120
 
114
121
  # Native Python handling
@@ -128,10 +135,7 @@ def get_model_field(model: Any, **kwargs) -> mf.Field:
128
135
 
129
136
  if is_final_type(model):
130
137
  arguments = get_args(model)
131
- if arguments:
132
- subtyp = arguments[0]
133
- else:
134
- subtyp = Any
138
+ subtyp = arguments[0] if arguments else Any
135
139
  return get_model_field(subtyp, **kwargs)
136
140
 
137
141
  # enumerations
@@ -142,9 +146,9 @@ def get_model_field(model: Any, **kwargs) -> mf.Field:
142
146
  if typing_inspect.is_union_type(model):
143
147
  if typing_inspect.is_optional_type(model):
144
148
  kwargs["allow_none"] = kwargs.get("allow_none", True)
145
- kwargs["dump_default"] = kwargs.get("dump_default", None)
149
+ kwargs["dump_default"] = kwargs.get("dump_default")
146
150
  if not kwargs.get("required"):
147
- kwargs["load_default"] = kwargs.get("load_default", None)
151
+ kwargs["load_default"] = kwargs.get("load_default")
148
152
  kwargs.setdefault("required", False)
149
153
 
150
154
  arguments = get_args(model)
@@ -152,44 +156,47 @@ def get_model_field(model: Any, **kwargs) -> mf.Field:
152
156
  if len(subtypes) == 1:
153
157
  return get_model_field(model, **kwargs)
154
158
 
155
- return UnionField(
156
- [(subtyp, get_model_field(subtyp, required=True)) for subtyp in subtypes],
157
- **kwargs
158
- )
159
+ union_types = []
160
+ for subtyp in subtypes:
161
+ field = get_model_field(subtyp, required=True)
162
+ if field is not None:
163
+ union_types.append((subtyp, field))
164
+
165
+ return UnionField(union_types, **kwargs)
159
166
 
160
167
  origin = get_origin(model)
161
168
  if origin not in PY_ITERABLES:
162
169
  raise Exception(f'Unknown model type, model is {model}')
163
170
 
164
171
  arguments = get_args(model)
165
- if origin in (list, List):
172
+ if origin in (list, list):
166
173
  child_type = get_model_field(arguments[0])
167
- return mf.List(child_type, **kwargs)
174
+ return mf.List(child_type, **kwargs) # type: ignore
168
175
 
169
176
  if origin in (collections.abc.Sequence, Sequence) or (
170
- origin in (tuple, Tuple)
177
+ origin in (tuple, tuple)
171
178
  and len(arguments) == 2
172
179
  and arguments[1] is Ellipsis
173
180
  ):
174
181
  child_type = get_model_field(arguments[0])
175
- return collection_field.Sequence(child_type, **kwargs)
182
+ return collection_field.Sequence(child_type, **kwargs) # type: ignore
176
183
 
177
- if origin in (set, Set):
184
+ if origin in (set, set):
178
185
  child_type = get_model_field(arguments[0])
179
- return collection_field.Set(child_type, frozen=False, **kwargs)
186
+ return collection_field.Set(child_type, frozen=False, **kwargs) # type: ignore
180
187
 
181
- if origin in (frozenset, FrozenSet):
188
+ if origin in (frozenset, frozenset):
182
189
  child_type = get_model_field(arguments[0])
183
- return collection_field.Set(child_type, frozen=True, **kwargs)
190
+ return collection_field.Set(child_type, frozen=True, **kwargs) # type: ignore
184
191
 
185
- if origin in (tuple, Tuple):
186
- child_types = (
192
+ if origin in (tuple, tuple):
193
+ child_types = tuple(
187
194
  get_model_field(arg)
188
195
  for arg in arguments
189
196
  )
190
- return mf.Tuple(child_types, **kwargs)
197
+ return mf.Tuple(child_types, **kwargs) # type: ignore
191
198
 
192
- if origin in (dict, Dict, collections.abc.Mapping, Mapping):
199
+ if origin in (dict, dict, collections.abc.Mapping, Mapping):
193
200
  key_type = get_model_field(arguments[0])
194
201
  value_type = get_model_field(arguments[1])
195
202
  return mf.Dict(keys=key_type, values=value_type, **kwargs)
@@ -216,7 +223,7 @@ def is_optional(field):
216
223
  return get_origin(field) in (Union, UnionType) and type(None) in get_args(field)
217
224
 
218
225
 
219
- def get_path_param_names(path: str) -> Set[str]:
226
+ def get_path_param_names(path: str) -> set[str]:
220
227
  return set(re.findall("{(.*?)}", path))
221
228
 
222
229
 
@@ -229,16 +236,47 @@ def generate_unique_id(route: "APIRoute") -> str:
229
236
  return operation_id
230
237
 
231
238
 
232
- def is_marshmallow_schema(obj):
233
- return (inspect.isclass(obj) and issubclass(obj, ma.Schema)) or isinstance(obj, ma.Schema)
239
+ class MaDataclassProtocol(Protocol):
240
+ Schema: NotRequired[type[ma.Schema]]
241
+
242
+
243
+ def is_marshmallow_schema(obj: Any) -> TypeGuard[ma.Schema | type[ma.Schema]]:
244
+ try:
245
+ return (inspect.isclass(obj) and issubclass(obj, ma.Schema)) or isinstance(obj, ma.Schema)
246
+ except TypeError:
247
+ # This is a workaround for the case where obj is a generic type
248
+ # and issubclass raises a TypeError.
249
+ return False
250
+
251
+
252
+ def is_marshmallow_field(obj: Any) -> TypeGuard[mf.Field | type[mf.Field]]:
253
+ try:
254
+ return (inspect.isclass(obj) and issubclass(obj, mf.Field)) or isinstance(obj, mf.Field)
255
+ except TypeError:
256
+ # This is a workaround for the case where obj is a generic type
257
+ # and issubclass raises a TypeError.
258
+ return False
234
259
 
235
260
 
236
- def is_marshmallow_field(obj):
237
- return (inspect.isclass(obj) and issubclass(obj, mf.Field)) or isinstance(obj, mf.Field)
261
+ def is_marshmallow_field_or_generic(obj: Any) -> TypeGuard[mf.Field | type[mf.Field]]:
262
+ try:
263
+ return (
264
+ (inspect.isclass(obj) and issubclass(obj, mf.Field))
265
+ or isinstance(obj, mf.Field)
266
+ or (
267
+ isinstance(obj, typing_inspect.typingGenericAlias)
268
+ and lenient_issubclass(get_origin(obj), mf.Field)
269
+ )
270
+ )
271
+ except TypeError:
272
+ # This is a workaround for the case where obj is a generic type
273
+ # and issubclass raises a TypeError.
274
+ return False
238
275
 
276
+ def is_marshmallow_dataclass(obj: MaDataclassProtocol | Any) -> TypeGuard[MaDataclassProtocol]:
277
+ schema = getattr(obj, 'Schema', None)
239
278
 
240
- def is_marshmallow_dataclass(obj):
241
- return is_dataclass(obj) and hasattr(obj, 'Schema') and is_marshmallow_schema(obj.Schema)
279
+ return is_dataclass(obj) and schema is not None and is_marshmallow_schema(schema)
242
280
 
243
281
 
244
282
  def is_async_gen_callable(call: Callable[..., Any]) -> bool:
@@ -259,29 +297,32 @@ async def solve_generator(
259
297
  *,
260
298
  call: Callable[..., Any],
261
299
  stack: AsyncExitStack,
262
- gen_kwargs: Dict[str, Any],
300
+ gen_kwargs: dict[str, Any],
263
301
  ) -> Any:
264
302
  if is_gen_callable(call):
265
303
  cm = contextmanager_in_threadpool(contextmanager(call)(**gen_kwargs))
266
304
  elif is_async_gen_callable(call):
267
305
  cm = asynccontextmanager(call)(**gen_kwargs)
306
+ else:
307
+ raise ValueError(f"Cannot solve generator for {call}")
268
308
  return await stack.enter_async_context(cm)
269
309
 
270
310
 
271
- def lenient_issubclass(cls: Any, class_or_tuple: Union[Type[Any], Tuple[Type[Any], ...], None]) -> bool:
311
+ def lenient_issubclass(cls: Any, class_or_tuple: type[Any] | tuple[type[Any | UnionType], ...] | UnionType) -> bool:
272
312
  try:
273
- return isinstance(cls, type) and issubclass(cls, class_or_tuple) # type: ignore[arg-type]
313
+ return isinstance(cls, type) and issubclass(cls, class_or_tuple)
274
314
  except TypeError:
275
- if isinstance(cls, _GenericAlias):
276
- return False
277
- raise # pragma: no cover
315
+ return False
278
316
 
279
317
 
280
- def eq_marshmallow_fields(left: mf.Field, right: mf.Field) -> bool:
318
+ def eq_marshmallow_fields(left: mf.Field | Any, right: mf.Field | Any) -> bool:
281
319
  '''
282
320
  Marshmallow Fields don't have an __eq__ functions.
283
321
  This compares them instead.
284
322
  '''
323
+ if not (isinstance(left, mf.Field) and isinstance(right, mf.Field)):
324
+ return False
325
+
285
326
  left_dict = left.__dict__.copy()
286
327
  left_dict.pop('_creation_index', None)
287
328
  right_dict = right.__dict__.copy()
@@ -290,7 +331,7 @@ def eq_marshmallow_fields(left: mf.Field, right: mf.Field) -> bool:
290
331
  return left_dict == right_dict
291
332
 
292
333
 
293
- def __dict_creator__(current, segments, i, hints=()):
334
+ def _dict_creator(current, segments, i, hints: Sequence | None = None):
294
335
  '''
295
336
  Create missing path components. Always create a dictionary.
296
337
 
@@ -299,17 +340,17 @@ def __dict_creator__(current, segments, i, hints=()):
299
340
  segment = segments[i]
300
341
 
301
342
  # Infer the type from the hints provided.
302
- if i < len(hints):
343
+ if hints and i < len(hints):
303
344
  current[segment] = hints[i][1]()
304
345
  else:
305
346
  current[segment] = {}
306
347
 
307
348
 
308
- def dict_safe_add(d: Dict, path: str, value: Any):
309
- dpath.new(d, path, value, separator='.', creator=__dict_creator__)
349
+ def dict_safe_add(d: dict, path: str, value: Any):
350
+ dpath.new(d, path, value, separator='.', creator=_dict_creator)
310
351
 
311
352
 
312
- def deep_dict_update(main_dict: Dict[Any, Any], update_dict: Dict[Any, Any]) -> None:
353
+ def deep_dict_update(main_dict: dict[Any, Any], update_dict: dict[Any, Any]) -> None:
313
354
  for key, value in update_dict.items():
314
355
  if (
315
356
  key in main_dict
@@ -327,7 +368,7 @@ def deep_dict_update(main_dict: Dict[Any, Any], update_dict: Dict[Any, Any]) ->
327
368
  main_dict[key] = value
328
369
 
329
370
 
330
- def create_response_model(type_: Type[Any]) -> ma.Schema | mf.Field | None:
371
+ def create_response_model(type_: type[Any] | Any | ma.Schema | mf.Field) -> mf.Field | None:
331
372
  if type_ in [inspect._empty, None] or (inspect.isclass(type_) and issubclass(type_, Response)):
332
373
  return None
333
374
 
@@ -343,7 +384,7 @@ def create_response_model(type_: Type[Any]) -> ma.Schema | mf.Field | None:
343
384
  def get_value_or_default(
344
385
  first_item: DefaultPlaceholder | DefaultType,
345
386
  *extra_items: DefaultPlaceholder | DefaultType,
346
- ) -> DefaultPlaceholder | DefaultType:
387
+ ) -> Any:
347
388
  """
348
389
  Pass items or `DefaultPlaceholder`s by descending priority.
349
390
 
@@ -351,12 +392,15 @@ def get_value_or_default(
351
392
 
352
393
  Otherwise, the first item (a `DefaultPlaceholder`) will be returned.
353
394
  """
354
- items = (first_item,) + extra_items
395
+ items = (first_item, *extra_items)
355
396
  for item in items:
356
397
  if not isinstance(item, DefaultPlaceholder):
357
398
  return item
358
- return first_item
359
399
 
400
+ if isinstance(first_item, DefaultPlaceholder):
401
+ return first_item.value
402
+ else:
403
+ return first_item
360
404
 
361
405
  def get_name(endpoint: Callable) -> str:
362
406
  if inspect.isroutine(endpoint) or inspect.isclass(endpoint):
@@ -383,14 +427,14 @@ def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
383
427
  return typed_signature
384
428
 
385
429
 
386
- def get_typed_annotation(annotation: Any, globalns: Dict[str, Any]) -> Any:
430
+ def get_typed_annotation(annotation: Any, globalns: dict[str, Any]) -> Any:
387
431
  if isinstance(annotation, str):
388
432
  annotation = ForwardRef(annotation)
389
433
  annotation = evaluate_forwardref(annotation, globalns, globalns)
390
434
  return annotation
391
435
 
392
436
 
393
- def get_typed_return_annotation(call: Callable[..., Any]) -> Any:
437
+ def get_typed_return_annotation(call: Callable[..., T]) -> T | None:
394
438
  signature = inspect.signature(call)
395
439
  annotation = signature.return_annotation
396
440
 
starmallow/websockets.py CHANGED
@@ -13,7 +13,7 @@ class APIWebSocket(WebSocket):
13
13
  async def receive_json(
14
14
  self,
15
15
  mode: str = "text",
16
- model: ma.Schema = None,
16
+ model: ma.Schema | type[ma.Schema] | None = None,
17
17
  ) -> Any:
18
18
  if mode not in {"text", "binary"}:
19
19
  raise RuntimeError('The "mode" argument should be "text" or "binary".')
@@ -24,10 +24,7 @@ class APIWebSocket(WebSocket):
24
24
  message = await self.receive()
25
25
  self._raise_on_disconnect(message)
26
26
 
27
- if mode == "text":
28
- text = message["text"]
29
- else:
30
- text = message["bytes"].decode("utf-8")
27
+ text = message["text"] if mode == "text" else message["bytes"].decode("utf-8")
31
28
 
32
29
  if model:
33
30
  if isinstance(model, ma.Schema):
@@ -44,7 +41,7 @@ class APIWebSocket(WebSocket):
44
41
  self,
45
42
  data: Any,
46
43
  mode: str = "text",
47
- model: ma.Schema = None,
44
+ model: ma.Schema | type[ma.Schema] | None = None,
48
45
  ) -> None:
49
46
  if mode not in {"text", "binary"}:
50
47
  raise RuntimeError('The "mode" argument should be "text" or "binary".')
@@ -1,10 +1,11 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: starmallow
3
- Version: 0.7.0
3
+ Version: 0.9.0
4
4
  Summary: StarMallow framework
5
5
  Project-URL: Homepage, https://github.com/mvanderlee/starmallow
6
6
  Author-email: Michiel Vanderlee <jmt.vanderlee@gmail.com>
7
- License: MIT
7
+ License-Expression: MIT
8
+ License-File: LICENSE.md
8
9
  Classifier: Development Status :: 3 - Alpha
9
10
  Classifier: Environment :: Web Environment
10
11
  Classifier: Framework :: AsyncIO
@@ -17,6 +18,8 @@ Classifier: Programming Language :: Python
17
18
  Classifier: Programming Language :: Python :: 3 :: Only
18
19
  Classifier: Programming Language :: Python :: 3.10
19
20
  Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
20
23
  Classifier: Topic :: Internet
21
24
  Classifier: Topic :: Internet :: WWW/HTTP
22
25
  Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
@@ -28,29 +31,27 @@ Classifier: Typing :: Typed
28
31
  Requires-Python: >=3.10
29
32
  Requires-Dist: apispec[marshmallow]<7,>=6
30
33
  Requires-Dist: dpath<3,>=2.1.0
31
- Requires-Dist: marshmallow-dataclass<9,>=8.5.1
32
- Requires-Dist: marshmallow<4,>=3.18.0
33
- Requires-Dist: python-multipart<0.0.7,>=0.0.5
34
+ Requires-Dist: marshmallow-dataclass2<9,>=8.8.1
35
+ Requires-Dist: marshmallow>=3.18.0
36
+ Requires-Dist: python-multipart>=0.0.20
34
37
  Requires-Dist: pyyaml>=5.4.1
35
- Requires-Dist: starlette<1,>=0.35
38
+ Requires-Dist: starlette>=0.46
36
39
  Provides-Extra: all
37
40
  Requires-Dist: orjson; extra == 'all'
38
41
  Requires-Dist: ujson>=3.2.1; extra == 'all'
39
42
  Requires-Dist: uvicorn[standard]>=0.12.0; extra == 'all'
40
43
  Provides-Extra: dev
41
- Requires-Dist: isort<6.0.0,>=5.0.6; extra == 'dev'
42
- Requires-Dist: pre-commit<4,>=3.2.0; extra == 'dev'
43
- Requires-Dist: ruff==0.0.260; extra == 'dev'
44
- Requires-Dist: uvicorn[standard]<0.22.0,>=0.17.0; extra == 'dev'
44
+ Requires-Dist: pre-commit>=4; extra == 'dev'
45
+ Requires-Dist: ruff==0.11.6; extra == 'dev'
46
+ Requires-Dist: uvicorn[standard]>=0.34; extra == 'dev'
45
47
  Provides-Extra: publish
46
48
  Requires-Dist: hatch>=1.7.0; extra == 'publish'
47
49
  Provides-Extra: test
48
50
  Requires-Dist: coverage[toml]<8.0,>=6.5.0; extra == 'test'
49
51
  Requires-Dist: httpx>=0.22.0; extra == 'test'
50
- Requires-Dist: isort<6.0.0,>=5.0.6; extra == 'test'
51
52
  Requires-Dist: mypy<2,>=1.1.1; extra == 'test'
52
- Requires-Dist: pytest<8.0.0,>=7.1.3; extra == 'test'
53
- Requires-Dist: ruff==0.0.260; extra == 'test'
53
+ Requires-Dist: pytest>=8; extra == 'test'
54
+ Requires-Dist: ruff==0.11.6; extra == 'test'
54
55
  Description-Content-Type: text/markdown
55
56
 
56
57
  # StarMallow
@@ -68,7 +69,7 @@ Create a file `main.py` with:
68
69
 
69
70
  ```python
70
71
  from typing import Annotated
71
- from marshmallow_dataclass import dataclass
72
+ from marshmallow_dataclass2 import dataclass
72
73
  from starmallow import Body, Path, StarMallow
73
74
 
74
75
  app = StarMallow()
@@ -131,7 +132,7 @@ INFO: Application startup complete.
131
132
  You can also use class-based views. This can make it easier to organize your code and gives you an easy migration path if you use [flask-smorest](https://flask-smorest.readthedocs.io/)
132
133
 
133
134
  ```python
134
- from marshmallow_dataclass import dataclass
135
+ from marshmallow_dataclass2 import dataclass
135
136
  from starmallow import StarMallow
136
137
  from starmallow.decorators import route
137
138
  from starmallow.endpoints import APIHTTPEndpoint
@@ -0,0 +1,43 @@
1
+ starmallow/__init__.py,sha256=tGlfbHYbodKJlydvWAPia0GpXyB1nRcjV5Quz2iZeUw,322
2
+ starmallow/applications.py,sha256=wI3mViPAgMAGDUy0PzDLyd6GFQaFY_d1-85LBASATNY,30396
3
+ starmallow/background.py,sha256=asjTMgO25zqZiKsxcEVBGPKd_Nb7RVZDEmzR4PNy6-k,996
4
+ starmallow/concurrency.py,sha256=YNIFo8jmHZYXfFkqzL1xFiE5QFwWYnGUsYgAROv041Q,1404
5
+ starmallow/constants.py,sha256=u0h8cJKhJY0oIZqzr7wpEZG2bPLrw5FroMnn3d8KBNQ,129
6
+ starmallow/dataclasses.py,sha256=P8Eft25Q5UBhDp-3b0T-n2IOtjrQpxmRUxs3WAwlRFQ,2787
7
+ starmallow/datastructures.py,sha256=oq2Dz6zcoQx9ctMSSviZMAX_wvNTT9ytkSBtZjcg7bY,853
8
+ starmallow/decorators.py,sha256=VGzfFYualOcplRK6L3Tu2GrCl3a5yrOgADqWMokqzcQ,4036
9
+ starmallow/delimited_field.py,sha256=MKQnCp-B8q1R69G6aCgthtWVCISqkKBFpTSnwcfC_8A,4429
10
+ starmallow/docs.py,sha256=tc077aDFAzHTjpnEpqS8BVMhVolaYFmrqQ2obd1Jsbg,6229
11
+ starmallow/endpoint.py,sha256=JVJTglAkAydVKIsgKr4dR_X9gzso6OHr_d6xUhq1MNU,17794
12
+ starmallow/endpoints.py,sha256=2RuKhEX0EpEyWUdlKVfD0WXz_8zNQEduCoZ4UJkybZc,953
13
+ starmallow/exception_handlers.py,sha256=gr2qLYWEtsIEH28n7OreEiiLVz6Y7b6osRyS9esJbBk,891
14
+ starmallow/exceptions.py,sha256=arXkENa6dV626t_IDWZKqrh6laeV54PWbVIW0dafT2o,843
15
+ starmallow/fields.py,sha256=5zXP2JLyTpVnVhl23GYHY9W3Sc5Oc_kRvOWmI7WWNRM,1283
16
+ starmallow/generics.py,sha256=CE_Wf0vIqkup0mirGa-PL5511DT-avaCkC1Jx4sMe_U,1102
17
+ starmallow/params.py,sha256=EDnUNVNfRY7uv5D1rzfmIrWKJ85R2ku4l13w1QKG4E8,8720
18
+ starmallow/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
+ starmallow/request_resolver.py,sha256=o9OID0wOUCG1iUuvJVT6-ghzG61Pv08QiznFjzZZ8RE,11689
20
+ starmallow/requests.py,sha256=o_yNhH9WJ35uAGuoXa5_gihevHeaveOvkP525dbwQSU,843
21
+ starmallow/responses.py,sha256=H6Ze0R5JG9O8eaS4dffiAn3lI6Y9-hz3aDHdWKGk_nw,2023
22
+ starmallow/routing.py,sha256=dK6ayN_Ruqz3ZGNQ3pFwqnguF7Vl0oIfD7e8OnnED_Y,45364
23
+ starmallow/schema_generator.py,sha256=Y4o8v5OUgTZA2byTOcUKONimdF8JjiwD8FZLxCVOF0I,19210
24
+ starmallow/serializers.py,sha256=Z-42L6is9klknpJ3r1DcGjB7t_txPfRvp-Rvj87_n70,12622
25
+ starmallow/types.py,sha256=uPjoKwKF06n7b2yTtm0WAO1a_SOwRYJG1xzDy_GKAUg,879
26
+ starmallow/utils.py,sha256=CudZ2x7iWHr6xi7RT848aSIZuAfDUFzWVCxHuyKvk-E,14146
27
+ starmallow/websockets.py,sha256=54ctFGgA4A3VFwpUFmlu8aVmHOo4R6x3O_-z5r2BsdI,2232
28
+ starmallow/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
+ starmallow/ext/marshmallow/__init__.py,sha256=33jENGdfPq4-CDG0LOmN3KOGW1pXTy7a2oMwy4hrYzM,208
30
+ starmallow/ext/marshmallow/openapi.py,sha256=SkaGp80qvQxDSatxygN3IIKXQ7GTa01rioY_uddTxEs,10164
31
+ starmallow/middleware/__init__.py,sha256=vtNm85Z9pUPjJd-9giJGg3YL1wO7Jm5ooXBm31pDOK8,53
32
+ starmallow/middleware/asyncexitstack.py,sha256=wxugZgPg5yxuXxZjPm-_PMG7hAfOeo2lRLR07eaSWS8,1160
33
+ starmallow/security/__init__.py,sha256=1rQFBIGnEbE51XDZSSi9NgPjXLScFq3RoLu4vk0KVYw,191
34
+ starmallow/security/api_key.py,sha256=8WH52R4OjLKYSkXj35AgayFsXM7JQVp1Pf0DzpMl4ms,3108
35
+ starmallow/security/base.py,sha256=d2bMKCbPB8wh4Ce0b5xSYS9ZHeeyVLnyjpwK_NE505M,1404
36
+ starmallow/security/http.py,sha256=oOG436aohU9uNM40B0LP0Vg8Pcroj5pjzE1NRGiqOOs,6683
37
+ starmallow/security/oauth2.py,sha256=G72-wJyvrGyHUbg9hbzf44RBN8zFalZYnHKpRC2I1po,10108
38
+ starmallow/security/open_id_connect_url.py,sha256=9E-Zwnt-IR3jimOMkvIwnGHTuJMlGmjs7LCf1SGtKT8,1415
39
+ starmallow/security/utils.py,sha256=7tziAa2Cwa7xhwM_NF4DSY3BHoqVaWgJ21VuV8LvhrY,253
40
+ starmallow-0.9.0.dist-info/METADATA,sha256=ZQ_7tnMWg4IhxxCo3FhmBwa8OGc42qBRdOwBnBeK9y8,5611
41
+ starmallow-0.9.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
42
+ starmallow-0.9.0.dist-info/licenses/LICENSE.md,sha256=QelyGgOzch8CXzy6HrYwHh7nmj0rlWkDA0YzmZ3CPaY,1084
43
+ starmallow-0.9.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.26.3
2
+ Generator: hatchling 1.27.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
starmallow/union_field.py DELETED
@@ -1,86 +0,0 @@
1
- '''Copied from marshmallow_dataclass, https://github.com/lovasoa/marshmallow_dataclass/blob/master/marshmallow_dataclass/union_field.py
2
- Didn't want to add the dependency to this project1
3
- '''
4
-
5
- import copy
6
- import inspect
7
- from typing import Any, List, Optional, Tuple
8
-
9
- import typeguard
10
- from marshmallow import Schema, ValidationError, fields
11
-
12
- try:
13
- from typeguard import TypeCheckError # type: ignore[attr-defined]
14
- except ImportError:
15
- # typeguard < 3
16
- TypeCheckError = TypeError # type: ignore[misc, assignment]
17
-
18
- if "argname" not in inspect.signature(typeguard.check_type).parameters:
19
-
20
- def _check_type(value, expected_type, argname: str):
21
- return typeguard.check_type(value=value, expected_type=expected_type)
22
-
23
- else:
24
- # typeguard < 3.0.0rc2
25
- def _check_type(value, expected_type, argname: str):
26
- return typeguard.check_type( # type: ignore[call-overload]
27
- value=value, expected_type=expected_type, argname=argname,
28
- )
29
-
30
-
31
- class Union(fields.Field):
32
- """A union field, composed other `Field` classes or instances.
33
- This field serializes elements based on their type, with one of its child fields.
34
-
35
- Example: ::
36
-
37
- number_or_string = UnionField([
38
- (float, fields.Float()),
39
- (str, fields.Str())
40
- ])
41
-
42
- :param union_fields: A list of types and their associated field instance.
43
- :param kwargs: The same keyword arguments that :class:`Field` receives.
44
- """
45
-
46
- def __init__(self, union_fields: List[Tuple[type, fields.Field]], **kwargs):
47
- super().__init__(**kwargs)
48
- self.union_fields = union_fields
49
-
50
- def _bind_to_schema(self, field_name: str, schema: Schema) -> None:
51
- super()._bind_to_schema(field_name, schema)
52
- new_union_fields = []
53
- for typ, field in self.union_fields:
54
- field = copy.deepcopy(field)
55
- field._bind_to_schema(field_name, self)
56
- new_union_fields.append((typ, field))
57
-
58
- self.union_fields = new_union_fields
59
-
60
- def _serialize(self, value: Any, attr: Optional[str], obj, **kwargs) -> Any:
61
- errors = []
62
- if value is None:
63
- return value
64
- for typ, field in self.union_fields:
65
- try:
66
- _check_type(value=value, expected_type=typ, argname=attr or "anonymous")
67
- return field._serialize(value, attr, obj, **kwargs)
68
- except TypeCheckError as e:
69
- errors.append(e)
70
- raise TypeError(
71
- f"Unable to serialize value with any of the fields in the union: {errors}",
72
- )
73
-
74
- def _deserialize(self, value: Any, attr: Optional[str], data, **kwargs) -> Any:
75
- errors = []
76
- for typ, field in self.union_fields:
77
- try:
78
- result = field.deserialize(value, **kwargs)
79
- _check_type(
80
- value=result, expected_type=typ, argname=attr or "anonymous",
81
- )
82
- return result
83
- except (TypeCheckError, ValidationError) as e:
84
- errors.append(e)
85
-
86
- raise ValidationError(errors)