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.
Files changed (41) hide show
  1. algopy/__init__.py +42 -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 +47 -0
  8. algopy_testing/arc4.py +1222 -0
  9. algopy_testing/constants.py +17 -0
  10. algopy_testing/context.py +769 -0
  11. algopy_testing/decorators/__init__.py +0 -0
  12. algopy_testing/decorators/abimethod.py +146 -0
  13. algopy_testing/decorators/subroutine.py +9 -0
  14. algopy_testing/enums.py +39 -0
  15. algopy_testing/gtxn.py +239 -0
  16. algopy_testing/itxn.py +353 -0
  17. algopy_testing/models/__init__.py +23 -0
  18. algopy_testing/models/account.py +128 -0
  19. algopy_testing/models/application.py +72 -0
  20. algopy_testing/models/asset.py +109 -0
  21. algopy_testing/models/contract.py +69 -0
  22. algopy_testing/models/global_values.py +67 -0
  23. algopy_testing/models/gtxn.py +40 -0
  24. algopy_testing/models/itxn.py +34 -0
  25. algopy_testing/models/transactions.py +158 -0
  26. algopy_testing/models/txn.py +111 -0
  27. algopy_testing/models/unsigned_builtins.py +15 -0
  28. algopy_testing/op.py +639 -0
  29. algopy_testing/primitives/__init__.py +6 -0
  30. algopy_testing/primitives/biguint.py +147 -0
  31. algopy_testing/primitives/bytes.py +173 -0
  32. algopy_testing/primitives/string.py +67 -0
  33. algopy_testing/primitives/uint64.py +210 -0
  34. algopy_testing/py.typed +0 -0
  35. algopy_testing/state/__init__.py +4 -0
  36. algopy_testing/state/global_state.py +73 -0
  37. algopy_testing/state/local_state.py +54 -0
  38. algopy_testing/utils.py +156 -0
  39. algorand_python_testing-0.1.0.dist-info/METADATA +29 -0
  40. algorand_python_testing-0.1.0.dist-info/RECORD +41 -0
  41. 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))
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"]
@@ -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