Skip to content

Pickle enums by value instead of name (restores pre-3.11 behavior) to support dataclasses.asdict #22

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

Merged
merged 6 commits into from
May 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions construct_typed/dataclass_struct.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class DataclassMixin:
methods exists and every name can be used.
"""

__dataclass_fields__: "t.ClassVar[t.Dict[str, dataclasses.Field[t.Any]]]"

def __getitem__(self, key: str) -> t.Any:
return getattr(self, key)

Expand Down Expand Up @@ -210,7 +212,7 @@ def _decode(
value = obj[field.name]
setattr(dc, field.name, value)

return dc
return dc # type: ignore

def _encode(
self, obj: DataclassType, context: Context, path: PathType
Expand Down Expand Up @@ -269,4 +271,4 @@ def DataclassBitStruct(
TContainerMixin = DataclassMixin
TContainerBase = DataclassMixin
TStructField = csfield
sfield = csfield
sfield = csfield
14 changes: 14 additions & 0 deletions construct_typed/tenum.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ def _missing_(cls, value: t.Any) -> t.Optional[enum.Enum]:
return pseudo_member
return None # will raise the ValueError in Enum.__new__

def __reduce_ex__(self, proto: t.Any) -> t.Tuple[t.Any, ...]:
"""
Pickle enums by value instead of name (restores pre-3.11 behavior).
See https://github.com/python/cpython/pull/26658 for why this exists.
"""
return self.__class__, (self._value_,)


EnumType = t.TypeVar("EnumType", bound=EnumBase)

Expand Down Expand Up @@ -171,6 +178,13 @@ def _missing_(cls, value: t.Any) -> t.Any:
new_member.__doc__ = "missing value"
return new_member

def __reduce_ex__(self, proto: t.Any) -> t.Tuple[t.Any, ...]:
"""
Pickle enums by value instead of name (restores pre-3.11 behavior).
See https://github.com/python/cpython/pull/26658 for why this exists.
"""
return self.__class__, (self._value_,)


FlagsEnumType = t.TypeVar("FlagsEnumType", bound=FlagsEnumBase)

Expand Down
51 changes: 51 additions & 0 deletions tests/test_typed.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,32 @@ class E(enum.Enum):
assert raises(lambda: cst.TEnum(cs.Byte, cls)) == TypeError


def test_tenum_asdict() -> None:
# see: https://github.com/timrid/construct-typing/issues/21
import construct_typed as cst
import dataclasses

class TestEnum(cst.EnumBase):
one = 1
two = 2
four = 4
eight = 8

@dataclasses.dataclass
class SomeDataclass:
a: TestEnum

dc = SomeDataclass(TestEnum.one)
dc_dict = dataclasses.asdict(dc)
assert dc_dict["a"] == dc.a
assert dc_dict["a"] is dc.a

dc = SomeDataclass(TestEnum(5))
dc_dict = dataclasses.asdict(dc)
assert dc_dict["a"] == dc.a
assert dc_dict["a"] is dc.a


def test_tenum_docstring() -> None:
class TestEnum(cst.EnumBase):
"""
Expand Down Expand Up @@ -472,6 +498,31 @@ class TestEnum(cst.FlagsEnumBase):
assert raises(d.build, 2) == TypeError


def test_tenum_flags_asdict() -> None:
import construct_typed as cst
import dataclasses

class TestEnum(cst.FlagsEnumBase):
one = 1
two = 2
four = 4
eight = 8

@dataclasses.dataclass
class SomeDataclass:
a: TestEnum

dc = SomeDataclass(TestEnum.one)
dc_dict = dataclasses.asdict(dc)
assert dc_dict["a"] == dc.a
assert dc_dict["a"] is dc.a

dc = SomeDataclass(TestEnum(5))
dc_dict = dataclasses.asdict(dc)
assert dc_dict["a"] == dc.a
assert dc_dict["a"] is dc.a


def test_tenum_flags_docstring() -> None:
class TestEnum(cst.FlagsEnumBase):
"""
Expand Down