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 mashmallow fields.Tuple creation #434

Merged
merged 5 commits into from
Jul 24, 2023
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
20 changes: 18 additions & 2 deletions dataclasses_json/mm.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,15 @@ def _deserialize(self, value, attr, data, **kwargs):
return super()._deserialize(tmp_value, attr, data, **kwargs)


class _TupleVarLen(fields.List):
"""
variable-length homogeneous tuples
"""
def _deserialize(self, value, attr, data, **kwargs):
optional_list = super()._deserialize(value, attr, data, **kwargs)
return None if optional_list is None else tuple(optional_list)


TYPES = {
typing.Mapping: fields.Mapping,
typing.MutableMapping: fields.Mapping,
Expand Down Expand Up @@ -244,10 +253,17 @@ def inner(type_, options):
origin = getattr(type_, '__origin__', type_)
args = [inner(a, {}) for a in getattr(type_, '__args__', []) if
a is not type(None)]


if type_ == Ellipsis:
return type_

if _is_optional(type_):
options["allow_none"] = True

if origin is tuple:
if len(args) == 2 and args[1] == Ellipsis:
return _TupleVarLen(args[0], **options)
else:
return fields.Tuple(args, **options)
if origin in TYPES:
return TYPES[origin](*args, **options)

Expand Down
37 changes: 35 additions & 2 deletions tests/test_tuples.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import dataclasses
import json
from enum import Enum
from typing import Tuple
Expand Down Expand Up @@ -49,16 +50,31 @@ class TestEncoder:
def test_enum_with_tuple(self):
assert hetero_tuple_data.to_json() == hetero_tuple_json, f'Actual: {hetero_tuple_data.to_json()}, Expected: {hetero_tuple_json}'
assert hetero_tuple_data.to_dict(encode_json=True) == json.loads(hetero_tuple_json), f'Actual: {hetero_tuple_data.to_dict()}, Expected: {json.loads(hetero_tuple_json)}'

def test_nested_tuple(self):
assert nested_tuple_data.to_json() == nested_tuple_json, f'Actual: {nested_tuple_data.to_json()}, Expected: {nested_tuple_json}'
assert nested_tuple_data.to_dict(encode_json=True) == json.loads(nested_tuple_json), f'Actual: {nested_tuple_data.to_dict()}, Expected: {json.loads(nested_tuple_json)}'

def test_ellipsis_tuple(self):
assert ellipsis_tuple_data.to_json() == ellipsis_tuple_json, f'Actual: {ellipsis_tuple_data.to_json()}, Expected: {ellipsis_tuple_json}'
assert ellipsis_tuple_data.to_dict(encode_json=True) == json.loads(ellipsis_tuple_json), f'Actual: {ellipsis_tuple_data.to_dict()}, Expected: {json.loads(ellipsis_tuple_json)}'


class TestSchemaEncoder:
def test_enum_with_tuple(self):
js = DataWithHeterogeneousTuple.schema().dumps(hetero_tuple_data)
assert js == hetero_tuple_json, f'Actual: {js}, Expected: {hetero_tuple_json}'

def test_nested_tuple(self):
js = DataWithNestedTuple.schema().dumps(nested_tuple_data)
assert js == nested_tuple_json, f'Actual: {js}, Expected: {nested_tuple_json}'

def test_ellipsis_tuple(self):
js = DataWithEllipsisTuple.schema().dumps(ellipsis_tuple_data)
assert js == ellipsis_tuple_json, f'Actual: {js}, Expected: {ellipsis_tuple_json}'



class TestDecoder:
def test_enum_with_tuple(self):
tuple_data_from_json = DataWithHeterogeneousTuple.from_json(hetero_tuple_json)
Expand All @@ -74,3 +90,20 @@ def test_ellipsis_tuple(self):
tuple_data_from_json = DataWithEllipsisTuple.from_json(ellipsis_tuple_json)
assert ellipsis_tuple_data == tuple_data_from_json
assert tuple_data_from_json.to_json() == ellipsis_tuple_json


class TestSchemaDecoder:
def test_enum_with_tuple(self):
tuple_data_from_json = DataWithHeterogeneousTuple.schema().loads(hetero_tuple_json)
assert hetero_tuple_data == tuple_data_from_json
assert tuple_data_from_json.to_json() == hetero_tuple_json

def test_nested_tuple(self):
tuple_data_from_json = DataWithNestedTuple.schema().loads(nested_tuple_json)
assert nested_tuple_data == tuple_data_from_json
assert tuple_data_from_json.to_json() == nested_tuple_json

def test_ellipsis_tuple(self):
tuple_data_from_json = DataWithEllipsisTuple.schema().loads(ellipsis_tuple_json)
assert ellipsis_tuple_data == tuple_data_from_json
assert tuple_data_from_json.to_json() == ellipsis_tuple_json