Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix frozenset deserialization. #342

Merged
merged 1 commit into from
Feb 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions apischema/deserialization/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
FieldsConstructor,
FlattenedField,
FloatMethod,
FrozenSetMethod,
IntMethod,
ListCheckOnlyMethod,
ListMethod,
Expand Down Expand Up @@ -360,13 +361,19 @@ def factory(constraints: Optional[Constraints], _) -> DeserializationMethod:
value_method = value_factory.method
list_constraints = constraints_validators(constraints)[list]
method: DeserializationMethod
if issubclass(cls, collections.abc.Set):
if issubclass(cls, collections.abc.Set) and not issubclass(cls, frozenset):
return SetMethod(list_constraints, value_method)
elif self.no_copy and check_only(value_method):

if self.no_copy and check_only(value_method):
method = ListCheckOnlyMethod(list_constraints, value_method)
else:
method = ListMethod(list_constraints, value_method)
return VariadicTupleMethod(method) if issubclass(cls, tuple) else method

if issubclass(cls, tuple):
return VariadicTupleMethod(method)
if issubclass(cls, frozenset):
return FrozenSetMethod(method)
return method

return self._factory(factory, list)

Expand Down
8 changes: 8 additions & 0 deletions apischema/deserialization/methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,14 @@ def deserialize(self, data: Any) -> Any:
return values


@dataclass
class FrozenSetMethod(DeserializationMethod):
method: DeserializationMethod

def deserialize(self, data: Any) -> Any:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need to rewrite a complete method for frozenset; ListMethod should be reused, as it's already the case for variadic tuple

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the way, I think super() was not supported (in Cython generation) when I wrote VariadicTupleMethod, but this one, as well as FrozenSetMethod should simply inherit ListMethod and use for example return tuple(super().deserialize(data))

Copy link
Contributor Author

@pchanial pchanial Feb 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After inspection, it is not straightforward, since VariadicTupleMethod could inherit from ListCheckOnlyMethod or ListMethod.
It looks like all collection deserialization methods could benefit from only checking their item values, so I'd say that it would be better to do some refactoring here.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, it was not a question of super() support … I was too quick in my response. So composition should be used the same way VariadicTupleMethod does.

return frozenset(self.method.deserialize(data))


@dataclass
class VariadicTupleMethod(DeserializationMethod):
method: DeserializationMethod
Expand Down
7 changes: 5 additions & 2 deletions tests/unit/test_deserialization_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import (
AbstractSet,
Any,
FrozenSet,
List,
Mapping,
Optional,
Expand All @@ -30,6 +31,7 @@
def bijection(cls, data, expected):
obj = deserialize(cls, data)
assert obj == expected
assert type(obj) is type(expected)
assert serialize(cls, obj) == data


Expand Down Expand Up @@ -97,7 +99,8 @@ def test_primitive_error(data):
(List, [0, SimpleDataclass(0)]),
(Set, {0, SimpleDataclass(0)}),
(Sequence, [0, SimpleDataclass(0)]),
(AbstractSet, frozenset([0, SimpleDataclass(0)])),
(AbstractSet, {0, SimpleDataclass(0)}),
(FrozenSet, frozenset([0, SimpleDataclass(0)])),
],
)
def test_collection(cls, expected):
Expand Down Expand Up @@ -188,7 +191,7 @@ def test_with_class_context():
class BigInt(int):
pass

bijection(BigInt, 100, 100)
bijection(BigInt, 100, BigInt(100))


def test_properties():
Expand Down