Skip to content

Commit 61e8105

Browse files
committed
Fix desert handling of metadata not being passed to fields
1 parent 0c43ff5 commit 61e8105

File tree

2 files changed

+42
-25
lines changed

2 files changed

+42
-25
lines changed

src/desert/_make.py

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,8 @@ def field_for_schema(
220220

221221
if default is not marshmallow.missing:
222222
desert_metadata.setdefault("dump_default", default)
223-
desert_metadata.setdefault("allow_none", True)
224223
desert_metadata.setdefault("load_default", default)
224+
desert_metadata.setdefault("allow_none", True)
225225

226226
field = None
227227

@@ -235,9 +235,20 @@ def field_for_schema(
235235
field.metadata.update(metadata)
236236
return field
237237

238+
field_args = {
239+
k: v
240+
for k, v in desert_metadata.items()
241+
if k
242+
in [
243+
"dump_default",
244+
"load_default",
245+
"allow_none",
246+
]
247+
}
248+
238249
# Base types
239250
if not field and typ in _native_to_marshmallow:
240-
field = _native_to_marshmallow[typ](dump_default=default)
251+
field = _native_to_marshmallow[typ](**field_args)
241252

242253
# Generic types
243254
origin = typing_inspect.get_origin(typ)
@@ -253,16 +264,18 @@ def field_for_schema(
253264
collections.abc.Sequence,
254265
collections.abc.MutableSequence,
255266
):
256-
field = marshmallow.fields.List(field_for_schema(arguments[0]))
267+
field = marshmallow.fields.List(
268+
field_for_schema(arguments[0]), **field_args
269+
)
257270

258271
if origin in (tuple, t.Tuple) and Ellipsis not in arguments:
259272
field = marshmallow.fields.Tuple( # type: ignore[no-untyped-call]
260-
tuple(field_for_schema(arg) for arg in arguments)
273+
tuple(field_for_schema(arg) for arg in arguments), **field_args
261274
)
262275
elif origin in (tuple, t.Tuple) and Ellipsis in arguments:
263-
264276
field = VariadicTuple(
265-
field_for_schema(only(arg for arg in arguments if arg != Ellipsis))
277+
field_for_schema(only(arg for arg in arguments if arg != Ellipsis)),
278+
**field_args,
266279
)
267280
elif origin in (
268281
dict,
@@ -275,22 +288,15 @@ def field_for_schema(
275288
field = marshmallow.fields.Dict(
276289
keys=field_for_schema(arguments[0]),
277290
values=field_for_schema(arguments[1]),
291+
**field_args,
278292
)
279293
elif typing_inspect.is_optional_type(typ):
280294
[subtyp] = (t for t in arguments if t is not NoneType)
281295
# Treat optional types as types with a None default
282-
metadata[_DESERT_SENTINEL]["dump_default"] = metadata.get(
283-
"dump_default", None
284-
)
285-
metadata[_DESERT_SENTINEL]["load_default"] = metadata.get(
286-
"load_default", None
287-
)
288-
metadata[_DESERT_SENTINEL]["required"] = False
289-
290-
field = field_for_schema(subtyp, metadata=metadata, default=None)
291-
field.dump_default = None
292-
field.load_default = None
293-
field.allow_none = True
296+
metadata[_DESERT_SENTINEL]["allow_none"] = True
297+
if default is marshmallow.missing:
298+
default = None
299+
field = field_for_schema(subtyp, metadata=metadata, default=default)
294300

295301
elif typing_inspect.is_union_type(typ):
296302
subfields = [field_for_schema(subtyp) for subtyp in arguments]
@@ -302,7 +308,7 @@ def field_for_schema(
302308
newtype_supertype = getattr(typ, "__supertype__", None)
303309
if newtype_supertype and typing_inspect.is_new_type(typ):
304310
metadata.setdefault("description", typ.__name__)
305-
field = field_for_schema(newtype_supertype, default=default)
311+
field = field_for_schema(newtype_supertype, metadata=metadata, default=default)
306312

307313
# enumerations
308314
if type(typ) is enum.EnumMeta:
@@ -315,7 +321,7 @@ def field_for_schema(
315321

316322
if field is None:
317323
nested = forward_reference or class_schema(typ)
318-
field = marshmallow.fields.Nested(nested)
324+
field = marshmallow.fields.Nested(nested, **field_args)
319325

320326
field.metadata.update(metadata)
321327

@@ -350,11 +356,11 @@ def _get_field_default(
350356
if isinstance(field, dataclasses.Field):
351357
# misc: https://github.com/python/mypy/issues/10750
352358
# comparison-overlap: https://github.com/python/typeshed/pull/5900
353-
if field.default_factory != dataclasses.MISSING:
354-
return dataclasses.MISSING
355-
if field.default is dataclasses.MISSING:
356-
return marshmallow.missing
357-
return field.default
359+
if field.default_factory is not dataclasses.MISSING:
360+
return field.default_factory
361+
if field.default is not dataclasses.MISSING:
362+
return field.default
363+
return marshmallow.missing
358364
elif isinstance(field, attr.Attribute):
359365
if field.default == attr.NOTHING:
360366
return marshmallow.missing

tests/test_make.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,17 @@ class A:
224224
assert data == A(None) # type: ignore[call-arg]
225225

226226

227+
def test_optional_default(module: DataclassModule) -> None:
228+
"""Setting an optional type allows passing None."""
229+
230+
@module.dataclass
231+
class A:
232+
x: t.Optional[int] = 1
233+
234+
data = desert.schema_class(A)().load({})
235+
assert data == A(1) # type: ignore[call-arg]
236+
237+
227238
def test_custom_field(module: DataclassModule) -> None:
228239
@module.dataclass
229240
class A:

0 commit comments

Comments
 (0)