algorand-python-testing 0.0.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. algopy/__init__.py +58 -0
  2. algopy/arc4.py +1 -0
  3. algopy/gtxn.py +1 -0
  4. algopy/itxn.py +1 -0
  5. algopy/op.py +1 -0
  6. algopy/py.typed +0 -0
  7. algopy_testing/__init__.py +55 -0
  8. algopy_testing/arc4.py +1533 -0
  9. algopy_testing/constants.py +22 -0
  10. algopy_testing/context.py +1194 -0
  11. algopy_testing/decorators/__init__.py +0 -0
  12. algopy_testing/decorators/abimethod.py +204 -0
  13. algopy_testing/decorators/baremethod.py +83 -0
  14. algopy_testing/decorators/subroutine.py +9 -0
  15. algopy_testing/enums.py +42 -0
  16. algopy_testing/gtxn.py +261 -0
  17. algopy_testing/itxn.py +665 -0
  18. algopy_testing/models/__init__.py +31 -0
  19. algopy_testing/models/account.py +128 -0
  20. algopy_testing/models/application.py +72 -0
  21. algopy_testing/models/asset.py +109 -0
  22. algopy_testing/models/block.py +34 -0
  23. algopy_testing/models/box.py +158 -0
  24. algopy_testing/models/contract.py +82 -0
  25. algopy_testing/models/gitxn.py +42 -0
  26. algopy_testing/models/global_values.py +72 -0
  27. algopy_testing/models/gtxn.py +56 -0
  28. algopy_testing/models/itxn.py +85 -0
  29. algopy_testing/models/logicsig.py +44 -0
  30. algopy_testing/models/template_variable.py +23 -0
  31. algopy_testing/models/transactions.py +158 -0
  32. algopy_testing/models/txn.py +113 -0
  33. algopy_testing/models/unsigned_builtins.py +36 -0
  34. algopy_testing/op.py +1098 -0
  35. algopy_testing/primitives/__init__.py +6 -0
  36. algopy_testing/primitives/biguint.py +148 -0
  37. algopy_testing/primitives/bytes.py +174 -0
  38. algopy_testing/primitives/string.py +68 -0
  39. algopy_testing/primitives/uint64.py +213 -0
  40. algopy_testing/protocols.py +18 -0
  41. algopy_testing/py.typed +0 -0
  42. algopy_testing/state/__init__.py +4 -0
  43. algopy_testing/state/global_state.py +73 -0
  44. algopy_testing/state/local_state.py +54 -0
  45. algopy_testing/utilities/__init__.py +3 -0
  46. algopy_testing/utilities/budget.py +23 -0
  47. algopy_testing/utilities/log.py +55 -0
  48. algopy_testing/utils.py +249 -0
  49. algorand_python_testing-0.0.0b1.dist-info/METADATA +81 -0
  50. algorand_python_testing-0.0.0b1.dist-info/RECORD +52 -0
  51. algorand_python_testing-0.0.0b1.dist-info/WHEEL +4 -0
  52. algorand_python_testing-0.0.0b1.dist-info/licenses/LICENSE +14 -0
