starmallow 0.6.5__py3-none-any.whl → 0.8.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/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "0.6.5"
1
+ __version__ = "0.8.0"
2
2
 
3
3
  from .applications import StarMallow
4
4
  from .exceptions import RequestValidationError
starmallow/endpoint.py CHANGED
@@ -20,6 +20,7 @@ from typing import (
20
20
  import marshmallow as ma
21
21
  import marshmallow.fields as mf
22
22
  from marshmallow.utils import missing as missing_
23
+ from marshmallow_dataclass2 import class_schema, is_generic_alias_of_dataclass
23
24
  from starlette.background import BackgroundTasks
24
25
  from starlette.requests import HTTPConnection, Request
25
26
  from starlette.responses import Response
@@ -243,6 +244,9 @@ class EndpointMixin:
243
244
  if is_marshmallow_dataclass(model):
244
245
  model = model.Schema
245
246
 
247
+ if is_generic_alias_of_dataclass(model):
248
+ model = class_schema(model)
249
+
246
250
  if isinstance(model, NewType) and getattr(model, '_marshmallow_field', None):
247
251
  return model._marshmallow_field(**kwargs)
248
252
  elif is_marshmallow_schema(model):
@@ -2,7 +2,7 @@ from typing import Any
2
2
 
3
3
  import marshmallow as ma
4
4
  import marshmallow.fields as mf
5
- import marshmallow_dataclass.collection_field as collection_field
5
+ import marshmallow_dataclass2.collection_field as collection_field
6
6
  from apispec import APISpec
7
7
  from apispec.ext.marshmallow.common import get_fields
8
8
  from apispec.ext.marshmallow.field_converter import (
@@ -12,6 +12,7 @@ from apispec.ext.marshmallow.field_converter import (
12
12
  )
13
13
  from apispec.ext.marshmallow.openapi import OpenAPIConverter as ApiSpecOpenAPIConverter
14
14
  from marshmallow.utils import is_collection
15
+ from marshmallow_dataclass2.union_field import Union as UnionField
15
16
  from packaging.version import Version
16
17
 
17
18
  from starmallow.utils import MARSHMALLOW_ITERABLES
@@ -98,6 +99,7 @@ class OpenAPIConverter(ApiSpecOpenAPIConverter):
98
99
  self.add_attribute_function(self.field2title)
99
100
  self.add_attribute_function(self.field2uniqueItems)
100
101
  self.add_attribute_function(self.field2enum)
102
+ self.add_attribute_function(self.field2union)
101
103
 
102
104
  # Overriding to add exclusiveMinimum and exclusiveMaximum support
103
105
  def field2range(self: FieldConverterMixin, field: mf.Field, ret) -> dict:
@@ -211,6 +213,35 @@ class OpenAPIConverter(ApiSpecOpenAPIConverter):
211
213
 
212
214
  return ret
213
215
 
216
+ def field2union(self: FieldConverterMixin, field: mf.Field, **kwargs: Any) -> dict:
217
+ ret = {}
218
+
219
+ if isinstance(field, UnionField):
220
+ union_types = []
221
+ untyped = False
222
+ for _, subfield in field.union_fields:
223
+ for field_class in type(subfield).__mro__:
224
+ if field_class in self.field_mapping:
225
+ type_, fmt = self.field_mapping[field_class]
226
+
227
+ union_type = {}
228
+ if type_:
229
+ union_type['type'] = type_
230
+ if fmt:
231
+ union_type['fmt'] = fmt
232
+
233
+ if union_type:
234
+ union_types.append(union_type)
235
+ else:
236
+ # at least one untyped, so can't reliably create a schema
237
+ untyped = True
238
+ break
239
+
240
+ if union_types and not untyped:
241
+ ret['type'] = {'oneOf': union_types}
242
+
243
+ return ret
244
+
214
245
  # Overrice to add 'deprecated' support
215
246
  def _field2parameter(
216
247
  self, field: mf.Field, *, name: str, location: str,
@@ -1,7 +1,7 @@
1
1
  from enum import Enum
2
2
  from typing import ClassVar, Optional
3
3
 
4
- from marshmallow_dataclass import dataclass as ma_dataclass
4
+ from marshmallow_dataclass2 import dataclass as ma_dataclass
5
5
  from starlette.requests import Request
6
6
  from starlette.status import HTTP_403_FORBIDDEN
7
7
 
@@ -2,7 +2,7 @@ from enum import Enum
2
2
  from typing import Any, ClassVar, Dict, Optional
3
3
 
4
4
  import marshmallow as ma
5
- from marshmallow_dataclass import dataclass as ma_dataclass
5
+ from marshmallow_dataclass2 import dataclass as ma_dataclass
6
6
 
7
7
 
8
8
  # Provided by: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#security-scheme-object
@@ -2,7 +2,7 @@ import binascii
2
2
  from base64 import b64decode
3
3
  from typing import ClassVar, Optional
4
4
 
5
- from marshmallow_dataclass import dataclass as ma_dataclass
5
+ from marshmallow_dataclass2 import dataclass as ma_dataclass
6
6
  from starlette.requests import Request
7
7
  from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN
8
8
 
@@ -1,7 +1,7 @@
1
1
  from typing import Any, Dict, List, Optional, Union
2
2
 
3
3
  import marshmallow as ma
4
- from marshmallow_dataclass import dataclass as ma_dataclass
4
+ from marshmallow_dataclass2 import dataclass as ma_dataclass
5
5
  from starlette.requests import Request
6
6
  from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN
7
7
 
@@ -1,6 +1,6 @@
1
1
  from typing import Optional
2
2
 
3
- from marshmallow_dataclass import dataclass as ma_dataclass
3
+ from marshmallow_dataclass2 import dataclass as ma_dataclass
4
4
  from starlette.exceptions import HTTPException
5
5
  from starlette.requests import Request
6
6
  from starlette.status import HTTP_403_FORBIDDEN
starmallow/types.py CHANGED
@@ -2,7 +2,7 @@ import uuid
2
2
  from typing import Any, Callable, List, TypeVar, Union
3
3
 
4
4
  import marshmallow.fields as mf
5
- from marshmallow_dataclass import NewType
5
+ from marshmallow_dataclass2 import NewType
6
6
 
7
7
  import starmallow.fields as sf
8
8
  from starmallow.endpoints import APIHTTPEndpoint
starmallow/utils.py CHANGED
@@ -33,8 +33,11 @@ from typing import (
33
33
  import dpath.util
34
34
  import marshmallow as ma
35
35
  import marshmallow.fields as mf
36
- import marshmallow_dataclass.collection_field as collection_field
36
+ import marshmallow_dataclass2.collection_field as collection_field
37
+ import typing_inspect
37
38
  from marshmallow.validate import Equal, OneOf
39
+ from marshmallow_dataclass2 import class_schema, is_generic_alias_of_dataclass
40
+ from marshmallow_dataclass2.union_field import Union as UnionField
38
41
  from starlette.responses import Response
39
42
  from typing_inspect import is_final_type, is_generic_type, is_literal_type
40
43
 
@@ -103,6 +106,9 @@ def get_model_field(model: Any, **kwargs) -> mf.Field:
103
106
  if is_marshmallow_dataclass(model):
104
107
  model = model.Schema
105
108
 
109
+ if is_generic_alias_of_dataclass(model):
110
+ model = class_schema(model)
111
+
106
112
  if is_marshmallow_schema(model):
107
113
  return mf.Nested(model if isinstance(model, ma.Schema) else model())
108
114
 
@@ -136,6 +142,25 @@ def get_model_field(model: Any, **kwargs) -> mf.Field:
136
142
  if not is_generic_type(model) and lenient_issubclass(model, Enum):
137
143
  return mf.Enum(model, **kwargs)
138
144
 
145
+ # Union
146
+ if typing_inspect.is_union_type(model):
147
+ if typing_inspect.is_optional_type(model):
148
+ kwargs["allow_none"] = kwargs.get("allow_none", True)
149
+ kwargs["dump_default"] = kwargs.get("dump_default", None)
150
+ if not kwargs.get("required"):
151
+ kwargs["load_default"] = kwargs.get("load_default", None)
152
+ kwargs.setdefault("required", False)
153
+
154
+ arguments = get_args(model)
155
+ subtypes = [t for t in arguments if t is not NoneType] # type: ignore
156
+ if len(subtypes) == 1:
157
+ return get_model_field(model, **kwargs)
158
+
159
+ return UnionField(
160
+ [(subtyp, get_model_field(subtyp, required=True)) for subtyp in subtypes],
161
+ **kwargs
162
+ )
163
+
139
164
  origin = get_origin(model)
140
165
  if origin not in PY_ITERABLES:
141
166
  raise Exception(f'Unknown model type, model is {model}')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: starmallow
3
- Version: 0.6.5
3
+ Version: 0.8.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>
@@ -28,7 +28,7 @@ Classifier: Typing :: Typed
28
28
  Requires-Python: >=3.10
29
29
  Requires-Dist: apispec[marshmallow]<7,>=6
30
30
  Requires-Dist: dpath<3,>=2.1.0
31
- Requires-Dist: marshmallow-dataclass<9,>=8.5.1
31
+ Requires-Dist: marshmallow-dataclass2<9,>=8.8.1
32
32
  Requires-Dist: marshmallow<4,>=3.18.0
33
33
  Requires-Dist: python-multipart<0.0.7,>=0.0.5
34
34
  Requires-Dist: pyyaml>=5.4.1
@@ -68,7 +68,7 @@ Create a file `main.py` with:
68
68
 
69
69
  ```python
70
70
  from typing import Annotated
71
- from marshmallow_dataclass import dataclass
71
+ from marshmallow_dataclass2 import dataclass
72
72
  from starmallow import Body, Path, StarMallow
73
73
 
74
74
  app = StarMallow()
@@ -131,7 +131,7 @@ INFO: Application startup complete.
131
131
  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
132
 
133
133
  ```python
134
- from marshmallow_dataclass import dataclass
134
+ from marshmallow_dataclass2 import dataclass
135
135
  from starmallow import StarMallow
136
136
  from starmallow.decorators import route
137
137
  from starmallow.endpoints import APIHTTPEndpoint
@@ -1,4 +1,4 @@
1
- starmallow/__init__.py,sha256=BM647YLqYd8UyAgINXcC9B8aSFtZNYWdO3Gridcnn2s,322
1
+ starmallow/__init__.py,sha256=_rVQsLfQ3QzRRXvJYKcuR9Ki79d7xvWU1Mhk7E4y2KY,322
2
2
  starmallow/applications.py,sha256=mSL4YDozP8n6v22g4NX7EAMXmGhzzhtjtZd68YHcFvw,31720
3
3
  starmallow/background.py,sha256=qxT6-9SfnkcGZzvecNOpIsCOMW0TPwDtpQ81GHI28P0,995
4
4
  starmallow/concurrency.py,sha256=MVRjo4Vqss_yqhaoeVt3xb7rLaSuAq_q9uYgTwbsojE,1375
@@ -8,7 +8,7 @@ starmallow/datastructures.py,sha256=iH_KJuJ6kBCWEsnHFLdA3iyb6ZxhfdMHYrJlhiEZtDU,
8
8
  starmallow/decorators.py,sha256=MYk3WEFRSfQTN0Y3JoL3Y_Cz47gatMrVEPtNDw42XwU,4105
9
9
  starmallow/delimited_field.py,sha256=gonWgYg6G5xH2yXAyfDgkePmQ8dUaRSp2hdJ3mCfOBw,3466
10
10
  starmallow/docs.py,sha256=eA39LunVMEoPU5ge4qxm2eiJbrFTUSUu5EhG1L_LKxk,6268
11
- starmallow/endpoint.py,sha256=aNsbN0mdcVvIIcdgqICRgoGjIYJAeSVFXcpDlAji0Oc,16017
11
+ starmallow/endpoint.py,sha256=WChgjb47lYUrbyeaQiyUbuyHFCREYPiZJMNLqnz_iuA,16186
12
12
  starmallow/endpoints.py,sha256=UrwVZCxbmWI20iNtJ0oXxo4d3-y12TjsOGs_jnStTiU,939
13
13
  starmallow/exception_handlers.py,sha256=gr2qLYWEtsIEH28n7OreEiiLVz6Y7b6osRyS9esJbBk,891
14
14
  starmallow/exceptions.py,sha256=vabtPJkTmtCdC8_2OPBE8Osz0v0KxaSOX6IWf1jgNkc,872
@@ -20,22 +20,22 @@ starmallow/responses.py,sha256=k2pf_m21ykf_FECdODUz400pMucMJJf_Zm8TXFujvaU,2012
20
20
  starmallow/routing.py,sha256=VSotmrEerVzuUfn20mpmSbuRVS4XiHrPtNRvBP8KJ4M,45397
21
21
  starmallow/schema_generator.py,sha256=yi368FwF9B50ZHSNOG0rvYVirVUeMFq2kXkUDeJUz4w,17961
22
22
  starmallow/serializers.py,sha256=rBEKMNgONgz_bai12uDvAEMCI_aEFGsqMSeIoWtlrOI,12514
23
- starmallow/types.py,sha256=8GXWjvzXQhF5NMHf14fbid6uErxVd1Xk_w2I4FoUgZ4,717
24
- starmallow/utils.py,sha256=WHw3MhCy1McGLEuhIDveH0NW8zpdyxSuvjVVx7NiAkU,11453
23
+ starmallow/types.py,sha256=xp4eitWenXRZCPlsopTfG4aMs86kOrvioDTCVLg-MXU,718
24
+ starmallow/utils.py,sha256=lI6qWalsBsEaJLSiGsCC_vBN6Sw-r5aDTl9hWuHCW-Q,12459
25
25
  starmallow/websockets.py,sha256=yIz3LzTBMNclpEoG7oTMbQwxbcdKNU6M8XcqZMyBTuA,2223
26
26
  starmallow/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
27
  starmallow/ext/marshmallow/__init__.py,sha256=33jENGdfPq4-CDG0LOmN3KOGW1pXTy7a2oMwy4hrYzM,208
28
- starmallow/ext/marshmallow/openapi.py,sha256=5aGvbwLGVucsVhXExpYeyt8n5dQTzazrf-nuh6mVhmA,9017
28
+ starmallow/ext/marshmallow/openapi.py,sha256=LHFcdw8ISpPsS2drefL2h8AiKk_B_I7UffloEH3-xXI,10202
29
29
  starmallow/middleware/__init__.py,sha256=vtNm85Z9pUPjJd-9giJGg3YL1wO7Jm5ooXBm31pDOK8,53
30
30
  starmallow/middleware/asyncexitstack.py,sha256=0GPhQSxqSVmAiVIqBIN5slueWYZ8bwh9f2bBPy7AbP0,1191
31
31
  starmallow/security/__init__.py,sha256=1rQFBIGnEbE51XDZSSi9NgPjXLScFq3RoLu4vk0KVYw,191
32
- starmallow/security/api_key.py,sha256=E326Sxb_qhWbfN70vHuq4KEJcToW1Fxw0qGL0pHmQjc,3144
33
- starmallow/security/base.py,sha256=_7PR7tepr0CHJxg6uTc_cBAeY90jBS5gu8z5598yEM0,1144
34
- starmallow/security/http.py,sha256=cpGjM1kFDq3i_bOY96kMkf4cspBUxFkkET9lTK3NA-0,6607
35
- starmallow/security/oauth2.py,sha256=1nv1580PY4cwgu5gzpQCf2MfMNv2Cfv05753AUHPOhQ,10005
36
- starmallow/security/open_id_connect_url.py,sha256=IPsL2YzWc2mPwJbrUn6oFRTi7uRAG6mR62CGwmzBs1k,1399
32
+ starmallow/security/api_key.py,sha256=OWogzuwqPC3H0xf4jzx_RQjC8UoM8WDVtOhjq1lQ5ik,3145
33
+ starmallow/security/base.py,sha256=PtTsBViTUEtNUT9q_zFFydnOqIvGLfKggtgyCYeYRZk,1145
34
+ starmallow/security/http.py,sha256=O4mUrBHp6JTJNewOvcG_dgsx0IwjMonL8usswOqGdOY,6608
35
+ starmallow/security/oauth2.py,sha256=wlc5K6rURqOrMLwSYeOcd_2ZtFcvMqG3OwbiAwpft3E,10006
36
+ starmallow/security/open_id_connect_url.py,sha256=NONotu-uAWJUps06wdyVCJSRrK71Bl23z8gWWi7ymmw,1400
37
37
  starmallow/security/utils.py,sha256=bd8T0YM7UQD5ATKucr1bNtAvz_Y3__dVNAv5UebiPvc,293
38
- starmallow-0.6.5.dist-info/METADATA,sha256=cAAboTKZYPwxOZEqzjIxZx6lfcMlRLPbee8vRRAQkfo,5615
39
- starmallow-0.6.5.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
40
- starmallow-0.6.5.dist-info/licenses/LICENSE.md,sha256=QelyGgOzch8CXzy6HrYwHh7nmj0rlWkDA0YzmZ3CPaY,1084
41
- starmallow-0.6.5.dist-info/RECORD,,
38
+ starmallow-0.8.0.dist-info/METADATA,sha256=w3SFqGhVi71VdQQZBQyQsM0Kk7dPvrGRAte3GpVAH5M,5618
39
+ starmallow-0.8.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
40
+ starmallow-0.8.0.dist-info/licenses/LICENSE.md,sha256=QelyGgOzch8CXzy6HrYwHh7nmj0rlWkDA0YzmZ3CPaY,1084
41
+ starmallow-0.8.0.dist-info/RECORD,,