Skip to content

Commit

Permalink
Fix: if field has custom decoder, schema takes it into account (#462)
Browse files Browse the repository at this point in the history
  • Loading branch information
deansg authored Aug 7, 2023
1 parent d1a4464 commit 04ddea8
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 3 deletions.
14 changes: 12 additions & 2 deletions dataclasses_json/mm.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,10 +253,10 @@ 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:
Expand Down Expand Up @@ -318,6 +318,16 @@ def schema(cls, mixin, infer_missing):
options['data_key'] = metadata.letter_case(field.name)

t = build_type(type_, options, mixin, field, cls)
if field.metadata.get('dataclasses_json', {}).get('decoder'):
# If the field defines a custom decoder, it should completely replace the Marshmallow field's conversion
# logic.
# From Marshmallow's documentation for the _deserialize method:
# "Deserialize value. Concrete :class:`Field` classes should implement this method. "
# This is the method that Field implementations override to perform the actual deserialization logic.
# In this case we specifically override this method instead of `deserialize` to minimize potential
# side effects, and only cancel the actual value deserialization.
t._deserialize = lambda v, *_a, **_kw: v

# if type(t) is not fields.Field: # If we use `isinstance` we would return nothing.
if field.type != typing.Optional[CatchAllVar]:
schema[field.name] = t
Expand Down
10 changes: 10 additions & 0 deletions tests/entities.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,16 @@ class DataClassWithErroneousDecode:
id: float = field(metadata=config(decoder=lambda: None))


def split_str(data: str, *_args, **_kwargs):
return data.split(',')


@dataclass_json
@dataclass
class DataClassDifferentTypeDecode:
lst: List[str] = field(default=None, metadata=config(decoder=split_str))


@dataclass_json
@dataclass
class DataClassMappingBadDecode:
Expand Down
7 changes: 6 additions & 1 deletion tests/test_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
import pytest

from .entities import (DataClassDefaultListStr, DataClassDefaultOptionalList, DataClassList, DataClassOptional,
DataClassWithNestedOptional, DataClassWithNestedOptionalAny, DataClassWithNestedAny)
DataClassWithNestedOptional, DataClassWithNestedOptionalAny, DataClassWithNestedAny,
DataClassDifferentTypeDecode)
from .test_letter_case import CamelCasePerson, KebabCasePerson, SnakeCasePerson, FieldNamePerson

test_do_list = """[{}, {"children": [{"name": "a"}, {"name": "b"}]}]"""
Expand Down Expand Up @@ -47,3 +48,7 @@ def test_nested_optional_any(self):
def test_nested_any_accepts_optional(self):
DataClassWithNestedAny.schema().loads(nested_optional_data)
assert True

def test_accounts_for_decode(self):
assert DataClassDifferentTypeDecode.schema().load({'lst': '1,2,3'}) == \
DataClassDifferentTypeDecode(lst=['1', '2', '3'])

6 comments on commit 04ddea8

@github-actions
Copy link

Choose a reason for hiding this comment

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

Coverage

Coverage Report
FileStmtsMissCoverMissing
dataclasses_json
   cfg.py51492%80, 84–86
   core.py234996%38–41, 51, 64, 66, 81, 83, 169
   mm.py2012986%33–36, 42–45, 53–56, 62–65, 88, 161–162, 167, 171, 175, 180, 184, 188, 196, 202, 207, 216, 221, 226, 244–251
   stringcase.py25388%59, 76, 97
   undefined.py143299%24, 38
   utils.py1282977%11–24, 44–49, 60–64, 74, 99–100, 108–109, 158, 177, 202
tests
   entities.py226299%229, 235
   test_annotations.py814248%50–67, 78–102, 106–122
   test_api.py142199%139
   test_str_subclass.py22195%9
TOTAL248412295% 

Tests Skipped Failures Errors Time
292 1 💤 0 ❌ 0 🔥 3.204s ⏱️

@github-actions
Copy link

Choose a reason for hiding this comment

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

Coverage

Coverage Report
FileStmtsMissCoverMissing
dataclasses_json
   cfg.py51492%80, 84–86
   core.py2351096%38–41, 51, 64, 66, 81, 83, 169, 197
   mm.py2023085%33–36, 42–45, 53–56, 62–65, 88, 161–162, 167, 171, 175, 180, 184, 188, 196, 202, 207, 216, 221, 226, 235, 244–251
   stringcase.py25388%59, 76, 97
   undefined.py143299%24, 38
   utils.py1283672%11–24, 44–49, 60–64, 74, 99–100, 108–109, 124–132, 158, 177, 202