@@ -0,0 +1,6 @@
1
+ from algopy_testing.primitives.biguint import BigUInt
2
+ from algopy_testing.primitives.bytes import Bytes
3
+ from algopy_testing.primitives.string import String
4
+ from algopy_testing.primitives.uint64 import UInt64
5
+
6
+ __all__ = ["BigUInt", "Bytes", "String", "UInt64"]
@@ -0,0 +1,148 @@
1
+ from __future__ import annotations
2
+
3
+ import functools
4
+
5
+ from algopy_testing.constants import UINT64_BYTES_LENGTH
6
+ from algopy_testing.primitives.bytes import Bytes
7
+ from algopy_testing.primitives.uint64 import UInt64
8
+ from algopy_testing.protocols import BytesBacked
9
+ from algopy_testing.utils import as_bytes, as_int, as_int512, int_to_bytes
10
+
11
+ # TypeError, ValueError are used for operations that are compile time errors
12
+ # ArithmeticError and subclasses are used for operations that would fail during AVM execution
13
+
14
+
15
+ @functools.total_ordering
16
+ class BigUInt(BytesBacked):
17
+ """
18
+ A python implementation of an TEAL bigint type represented by AVM []byte type
19
+ """
20
+
21
+ __value: bytes # underlying 'bytes' value representing the BigUInt
22
+
23
+ def __init__(self, value: UInt64 | int = 0) -> None:
24
+ self.__value = (
25
+ _int_to_bytes(value.value, UINT64_BYTES_LENGTH)
26
+ if isinstance(value, UInt64)
27
+ else _int_to_bytes(value)
28
+ )
29
+
30
+ def __repr__(self) -> str:
31
+ return f"{self.value}u"
32
+
33
+ def __str__(self) -> str:
34
+ return str(self.value)
35
+
36
+ # comparison operations should not error when comparing any uint type
37
+ # e.g. UInt64, BigUInt, arc4.UInt*, arc4.BigUint*
38
+ # however will raise a TypeError if compared to something that is not a numeric value
39
+ # as this would be a compile error when compiled to TEAL
40
+ def __eq__(self, other: object) -> bool:
41
+ return as_int512(self) == as_int512(other)
42
+
43
+ def __lt__(self, other: BigUInt | UInt64 | int) -> bool:
44
+ return as_int512(self) < as_int512(other)
45
+
46
+ def __bool__(self) -> bool:
47
+ return bool(as_int512(self))
48
+
49
+ def __add__(self, other: BigUInt | UInt64 | int) -> BigUInt:
50
+ return _checked_result(as_int512(self) + as_int512(other), "+")
51
+
52
+ # the reflected dunder methods (__radd__, __rsub___, etc.) should only be called when the
53
+ # BigUInt is on the right hand side and UInt64 or int on the left
54
+ def __radd__(self, other: int | UInt64) -> BigUInt:
55
+ return self + other
56
+
57
+ def __sub__(self, other: BigUInt | UInt64 | int) -> BigUInt:
58
+ return _checked_result(as_int512(self) - as_int512(other), "-")
59
+
60
+ def __rsub__(self, other: int | UInt64) -> BigUInt:
61
+ return _as_biguint(other) - self
62
+
63
+ def __mul__(self, other: BigUInt | UInt64 | int) -> BigUInt:
64
+ return _checked_result(as_int512(self) * as_int512(other), "*")
65
+
66
+ def __rmul__(self, other: int | UInt64) -> BigUInt:
67
+ return self * other
68
+
69
+ def __floordiv__(self, denominator: BigUInt | UInt64 | int) -> BigUInt:
70
+ return _checked_result(as_int512(self) // as_int512(denominator), "//")
71
+
72
+ def __rfloordiv__(self, numerator: int | UInt64) -> BigUInt:
73
+ return _as_biguint(numerator) // self
74
+
75
+ def __mod__(self, denominator: BigUInt | UInt64 | int) -> BigUInt:
76
+ return _checked_result(as_int512(self) % as_int512(denominator), "%")
77
+
78
+ def __rmod__(self, numerator: int | UInt64) -> BigUInt:
79
+ return _as_biguint(numerator) % self
80
+
81
+ def __and__(self, other: BigUInt | UInt64 | int) -> BigUInt:
82
+ assert as_int512(self) >= 0
83
+ assert as_int512(other) >= 0
84
+ return BigUInt.from_bytes(self.bytes & _as_biguint(other).bytes)
85
+
86
+ def __rand__(self, other: BigUInt | UInt64 | int) -> BigUInt:
87
+ return self & other
88
+
89
+ def __or__(self, other: BigUInt | UInt64 | int) -> BigUInt:
90
+ assert as_int512(self) >= 0
91
+ assert as_int512(other) >= 0
92
+ return BigUInt.from_bytes(self.bytes | _as_biguint(other).bytes)
93
+
94
+ def __ror__(self, other: BigUInt | UInt64 | int) -> BigUInt:
95
+ return self | other
96
+
97
+ def __xor__(self, other: BigUInt | UInt64 | int) -> BigUInt:
98
+ assert as_int512(self) >= 0
99
+ assert as_int512(other) >= 0
100
+ return BigUInt.from_bytes(self.bytes ^ _as_biguint(other).bytes)
101
+
102
+ def __rxor__(self, other: BigUInt | UInt64 | int) -> BigUInt:
103
+ return self ^ other
104
+
105
+ def __pos__(self) -> BigUInt:
106
+ """
107
+ Compute the unary positive of the BigUInt.
108
+
109
+ Returns:
110
+ BigUInt: The result of the unary positive operation.
111
+ """
112
+ return BigUInt(+as_int512(self))
113
+
114
+ @classmethod
115
+ def from_bytes(cls, value: Bytes | bytes) -> BigUInt:
116
+ """Construct an instance from the underlying bytes (no validation)"""
117
+ result = cls()
118
+ result.__value = as_bytes(value)
119
+ return result
120
+
121
+ @property
122
+ def bytes(self) -> Bytes:
123
+ """Get the underlying Bytes"""
124
+ return Bytes(self.__value)
125
+
126
+ @property
127
+ def value(self) -> int:
128
+ """Get the underlying int"""
129
+ return int.from_bytes(self.__value)
130
+
131
+
132
+ def _checked_result(result: int, op: str) -> BigUInt:
133
+ """Ensures `result` is a valid BigUInt value
134
+
135
+ Raises:
136
+ ArithmeticError: If `result` of `op` is negative"""
137
+ if result < 0:
138
+ raise ArithmeticError(f"{op} underflows")
139
+ return BigUInt(result)
140
+
141
+
142
+ def _as_biguint(value: object) -> BigUInt:
143
+ return BigUInt(value if (isinstance(value, UInt64)) else as_int(value, max=None))
144
+
145
+
146
+ def _int_to_bytes(x: int, pad_to: int | None = None) -> bytes:
147
+ x = as_int(x, max=None)
148
+ return int_to_bytes(x, pad_to=pad_to)
@@ -0,0 +1,174 @@
1
+ from __future__ import annotations
2
+
3
+ import base64
4
+ import operator
5
+ from typing import TYPE_CHECKING
6
+
7
+ if TYPE_CHECKING:
8
+ from collections.abc import Iterator
9
+
10
+
11
+ from itertools import zip_longest
12
+
13
+ from algopy_testing.constants import MAX_BYTES_SIZE
14
+ from algopy_testing.primitives.uint64 import UInt64
15
+ from algopy_testing.utils import as_bytes, as_int64
16
+
17
+ # TypeError, ValueError are used for operations that are compile time errors
18
+ # ArithmeticError and subclasses are used for operations that would fail during AVM execution
19
+
20
+
21
+ class Bytes:
22
+ """
23
+ A python implementation of an AVM []byte
24
+ """
25
+
26
+ value: bytes # underlying bytes value representing the []byte
27
+
28
+ def __init__(self, value: bytes = b"") -> None:
29
+ self.value = as_bytes(value)
30
+
31
+ def __repr__(self) -> str:
32
+ return repr(self.value)
33
+
34
+ def __str__(self) -> str:
35
+ return str(self.value)
36
+
37
+ def __bool__(self) -> bool:
38
+ return bool(self.value)
39
+
40
+ def __add__(self, other: Bytes | bytes) -> Bytes:
41
+ """Concatenate Bytes with another Bytes or bytes literal
42
+ e.g. `Bytes(b"Hello ") + b"World"`."""
43
+ if isinstance(other, Bytes):
44
+ return _checked_result(self.value + other.value, "+")
45
+ else:
46
+ result = self.value + as_bytes(other)
47
+ return _checked_result(result, "+")
48
+
49
+ def __radd__(self, other: bytes) -> Bytes:
50
+ """Concatenate Bytes with another Bytes or bytes literal
51
+ e.g. `b"Hello " + Bytes(b"World")`."""
52
+ return _checked_result(other + self.value, "+")
53
+
54
+ def __len__(self) -> int:
55
+ return len(self.value)
56
+
57
+ def __iter__(self) -> Iterator[Bytes]:
58
+ """Bytes can be iterated, yielding each consecutive byte"""
59
+ return _BytesIter(self, 1)
60
+
61
+ def __reversed__(self) -> Iterator[Bytes]:
62
+ """Bytes can be iterated in reverse, yield each preceding byte starting at the end"""
63
+ return _BytesIter(self, -1)
64
+
65
+ def __getitem__(
66
+ self, index: UInt64 | int | slice
67
+ ) -> Bytes: # maps to substring/substring3 if slice, extract/extract3 otherwise?
68
+ """Returns a Bytes containing a single byte if indexed with UInt64 or int
69
+ otherwise the substring o bytes described by the slice"""
70
+ if isinstance(index, slice):
71
+ return Bytes(self.value[index])
72
+ else:
73
+ int_index = as_int64(index)
74
+ # my_bytes[0:1] => b'j' whereas my_bytes[0] => 106
75
+ return Bytes(self.value[slice(int_index, int_index + 1)])
76
+
77
+ def __eq__(self, other: object) -> bool:
78
+ return self.value == as_bytes(other)
79
+
80
+ def __and__(self, other: bytes | Bytes) -> Bytes:
81
+ return self._operate_bitwise(other, "and_")
82
+
83
+ def __rand__(self, other: bytes) -> Bytes:
84
+ return self & other
85
+
86
+ def __or__(self, other: bytes | Bytes) -> Bytes:
87
+ return self._operate_bitwise(other, "or_")
88
+
89
+ def __ror__(self, other: bytes) -> Bytes:
90
+ return self | other
91
+
92
+ def __xor__(self, other: bytes | Bytes) -> Bytes:
93
+ return self._operate_bitwise(other, "xor")
94
+
95
+ def __rxor__(self, other: bytes) -> Bytes:
96
+ return self ^ other
97
+
98
+ def __invert__(self) -> Bytes:
99
+ """
100
+ Compute the bitwise inversion of the Bytes.
101
+
102
+ Returns:
103
+ Bytes: The result of the bitwise inversion operation.
104
+ """
105
+ return Bytes(bytes(~x + 256 for x in self.value))
106
+
107
+ def _operate_bitwise(self, other: bytes | Bytes, operator_name: str) -> Bytes:
108
+ op = getattr(operator, operator_name)
109
+ maybe_bytes = as_bytes(other)
110
+ # pad the shorter of self.value and other bytes with leading zero
111
+ # by reversing them as zip_longest fills at the end
112
+ return Bytes(
113
+ bytes(
114
+ reversed(
115
+ bytes(
116
+ op(a[0], a[1])
117
+ for a in zip_longest(
118
+ reversed(self.value), reversed(maybe_bytes), fillvalue=0
119
+ )
120
+ )
121
+ )
122
+ )
123
+ )
124
+
125
+ @property
126
+ def length(self) -> UInt64:
127
+ """Returns the length of the Bytes"""
128
+ return UInt64(len(self.value))
129
+
130
+ @staticmethod
131
+ def from_base32(value: str) -> Bytes:
132
+ """Creates Bytes from a base32 encoded string e.g. `Bytes.from_base32("74======")`"""
133
+ return Bytes(base64.b32decode(value))
134
+
135
+ @staticmethod
136
+ def from_base64(value: str) -> Bytes:
137
+ """Creates Bytes from a base64 encoded string e.g. `Bytes.from_base64("RkY=")`"""
138
+ return Bytes(base64.b64decode(value))
139
+
140
+ @staticmethod
141
+ def from_hex(value: str) -> Bytes:
142
+ """Creates Bytes from a hex/octal encoded string e.g. `Bytes.from_hex("FF")`"""
143
+ return Bytes(base64.b16decode(value))
144
+
145
+
146
+ class _BytesIter:
147
+ value: Bytes
148
+
149
+ def __init__(self, sequence: Bytes, step: int = 1):
150
+ self.value = sequence
151
+ self.current = 0 if step > 0 else len(sequence) - 1
152
+ self.step = step
153
+ self.myend = len(sequence) - 1 if step > 0 else 0
154
+
155
+ def __iter__(self) -> _BytesIter:
156
+ return self
157
+
158
+ def __next__(self) -> Bytes:
159
+ # if current is one step over the end
160
+ if self.current == self.myend + self.step:
161
+ raise StopIteration
162
+
163
+ self.current += self.step
164
+ return self.value[self.current - self.step]
165
+
166
+
167
+ def _checked_result(result: bytes, op: str) -> Bytes:
168
+ """Ensures `result` is a valid Bytes value
169
+
170
+ Raises:
171
+ ArithmeticError: If `result` of `op` is out of bounds"""
172
+ if len(result) > MAX_BYTES_SIZE:
173
+ raise OverflowError(f"{op} overflows")
174
+ return Bytes(result)
@@ -0,0 +1,68 @@
1
+ from __future__ import annotations
2
+
3
+ from algopy_testing.primitives.bytes import Bytes
4
+ from algopy_testing.protocols import BytesBacked
5
+ from algopy_testing.utils import as_bytes, as_string
6
+
7
+
8
+ class String(BytesBacked):
9
+ """
10
+ Represents a UTF-8 encoded string backed by Bytes, accessible via .bytes.
11
+
12
+ Works with str literals instead of bytes literals. Due to lack of AVM support for unicode,
13
+ indexing and length operations are not supported. Use .bytes.length for byte length.
14
+ """
15
+
16
+ _value: bytes # underlying 'bytes' value representing the String
17
+
18
+ def __init__(self, value: str = "") -> None:
19
+ if not isinstance(value, str):
20
+ raise TypeError(f"expected str, got {type(value).__name__!r}")
21
+ self._value = value.encode("utf-8")
22
+
23
+ def __repr__(self) -> str:
24
+ return repr(self.value)
25
+
26
+ def __str__(self) -> str:
27
+ return str(self.value)
28
+
29
+ def __bool__(self) -> bool:
30
+ return bool(self.value)
31
+
32
+ def __eq__(self, other: object) -> bool:
33
+ return self.value == as_string(other)
34
+
35
+ def __contains__(self, item: object) -> bool:
36
+ return as_string(item) in self.value
37
+
38
+ def __add__(self, other: String | str) -> String:
39
+ return String(self.value + as_string(other))
40
+
41
+ def __radd__(self, other: String | str) -> String:
42
+ return String(as_string(other) + self.value)
43
+
44
+ def startswith(self, prefix: String | str) -> bool:
45
+ return self.value.startswith(as_string(prefix))
46
+
47
+ def endswith(self, suffix: String | str) -> bool:
48
+ return self.value.endswith(as_string(suffix))
49
+
50
+ def join(self, others: tuple[String, ...], /) -> String:
51
+ return String(self.value.join(map(as_string, others)))
52
+
53
+ @classmethod
54
+ def from_bytes(cls, value: Bytes | bytes) -> String:
55
+ """Construct an instance from the underlying bytes (no validation)"""
56
+ value = as_bytes(value)
57
+ result = cls()
58
+ result._value = bytes(value)
59
+ return result
60
+
61
+ @property
62
+ def bytes(self) -> Bytes:
63
+ """Get the underlying Bytes"""
64
+ return Bytes(self._value)
65
+
66
+ @property
67
+ def value(self) -> str:
68
+ return self._value.decode("utf-8")
@@ -0,0 +1,213 @@
1
+ from __future__ import annotations
2
+
3
+ import functools
4
+
5
+ from algopy_testing.constants import MAX_UINT64
6
+ from algopy_testing.utils import as_int64
7
+
8
+ # TypeError, ValueError are used for operations that are compile time errors
9
+ # ArithmeticError and subclasses are used for operations that would fail during AVM execution
10
+
11
+
12
+ @functools.total_ordering
13
+ class UInt64:
14
+ """
15
+ A python implementation of an AVM 64-bit unsigned integer
16
+ """
17
+
18
+ value: int # underlying 'int' value representing the UInt64
19
+
20
+ def __init__(self, value: int = 0) -> None:
21
+ self.value = as_int64(value)
22
+
23
+ def __repr__(self) -> str:
24
+ return f"{self.value}u"
25
+
26
+ def __str__(self) -> str:
27
+ return str(self.value)
28
+
29
+ # comparison operations should not error when comparing any uint type
30
+ # e.g. UInt64, BigUInt, arc4.UInt*, arc4.BigUint*
31
+ # however will raise a TypeError if compared to something that is not a numeric value
32
+ # as this would be a compile error when compiled to TEAL
33
+ def __eq__(self, other: object) -> bool:
34
+ maybe_int = _as_maybe_uint64(other)
35
+ # returning NotImplemented here will allow BigUInt to handle larger comparisons
36
+ if maybe_int is None:
37
+ return NotImplemented
38
+ return self.value == maybe_int
39
+
40
+ def __lt__(self, other: int | UInt64) -> bool:
41
+ maybe_int = _as_maybe_uint64(other)
42
+ # returning NotImplemented here will allow BigUInt to handle larger comparisons
43
+ if maybe_int is None:
44
+ return NotImplemented
45
+ return self.value < maybe_int
46
+
47
+ def __bool__(self) -> bool:
48
+ return self.value != 0
49
+
50
+ def __add__(self, other: int | UInt64) -> UInt64:
51
+ maybe_int = _as_maybe_uint64(other)
52
+ # returning NotImplemented here will allow BigUInt (and others) to upcast
53
+ # a BigUint + UInt64 operation
54
+ if maybe_int is None:
55
+ return NotImplemented
56
+ return _checked_result(self.value + maybe_int, "+")
57
+
58
+ # the reflected dunder methods (__radd__, __rsub___, etc.) should only be called when the
59
+ # UInt64 is on the right hand side and an int on the left
60
+ def __radd__(self, other: int) -> UInt64:
61
+ return self + other
62
+
63
+ def __sub__(self, other: int | UInt64) -> UInt64:
64
+ maybe_int = _as_maybe_uint64(other)
65
+ if maybe_int is None:
66
+ return NotImplemented
67
+ return _checked_result(self.value - maybe_int, "-")
68
+
69
+ def __rsub__(self, other: int) -> UInt64:
70
+ return _as_uint64(other) - self
71
+
72
+ def __mul__(self, other: int | UInt64) -> UInt64:
73
+ maybe_int = _as_maybe_uint64(other)
74
+ if maybe_int is None:
75
+ return NotImplemented
76
+ return _checked_result(self.value * maybe_int, "*")
77
+
78
+ def __rmul__(self, other: int) -> UInt64:
79
+ return self * other
80
+
81
+ def __floordiv__(self, denominator: int | UInt64) -> UInt64:
82
+ maybe_int = _as_maybe_uint64(denominator)
83
+ if maybe_int is None:
84
+ return NotImplemented
85
+ return _checked_result(self.value // maybe_int, "//")
86
+
87
+ def __rfloordiv__(self, numerator: int) -> UInt64:
88
+ return _as_uint64(numerator) // self
89
+
90
+ def __mod__(self, denominator: int | UInt64) -> UInt64:
91
+ maybe_int = _as_maybe_uint64(denominator)
92
+ if maybe_int is None:
93
+ return NotImplemented
94
+
95
+ return _checked_result(self.value % maybe_int, "%")
96
+
97
+ def __rmod__(self, numerator: int) -> UInt64:
98
+ return _as_uint64(numerator) % self
99
+
100
+ def __pow__(self, exp: int | UInt64, modulo: int | UInt64 | None = None) -> UInt64:
101
+ exp_int = _as_maybe_uint64(exp)
102
+ if exp_int is None:
103
+ return NotImplemented
104
+ if modulo is not None:
105
+ return NotImplemented
106
+ if self.value == 0 and exp_int == 0:
107
+ raise ArithmeticError("UInt64(0)**UInt64(0) is undefined")
108
+ return _checked_result(self.value**exp_int, "**")
109
+
110
+ def __rpow__(self, base: int, modulo: int | UInt64 | None = None) -> UInt64:
111
+ return pow(_as_uint64(base), self, modulo)
112
+
113
+ def __and__(self, other: int | UInt64) -> UInt64:
114
+ maybe_int = _as_maybe_uint64(other)
115
+ if maybe_int is None:
116
+ return NotImplemented
117
+ return UInt64(self.value & maybe_int)
118
+
119
+ def __rand__(self, other: int) -> UInt64:
120
+ return self & other
121
+
122
+ def __or__(self, other: int | UInt64) -> UInt64:
123
+ maybe_int = _as_maybe_uint64(other)
124
+ if maybe_int is None:
125
+ return NotImplemented
126
+ return UInt64(self.value | maybe_int)
127
+
128
+ def __ror__(self, other: int | UInt64) -> UInt64:
129
+ return self | other
130
+
131
+ def __xor__(self, other: int | UInt64) -> UInt64:
132
+ maybe_int = _as_maybe_uint64(other)
133
+ if maybe_int is None:
134
+ return NotImplemented
135
+ return UInt64(self.value ^ maybe_int)
136
+
137
+ def __rxor__(self, other: int | UInt64) -> UInt64:
138
+ return self ^ other
139
+
140
+ def __lshift__(self, other: int | UInt64) -> UInt64:
141
+ shift = as_int64(other)
142
+ if shift > 63:
143
+ raise ArithmeticError("expected shift <= 63")
144
+ return UInt64((self.value << shift) & MAX_UINT64)
145
+
146
+ def __rlshift__(self, other: int | UInt64) -> UInt64:
147
+ return _as_uint64(other) << self
148
+
149
+ def __rshift__(self, other: int | UInt64) -> UInt64:
150
+ shift = as_int64(other)
151
+ if shift > 63:
152
+ raise ArithmeticError("expected shift <= 63")
153
+ return UInt64((self.value >> shift) & MAX_UINT64)
154
+
155
+ def __rrshift__(self, other: int | UInt64) -> UInt64:
156
+ return _as_uint64(other) >> self
157
+
158
+ def __invert__(self) -> UInt64:
159
+ """
160
+ Compute the bitwise inversion of the UInt64.
161
+
162
+ Returns:
163
+ UInt64: The result of the bitwise inversion operation.
164
+ """
165
+ return UInt64(~self.value & MAX_UINT64)
166
+
167
+ def __index__(self) -> int:
168
+ """
169
+ Return the internal integer value of the UInt64 for use in indexing/slice expressions.
170
+
171
+ Returns:
172
+ int: The internal integer value of the UInt64.
173
+ """
174
+ return self.value
175
+
176
+ def __pos__(self) -> UInt64:
177
+ """
178
+ Compute the unary positive of the UInt64.
179
+
180
+ Returns:
181
+ UInt64: The result of the unary positive operation.
182
+ """
183
+ return UInt64(+self.value)
184
+
185
+ def __hash__(self) -> int:
186
+ return hash(self.value)
187
+
188
+
189
+ def _as_maybe_uint64(value: object) -> int | None:
190
+ """Returns int value if `value` is an int or UInt64, otherwise None"""
191
+ match value:
192
+ case int(int_value):
193
+ return as_int64(int_value)
194
+ case UInt64(value=int_value):
195
+ return int_value
196
+ case _:
197
+ return None
198
+
199
+
200
+ def _checked_result(result: int, op: str) -> UInt64:
201
+ """Ensures `result` is a valid UInt64 value
202
+
203
+ Raises:
204
+ ArithmeticError: If `result` of `op` is out of bounds"""
205
+ if result < 0:
206
+ raise ArithmeticError(f"{op} underflows")
207
+ if result > MAX_UINT64:
208
+ raise OverflowError(f"{op} overflows")
209
+ return UInt64(result)
210
+
211
+
212
+ def _as_uint64(value: object) -> UInt64:
213
+ return UInt64(as_int64(value))
@@ -0,0 +1,18 @@
1
+ from __future__ import annotations
2
+
3
+ import typing
4
+
5
+ if typing.TYPE_CHECKING:
6
+ import algopy
7
+
8
+
9
+ class BytesBacked(typing.Protocol):
10
+ """Represents a type that is a single bytes value"""
11
+
12
+ @classmethod
13
+ def from_bytes(cls, value: algopy.Bytes | bytes, /) -> typing.Self:
14
+ """Construct an instance from the underlying bytes (no validation)"""
15
+
16
+ @property
17
+ def bytes(self) -> algopy.Bytes:
18
+ """Get the underlying Bytes"""
File without changes
@@ -0,0 +1,4 @@
1
+ from algopy_testing.state.global_state import GlobalState
2
+ from algopy_testing.state.local_state import LocalState
3
+
4
+ __all__ = ["GlobalState", "LocalState"]