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

Unstructuring subclasses #354

Open
AdrianSosic opened this issue Apr 14, 2023 · 2 comments
Open

Unstructuring subclasses #354

AdrianSosic opened this issue Apr 14, 2023 · 2 comments

Comments

@AdrianSosic
Copy link
Contributor

  • cattrs version: 22.2.0
  • Python version: 3.8
  • Operating System: macOS

Description

Hi @Tinche, I am currently working on several projects where I need to (un)structure objects of subclasses of a common base class. Digging through the documentation and also some issues here on Github, I eventually found your example how this can be done with the current version of cattrs. However, I think I have found two problems (bugs? unsupported features?) with the approach and would love to hear your opinion.

But before I go into the details, a quick question upfront: Is this going to remain the "recommended" approach to unstructure objects from a class hierarchy? I am asking because I also saw #312 which seems about to be merged but I am not entirely sure how/if it's going to replace your described method.

So here are now the two issues I noticed:

  1. With the version described above, your example no longer seems to work. In order to get it running, one now needs to explicitly set slotted=False. I guess this is probably due to due the changes defaults (see also Weird output while structuring unions with custom disambiguation function #267)? On the other hand, I wonder why your example then originally worked because it already uses define instead of attr.s?
  2. Still, I struggled a lot to get it running in my own code until I finally found the problem: The approach crashes when using from __future__ import annotations and throws the following exception:
Traceback (most recent call last):
  File "/usr/local/Caskroom/mambaforge/base/envs/baybe/lib/python3.8/site-packages/cattrs/converters.py", line 461, in _structure_attribute
    return self._structure_func.dispatch(type_)(value, type_)
  File "/usr/local/Caskroom/mambaforge/base/envs/baybe/lib/python3.8/site-packages/cattrs/converters.py", line 377, in _structure_error
    raise StructureHandlerNotFoundError(msg, type_=cl)
cattrs.errors.StructureHandlerNotFoundError: Unsupported type: 'int'. Register a structure hook for it.

Is the second issue related to #215 or is this a bug?

@Tinche
Copy link
Member

Tinche commented Apr 14, 2023

A little difficult to answer these without concrete examples.

I think #312 is a better bet than the old snippets since it's a more sophisticated approach, and it's a better base for experimentation.

@AdrianSosic
Copy link
Contributor Author

Thanks for your reply! The code example is really just your code + the future import:

from __future__ import annotations

from attr import define

from cattr import GenConverter

c = GenConverter()


@define
class Base:
   a: int


@define
class SubOne(Base):
   b: int


@define
class SubTwo(Base):
   c: int


def unstructure_base(b):
   return {"_type": b.__class__.__name__, **c.unstructure_attrs_asdict(b)}


c.register_unstructure_hook(Base, unstructure_base)


print(c.unstructure(SubOne(1, 2)))


def structure_base(val, _):
   sub_name = val.pop("_type")
   for klass in Base.__subclasses__():
       if klass.__name__ == sub_name:
           return c.structure_attrs_fromdict(val, klass)
   return c.structure_attrs_fromdict(val, Base)


c.register_structure_hook(Base, structure_base)

print(c.structure(c.unstructure(SubOne(1, 2)), Base))
print(c.structure(c.unstructure(SubTwo(1, 2)), Base))
print(c.structure(c.unstructure(Base(1)), Base))

However, I've now also tested with python 3.11 and there everything works as expected! So the issue seems really related to how the future imports are handled in older python versions 🤔 Nonetheless, it might be worth mentioning this somewhere in the docs once one of us figures out the source of the problem...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants