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