konradhalas / dacite

Simple creation of data classes from dictionaries.
MIT License
1.72k stars 107 forks source link

Support for Enums #61

Closed larsrinn closed 4 years ago

larsrinn commented 5 years ago

Hey,

very cool library, thanks for it.

Is there any intention to add support for Enums in order let the following code work:

from dataclasses import dataclass
from enum import Enum

from dacite import from_dict

class SomeEnum(Enum):
    FIRST_VALUE = "first"
    SECOND_VALUE = "second"

@dataclass
class DataClass:
    message: str
    value: SomeEnum

data = {"message": "hello", "value": "first"}
from_dict(data_class=DataClass, data=data)

Currently this raises

dacite.exceptions.WrongTypeError: wrong type for field "value" - should be "SomeEnum" instead of "str"

But actually it should be quite easy to do the conversion to the enum along the deserialization (SomeEnum("first") yields <SomeEnum.FIRST_VALUE: 'first'>)

bibz commented 5 years ago

I am currently working around this by setting the type hooks in the configuration. As you said, instantiating an enum with one of its value element returns the enum instance we want, so the mapping here is straightforward: we use the enum name directly.

For your example, the following would work:

dacite.from_dict(
    data_class=DataClass,
    data=data,
    config=dacite.Config(type_hooks={SomeEnum: SomeEnum}),
)
stickystyle commented 5 years ago

Any idea how to make this work with enums that have int's as their values, such as the below?

class POStatusInt(Enum):
    NOT_FINALIZED = 0
    COMPLETE = 5
    NEW = 10
    AVAILABLE = 15
    IN_STOCK = 20
    OPEN = 25
larsrinn commented 5 years ago

Any idea how to make this work with enums that have int's as their values, such as the below?

IntEnum might help you: https://docs.python.org/3/library/enum.html#enum.IntEnum

@konradhalas Are you still interested in this project?

konradhalas commented 5 years ago

@larsrinn yep, sorry for a delay, vacations etc. I will try to find some time ASAP to answer questions/fix issues.

stickystyle commented 5 years ago

Thanks @larsrinn , that did the trick.

konradhalas commented 4 years ago

Hi @larsrinn thank you very much for reporting this issue and sorry for waiting so long.

I understand your point of view and I'm also using enums a lot, but TBH I don't think we should add such special case to dacite. If we add enums why do not support e.g. Decimal?

You have 2 possible solutions:

(BTW: I think that type_hooks={X: X} is so common solution that I will add new parameter, eg. cast=[X] which will be equal to type_hooks={X: X})

antonagestam commented 4 years ago

@konradhalas Would you be open to implementing some sort of plugin system that could support Enums? In my opinion it would be great to support Decimal too, but I see how it seems like an intense maintenance burden to support an arbitrary number of stdlib types.

antonagestam commented 4 years ago

Maybe type hooks could be extended to also work for subclasses?

class Color(enum.IntEnum):
    green = 1
    blue = 2

@dataclass
class Tinted:
    color: Color

# --

from dacite import use_own_cls, from_dict

data = {"color": 1}
tinted = from_dict(
    data_class=Tinted,
    data=data,
    config=Config(type_hooks={Enum: use_own_cls}),
)
assert tinted.color == Color.green

Would you be interested in a more generic solution like that?

konradhalas commented 4 years ago

@antonagestam it's an interesting idea.

Maybe cast (https://github.com/konradhalas/dacite#casting) should work like you described. So instead of being just a shortcut for type_hooks we can allow users to pass base classes. In that case your example could be handled by Config(cast=[Enum]).

What do you think? Does it make sense?

antonagestam commented 4 years ago

@konradhalas I think that makes sense :)

antonagestam commented 4 years ago

@konradhalas Would you be open to reviewing a PR that implements that?

konradhalas commented 4 years ago

@antonagestam it almost ready :) I hope I will push it during upcoming weekend.

antonagestam commented 4 years ago

@konradhalas Oh, very cool! Thanks for working on it! 🙌

konradhalas commented 4 years ago

@antonagestam If you are interested I implemented this feature here - https://github.com/konradhalas/dacite/pull/70