tests
   entities.py226299%229, 235
   test_annotations.py814248%50–67, 78–102, 106–122
   test_api.py142497%88, 99, 139–140
   test_str_subclass.py22195%9
   test_union.py981090%87–94, 108–115
TOTAL248614494% 

Tests Skipped Failures Errors Time
292 3 💤 0 ❌ 0 🔥 3.275s ⏱️

@github-actions
Copy link

Choose a reason for hiding this comment

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

Coverage

Coverage Report
FileStmtsMissCoverMissing
dataclasses_json
   cfg.py51492%80, 84–86
   core.py235996%38–41, 51, 64, 66, 81, 83, 169
   mm.py2012986%33–36, 42–45, 53–56, 62–65, 88, 161–162, 167, 171, 175, 180, 184, 188, 196, 202, 207, 216, 221, 226, 244–251
   stringcase.py25388%59, 76, 97
   undefined.py141299%24, 38
   utils.py1282977%11–24, 44–49, 60–64, 74, 99–100, 108–109, 158, 177, 202
tests
   entities.py206299%229, 235
   test_annotations.py804248%50–67, 78–102, 106–122
   test_api.py140299%139–140
   test_str_subclass.py22195%9
TOTAL237212395% 

Tests Skipped Failures Errors Time
292 1 💤 0 ❌ 0 🔥 3.323s ⏱️

@github-actions
Copy link

Choose a reason for hiding this comment

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

Coverage

Coverage Report
FileStmtsMissCoverMissing
dataclasses_json
   cfg.py51492%80, 84–86
   core.py2351096%38–41, 51, 64, 66, 81, 83, 169, 197
   mm.py2023085%33–36, 42–45, 53–56, 62–65, 88, 161–162, 167, 171, 175, 180, 184, 188, 196, 202, 207, 216, 221, 226, 235, 244–251
   stringcase.py25388%59, 76, 97
   undefined.py143299%24, 38
   utils.py1283672%11–24, 44–49, 60–64, 74, 99–100, 108–109, 124–132, 158, 177, 202
tests
   entities.py226299%229, 235
   test_annotations.py814248%50–67, 78–102, 106–122
   test_api.py142497%88, 99, 139–140
   test_str_subclass.py22195%9
   test_union.py981090%87–94, 108–115
TOTAL248614494% 

Tests Skipped Failures Errors Time
292 3 💤 0 ❌ 0 🔥 4.028s ⏱️

@github-actions
Copy link

Choose a reason for hiding this comment

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

Coverage

Coverage Report
FileStmtsMissCoverMissing
dataclasses_json
   cfg.py51492%80, 84–86
   core.py234996%38–41, 51, 64, 66, 81, 83, 169
   mm.py2012986%33–36, 42–45, 53–56, 62–65, 88, 161–162, 167, 171, 175, 180, 184, 188, 196, 202, 207, 216, 221, 226, 244–251
   stringcase.py25388%59, 76, 97
   undefined.py143299%24, 38
   utils.py1282977%11–24, 44–49, 60–64, 74, 99–100, 108–109, 158, 177, 202
tests
   entities.py226299%229, 235
   test_annotations.py814248%50–67, 78–102, 106–122
   test_api.py142299%139–140
   test_str_subclass.py22195%9
TOTAL248412395% 

Tests Skipped Failures Errors Time
292 1 💤 0 ❌ 0 🔥 4.241s ⏱️

@github-actions
Copy link

Choose a reason for hiding this comment

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

Coverage

Coverage Report
FileStmtsMissCoverMissing
dataclasses_json
   cfg.py51492%80, 84–86
   core.py2351096%38–41, 51, 64, 66, 81, 83, 169, 197
   mm.py2023085%33–36, 42–45, 53–56, 62–65, 88, 161–162, 167, 171, 175, 180, 184, 188, 196, 202, 207, 216, 221, 226, 235, 244–251
   stringcase.py25388%59, 76, 97
   undefined.py143299%24, 38
   utils.py1283672%11–24, 44–49, 60–64, 74, 99–100, 108–109, 124–132, 158, 177, 202
tests
   entities.py226299%229, 235
   test_annotations.py814248%50–67, 78–102, 106–122
   test_api.py142497%88, 99, 139–140
   test_str_subclass.py22195%9
   test_union.py981090%87–94, 108–115
TOTAL248614494% 

Tests Skipped Failures Errors Time
292 3 💤 0 ❌ 0 🔥 6.046s ⏱️

Please sign in to comment.