@@ -60,6 +60,7 @@ class User:
60
60
import datetime
61
61
import decimal
62
62
import inspect
63
+ import typing as t
63
64
import typing
64
65
import uuid
65
66
from enum import Enum
@@ -75,7 +76,6 @@ class User:
75
76
from typing import Type
76
77
from typing import Union
77
78
from typing import cast
78
-
79
79
import attr
80
80
import marshmallow
81
81
import typing_inspect
@@ -86,6 +86,7 @@ class User:
86
86
__all__ = ["dataclass" , "add_schema" , "class_schema" , "field_for_schema" ]
87
87
88
88
NoneType = type (None )
89
+ T = t .TypeVar ("T" )
89
90
90
91
91
92
def class_schema (clazz : type , meta : Dict [str , Any ] = {}) -> Type [marshmallow .Schema ]:
@@ -157,6 +158,19 @@ def class_schema(clazz: type, meta: Dict[str, Any] = {}) -> Type[marshmallow.Sch
157
158
}
158
159
159
160
161
+ class VariadicTuple (marshmallow .fields .List ):
162
+ """Homogenous tuple with variable number of entries."""
163
+
164
+ def _deserialize (self , * args , ** kwargs ):
165
+ return tuple (super ()._deserialize (* args , ** kwargs ))
166
+
167
+
168
+ def only (items : t .Iterable [T ]) -> T :
169
+ """Return the only item in an iterable or raise ValueError."""
170
+ [x ] = items
171
+ return x
172
+
173
+
160
174
def field_for_schema (
161
175
typ : type , default = marshmallow .missing , metadata : Mapping [str , Any ] = None
162
176
) -> marshmallow .fields .Field :
@@ -201,7 +215,7 @@ def field_for_schema(
201
215
202
216
if default is not marshmallow .missing :
203
217
desert_metadata .setdefault ("default" , default )
204
- desert_metadata .setdefault (' allow_none' , True )
218
+ desert_metadata .setdefault (" allow_none" , True )
205
219
if not desert_metadata .get (
206
220
"required"
207
221
): # 'missing' must not be set for required fields.
@@ -223,14 +237,22 @@ def field_for_schema(
223
237
224
238
# Generic types
225
239
origin = typing_inspect .get_origin (typ )
240
+
226
241
if origin :
227
242
arguments = typing_inspect .get_args (typ , True )
243
+
228
244
if origin in (list , List ):
229
245
field = marshmallow .fields .List (field_for_schema (arguments [0 ]))
230
- if origin in (tuple , Tuple ):
246
+
247
+ if origin in (tuple , t .Tuple ) and Ellipsis not in arguments :
231
248
field = marshmallow .fields .Tuple (
232
249
tuple (field_for_schema (arg ) for arg in arguments )
233
250
)
251
+ elif origin in (tuple , t .Tuple ) and Ellipsis in arguments :
252
+
253
+ field = VariadicTuple (
254
+ field_for_schema (only (arg for arg in arguments if arg != Ellipsis ))
255
+ )
234
256
elif origin in (dict , Dict ):
235
257
field = marshmallow .fields .Dict (
236
258
keys = field_for_schema (arguments [0 ]),
@@ -243,7 +265,6 @@ def field_for_schema(
243
265
metadata [_DESERT_SENTINEL ]["missing" ] = metadata .get ("missing" , None )
244
266
metadata [_DESERT_SENTINEL ]["required" ] = False
245
267
246
-
247
268
field = field_for_schema (subtyp , metadata = metadata , default = None )
248
269
field .default = None
249
270
field .missing = None
0 commit comments