lidatong / dataclasses-json

Easily serialize Data Classes to and from JSON
MIT License
1.36k stars 153 forks source link

[BUG] enum.IntFlag leads to RecursionError #445

Closed powellnorma closed 1 year ago

powellnorma commented 1 year ago

Description

  File "/test_venv/lib64/python3.11/site-packages/dataclasses_json/core.py", line 393, in <genexpr>
    return list(_asdict(v, encode_json=encode_json) for v in obj)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/test_venv/lib64/python3.11/site-packages/dataclasses_json/core.py", line 393, in _asdict
    return list(_asdict(v, encode_json=encode_json) for v in obj)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/test_venv/lib64/python3.11/site-packages/dataclasses_json/core.py", line 393, in <genexpr>
    return list(_asdict(v, encode_json=encode_json) for v in obj)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/test_venv/lib64/python3.11/site-packages/dataclasses_json/core.py", line 387, in _asdict
    elif isinstance(obj, Mapping):
         ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.11/typing.py", line 1292, in __instancecheck__
    return self.__subclasscheck__(type(obj))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.11/typing.py", line 1570, in __subclasscheck__
    return issubclass(cls, self.__origin__)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen abc>", line 123, in __subclasscheck__
RecursionError: maximum recursion depth exceeded in comparison

Code snippet that reproduces the issue

import enum
from dataclasses import dataclass
from dataclasses_json import DataClassJsonMixin

class MyFlag(enum.IntFlag):
#class MyFlag(enum.IntEnum):  # Works
    A = enum.auto()
    C = enum.auto()

@dataclass
class State(DataClassJsonMixin):
    #flag: MyFlag  # fails too
    flag: int

s = State(MyFlag.A)
print(s.to_dict())

Describe the results you expected

Should have worked

Python version you are using

Python 3.11.3

Environment description

dataclasses-json==0.5.13 marshmallow==3.20.1 mypy-extensions==1.0.0 packaging==23.1 typing-inspect==0.9.0 typing_extensions==4.7.1

powellnorma commented 1 year ago

I think this might be fixable by replacing: https://github.com/lidatong/dataclasses-json/blob/5a1174cec19837219ead1dd87c335f1ee235ff58/dataclasses_json/core.py#L391-L392

with something like:

    elif isinstance(obj, Collection) and not isinstance(obj, (str, bytes, int)):
matt035343 commented 1 year ago

Thanks for pointing this out! I think a better (generic) solution is to test against the Enum class, not to integers specifically (because ints are not collections).

I will have a look later this week.

matt035343 commented 1 year ago

Apparently, isinstance(<some instance member of IntFlag or Flag>, Collection) returns True in Python 3.11, but not in 3.9 or 3.8. (Have not tested 3.10). I have opened a PR fixing this #447.

george-zubrienko commented 1 year ago

To be released in 0.5.14 end of this week