starmallow 0.6.5__tar.gz → 0.8.0__tar.gz
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-0.6.5 → starmallow-0.8.0}/PKG-INFO +4 -4
- {starmallow-0.6.5 → starmallow-0.8.0}/README.md +2 -2
- {starmallow-0.6.5 → starmallow-0.8.0}/examples/cache_server.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/examples/flask_server.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/examples/goals.ipynb +2 -2
- {starmallow-0.6.5 → starmallow-0.8.0}/examples/sample_server.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/pyproject.toml +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/__init__.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/endpoint.py +4 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/ext/marshmallow/openapi.py +32 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/security/api_key.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/security/base.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/security/http.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/security/oauth2.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/security/open_id_connect_url.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/types.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/utils.py +26 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/basic_api.py +6 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/api_key/test_api_key_cookie.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/api_key/test_api_key_cookie_description.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/api_key/test_api_key_cookie_optional.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/api_key/test_api_key_header.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/api_key/test_api_key_header_description.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/api_key/test_api_key_header_optional.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/api_key/test_api_key_query.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/api_key/test_api_key_query_description.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/api_key/test_api_key_query_optional.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/oauth2/test_oauth2.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/oauth2/test_oauth2_optional.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/oauth2/test_oauth2_optional_description.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/openid_connect/test_openid_connect.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/openid_connect/test_openid_connect_description.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/openid_connect/test_openid_connect_optional.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/test_additional_properties.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/test_additional_responses_custom_model_in_callback.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/test_additional_responses_custom_validationerror.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/test_additional_responses_response_class.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/test_additional_responses_router.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/test_annotated.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/test_basic_api.py +49 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/test_dataclass_fields.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/test_delimited_params.py +1 -0
- starmallow-0.8.0/tests/test_generics.py +149 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/test_input.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/test_requests_orjson.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/test_requests_ujson.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/test_responses.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/test_responses_orjson.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/test_responses_ujson.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/test_ws_router.py +1 -1
- {starmallow-0.6.5 → starmallow-0.8.0}/.editorconfig +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/.gitignore +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/.pre-commit-config.yaml +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/Dockerfile +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/LICENSE.md +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/docker-compose.yml +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/docs/design_ideas.md +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/examples/__init__.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/examples/gunicorn.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/examples/recommended_server.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/applications.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/background.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/concurrency.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/constants.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/dataclasses.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/datastructures.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/decorators.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/delimited_field.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/docs.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/endpoints.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/exception_handlers.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/exceptions.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/ext/__init__.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/ext/marshmallow/__init__.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/fields.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/middleware/__init__.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/middleware/asyncexitstack.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/params.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/request_resolver.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/requests.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/responses.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/routing.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/schema_generator.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/security/__init__.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/security/utils.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/serializers.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/starmallow/websockets.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/__init__.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/__init__.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/api_key/__init__.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/http/__init__.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/http/test_http_base.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/http/test_http_base_description.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/http/test_http_base_optional.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/http/test_http_basic.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/http/test_http_basic_realm.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/http/test_http_basic_realm_description.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/http/test_http_bearer.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/http/test_http_bearer_description.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/http/test_http_bearer_optional.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/http/test_http_digest.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/http/test_http_digest_description.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/http/test_http_digest_optional.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/oauth2/__init__.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/oauth2/test_oauth2_authorization_code_bearer.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/oauth2/test_oauth2_authorization_code_bearer_description.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/oauth2/test_oauth2_password_bearer_optional.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/oauth2/test_oauth2_password_bearer_optional_description.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/security/openid_connect/__init__.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/test_additional_response_extra.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/test_additional_responses_bad.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/test_additional_responses_default_validationerror.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/test_http_endpoints.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/test_middleware.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/test_resolved_param_contextmanagers.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/test_resolved_params.py +0 -0
- {starmallow-0.6.5 → starmallow-0.8.0}/tests/utils.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: starmallow
|
3
|
-
Version: 0.
|
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-
|
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
|
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
|
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
|
@@ -13,7 +13,7 @@ Create a file `main.py` with:
|
|
13
13
|
|
14
14
|
```python
|
15
15
|
from typing import Annotated
|
16
|
-
from
|
16
|
+
from marshmallow_dataclass2 import dataclass
|
17
17
|
from starmallow import Body, Path, StarMallow
|
18
18
|
|
19
19
|
app = StarMallow()
|
@@ -76,7 +76,7 @@ INFO: Application startup complete.
|
|
76
76
|
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/)
|
77
77
|
|
78
78
|
```python
|
79
|
-
from
|
79
|
+
from marshmallow_dataclass2 import dataclass
|
80
80
|
from starmallow import StarMallow
|
81
81
|
from starmallow.decorators import route
|
82
82
|
from starmallow.endpoints import APIHTTPEndpoint
|
@@ -5,7 +5,7 @@ import hashlib
|
|
5
5
|
import marshmallow.fields as mf
|
6
6
|
from brotli_asgi import BrotliMiddleware
|
7
7
|
from marshmallow.validate import Range
|
8
|
-
from
|
8
|
+
from marshmallow_dataclass2 import dataclass as ma_dataclass
|
9
9
|
from starlette.middleware import Middleware
|
10
10
|
from starlette_context import middleware, plugins
|
11
11
|
|
@@ -9,7 +9,7 @@ from flask import json as flask_json
|
|
9
9
|
from flask.views import MethodView
|
10
10
|
# from flask_compress import Compress
|
11
11
|
from flask_smorest import Api, Blueprint
|
12
|
-
from
|
12
|
+
from marshmallow_dataclass2 import dataclass as ma_dataclass
|
13
13
|
|
14
14
|
default_flask_config = {
|
15
15
|
'OPENAPI_VERSION': '3.0.3',
|
@@ -23,7 +23,7 @@
|
|
23
23
|
"outputs": [],
|
24
24
|
"source": [
|
25
25
|
"import marshmallow.fields as mf\n",
|
26
|
-
"from
|
26
|
+
"from marshmallow_dataclass2 import dataclass as ma_dataclass\n",
|
27
27
|
"from starmallow import APIRouter\n",
|
28
28
|
"from starmallow.params import (\n",
|
29
29
|
" Header, \n",
|
@@ -150,7 +150,7 @@
|
|
150
150
|
"from typing import Any, Callable, TypeVar, Generic, Optional, _SpecialForm\n",
|
151
151
|
"\n",
|
152
152
|
"import marshmallow.fields as mf\n",
|
153
|
-
"from
|
153
|
+
"from marshmallow_dataclass2 import dataclass as ma_dataclass\n",
|
154
154
|
"\n",
|
155
155
|
"@ma_dataclass\n",
|
156
156
|
"class CreateRequest:\n",
|
@@ -5,7 +5,7 @@ import aiofiles.tempfile
|
|
5
5
|
import marshmallow.fields as mf
|
6
6
|
import orjson
|
7
7
|
from marshmallow.validate import Range
|
8
|
-
from
|
8
|
+
from marshmallow_dataclass2 import dataclass as ma_dataclass
|
9
9
|
from starlette.middleware import Middleware
|
10
10
|
from starlette.middleware.gzip import GZipMiddleware
|
11
11
|
from starlette_context import middleware, plugins
|
@@ -38,7 +38,7 @@ dependencies = [
|
|
38
38
|
"apispec[marshmallow] >=6,<7",
|
39
39
|
"dpath >=2.1.0,<3",
|
40
40
|
"marshmallow >=3.18.0,<4",
|
41
|
-
"marshmallow-
|
41
|
+
"marshmallow-dataclass2 >=8.8.1,<9",
|
42
42
|
"python-multipart >=0.0.5,<0.0.7",
|
43
43
|
"pyyaml >=5.4.1",
|
44
44
|
"starlette >=0.35,<1",
|
@@ -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
|
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
|
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
|
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
|
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
|
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
|
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
|
@@ -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
|
5
|
+
from marshmallow_dataclass2 import NewType
|
6
6
|
|
7
7
|
import starmallow.fields as sf
|
8
8
|
from starmallow.endpoints import APIHTTPEndpoint
|
@@ -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
|
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}')
|
@@ -6,7 +6,7 @@ from typing import Final, FrozenSet, Literal, Optional, Union
|
|
6
6
|
from uuid import UUID
|
7
7
|
|
8
8
|
from marshmallow.validate import Length, Range, Regexp
|
9
|
-
from
|
9
|
+
from marshmallow_dataclass2 import dataclass as ma_dataclass
|
10
10
|
|
11
11
|
from starmallow import Path, Query, StarMallow
|
12
12
|
|
@@ -116,6 +116,11 @@ def get_final_id(item_id: FinalItem = Path()):
|
|
116
116
|
return item_id
|
117
117
|
|
118
118
|
|
119
|
+
@app.get("/path/union/{item_id}")
|
120
|
+
def get_union_id(item_id: int | float | bool):
|
121
|
+
return item_id
|
122
|
+
|
123
|
+
|
119
124
|
#########################################################
|
120
125
|
# Test Path parameters
|
121
126
|
#########################################################
|
@@ -5,7 +5,7 @@ from typing import Annotated, Literal, Optional, Union
|
|
5
5
|
import marshmallow as ma
|
6
6
|
import marshmallow.fields as mf
|
7
7
|
import pytest
|
8
|
-
from
|
8
|
+
from marshmallow_dataclass2 import dataclass as ma_dataclass
|
9
9
|
from starlette.testclient import TestClient
|
10
10
|
|
11
11
|
from starmallow import Body, Header, Path, Query, ResolvedParam, StarMallow
|
@@ -470,6 +470,51 @@ openapi_schema = {
|
|
470
470
|
],
|
471
471
|
},
|
472
472
|
},
|
473
|
+
"/path/union/{item_id}": {
|
474
|
+
"get": {
|
475
|
+
"responses": {
|
476
|
+
"200": {
|
477
|
+
"description": "Successful Response",
|
478
|
+
"content": {"application/json": {"schema": {}}},
|
479
|
+
},
|
480
|
+
"422": {
|
481
|
+
"description": "Validation Error",
|
482
|
+
"content": {
|
483
|
+
"application/json": {
|
484
|
+
"schema": {
|
485
|
+
"$ref": "#/components/schemas/HTTPValidationError",
|
486
|
+
},
|
487
|
+
},
|
488
|
+
},
|
489
|
+
},
|
490
|
+
},
|
491
|
+
"summary": "Get Union Id",
|
492
|
+
"operationId": "get_union_id_path_union__item_id__get",
|
493
|
+
"parameters": [
|
494
|
+
{
|
495
|
+
'in': 'path',
|
496
|
+
'name': 'item_id',
|
497
|
+
'required': True,
|
498
|
+
'schema': {
|
499
|
+
'title': 'Item Id',
|
500
|
+
"type": {
|
501
|
+
"oneOf": [
|
502
|
+
{
|
503
|
+
"type": "integer",
|
504
|
+
},
|
505
|
+
{
|
506
|
+
"type": "number",
|
507
|
+
},
|
508
|
+
{
|
509
|
+
"type": "boolean",
|
510
|
+
},
|
511
|
+
],
|
512
|
+
},
|
513
|
+
},
|
514
|
+
},
|
515
|
+
],
|
516
|
+
},
|
517
|
+
},
|
473
518
|
"/path/param/{item_id}": {
|
474
519
|
"get": {
|
475
520
|
"responses": {
|
@@ -2259,7 +2304,10 @@ openapi_schema = {
|
|
2259
2304
|
"path,expected_status,expected_response",
|
2260
2305
|
[
|
2261
2306
|
("/api_route", 200, {"message": "Hello World"}),
|
2262
|
-
("/
|
2307
|
+
("/path/union/500.0", 200, 500.0),
|
2308
|
+
("/path/union/200", 200, 200),
|
2309
|
+
("/path/union/true", 200, True),
|
2310
|
+
("/api_route", 200, {"message": "Hello World"}),
|
2263
2311
|
("/nonexistent", 404, {"detail": "Not Found"}),
|
2264
2312
|
("/openapi.json", 200, openapi_schema),
|
2265
2313
|
],
|
@@ -235,6 +235,7 @@ openapi_schema = {
|
|
235
235
|
[
|
236
236
|
("/path/1,2,3,4", {}, 200, [1, 2, 3, 4]),
|
237
237
|
("/query?item_ids=5,4,3,2", {}, 200, [5, 4, 3, 2]),
|
238
|
+
("/query?item_ids=", {}, 200, []),
|
238
239
|
("/header", {'item_ids': '6,8,7'}, 200, [6, 8, 7]),
|
239
240
|
("/openapi.json", {}, 200, openapi_schema),
|
240
241
|
],
|
@@ -0,0 +1,149 @@
|
|
1
|
+
'''Test Resolved Params'''
|
2
|
+
from dataclasses import field
|
3
|
+
from typing import Annotated, Generic, TypeVar
|
4
|
+
|
5
|
+
import pytest
|
6
|
+
from marshmallow_dataclass2 import dataclass as ma_dataclass
|
7
|
+
from starlette.testclient import TestClient
|
8
|
+
|
9
|
+
from starmallow import Query, StarMallow
|
10
|
+
|
11
|
+
from .utils import assert_json
|
12
|
+
|
13
|
+
app = StarMallow()
|
14
|
+
|
15
|
+
T = TypeVar("T")
|
16
|
+
|
17
|
+
############################################################
|
18
|
+
# Models - classes and schemas
|
19
|
+
############################################################
|
20
|
+
# region - VS Code folding marker - https://code.visualstudio.com/docs/editor/codebasics#_folding
|
21
|
+
|
22
|
+
|
23
|
+
@ma_dataclass
|
24
|
+
class QueryParameters(Generic[T]):
|
25
|
+
id: T = None
|
26
|
+
|
27
|
+
|
28
|
+
@ma_dataclass
|
29
|
+
class PageableResponse(Generic[T]):
|
30
|
+
items: list[T] = field(default_factory=list)
|
31
|
+
# endregion
|
32
|
+
|
33
|
+
|
34
|
+
############################################################
|
35
|
+
# Test API
|
36
|
+
############################################################
|
37
|
+
# region
|
38
|
+
@app.get("/data")
|
39
|
+
def get_data(
|
40
|
+
params: Annotated[QueryParameters[int], Query()],
|
41
|
+
) -> PageableResponse[str]:
|
42
|
+
return {'items': ['foo', 'bar']}
|
43
|
+
# endregion
|
44
|
+
|
45
|
+
|
46
|
+
############################################################
|
47
|
+
# Tests
|
48
|
+
############################################################
|
49
|
+
# region
|
50
|
+
client = TestClient(app)
|
51
|
+
|
52
|
+
openapi_schema = {
|
53
|
+
"openapi": "3.0.2",
|
54
|
+
"info": {"title": "StarMallow", "version": "0.1.0"},
|
55
|
+
"paths": {
|
56
|
+
"/data": {
|
57
|
+
"get": {
|
58
|
+
"responses": {
|
59
|
+
"200": {
|
60
|
+
"description": "Successful Response",
|
61
|
+
"content": {
|
62
|
+
"application/json": {
|
63
|
+
"schema": {
|
64
|
+
"$ref": "#/components/schemas/PageableResponse",
|
65
|
+
},
|
66
|
+
},
|
67
|
+
},
|
68
|
+
},
|
69
|
+
"422": {
|
70
|
+
"description": "Validation Error",
|
71
|
+
"content": {
|
72
|
+
"application/json": {
|
73
|
+
"schema": {
|
74
|
+
"$ref": "#/components/schemas/HTTPValidationError",
|
75
|
+
},
|
76
|
+
},
|
77
|
+
},
|
78
|
+
},
|
79
|
+
},
|
80
|
+
"summary": "Get Data",
|
81
|
+
"operationId": "get_data_data_get",
|
82
|
+
"parameters": [
|
83
|
+
{
|
84
|
+
"required": False,
|
85
|
+
"schema": {
|
86
|
+
"default": None,
|
87
|
+
"nullable": True,
|
88
|
+
"type": "integer",
|
89
|
+
"title": "Id",
|
90
|
+
},
|
91
|
+
"name": "id",
|
92
|
+
"in": "query",
|
93
|
+
},
|
94
|
+
],
|
95
|
+
},
|
96
|
+
},
|
97
|
+
},
|
98
|
+
"components": {
|
99
|
+
"schemas": {
|
100
|
+
"HTTPValidationError": {
|
101
|
+
'properties': {
|
102
|
+
'detail': {
|
103
|
+
'description': 'Error detail',
|
104
|
+
'title': 'Detail',
|
105
|
+
},
|
106
|
+
'errors': {
|
107
|
+
'description': 'Exception or error type',
|
108
|
+
'title': 'Errors',
|
109
|
+
},
|
110
|
+
'status_code': {
|
111
|
+
'description': 'HTTP status code',
|
112
|
+
'title': 'Status Code',
|
113
|
+
'type': 'integer',
|
114
|
+
},
|
115
|
+
},
|
116
|
+
'required': ['detail', 'status_code'],
|
117
|
+
'title': 'HTTPValidationError',
|
118
|
+
'type': 'object',
|
119
|
+
},
|
120
|
+
"PageableResponse": {
|
121
|
+
"properties": {
|
122
|
+
"items": {
|
123
|
+
"items": {
|
124
|
+
"type": "string",
|
125
|
+
},
|
126
|
+
"title": "Items",
|
127
|
+
"type": "array",
|
128
|
+
},
|
129
|
+
},
|
130
|
+
"title": "PageableResponse",
|
131
|
+
"type": "object",
|
132
|
+
},
|
133
|
+
},
|
134
|
+
},
|
135
|
+
}
|
136
|
+
|
137
|
+
|
138
|
+
@pytest.mark.parametrize(
|
139
|
+
"path,expected_status,expected_response",
|
140
|
+
[
|
141
|
+
("/data?id=50", 200, {"items": ["foo", "bar"]}),
|
142
|
+
("/openapi.json", 200, openapi_schema),
|
143
|
+
],
|
144
|
+
)
|
145
|
+
def test_get_path(path, expected_status, expected_response):
|
146
|
+
response = client.get(path)
|
147
|
+
assert response.status_code == expected_status
|
148
|
+
assert_json(response.json(), expected_response)
|
149
|
+
# endregion
|
@@ -7,7 +7,7 @@ from typing import Any, Dict, Literal, Optional, Union
|
|
7
7
|
import marshmallow as ma
|
8
8
|
import marshmallow.fields as mf
|
9
9
|
import pytest
|
10
|
-
from
|
10
|
+
from marshmallow_dataclass2 import dataclass as ma_dataclass
|
11
11
|
from starlette.background import BackgroundTasks
|
12
12
|
from starlette.requests import HTTPConnection, Request
|
13
13
|
from starlette.responses import Response
|
@@ -6,7 +6,7 @@ from uuid import UUID
|
|
6
6
|
|
7
7
|
import marshmallow.fields as mf
|
8
8
|
import pytest
|
9
|
-
from
|
9
|
+
from marshmallow_dataclass2 import dataclass as ma_dataclass
|
10
10
|
from starlette.testclient import TestClient
|
11
11
|
|
12
12
|
from starmallow import Body, StarMallow
|
@@ -6,7 +6,7 @@ from uuid import UUID
|
|
6
6
|
|
7
7
|
import marshmallow.fields as mf
|
8
8
|
import pytest
|
9
|
-
from
|
9
|
+
from marshmallow_dataclass2 import dataclass as ma_dataclass
|
10
10
|
from starlette.testclient import TestClient
|
11
11
|
|
12
12
|
from starmallow import Body, StarMallow
|
@@ -6,7 +6,7 @@ from uuid import UUID
|
|
6
6
|
|
7
7
|
import marshmallow.fields as mf
|
8
8
|
import pytest
|
9
|
-
from
|
9
|
+
from marshmallow_dataclass2 import dataclass as ma_dataclass
|
10
10
|
from starlette.testclient import TestClient
|
11
11
|
|
12
12
|
from starmallow import StarMallow
|
@@ -6,7 +6,7 @@ from uuid import UUID
|
|
6
6
|
|
7
7
|
import marshmallow.fields as mf
|
8
8
|
import pytest
|
9
|
-
from
|
9
|
+
from marshmallow_dataclass2 import dataclass as ma_dataclass
|
10
10
|
from starlette.testclient import TestClient
|
11
11
|
|
12
12
|
from starmallow import StarMallow
|
@@ -6,7 +6,7 @@ from uuid import UUID
|
|
6
6
|
|
7
7
|
import marshmallow.fields as mf
|
8
8
|
import pytest
|
9
|
-
from
|
9
|
+
from marshmallow_dataclass2 import dataclass as ma_dataclass
|
10
10
|
from starlette.testclient import TestClient
|
11
11
|
|
12
12
|
from starmallow import StarMallow
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{starmallow-0.6.5 → starmallow-0.8.0}/tests/security/http/test_http_basic_realm_description.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{starmallow-0.6.5 → starmallow-0.8.0}/tests/security/oauth2/test_oauth2_authorization_code_bearer.py
RENAMED
File without changes
|
File without changes
|
{starmallow-0.6.5 → starmallow-0.8.0}/tests/security/oauth2/test_oauth2_password_bearer_optional.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{starmallow-0.6.5 → starmallow-0.8.0}/tests/test_additional_responses_default_validationerror.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|