Skip to content

Commit

Permalink
feat: make dynamic structs pickleable with this one simple trick (#1990)
Browse files Browse the repository at this point in the history
Co-authored-by: Juliya Smith <jules@apeworx.io>
  • Loading branch information
banteg and antazoey authored Apr 4, 2024
1 parent 25b61a1 commit 4c9ea14
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 4 deletions.
8 changes: 6 additions & 2 deletions src/ape/utils/abi.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,11 +312,11 @@ def contains(struct, key):
return key in struct.__dataclass_fields__

def is_equal(struct, other) -> bool:
_len = len(other)
if not hasattr(other, "__len__"):
return NotImplemented

elif _len != len(struct):
_len = len(other)
if _len != len(struct):
return False

if hasattr(other, "items"):
Expand Down Expand Up @@ -355,6 +355,9 @@ def items(struct) -> List[Tuple]:
def values(struct) -> List[Any]:
return [x[1] for x in struct.items()]

def reduce(struct) -> tuple:
return (create_struct, (name, types, output_values))

# NOTE: Should never be "_{i}", but mypy complains and we need a unique value
properties = [m.name or f"_{i}" for i, m in enumerate(types)]
methods = {
Expand All @@ -363,6 +366,7 @@ def values(struct) -> List[Any]:
"__setitem__": set_item,
"__contains__": contains,
"__len__": length,
"__reduce__": reduce,
"items": items,
"values": values,
}
Expand Down
51 changes: 49 additions & 2 deletions tests/functional/utils/test_abi.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import pickle
from copy import deepcopy

import pytest
from eth_pydantic_types import HexBytes
from ethpm_types.abi import EventABI, EventABIType
from ethpm_types.abi import ABIType, EventABI, EventABIType

from ape.utils.abi import LogInputABICollection
from ape.utils.abi import LogInputABICollection, create_struct


@pytest.fixture
Expand Down Expand Up @@ -79,3 +82,47 @@ def test_decoding_with_strict(collection, topics, log_data_missing_trailing_zero
"stakingLimit": 0,
}
assert actual == expected


class TestStruct:
@pytest.fixture
def struct(self):
return create_struct(
"MyStruct", (ABIType(name="proptest", type="string"),), ("output_value_0",)
)

def test_get_and_set_item(self, struct):
assert struct["proptest"] == "output_value_0"
struct["proptest"] = "something else"
assert struct["proptest"] == "something else"

def test_is_equal(self, struct):
# Show struct equality works when props are the same.
assert struct == struct # even self
new_struct = deepcopy(struct)
assert struct == new_struct
# Show changing a property makes them unequal.
new_struct["proptest"] = "something else"
assert struct != new_struct
# Show testing with other types works w/o erroring.
assert struct != 47

def test_contains(self, struct):
assert "proptest" in struct

def test_len(self, struct):
assert len(struct) == 1

def test_items(self, struct):
actual = struct.items()
assert len(actual) == 1
assert actual[0] == ("proptest", "output_value_0")

def test_values(self, struct):
actual = struct.values()
assert len(actual) == 1
assert actual[0] == "output_value_0"

def test_pickle(self, struct):
actual = pickle.dumps(struct)
assert isinstance(actual, bytes)

0 comments on commit 4c9ea14

Please sign in to comment.