Skip to content

Commit

Permalink
omit_defaults supports factory tuple/frozenset
Browse files Browse the repository at this point in the history
Previously `omit_defaults` would only detect `tuple`/`frozenset` fields
containing default values if the default was specified by value. We now
also support if these types are specified via `default_factory`.

Note that since `tuple` and `frozenset` values are immutable there's no
real reason to prefer `default_factory` for defaults of these types.
  • Loading branch information
jcrist committed Mar 10, 2024
1 parent b44800c commit 13a06dd
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 1 deletion.
10 changes: 9 additions & 1 deletion msgspec/_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -5675,7 +5675,15 @@ structmeta_process_default(StructMetaInfo *info, PyObject *name) {
type = Py_TYPE(obj);
}
else if (f->default_factory != NODEFAULT) {
default_val = Factory_New(f->default_factory);
if (f->default_factory == (PyObject *)&PyTuple_Type) {
default_val = PyTuple_New(0);
}
else if (f->default_factory == (PyObject *)&PyFrozenSet_Type) {
default_val = PyFrozenSet_New(NULL);
}
else {
default_val = Factory_New(f->default_factory);
}
if (default_val == NULL) return -1;
goto done;
}
Expand Down
16 changes: 16 additions & 0 deletions tests/test_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -1744,6 +1744,22 @@ class Test(Struct, omit_defaults=True):
res = proto.decode(proto.encode(obj))
assert res == sol

@pytest.mark.parametrize("typ", [tuple, list, set, frozenset, dict])
def test_omit_defaults_collections(self, proto, typ):
"""Check that using empty collections as default values are detected
regardless if they're specified by value or as a default_factory."""

class Test(Struct, omit_defaults=True):
a: typ = msgspec.field(default_factory=typ)
b: typ = msgspec.field(default=typ())
c: typ = typ()

ex = {"x": 1} if typ is dict else [1]

assert proto.encode(Test()) == proto.encode({})
for n in ["a", "b", "c"]:
assert proto.encode(Test(**{n: typ(ex)})) == proto.encode({n: ex})

def test_omit_defaults_positional(self, proto):
class Test(Struct, omit_defaults=True):
a: int
Expand Down

0 comments on commit 13a06dd

Please sign in to comment.