ethereum.base_types
Integer and array types which are used by—but not unique to—Ethereum.
Uint
represents non-negative integers of arbitrary size, while subclasses
of FixedUint
(like U256
or U32
) represent non-negative integers of
particular sizes.
Similarly, Bytes
represents arbitrarily long byte sequences, while
subclasses of FixedBytes
(like Bytes0
or Bytes64
) represent
sequences containing an exact number of bytes.
SlottedFreezable
A Protocol
implemented by data classes annotated with
@slotted_freezable
.
36 | @runtime_checkable |
---|
class SlottedFreezable:
_frozen
46 | _frozen: bool |
---|
U255_CEIL_VALUE
Smallest value that requires 256 bits to represent. Mostly used in signed
arithmetic operations, like sdiv
.
49 | U255_CEIL_VALUE = 2**255 |
---|
U256_CEIL_VALUE
Smallest value that requires 257 bits to represent. Used when converting a
U256
in two's complement format to a regular int
in U256.to_signed
.
57 | U256_CEIL_VALUE = 2**256 |
---|
Uint
Unsigned integer of arbitrary size.
class Uint:
__slots__
72 | __slots__ = () |
---|
from_be_bytes
Converts a sequence of bytes into an arbitrarily sized unsigned integer from its big endian representation.
from_le_bytes
Converts a sequence of bytes into an arbitrarily sized unsigned integer from its little endian representation.
__init__
def __init__(self, value: int) -> None:
91 | if not isinstance(value, int): |
---|---|
92 | raise TypeError() |
93 | |
94 | if value < 0: |
95 | raise OverflowError() |
__radd__
def __radd__(self, left: int) -> "Uint":
98 | return self.__add__(left) |
---|
__add__
def __add__(self, right: int) -> "Uint":
101 | if not isinstance(right, int): |
---|---|
102 | return NotImplemented |
103 | |
104 | if right < 0: |
105 | raise OverflowError() |
106 | |
107 | return int.__new__(self.__class__, int.__add__(self, right)) |
__iadd__
def __iadd__(self, right: int) -> "Uint":
110 | return self.__add__(right) |
---|
__sub__
def __sub__(self, right: int) -> "Uint":
113 | if not isinstance(right, int): |
---|---|
114 | return NotImplemented |
115 | |
116 | if right < 0 or self < right: |
117 | raise OverflowError() |
118 | |
119 | return int.__new__(self.__class__, int.__sub__(self, right)) |
__rsub__
def __rsub__(self, left: int) -> "Uint":
122 | if not isinstance(left, int): |
---|---|
123 | return NotImplemented |
124 | |
125 | if left < 0 or self > left: |
126 | raise OverflowError() |
127 | |
128 | return int.__new__(self.__class__, int.__rsub__(self, left)) |
__isub__
def __isub__(self, right: int) -> "Uint":
131 | return self.__sub__(right) |
---|
__mul__
def __mul__(self, right: int) -> "Uint":
134 | if not isinstance(right, int): |
---|---|
135 | return NotImplemented |
136 | |
137 | if right < 0: |
138 | raise OverflowError() |
139 | |
140 | return int.__new__(self.__class__, int.__mul__(self, right)) |
__rmul__
def __rmul__(self, left: int) -> "Uint":
143 | return self.__mul__(left) |
---|
__imul__
def __imul__(self, right: int) -> "Uint":
146 | return self.__mul__(right) |
---|
__floordiv__
def __floordiv__(self, right: int) -> "Uint":
152 | if not isinstance(right, int): |
---|---|
153 | return NotImplemented |
154 | |
155 | if right < 0: |
156 | raise OverflowError() |
157 | |
158 | return int.__new__(self.__class__, int.__floordiv__(self, right)) |
__rfloordiv__
def __rfloordiv__(self, left: int) -> "Uint":
161 | if not isinstance(left, int): |
---|---|
162 | return NotImplemented |
163 | |
164 | if left < 0: |
165 | raise OverflowError() |
166 | |
167 | return int.__new__(self.__class__, int.__rfloordiv__(self, left)) |
__ifloordiv__
def __ifloordiv__(self, right: int) -> "Uint":
170 | return self.__floordiv__(right) |
---|
__mod__
def __mod__(self, right: int) -> "Uint":
173 | if not isinstance(right, int): |
---|---|
174 | return NotImplemented |
175 | |
176 | if right < 0: |
177 | raise OverflowError() |
178 | |
179 | return int.__new__(self.__class__, int.__mod__(self, right)) |
__rmod__
def __rmod__(self, left: int) -> "Uint":
182 | if not isinstance(left, int): |
---|---|
183 | return NotImplemented |
184 | |
185 | if left < 0: |
186 | raise OverflowError() |
187 | |
188 | return int.__new__(self.__class__, int.__rmod__(self, left)) |
__imod__
def __imod__(self, right: int) -> "Uint":
191 | return self.__mod__(right) |
---|
__divmod__
def __divmod__(self, right: int) -> Tuple["Uint", "Uint"]:
194 | if not isinstance(right, int): |
---|---|
195 | return NotImplemented |
196 | |
197 | if right < 0: |
198 | raise OverflowError() |
199 | |
200 | result = int.__divmod__(self, right) |
201 | return ( |
202 | int.__new__(self.__class__, result[0]), |
203 | int.__new__(self.__class__, result[1]), |
204 | ) |
__rdivmod__
def __rdivmod__(self, left: int) -> Tuple["Uint", "Uint"]:
207 | if not isinstance(left, int): |
---|---|
208 | return NotImplemented |
209 | |
210 | if left < 0: |
211 | raise OverflowError() |
212 | |
213 | result = int.__rdivmod__(self, left) |
214 | return ( |
215 | int.__new__(self.__class__, result[0]), |
216 | int.__new__(self.__class__, result[1]), |
217 | ) |
__pow__
def __pow__(self, right: int, modulo: Optional[int]) -> "Uint":
222 | if modulo is not None: |
---|---|
223 | if not isinstance(modulo, int): |
224 | return NotImplemented |
225 |
|
226 | if modulo < 0: |
227 | raise OverflowError() |
228 | |
229 | if not isinstance(right, int): |
230 | return NotImplemented |
231 | |
232 | if right < 0: |
233 | raise OverflowError() |
234 | |
235 | return int.__new__(self.__class__, int.__pow__(self, right, modulo)) |
__rpow__
def __rpow__(self, left: int, modulo: Optional[int]) -> "Uint":
240 | if modulo is not None: |
---|---|
241 | if not isinstance(modulo, int): |
242 | return NotImplemented |
243 |
|
244 | if modulo < 0: |
245 | raise OverflowError() |
246 | |
247 | if not isinstance(left, int): |
248 | return NotImplemented |
249 | |
250 | if left < 0: |
251 | raise OverflowError() |
252 | |
253 | return int.__new__(self.__class__, int.__rpow__(self, left, modulo)) |
__ipow__
def __ipow__(self, right: int, modulo: Optional[int]) -> "Uint":
258 | return self.__pow__(right, modulo) |
---|
__xor__
def __xor__(self, right: int) -> "Uint":
261 | if not isinstance(right, int): |
---|---|
262 | return NotImplemented |
263 | |
264 | if right < 0: |
265 | raise OverflowError() |
266 | |
267 | return int.__new__(self.__class__, int.__xor__(self, right)) |
__rxor__
def __rxor__(self, left: int) -> "Uint":
270 | if not isinstance(left, int): |
---|---|
271 | return NotImplemented |
272 | |
273 | if left < 0: |
274 | raise OverflowError() |
275 | |
276 | return int.__new__(self.__class__, int.__rxor__(self, left)) |
__ixor__
def __ixor__(self, right: int) -> "Uint":
279 | return self.__xor__(right) |
---|
to_be_bytes32
Converts this arbitrarily sized unsigned integer into its big endian representation with exactly 32 bytes.
to_be_bytes
Converts this arbitrarily sized unsigned integer into its big endian representation, without padding.
def to_be_bytes(self) -> "Bytes":
291 | """ |
---|---|
292 | Converts this arbitrarily sized unsigned integer into its big endian |
293 | representation, without padding. |
294 | """ |
295 | bit_length = self.bit_length() |
296 | byte_length = (bit_length + 7) // 8 |
297 | return self.to_bytes(byte_length, "big") |
to_le_bytes
Converts this arbitrarily sized unsigned integer into its little endian representation, without padding.
def to_le_bytes(self, number_bytes: Optional[int]) -> "Bytes":
300 | """ |
---|---|
301 | Converts this arbitrarily sized unsigned integer into its little endian |
302 | representation, without padding. |
303 | """ |
304 | if number_bytes is None: |
305 | bit_length = self.bit_length() |
306 | number_bytes = (bit_length + 7) // 8 |
307 | return self.to_bytes(number_bytes, "little") |
T
310 | T = TypeVar("T", bound="FixedUint") |
---|
FixedUint
Superclass for fixed size unsigned integers. Not intended to be used directly, but rather to be subclassed.
class FixedUint:
MAX_VALUE
319 | MAX_VALUE: ClassVar["FixedUint"] |
---|
__slots__
324 | __slots__ = () |
---|
__init__
def __init__(self: T, value: int) -> None:
327 | if not isinstance(value, int): |
---|---|
328 | raise TypeError() |
329 | |
330 | if value < 0 or value > self.MAX_VALUE: |
331 | raise OverflowError() |
__radd__
__add__
wrapping_add
Return a new instance containing self + right (mod N)
.
Passing a right
value greater than MAX_VALUE
or less than zero
will raise a ValueError
, even if the result would fit in this integer
type.
def wrapping_add(self: T, right: int) -> T:
348 | """ |
---|---|
349 | Return a new instance containing `self + right (mod N)`. |
350 |
|
351 | Passing a `right` value greater than [`MAX_VALUE`] or less than zero |
352 | will raise a `ValueError`, even if the result would fit in this integer |
353 | type. |
354 |
|
355 | [`MAX_VALUE`]: ref:ethereum.base_types.FixedUint.MAX_VALUE |
356 | """ |
357 | if not isinstance(right, int): |
358 | return NotImplemented |
359 | |
360 | if right < 0 or right > self.MAX_VALUE: |
361 | raise OverflowError() |
362 | |
363 | # This is a fast way of ensuring that the result is < (2 ** 256) |
364 | return int.__new__( |
365 | self.__class__, int.__add__(self, right) & self.MAX_VALUE |
366 | ) |
__iadd__
__sub__
wrapping_sub
Return a new instance containing self - right (mod N)
.
Passing a right
value greater than MAX_VALUE
or less than zero
will raise a ValueError
, even if the result would fit in this integer
type.
def wrapping_sub(self: T, right: int) -> T:
381 | """ |
---|---|
382 | Return a new instance containing `self - right (mod N)`. |
383 |
|
384 | Passing a `right` value greater than [`MAX_VALUE`] or less than zero |
385 | will raise a `ValueError`, even if the result would fit in this integer |
386 | type. |
387 |
|
388 | [`MAX_VALUE`]: ref:ethereum.base_types.FixedUint.MAX_VALUE |
389 | """ |
390 | if not isinstance(right, int): |
391 | return NotImplemented |
392 | |
393 | if right < 0 or right > self.MAX_VALUE: |
394 | raise OverflowError() |
395 | |
396 | # This is a fast way of ensuring that the result is < (2 ** 256) |
397 | return int.__new__( |
398 | self.__class__, int.__sub__(self, right) & self.MAX_VALUE |
399 | ) |
__rsub__
__isub__
__mul__
wrapping_mul
Return a new instance containing self * right (mod N)
.
Passing a right
value greater than MAX_VALUE
or less than zero
will raise a ValueError
, even if the result would fit in this integer
type.
def wrapping_mul(self: T, right: int) -> T:
425 | """ |
---|---|
426 | Return a new instance containing `self * right (mod N)`. |
427 |
|
428 | Passing a `right` value greater than [`MAX_VALUE`] or less than zero |
429 | will raise a `ValueError`, even if the result would fit in this integer |
430 | type. |
431 |
|
432 | [`MAX_VALUE`]: ref:ethereum.base_types.FixedUint.MAX_VALUE |
433 | """ |
434 | if not isinstance(right, int): |
435 | return NotImplemented |
436 | |
437 | if right < 0 or right > self.MAX_VALUE: |
438 | raise OverflowError() |
439 | |
440 | # This is a fast way of ensuring that the result is < (2 ** 256) |
441 | return int.__new__( |
442 | self.__class__, int.__mul__(self, right) & self.MAX_VALUE |
443 | ) |
__rmul__
__imul__
__floordiv__
__rfloordiv__
__ifloordiv__
__mod__
__rmod__
__imod__
__divmod__
def __divmod__(self: T, right: int) -> Tuple[T, T]:
497 | if not isinstance(right, int): |
---|---|
498 | return NotImplemented |
499 | |
500 | if right < 0 or right > self.MAX_VALUE: |
501 | raise OverflowError() |
502 | |
503 | result = super(FixedUint, self).__divmod__(right) |
504 | return ( |
505 | int.__new__(self.__class__, result[0]), |
506 | int.__new__(self.__class__, result[1]), |
507 | ) |
__rdivmod__
def __rdivmod__(self: T, left: int) -> Tuple[T, T]:
510 | if not isinstance(left, int): |
---|---|
511 | return NotImplemented |
512 | |
513 | if left < 0 or left > self.MAX_VALUE: |
514 | raise OverflowError() |
515 | |
516 | result = super(FixedUint, self).__rdivmod__(left) |
517 | return ( |
518 | int.__new__(self.__class__, result[0]), |
519 | int.__new__(self.__class__, result[1]), |
520 | ) |
__pow__
def __pow__(self: T, right: int, modulo: Optional[int]) -> T:
525 | if modulo is not None: |
---|---|
526 | if not isinstance(modulo, int): |
527 | return NotImplemented |
528 |
|
529 | if modulo < 0 or modulo > self.MAX_VALUE: |
530 | raise OverflowError() |
531 | |
532 | if not isinstance(right, int): |
533 | return NotImplemented |
534 | |
535 | result = int.__pow__(self, right, modulo) |
536 | |
537 | if right < 0 or right > self.MAX_VALUE or result > self.MAX_VALUE: |
538 | raise OverflowError() |
539 | |
540 | return int.__new__(self.__class__, result) |
wrapping_pow
Return a new instance containing self ** right (mod modulo)
.
If omitted, modulo
defaults to Uint(self.MAX_VALUE) + 1
.
Passing a right
or modulo
value greater than MAX_VALUE
or
less than zero will raise a ValueError
, even if the result would fit
in this integer type.
def wrapping_pow(self: T, right: int, modulo: Optional[int]) -> T:
543 | """ |
---|---|
544 | Return a new instance containing `self ** right (mod modulo)`. |
545 |
|
546 | If omitted, `modulo` defaults to `Uint(self.MAX_VALUE) + 1`. |
547 |
|
548 | Passing a `right` or `modulo` value greater than [`MAX_VALUE`] or |
549 | less than zero will raise a `ValueError`, even if the result would fit |
550 | in this integer type. |
551 |
|
552 | [`MAX_VALUE`]: ref:ethereum.base_types.FixedUint.MAX_VALUE |
553 | """ |
554 | if modulo is not None: |
555 | if not isinstance(modulo, int): |
556 | return NotImplemented |
557 |
|
558 | if modulo < 0 or modulo > self.MAX_VALUE: |
559 | raise OverflowError() |
560 | |
561 | if not isinstance(right, int): |
562 | return NotImplemented |
563 | |
564 | if right < 0 or right > self.MAX_VALUE: |
565 | raise OverflowError() |
566 | |
567 | # This is a fast way of ensuring that the result is < (2 ** 256) |
568 | return int.__new__( |
569 | self.__class__, int.__pow__(self, right, modulo) & self.MAX_VALUE |
570 | ) |
__rpow__
def __rpow__(self: T, left: int, modulo: Optional[int]) -> T:
575 | if modulo is not None: |
---|---|
576 | if not isinstance(modulo, int): |
577 | return NotImplemented |
578 |
|
579 | if modulo < 0 or modulo > self.MAX_VALUE: |
580 | raise OverflowError() |
581 | |
582 | if not isinstance(left, int): |
583 | return NotImplemented |
584 | |
585 | if left < 0 or left > self.MAX_VALUE: |
586 | raise OverflowError() |
587 | |
588 | return int.__new__(self.__class__, int.__rpow__(self, left, modulo)) |
__ipow__
__and__
__or__
__xor__
__rxor__
__ixor__
__invert__
__rshift__
to_be_bytes
Converts this unsigned integer into its big endian representation, omitting leading zero bytes.
def to_be_bytes(self) -> "Bytes":
645 | """ |
---|---|
646 | Converts this unsigned integer into its big endian representation, |
647 | omitting leading zero bytes. |
648 | """ |
649 | bit_length = self.bit_length() |
650 | byte_length = (bit_length + 7) // 8 |
651 | return self.to_bytes(byte_length, "big") |
from_be_bytes
Converts a sequence of bytes into a fixed sized unsigned integer from its big endian representation.
655 | @classmethod |
---|
def from_be_bytes(cls: Type[T], buffer: "Bytes") -> T:
657 | """ |
---|---|
658 | Converts a sequence of bytes into a fixed sized unsigned integer |
659 | from its big endian representation. |
660 | """ |
661 | max_length = (7 + cls.MAX_VALUE.bit_length()) // 8 |
662 | if len(buffer) > max_length: |
663 | raise ValueError() |
664 | |
665 | return cls(int.from_bytes(buffer, "big")) |
U256
Unsigned integer, which can represent 0
to 2 ** 256 - 1
, inclusive.
class U256:
MAX_VALUE
673 | MAX_VALUE: ClassVar["U256"] |
---|
__slots__
678 | __slots__ = () |
---|
from_signed
Creates an unsigned integer representing value
using two's
complement.
680 | @classmethod |
---|
def from_signed(cls: Type, value: int) -> "U256":
682 | """ |
---|---|
683 | Creates an unsigned integer representing `value` using two's |
684 | complement. |
685 | """ |
686 | if value >= 0: |
687 | return cls(value) |
688 | |
689 | return cls(value & cls.MAX_VALUE) |
to_be_bytes32
Converts this 256-bit unsigned integer into its big endian representation with exactly 32 bytes.
to_signed
Decodes a signed integer from its two's complement representation.
def to_signed(self) -> int:
699 | """ |
---|---|
700 | Decodes a signed integer from its two's complement representation. |
701 | """ |
702 | if self.bit_length() < 256: |
703 | # This means that the sign bit is 0 |
704 | return int(self) |
705 | |
706 | # -1 * (2's complement of U256 value) |
707 | return int(self) - U256_CEIL_VALUE |
U256.MAX_VALUE
710 | U256.MAX_VALUE = int.__new__(U256, (2**256) - 1) |
---|
U32
Unsigned positive integer, which can represent 0
to 2 ** 32 - 1
,
inclusive.
class U32:
MAX_VALUE
719 | MAX_VALUE: ClassVar["U32"] |
---|
__slots__
724 | __slots__ = () |
---|
from_le_bytes
Converts a sequence of bytes into an arbitrarily sized unsigned integer from its little endian representation.
726 | @classmethod |
---|
to_le_bytes4
Converts this fixed sized unsigned integer into its little endian representation, with exactly 4 bytes.
to_le_bytes
Converts this fixed sized unsigned integer into its little endian representation, in the fewest bytes possible.
def to_le_bytes(self) -> "Bytes":
745 | """ |
---|---|
746 | Converts this fixed sized unsigned integer into its little endian |
747 | representation, in the fewest bytes possible. |
748 | """ |
749 | bit_length = self.bit_length() |
750 | byte_length = (bit_length + 7) // 8 |
751 | return self.to_bytes(byte_length, "little") |
U32.MAX_VALUE
754 | U32.MAX_VALUE = int.__new__(U32, (2**32) - 1) |
---|
U64
Unsigned positive integer, which can represent 0
to 2 ** 64 - 1
,
inclusive.
class U64:
MAX_VALUE
763 | MAX_VALUE: ClassVar["U64"] |
---|
__slots__
768 | __slots__ = () |
---|
from_le_bytes
Converts a sequence of bytes into an arbitrarily sized unsigned integer from its little endian representation.
770 | @classmethod |
---|
to_le_bytes8
Converts this fixed sized unsigned integer into its little endian representation, with exactly 8 bytes.
to_le_bytes
Converts this fixed sized unsigned integer into its little endian representation, in the fewest bytes possible.
def to_le_bytes(self) -> "Bytes":
789 | """ |
---|---|
790 | Converts this fixed sized unsigned integer into its little endian |
791 | representation, in the fewest bytes possible. |
792 | """ |
793 | bit_length = self.bit_length() |
794 | byte_length = (bit_length + 7) // 8 |
795 | return self.to_bytes(byte_length, "little") |
from_be_bytes
Converts a sequence of bytes into an unsigned 64 bit integer from its big endian representation.
U64.MAX_VALUE
809 | U64.MAX_VALUE = int.__new__(U64, (2**64) - 1) |
---|
B
812 | B = TypeVar("B", bound="FixedBytes") |
---|
FixedBytes
Superclass for fixed sized byte arrays. Not intended to be used directly, but should be subclassed.
class FixedBytes:
LENGTH
821 | LENGTH: int |
---|
__slots__
826 | __slots__ = () |
---|
__new__
Create a new instance, ensuring the result has the correct length.
def __new__(cls: Type[B], *args: Any, **kwargs: Any) -> B:
829 | """ |
---|---|
830 | Create a new instance, ensuring the result has the correct length. |
831 | """ |
832 | result = super(FixedBytes, cls).__new__(cls, *args, **kwargs) |
833 | if len(result) != cls.LENGTH: |
834 | raise ValueError( |
835 | f"expected {cls.LENGTH} bytes but got {len(result)}" |
836 | ) |
837 | return result |
Bytes0
Byte array of exactly zero elements.
class Bytes0:
LENGTH
845 | LENGTH = 0 |
---|
Bytes4
Byte array of exactly four elements.
class Bytes4:
LENGTH
856 | LENGTH = 4 |
---|
Bytes8
Byte array of exactly eight elements.
class Bytes8:
LENGTH
867 | LENGTH = 8 |
---|
Bytes20
Byte array of exactly 20 elements.
class Bytes20:
LENGTH
878 | LENGTH = 20 |
---|
Bytes32
Byte array of exactly 32 elements.
class Bytes32:
LENGTH
889 | LENGTH = 32 |
---|
Bytes48
Byte array of exactly 48 elements.
class Bytes48:
LENGTH
900 | LENGTH = 48 |
---|
Bytes64
Byte array of exactly 64 elements.
class Bytes64:
LENGTH
908 | LENGTH = 64 |
---|
Bytes96
Byte array of exactly 96 elements.
class Bytes96:
LENGTH
919 | LENGTH = 96 |
---|
Bytes256
Byte array of exactly 256 elements.
class Bytes256:
LENGTH
930 | LENGTH = 256 |
---|
Bytes
Sequence of bytes (octets) of arbitrary length.
936 | Bytes = bytes |
---|
_setattr_function
def _setattr_function(self: Any, attr: str, value: Any) -> None:
943 | if getattr(self, "_frozen", None): |
---|---|
944 | raise Exception("Mutating frozen dataclasses is not allowed.") |
945 | else: |
946 | object.__setattr__(self, attr, value) |
_delattr_function
def _delattr_function(self: Any, attr: str) -> None:
950 | if self._frozen: |
---|---|
951 | raise Exception("Mutating frozen dataclasses is not allowed.") |
952 | else: |
953 | object.__delattr__(self, attr) |
_make_init_function
def _make_init_function(f: Callable) -> Callable:
957 | def init_function(self: Any, *args: Any, **kwargs: Any) -> None: |
---|---|
958 | will_be_frozen = kwargs.pop("_frozen", True) |
959 | object.__setattr__(self, "_frozen", False) |
960 | f(self, *args, **kwargs) |
961 | self._frozen = will_be_frozen |
962 | |
963 | return init_function |
slotted_freezable
Monkey patches a dataclass so it can be frozen by setting _frozen
to
True
and uses __slots__
for efficiency.
Instances will be created frozen by default unless you pass _frozen=False
to __init__
.
def slotted_freezable(cls: Any) -> Any:
967 | """ |
---|---|
968 | Monkey patches a dataclass so it can be frozen by setting `_frozen` to |
969 | `True` and uses `__slots__` for efficiency. |
970 |
|
971 | Instances will be created frozen by default unless you pass `_frozen=False` |
972 | to `__init__`. |
973 | """ |
974 | cls.__slots__ = ("_frozen",) + tuple(cls.__annotations__) |
975 | cls.__init__ = _make_init_function(cls.__init__) |
976 | cls.__setattr__ = _setattr_function |
977 | cls.__delattr__ = _delattr_function |
978 | return type(cls)(cls.__name__, cls.__bases__, dict(cls.__dict__)) |
S
981 | S = TypeVar("S") |
---|
modify
Create a copy of obj
(which must be @slotted_freezable
), and modify
it by applying f
. The returned copy will be frozen.
def modify(obj: S, f: Callable[[S], None]) -> S:
985 | """ |
---|---|
986 | Create a copy of `obj` (which must be [`@slotted_freezable`]), and modify |
987 | it by applying `f`. The returned copy will be frozen. |
988 |
|
989 | [`@slotted_freezable`]: ref:ethereum.base_types.slotted_freezable |
990 | """ |
991 | assert is_dataclass(obj) |
992 | assert isinstance(obj, SlottedFreezable) |
993 | new_obj = replace(obj, _frozen=False) |
994 | f(new_obj) |
995 | new_obj._frozen = True |
996 | return new_obj |