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,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
@@ -0,0 +1,54 @@
1
+ from __future__ import annotations
2
+
3
+ import typing
4
+ from typing import TYPE_CHECKING
5
+
6
+ if TYPE_CHECKING:
7
+ import algopy
8
+
9
+ _T = typing.TypeVar("_T")
10
+
11
+
12
+ class LocalState(typing.Generic[_T]):
13
+ def __init__(
14
+ self,
15
+ type_: type[_T],
16
+ /,
17
+ *,
18
+ key: bytes | str = "",
19
+ description: str = "",
20
+ ) -> None:
21
+ self.type_ = type_
22
+ self.key = key
23
+ self.description = description
24
+ self._state: dict[object, _T] = {}
25
+
26
+ def _validate_local_state_key(self, key: algopy.Account | algopy.UInt64 | int) -> None:
27
+ from algopy import Account, UInt64
28
+
29
+ if not isinstance(key, Account | UInt64 | int):
30
+ raise TypeError(f"Invalid key type {type(key)} for LocalState")
31
+
32
+ def __setitem__(self, key: algopy.Account | algopy.UInt64 | int, value: _T) -> None:
33
+ self._validate_local_state_key(key)
34
+ self._state[key] = value
35
+
36
+ def __getitem__(self, key: algopy.Account | algopy.UInt64 | int) -> _T:
37
+ self._validate_local_state_key(key)
38
+ return self._state[key]
39
+
40
+ def __delitem__(self, key: algopy.Account | algopy.UInt64 | int) -> None:
41
+ self._validate_local_state_key(key)
42
+ del self._state[key]
43
+
44
+ def __contains__(self, key: algopy.Account | algopy.UInt64 | int) -> bool:
45
+ self._validate_local_state_key(key)
46
+ return key in self._state
47
+
48
+ def get(self, key: algopy.Account | algopy.UInt64 | int, default: _T | None = None) -> _T:
49
+ self._validate_local_state_key(key)
50
+ return self._state.get(key, default if default is not None else self.type_())
51
+
52
+ def maybe(self, key: algopy.Account | algopy.UInt64 | int) -> tuple[object, bool]:
53
+ self._validate_local_state_key(key)
54
+ return self._state.get(key), key in self._state
@@ -0,0 +1,3 @@
1
+ from algopy_testing.utilities.budget import OpUpFeeSource, ensure_budget
2
+
3
+ __all__ = ["OpUpFeeSource", "ensure_budget", "log"]
@@ -0,0 +1,23 @@
1
+ from __future__ import annotations
2
+
3
+ from algopy_testing import UInt64
4
+
5
+
6
+ class OpUpFeeSource(UInt64):
7
+ """Defines the source of fees for the OpUp utility."""
8
+
9
+ GroupCredit: OpUpFeeSource
10
+ AppAccount: OpUpFeeSource
11
+ Any: OpUpFeeSource
12
+
13
+
14
+ OpUpFeeSource.GroupCredit = OpUpFeeSource(0)
15
+ OpUpFeeSource.AppAccount = OpUpFeeSource(1)
16
+ OpUpFeeSource.Any = OpUpFeeSource(2)
17
+
18
+
19
+ def ensure_budget(
20
+ required_budget: UInt64 | int, # noqa: ARG001
21
+ fee_source: OpUpFeeSource = OpUpFeeSource.GroupCredit, # noqa: ARG001
22
+ ) -> None:
23
+ pass
@@ -0,0 +1,55 @@
1
+ from algopy_testing.primitives.bytes import Bytes
2
+ from algopy_testing.primitives.uint64 import UInt64
3
+ from algopy_testing.protocols import BytesBacked
4
+ from algopy_testing.utils import int_to_bytes
5
+
6
+
7
+ def log( # noqa: C901, PLR0912
8
+ *args: UInt64 | Bytes | BytesBacked | str | bytes | int,
9
+ sep: Bytes | bytes | str = b"",
10
+ ) -> None:
11
+ """Concatenates and logs supplied args as a single bytes value.
12
+
13
+ UInt64 args are converted to bytes and each argument is separated by `sep`.
14
+ Literal `str` values will be encoded as UTF8.
15
+ """
16
+ import algopy
17
+
18
+ from algopy_testing.context import get_test_context
19
+
20
+ context = get_test_context()
21
+ logs: list[bytes] = []
22
+
23
+ for arg in args:
24
+ if isinstance(arg, UInt64):
25
+ logs.append(int_to_bytes(arg.value))
26
+ elif isinstance(arg, Bytes):
27
+ logs.append(arg.value)
28
+ elif isinstance(arg, str):
29
+ logs.append(arg.encode("utf8"))
30
+ elif isinstance(arg, bytes):
31
+ logs.append(arg)
32
+ elif isinstance(arg, int):
33
+ logs.append(int_to_bytes(arg))
34
+ else:
35
+ logs.append(arg.bytes.value)
36
+
37
+ separator = b""
38
+ if isinstance(sep, Bytes):
39
+ separator = sep.value
40
+ elif isinstance(sep, str):
41
+ separator = sep.encode("utf8")
42
+ else:
43
+ separator = sep
44
+
45
+ active_txn = context.get_active_transaction()
46
+ if not active_txn:
47
+ raise ValueError("Cannot emit events outside of application call context!")
48
+ if active_txn.type != algopy.TransactionType.ApplicationCall:
49
+ raise ValueError("Cannot emit events outside of application call context!")
50
+ if not active_txn.app_id:
51
+ raise ValueError("Cannot emit event: missing `app_id` in associated call transaction!")
52
+ context.add_application_logs(
53
+ app_id=active_txn.app_id(),
54
+ logs=separator.join(logs),
55
+ )
@@ -0,0 +1,249 @@
1
+ from __future__ import annotations
2
+
3
+ import enum
4
+ import functools
5
+ import secrets
6
+ import typing
7
+ from types import UnionType
8
+ from typing import TYPE_CHECKING, get_args
9
+
10
+ import algosdk
11
+ import algosdk.transaction
12
+
13
+ from algopy_testing import arc4
14
+ from algopy_testing.constants import MAX_BYTES_SIZE, MAX_UINT8, MAX_UINT16, MAX_UINT64, MAX_UINT512
15
+
16
+ if TYPE_CHECKING:
17
+ import algopy
18
+
19
+
20
+ def as_int(value: object, *, max: int | None) -> int: # noqa: A002
21
+ """
22
+ Returns the underlying int value for any numeric type up to UInt512
23
+
24
+ Raises:
25
+ TypeError: If `value` is not a numeric type
26
+ ValueError: If not 0 <= `value` <= max
27
+ """
28
+
29
+ from algopy_testing.primitives.biguint import BigUInt
30
+ from algopy_testing.primitives.uint64 import UInt64
31
+
32
+ match value:
33
+ case int(int_value):
34
+ pass
35
+ case UInt64(value=int_value):
36
+ pass
37
+ case BigUInt(value=int_value):
38
+ pass
39
+ case arc4.UIntN():
40
+ int_value = value.native.value
41
+ case arc4.BigUIntN():
42
+ int_value = value.native.value
43
+ # TODO: add arc4 numerics
44
+ case _:
45
+ raise TypeError(f"value must be a numeric type, not {type(value).__name__!r}")
46
+ if int_value < 0:
47
+ raise ValueError(f"expected positive value, got {int_value}")
48
+ if max is not None and int_value > max:
49
+ raise ValueError(f"expected value <= {max}, got: {int_value}")
50
+ return int_value
51
+
52
+
53
+ def as_int8(value: object) -> int:
54
+ return as_int(value, max=MAX_UINT8)
55
+
56
+
57
+ def as_int16(value: object) -> int:
58
+ return as_int(value, max=MAX_UINT16)
59
+
60
+
61
+ def as_int64(value: object) -> int:
62
+ return as_int(value, max=MAX_UINT64)
63
+
64
+
65
+ def as_int512(value: object) -> int:
66
+ return as_int(value, max=MAX_UINT512)
67
+
68
+
69
+ def as_bytes(value: object, *, max_size: int = MAX_BYTES_SIZE) -> bytes:
70
+ """
71
+ Returns the underlying bytes value for bytes or Bytes type up to 4096
72
+
73
+ Raises:
74
+ TypeError: If `value` is not a bytes type
75
+ ValueError: If not 0 <= `len(value)` <= max_size
76
+ """
77
+ from algopy_testing.primitives.bytes import Bytes
78
+
79
+ match value:
80
+ case bytes(bytes_value):
81
+ pass
82
+ case Bytes(value=bytes_value):
83
+ pass
84
+ case _:
85
+ raise TypeError(f"value must be a bytes or Bytes type, not {type(value).__name__!r}")
86
+ if len(bytes_value) > max_size:
87
+ raise ValueError(f"expected value length <= {max_size}, got: {len(bytes_value)}")
88
+ return bytes_value
89
+
90
+
91
+ def as_string(value: object) -> str:
92
+ from algopy_testing.primitives.string import String
93
+
94
+ match value:
95
+ case str(string_value) | String(value=string_value):
96
+ return string_value
97
+ case arc4.String():
98
+ return value.native.value
99
+ case _:
100
+ raise TypeError(f"value must be a string or String type, not {type(value).__name__!r}")
101
+
102
+
103
+ def int_to_bytes(x: int, pad_to: int | None = None) -> bytes:
104
+ result = x.to_bytes((x.bit_length() + 7) // 8, "big")
105
+ result = (
106
+ b"\x00" * (pad_to - len(result)) if pad_to is not None and len(result) < pad_to else b""
107
+ ) + result
108
+
109
+ return result
110
+
111
+
112
+ def dummy_transaction_id() -> bytes:
113
+ private_key, address = algosdk.account.generate_account()
114
+
115
+ suggested_params = algosdk.transaction.SuggestedParams(fee=1000, first=0, last=1, gh="")
116
+ txn = algosdk.transaction.PaymentTxn(
117
+ sender=address,
118
+ receiver=address,
119
+ amt=1000,
120
+ sp=suggested_params,
121
+ note=secrets.token_bytes(8),
122
+ )
123
+
124
+ signed_txn = txn.sign(private_key)
125
+ txn_id = str(signed_txn.transaction.get_txid()).encode("utf-8")
126
+ return txn_id
127
+
128
+
129
+ class _TransactionStrType(enum.StrEnum):
130
+ PAYMENT = algosdk.constants.PAYMENT_TXN
131
+ KEYREG = algosdk.constants.KEYREG_TXN
132
+ ASSETCONFIG = algosdk.constants.ASSETCONFIG_TXN
133
+ ASSETTRANSFER = algosdk.constants.ASSETTRANSFER_TXN
134
+ ASSETFREEZE = algosdk.constants.ASSETFREEZE_TXN
135
+ APPCALL = algosdk.constants.APPCALL_TXN
136
+
137
+
138
+ @functools.cache
139
+ def txn_type_to_bytes(txn_type: int) -> algopy.Bytes:
140
+ import algopy
141
+
142
+ match txn_type:
143
+ case algopy.TransactionType.Payment:
144
+ result = _TransactionStrType.PAYMENT
145
+ case algopy.TransactionType.KeyRegistration:
146
+ result = _TransactionStrType.KEYREG
147
+ case algopy.TransactionType.AssetConfig:
148
+ result = _TransactionStrType.ASSETCONFIG
149
+ case algopy.TransactionType.AssetTransfer:
150
+ result = _TransactionStrType.ASSETTRANSFER
151
+ case algopy.TransactionType.AssetFreeze:
152
+ result = _TransactionStrType.ASSETFREEZE
153
+ case algopy.TransactionType.ApplicationCall:
154
+ result = _TransactionStrType.APPCALL
155
+ case _:
156
+ raise ValueError(f"invalid transaction type: {txn_type}")
157
+
158
+ return algopy.Bytes(bytes(result, encoding="utf-8"))
159
+
160
+
161
+ def is_instance(obj: object, class_or_tuple: type | UnionType) -> bool:
162
+ if isinstance(class_or_tuple, UnionType):
163
+ return any(is_instance(obj, arg) for arg in get_args(class_or_tuple))
164
+
165
+ if isinstance(obj, typing._ProtocolMeta): # type: ignore[type-check, unused-ignore]
166
+ return (
167
+ f"{obj.__module__}.{obj.__name__}"
168
+ == f"{class_or_tuple.__module__}.{class_or_tuple.__name__}" # type: ignore[union-attr, unused-ignore]
169
+ )
170
+
171
+ # Manual comparison by module and name
172
+ if (
173
+ hasattr(obj, "__module__")
174
+ and hasattr(obj, "__name__")
175
+ and (
176
+ obj.__module__,
177
+ obj.__name__, # type: ignore[attr-defined, unused-ignore]
178
+ )
179
+ == (
180
+ class_or_tuple.__module__,
181
+ class_or_tuple.__name__,
182
+ )
183
+ ):
184
+ return True
185
+
186
+ return isinstance(obj, class_or_tuple)
187
+
188
+
189
+ def abi_type_name_for_arg( # noqa: PLR0912, C901, PLR0911
190
+ *, arg: object, is_return_type: bool = False
191
+ ) -> str:
192
+ """
193
+ Returns the ABI type name for the given argument. Especially convenient for use with
194
+ algosdk to generate method signatures
195
+ """
196
+ import algopy
197
+
198
+ if is_instance(arg, algopy.arc4.String | algopy.String | str):
199
+ return "string"
200
+ if is_instance(arg, algopy.arc4.Bool | bool):
201
+ return "bool"
202
+ if is_instance(arg, algopy.BigUInt):
203
+ return "uint512"
204
+ if is_instance(arg, algopy.UInt64):
205
+ return "uint64"
206
+ if isinstance(arg, int):
207
+ return "uint64" if arg <= MAX_UINT64 else "uint512"
208
+ if is_instance(arg, algopy.Bytes | bytes):
209
+ return "byte[]"
210
+ if is_instance(arg, algopy.arc4.Address):
211
+ return "address"
212
+ if is_instance(arg, algopy.Asset):
213
+ return "uint64" if is_return_type else "asset"
214
+ if is_instance(arg, algopy.Account):
215
+ return "uint64" if is_return_type else "account"
216
+ if is_instance(arg, algopy.Application):
217
+ return "uint64" if is_return_type else "application"
218
+ if is_instance(arg, algopy.arc4.UIntN):
219
+ return "uint" + str(arg._bit_size) # type: ignore[attr-defined]
220
+ if is_instance(arg, algopy.arc4.BigUIntN):
221
+ return "uint" + str(arg._bit_size) # type: ignore[attr-defined]
222
+ if is_instance(arg, algopy.arc4.UFixedNxM):
223
+ return f"ufixed{arg._n}x{arg._m}" # type: ignore[attr-defined]
224
+ if is_instance(arg, algopy.arc4.BigUFixedNxM):
225
+ return f"ufixed{arg._n}x{arg._m}" # type: ignore[attr-defined]
226
+ if is_instance(arg, algopy.arc4.StaticArray):
227
+ return f"{abi_type_name_for_arg(arg=arg[0], # type: ignore[index]
228
+ is_return_type=is_return_type)}[{arg.length.value}]" # type: ignore[attr-defined]
229
+ if is_instance(arg, algopy.arc4.DynamicArray):
230
+ return f"{abi_type_name_for_arg(arg=arg[0], # type: ignore[index]
231
+ is_return_type=is_return_type)}[]"
232
+ if isinstance(arg, tuple):
233
+ return f"({','.join(abi_type_name_for_arg(arg=a,
234
+ is_return_type=is_return_type) for a in arg)})"
235
+ raise ValueError(f"Unsupported type {type(arg)}")
236
+
237
+
238
+ def abi_return_type_annotation_for_arg(arg: object) -> str:
239
+ """
240
+ Returns the ABI type name for the given argument. Especially convenient for use with
241
+ algosdk to generate method signatures
242
+ """
243
+
244
+ try:
245
+ return abi_type_name_for_arg(arg=arg, is_return_type=True)
246
+ except ValueError:
247
+ if arg is None:
248
+ return "void"
249
+ raise
@@ -0,0 +1,81 @@
1
+ Metadata-Version: 2.3
2
+ Name: algorand-python-testing
3
+ Version: 0.0.0b1
4
+ Summary: Algorand Python testing library
5
+ Project-URL: Documentation, https://github.com/algorandfoundation/puya/tree/main/algopy_testing#README.md
6
+ Project-URL: Issues, https://github.com/algorandfoundation/puya/issues
7
+ Project-URL: Source, https://github.com/algorandfoundation/puya/tree/main/algopy_testing
8
+ Author-email: Algorand Foundation <contact@algorand.foundation>
9
+ License-Expression: AGPL-3.0-or-later
10
+ License-File: LICENSE
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Topic :: Software Development :: Testing
16
+ Requires-Python: >=3.12
17
+ Requires-Dist: algorand-python>=1.2
18
+ Requires-Dist: coincurve>=19.0.1
19
+ Requires-Dist: ecdsa>=0.17.0
20
+ Requires-Dist: pycryptodomex<4,>=3.6.0
21
+ Requires-Dist: pynacl<2,>=1.4.0
22
+ Description-Content-Type: text/markdown
23
+
24
+ <div align="center">
25
+ <a href="https://github.com/algorandfoundation/algorand-python-testing"><img src="https://bafybeiaibjaf6zy6hvef2rrysaacsfsyb3hw4qqtgn657gw7k5tdzqdxzi.ipfs.nftstorage.link/" width=60%></a>
26
+ </div>
27
+
28
+ <p align="center">
29
+ <a target="_blank" href="https://algorandfoundation.github.io/algorand-python-testing/"><img src="https://img.shields.io/badge/docs-repository-74dfdc?logo=github&style=flat.svg" /></a>
30
+ <a target="_blank" href="https://developer.algorand.org/algokit/"><img src="https://img.shields.io/badge/learn-AlgoKit-74dfdc?logo=algorand&mac=flat.svg" /></a>
31
+ <a target="_blank" href="https://github.com/algorandfoundation/algorand-python-testing"><img src="https://img.shields.io/github/stars/algorandfoundation/algorand-python-testing?color=74dfdc&logo=star&style=flat" /></a>
32
+ <a target="_blank" href="https://developer.algorand.org/algokit/"><img src="https://api.visitorbadge.io/api/visitors?path=https%3A%2F%2Fgithub.com%2Falgorandfoundation%2Falgorand-python-testing&countColor=%2374dfdc&style=flat" /></a>
33
+ </p>
34
+
35
+ ---
36
+
37
+ Algorand Python Testing is a companion package to [Algorand Python](https://github.com/algorandfoundation/puya) that enables efficient unit testing of Algorand Python smart contracts in an offline environment. It emulates key AVM behaviors without requiring a network connection, offering fast and reliable testing capabilities with a familiar Pythonic interface.
38
+
39
+ [Documentation](https://algorandfoundation.github.io/algopy_testing/index.html) | [Algorand Python Documentation](https://algorandfoundation.github.io/puya/)
40
+
41
+ ## Quick start
42
+
43
+ The easiest way to use Algorand Python Testing is to instantiate a template with AlgoKit via `algokit init -t python`. This will give you a full development environment with testing capabilities built-in.
44
+
45
+ Alternatively, if you want to start from scratch:
46
+
47
+ 1. Ensure you have Python 3.12+
48
+ 2. Install [AlgoKit CLI](https://github.com/algorandfoundation/algokit-cli?tab=readme-ov-file#install)
49
+ 3. Install Algorand Python Testing into your project:
50
+ ```bash
51
+ pip install algopy-testing-python
52
+ ```
53
+ 4. Create a test file (e.g., `test_contract.py`):
54
+
55
+ ```python
56
+ from algopy_testing import algopy_testing_context
57
+ from your_contract import YourContract
58
+
59
+ def test_your_contract():
60
+ with algopy_testing_context() as ctx:
61
+ contract = YourContract() # Your test code here
62
+ ```
63
+
64
+ 5. Run your tests using your preferred Python testing framework (e.g., pytest, unittest)
65
+
66
+ For more detailed information, check out the [full documentation](https://algorandfoundation.github.io/algopy_testing).
67
+
68
+ ## Features
69
+
70
+ - Offline testing environment simulating core AVM functionality
71
+ - Compatible with popular Python testing frameworks
72
+ - Supports testing of ARC4 contracts, smart signatures, and more
73
+ - Provides tools for mocking blockchain state and transactions
74
+
75
+ ## Examples
76
+
77
+ For detailed examples showcasing various testing scenarios, refer to the [examples section](https://algorandfoundation.github.io/algopy_testing/examples.html) in the documentation.
78
+
79
+ ## Contributing
80
+
81
+ We welcome contributions to this project! Please read our [contributing guide](CONTRIBUTING.md) to get started.
@@ -0,0 +1,52 @@
1
+ algopy/__init__.py,sha256=nd5hpdHpkfb5srMM6vHKfbOmEIxTutr4Dh0b7wwFq8Y,1143
2
+ algopy/arc4.py,sha256=zE-cwwmeEBA0m22QiHIujeY0S5rdyf3gDqTyGgROAqI,48
3
+ algopy/gtxn.py,sha256=tS2waKIr3g79T1xcmE2iVcvu524xANsR8awXduUVoT0,48
4
+ algopy/itxn.py,sha256=gXida1ee8xMfRufZ24G4xPGGSe26Iwm9nqfHpQCxbpE,48
5
+ algopy/op.py,sha256=JmF0QiVmqF1Oqhj9VypCl7ef-r2r-dHPI0hxIS3LQD8,46
6
+ algopy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ algopy_testing/__init__.py,sha256=x7WpDet7utqy6M9MdE5OJeKLqNd1uAIPg_U2ZpDOenI,1150
8
+ algopy_testing/arc4.py,sha256=3rCZGKs4-mH3SymHOrp190edGS9MV_v3L5oP_5_GBsA,52015
9
+ algopy_testing/constants.py,sha256=L1-Vod3_k6NAmeHJ9d8qIIovSMlakudvd4VeR6BjPeo,733
10
+ algopy_testing/context.py,sha256=TswTYyLMw1_novoEnoLoIIGrkN-c2xBCWoWFW7AOvs8,39371
11
+ algopy_testing/enums.py,sha256=MJE7g_5wiI94a5oAaEdPGGaO7QS1wm5GgZlYtADwB1U,690
12
+ algopy_testing/gtxn.py,sha256=ul1iBYt8Kwc9Xob_pWVwa6dj9ueQ0iIz2DHkC72Aga4,7954
13
+ algopy_testing/itxn.py,sha256=lLz6dPBwTSvU5zmuKPQd0LkCLG1VJN2Q8Dmb6HRNb3s,23363
14
+ algopy_testing/op.py,sha256=e_VY3pvgUuW_y64yolzzuxeN2Gpg4XkDcJM5fNK-iOg,33046
15
+ algopy_testing/protocols.py,sha256=fPvuYAWKVVYM8oZE9hJtmRKW24Ba8VSzrBGz-7mDnIw,448
16
+ algopy_testing/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ algopy_testing/utils.py,sha256=PkDkHPAKJxyq6Jc0kWSVhF9MtIyNZmNv-hY2bPtwQ40,8393
18
+ algopy_testing/decorators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
+ algopy_testing/decorators/abimethod.py,sha256=8pRNP8EEExCkBk9i3mQukRrMngL6FnViJQKqWQjET1E,6756
20
+ algopy_testing/decorators/baremethod.py,sha256=g-HJl49g5q5T40QHXLNZnVKob4XtGaPcWWX_F3Z7HKY,2324
21
+ algopy_testing/decorators/subroutine.py,sha256=7bQt-RBdqmbzosMTIllBh1KCEDUSyZwNSxXc-1guflw,192
22
+ algopy_testing/models/__init__.py,sha256=yQdq6-32r3NfVZWn-n4ILdLOiFDf8oxrKfyHRQPnv-I,923
23
+ algopy_testing/models/account.py,sha256=vR_furAVjNGkT1x8vJ8BQeOuCyjg3Cwc1pIGUkwX-R0,4215
24
+ algopy_testing/models/application.py,sha256=leMvgKkYt7Kct4ayUwkL7rQy65mpqp52oGnuoNO-1xA,2203
25
+ algopy_testing/models/asset.py,sha256=qsvC7vGDKTH_PHqybc0SAgzPLAjPgzgwK0aDliUxoLk,3471
26
+ algopy_testing/models/block.py,sha256=JwzoQ3Z7EIvOaSjbMlzQTQ7DztYejw43rQeRptolADs,876
27
+ algopy_testing/models/box.py,sha256=CJrj9v0SwoJztyMVbe0Xu9ZHsAhWUdurAXjWo9lped8,5548
28
+ algopy_testing/models/contract.py,sha256=9TwDC-5Ncxor2wn4_EVQ8ku6RpRkTNDTeuU64RKAmP0,2307
29
+ algopy_testing/models/gitxn.py,sha256=MEWkVoK81Hoqz6Bb7uFAuGCZTb_G5lH9Vl1Dx_2nO7w,1594
30
+ algopy_testing/models/global_values.py,sha256=cT5JKbcSY2_VenF6Pn3BkSj_iaV2D5o4uJZYyKXs2xQ,2215
31
+ algopy_testing/models/gtxn.py,sha256=RJCwFyJvIbxwbF27RpWJaS04x7ZACKr1IPP3iXRS6Rk,1993
32
+ algopy_testing/models/itxn.py,sha256=sgN4wvzolwYmdDJC-4hh6PXDWSBU6-FCjm4tuLIb0Y4,2995
33
+ algopy_testing/models/logicsig.py,sha256=5l_7EL2_bVKZ_myc07La2G2_H8aYZF--Hd0tFMic2t0,1151
34
+ algopy_testing/models/template_variable.py,sha256=FbyP9JniPasbgqYtPRfSOF2u6LWkc45suRSmTfURi5g,759
35
+ algopy_testing/models/transactions.py,sha256=QATadTBJIaHERWuhXOsXEeSjVq-9QyHjJSAQrLOz5pc,4313
36
+ algopy_testing/models/txn.py,sha256=chTwHOVpy9elV4ZVTIzVXS_JKTmc1OF6vhmzdyXEC_A,3912
37
+ algopy_testing/models/unsigned_builtins.py,sha256=-6zoPjn1MTh0wzacsMhhLhAEy4DWdWq7TVFtHWP-QZA,826
38
+ algopy_testing/primitives/__init__.py,sha256=xERIGR6lbEN-cPTtR5eH0yeJIawvjg2XQnmIeiwICKQ,260
39
+ algopy_testing/primitives/biguint.py,sha256=Ss_wE9DCqweydiOFTQ3nCm4xZVTciD4b9qkFF0OVrNw,5199
40
+ algopy_testing/primitives/bytes.py,sha256=TI-M5hA4NNTdiNwZg_70EH08VFHOW0f9Wm7RwVeCItk,5634
41
+ algopy_testing/primitives/string.py,sha256=7eWrbFxu74nQ7ZdDg6GVsZ73VF_p24os7SnIoXKZuFk,2189
42
+ algopy_testing/primitives/uint64.py,sha256=-vQHLYza-K7Y_Pms-49pQQ44B78CNLEYBb6X6Ny-G6c,7110
43
+ algopy_testing/state/__init__.py,sha256=uRFuGSn7RMnhjVKWWdHnVtPNhNfTlQIoQaDPbcmkr3U,155
44
+ algopy_testing/state/global_state.py,sha256=1eJRY9SYzOdj_dnNUqF_vghh0t9Q6N1B1MzLX0hKPxU,1662
45
+ algopy_testing/state/local_state.py,sha256=7m98XD5Afbn72yvgw9Q_VLAEhObLBv_5trp11ZkGwuE,1788
46
+ algopy_testing/utilities/__init__.py,sha256=y43zYxwO37xO9CodAENU9y5p6DBVrkDOHnoZZO77nxc,126
47
+ algopy_testing/utilities/budget.py,sha256=P1pOKWCyya4wsqAwKgvp7y0p97hn2Kr2qFVRKm-sJZo,540
48
+ algopy_testing/utilities/log.py,sha256=5MDJY8Oh_COt5OLZXWqkvAJk2qdfBpfSGIpidV-HJ4k,1870
49
+ algorand_python_testing-0.0.0b1.dist-info/METADATA,sha256=zZpOXQVULRPTrkvGyeFCowcvmdZvIQkncTZEwLGz5-E,4172
50
+ algorand_python_testing-0.0.0b1.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
51
+ algorand_python_testing-0.0.0b1.dist-info/licenses/LICENSE,sha256=jRyAzzkz3HPr-knS8XWyDY8CyuU-L4RtydPP8uGWsUw,657
52
+ algorand_python_testing-0.0.0b1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.25.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,14 @@
1
+ Copyright (C) 2024 Algorand Foundation
2
+
3
+ This program is free software: you can redistribute it and/or modify
4
+ it under the terms of the GNU Affero General Public License as
5
+ published by the Free Software Foundation, either version 3 of the
6
+ License, or any later version.
7
+
8
+ This program is distributed in the hope that it will be useful,
9
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ GNU Affero General Public License for more details.
12
+
13
+ You should have received a copy of the GNU Affero General Public License
14
+ along with this program. If not, see <https://www.gnu.org/licenses/>.