Skip to content

Commit 2ae3c3a

Browse files
authored
Merge pull request #11 from ajrgrubbs/container-access-support
Support dict-style value access
2 parents 34e87a6 + 8c90d67 commit 2ae3c3a

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed

eip712_structs/struct.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,45 @@ def from_message(cls, message_dict: dict) -> 'StructTuple':
251251

252252
return result
253253

254+
@classmethod
255+
def _assert_key_is_member(cls, key):
256+
member_names = {tup[0] for tup in cls.get_members()}
257+
if key not in member_names:
258+
raise KeyError(f'"{key}" is not defined for this struct.')
259+
260+
@classmethod
261+
def _assert_property_type(cls, key, value):
262+
"""Eagerly check for a correct member type"""
263+
members = dict(cls.get_members())
264+
typ = members[key]
265+
266+
if isinstance(typ, type) and issubclass(typ, EIP712Struct):
267+
# We expect an EIP712Struct instance. Assert that's true, and check the struct signature too.
268+
if not isinstance(value, EIP712Struct) or value._encode_type(False) != typ._encode_type(False):
269+
raise ValueError(f'Given value is of type {type(value)}, but we expected {typ}')
270+
else:
271+
# Since it isn't a nested struct, its an EIP712Type
272+
try:
273+
typ.encode_value(value)
274+
except Exception as e:
275+
raise ValueError(f'The python type {type(value)} does not appear '
276+
f'to be supported for data type {typ}.') from e
277+
278+
def __getitem__(self, key):
279+
"""Provide access directly to the underlying value dictionary"""
280+
self._assert_key_is_member(key)
281+
return self.values.__getitem__(key)
282+
283+
def __setitem__(self, key, value):
284+
"""Provide access directly to the underlying value dictionary"""
285+
self._assert_key_is_member(key)
286+
self._assert_property_type(key, value)
287+
288+
return self.values.__setitem__(key, value)
289+
290+
def __delitem__(self, _):
291+
raise TypeError('Deleting entries from an EIP712Struct is not allowed.')
292+
254293

255294
class StructTuple(NamedTuple):
256295
message: EIP712Struct

tests/test_encode_data.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,3 +187,59 @@ def test_validation_errors():
187187
bool_type.encode_value(0)
188188
with pytest.raises(ValueError, match='Must be True or False.'):
189189
bool_type.encode_value(1)
190+
191+
192+
def test_value_access():
193+
class Foo(EIP712Struct):
194+
s = String()
195+
b = Bytes(32)
196+
197+
test_str = 'hello world'
198+
test_bytes = os.urandom(32)
199+
foo = Foo(s=test_str, b=test_bytes)
200+
201+
assert foo['s'] == test_str
202+
assert foo['b'] == test_bytes
203+
204+
test_bytes_2 = os.urandom(32)
205+
foo['b'] = test_bytes_2
206+
207+
assert foo['b'] == test_bytes_2
208+
209+
with pytest.raises(KeyError):
210+
foo['x'] = 'unacceptable'
211+
212+
# Check behavior when accessing a member that wasn't defined for the struct.
213+
with pytest.raises(KeyError):
214+
foo['x']
215+
# Lets cheat a lil bit for robustness- add an invalid 'x' member to the value dict, and check the error still raises
216+
foo.values['x'] = 'test'
217+
with pytest.raises(KeyError):
218+
foo['x']
219+
foo.values.pop('x')
220+
221+
with pytest.raises(ValueError):
222+
foo['s'] = b'unacceptable'
223+
with pytest.raises(ValueError):
224+
# Bytes do accept strings, but it has to be hex formatted.
225+
foo['b'] = 'unacceptable'
226+
227+
# Test behavior when attempting to set nested structs as values
228+
class Bar(EIP712Struct):
229+
s = String()
230+
f = Foo
231+
232+
class Baz(EIP712Struct):
233+
s = String()
234+
baz = Baz(s=test_str)
235+
236+
bar = Bar(s=test_str)
237+
bar['f'] = foo
238+
assert bar['f'] == foo
239+
240+
with pytest.raises(ValueError):
241+
# Expects a Foo type, so should throw an error
242+
bar['f'] = baz
243+
244+
with pytest.raises(TypeError):
245+
del foo['s']

0 commit comments

Comments
 (0)