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

JsonSerializable.with_load() forked subclass using .from_json does not pass in forked instance. #64

Closed
mjpieters opened this issue Jul 4, 2019 · 2 comments
Labels
bug Something isn't working

Comments

@mjpieters
Copy link

mjpieters commented Jul 4, 2019

You can create a forked JsonSerializable and attach attr_getters to this fork with a single .with_load() call, and then register deserializers. But those deserializers are ignored when using .from_json() on the subclass.

Demo:

import dataclasses, datetime, jsons

defaults = {"ham": lambda: "spam"}
forked = jsons.JsonSerializable.with_load(attr_getters=defaults, fork=True)
forked.set_deserializer(
    lambda td, cls, **kwargs: datetime.timedelta(seconds=td),
    datetime.timedelta
)

@dataclasses.dataclass
class Foo(forked):
    bar: datetime.timedelta
    ham: str

Foo.from_json({"bar": 42})

The above demo leads to a traceback:

Traceback (most recent call last):
  File ".../site-packages/jsons/_load_impl.py", line 85, in load
    return deserializer(json_obj, cls, **kwargs_)
  File ".../site-packages/jsons/deserializers/default_object.py", line 38, in default_object_deserializer
    constructor_args = _get_constructor_args(obj, cls, **kwargs)
  File ".../site-packages/jsons/deserializers/default_object.py", line 69, in _get_constructor_args
    constructor_args_in_obj = {key: value for key, value in args_gen if key}
  File ".../site-packages/jsons/deserializers/default_object.py", line 69, in <dictcomp>
    constructor_args_in_obj = {key: value for key, value in args_gen if key}
  File ".../site-packages/jsons/deserializers/default_object.py", line 68, in <genexpr>
    if sig_key != 'self')
  File ".../site-packages/jsons/deserializers/default_object.py", line 84, in _get_value_for_attr
    if obj and sig_key in obj:
TypeError: argument of type 'int' is not iterable

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File ".../site-packages/jsons/classes/json_serializable.py", line 123, in from_json
    return cls.load(json_obj, **kwargs)
  File ".../site-packages/jsons/classes/json_serializable.py", line 93, in _wrapper
    return load(inst, cls_, **{**kwargs_, **kwargs})
  File ".../site-packages/jsons/_load_impl.py", line 85, in load
    return deserializer(json_obj, cls, **kwargs_)
  File ".../site-packages/jsons/deserializers/default_object.py", line 38, in default_object_deserializer
    constructor_args = _get_constructor_args(obj, cls, **kwargs)
  File ".../site-packages/jsons/deserializers/default_object.py", line 69, in _get_constructor_args
    constructor_args_in_obj = {key: value for key, value in args_gen if key}
  File ".../site-packages/jsons/deserializers/default_object.py", line 69, in <dictcomp>
    constructor_args_in_obj = {key: value for key, value in args_gen if key}
  File ".../site-packages/jsons/deserializers/default_object.py", line 68, in <genexpr>
    if sig_key != 'self')
  File ".../site-packages/jsons/deserializers/default_object.py", line 87, in _get_value_for_attr
    meta_hints, **kwargs)
  File ".../site-packages/jsons/deserializers/default_object.py", line 122, in _get_value_from_obj
    value = load(obj[sig_key], cls_, meta_hints=new_hints, **kwargs)
  File ".../site-packages/jsons/_load_impl.py", line 89, in load
    raise DeserializationError(str(err), json_obj, cls)
jsons.exceptions.DeserializationError: argument of type 'int' is not iterable

because the replacement implementation for .load() generated by with_load() does not pass in the forked instance:

@classmethod
def _wrapper(cls_, inst, **kwargs_):
return load(inst, cls_, **{**kwargs_, **kwargs})
type_ = cls.fork() if fork else cls
type_.load = _wrapper

like the original .load() implementation does:

@classmethod
def load(cls: type, json_obj: object, **kwargs) -> object:
"""
See ``jsons.load``.
:param kwargs: the keyword args are passed on to the serializer
function.
:param json_obj: the object that is loaded into an instance of `cls`.
:return: this instance in a JSON representation (dict).
"""
return load(json_obj, cls, fork_inst=cls, **kwargs)

We can work around this by creating a fork first, then using forked.with_default(..., fork_inst=forked), but is less than ideal.

@ramonhagenaars ramonhagenaars added the bug Something isn't working label Jul 4, 2019
@ramonhagenaars
Copy link
Owner

Hi Martijn,

Thanks for reporting this, it is a bug indeed.

@ramonhagenaars
Copy link
Owner

This bug has been resolved in v0.9.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants