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.
- algopy/__init__.py +58 -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 +55 -0
- algopy_testing/arc4.py +1533 -0
- algopy_testing/constants.py +22 -0
- algopy_testing/context.py +1194 -0
- algopy_testing/decorators/__init__.py +0 -0
- algopy_testing/decorators/abimethod.py +204 -0
- algopy_testing/decorators/baremethod.py +83 -0
- algopy_testing/decorators/subroutine.py +9 -0
- algopy_testing/enums.py +42 -0
- algopy_testing/gtxn.py +261 -0
- algopy_testing/itxn.py +665 -0
- algopy_testing/models/__init__.py +31 -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/block.py +34 -0
- algopy_testing/models/box.py +158 -0
- algopy_testing/models/contract.py +82 -0
- algopy_testing/models/gitxn.py +42 -0
- algopy_testing/models/global_values.py +72 -0
- algopy_testing/models/gtxn.py +56 -0
- algopy_testing/models/itxn.py +85 -0
- algopy_testing/models/logicsig.py +44 -0
- algopy_testing/models/template_variable.py +23 -0
- algopy_testing/models/transactions.py +158 -0
- algopy_testing/models/txn.py +113 -0
- algopy_testing/models/unsigned_builtins.py +36 -0
- algopy_testing/op.py +1098 -0
- algopy_testing/primitives/__init__.py +6 -0
- algopy_testing/primitives/biguint.py +148 -0
- algopy_testing/primitives/bytes.py +174 -0
- algopy_testing/primitives/string.py +68 -0
- algopy_testing/primitives/uint64.py +213 -0
- algopy_testing/protocols.py +18 -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/utilities/__init__.py +3 -0
- algopy_testing/utilities/budget.py +23 -0
- algopy_testing/utilities/log.py +55 -0
- algopy_testing/utils.py +249 -0
- algorand_python_testing-0.0.0b1.dist-info/METADATA +81 -0
- algorand_python_testing-0.0.0b1.dist-info/RECORD +52 -0
- algorand_python_testing-0.0.0b1.dist-info/WHEEL +4 -0
- algorand_python_testing-0.0.0b1.dist-info/licenses/LICENSE +14 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class _GTxn:
|
|
6
|
+
def __getattr__(self, name: str) -> Callable[[int], typing.Any]:
|
|
7
|
+
from algopy_testing.context import get_test_context
|
|
8
|
+
|
|
9
|
+
context = get_test_context()
|
|
10
|
+
if not context:
|
|
11
|
+
raise ValueError(
|
|
12
|
+
"Test context is not initialized! Use `with algopy_testing_context()` to access "
|
|
13
|
+
"the context manager."
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
txn_group = context.get_transaction_group()
|
|
17
|
+
if not txn_group:
|
|
18
|
+
raise ValueError(
|
|
19
|
+
"No group transactions found in the context! Use `with algopy_testing_context()` "
|
|
20
|
+
"to access the context manager."
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
return lambda *args: self._handle_field(txn_group, name, *args)
|
|
24
|
+
|
|
25
|
+
def _handle_field(
|
|
26
|
+
self, txn_group: list[typing.Any], name: str, *args: typing.Any
|
|
27
|
+
) -> typing.Any:
|
|
28
|
+
if len(args) == 1:
|
|
29
|
+
return self._get_value(txn_group, name, args[0])
|
|
30
|
+
elif len(args) == 2:
|
|
31
|
+
return self._get_value(txn_group, name, args[0], args[1])
|
|
32
|
+
else:
|
|
33
|
+
raise ValueError(f"Invalid number of arguments for field '{name}'")
|
|
34
|
+
|
|
35
|
+
# TODO: refine mapping
|
|
36
|
+
def _map_fields(self, name: str) -> str:
|
|
37
|
+
field_mapping = {"type": "type_bytes", "type_enum": "type", "application_args": "app_args"}
|
|
38
|
+
return field_mapping.get(name, name)
|
|
39
|
+
|
|
40
|
+
def _get_value(
|
|
41
|
+
self, txn_group: list[typing.Any], name: str, index: int, second_arg: typing.Any = None
|
|
42
|
+
) -> object:
|
|
43
|
+
if index >= len(txn_group):
|
|
44
|
+
raise IndexError("Transaction index out of range")
|
|
45
|
+
gtxn = txn_group[index]
|
|
46
|
+
mapped_name = self._map_fields(name)
|
|
47
|
+
value = getattr(gtxn, mapped_name)
|
|
48
|
+
|
|
49
|
+
if callable(value) and second_arg is not None:
|
|
50
|
+
return value(second_arg)
|
|
51
|
+
elif value is None:
|
|
52
|
+
raise ValueError(f"'{name}' is not defined for {type(gtxn).__name__}")
|
|
53
|
+
return value
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
GTxn = _GTxn()
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class _ITxn:
|
|
6
|
+
def __getattr__(self, name: str) -> Callable[[], typing.Any]:
|
|
7
|
+
from algopy_testing.context import get_test_context
|
|
8
|
+
|
|
9
|
+
context = get_test_context()
|
|
10
|
+
if not context:
|
|
11
|
+
raise ValueError(
|
|
12
|
+
"Test context is not initialized! Use `with algopy_testing_context()` to access "
|
|
13
|
+
"the context manager."
|
|
14
|
+
)
|
|
15
|
+
if not context._inner_transaction_groups:
|
|
16
|
+
raise ValueError(
|
|
17
|
+
"No inner transaction found in the context! Use `with algopy_testing_context()` "
|
|
18
|
+
"to access the context manager."
|
|
19
|
+
)
|
|
20
|
+
last_itxn_group = context._inner_transaction_groups[-1]
|
|
21
|
+
|
|
22
|
+
if not last_itxn_group:
|
|
23
|
+
raise ValueError("No inner transaction found in the testing context!")
|
|
24
|
+
|
|
25
|
+
last_itxn = last_itxn_group[-1]
|
|
26
|
+
|
|
27
|
+
value = getattr(last_itxn, name)
|
|
28
|
+
if value is None:
|
|
29
|
+
raise ValueError(f"'{name}' is not defined for {type(last_itxn).__name__} ")
|
|
30
|
+
# mimic the static functions on ITxn with a lambda
|
|
31
|
+
return lambda: value
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
ITxn = _ITxn()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class _ITxnCreate:
|
|
38
|
+
@classmethod
|
|
39
|
+
def begin(cls) -> None:
|
|
40
|
+
from algopy_testing.context import get_test_context
|
|
41
|
+
|
|
42
|
+
context = get_test_context()
|
|
43
|
+
context._constructing_inner_transaction_group = []
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def next(cls) -> None:
|
|
47
|
+
from algopy_testing.context import get_test_context
|
|
48
|
+
|
|
49
|
+
context = get_test_context()
|
|
50
|
+
if context._constructing_inner_transaction:
|
|
51
|
+
context._constructing_inner_transaction_group.append(
|
|
52
|
+
context._constructing_inner_transaction
|
|
53
|
+
)
|
|
54
|
+
context._constructing_inner_transaction = None
|
|
55
|
+
|
|
56
|
+
@classmethod
|
|
57
|
+
def submit(cls) -> None:
|
|
58
|
+
from algopy_testing.context import get_test_context
|
|
59
|
+
|
|
60
|
+
context = get_test_context()
|
|
61
|
+
if context._constructing_inner_transaction:
|
|
62
|
+
context._constructing_inner_transaction_group.append(
|
|
63
|
+
context._constructing_inner_transaction
|
|
64
|
+
)
|
|
65
|
+
context._constructing_inner_transaction = None
|
|
66
|
+
context._inner_transaction_groups.append(context._constructing_inner_transaction_group)
|
|
67
|
+
context._constructing_inner_transaction_group = []
|
|
68
|
+
|
|
69
|
+
def __setattr__(self, name: str, value: typing.Any) -> None:
|
|
70
|
+
if name.startswith("set_"):
|
|
71
|
+
field = name[4:] # Remove 'set_' prefix
|
|
72
|
+
self._set_field(field, value)
|
|
73
|
+
else:
|
|
74
|
+
super().__setattr__(name, value)
|
|
75
|
+
|
|
76
|
+
def _set_field(self, field: str, value: typing.Any) -> None:
|
|
77
|
+
from algopy_testing.context import get_test_context
|
|
78
|
+
|
|
79
|
+
context = get_test_context()
|
|
80
|
+
if not context._constructing_inner_transaction:
|
|
81
|
+
raise ValueError("No active inner transaction. Call ITxnCreate.begin() first.")
|
|
82
|
+
setattr(context._constructing_inner_transaction, field, value)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
ITxnCreate = _ITxnCreate()
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
from functools import wraps
|
|
5
|
+
|
|
6
|
+
if typing.TYPE_CHECKING:
|
|
7
|
+
from collections.abc import Callable
|
|
8
|
+
|
|
9
|
+
import algopy
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class LogicSig:
|
|
13
|
+
"""A logic signature"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, func: Callable[[], bool | algopy.UInt64], name: str | None = None):
|
|
16
|
+
self.func = func
|
|
17
|
+
self.name = name or func.__name__
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@typing.overload
|
|
21
|
+
def logicsig(sub: Callable[[], bool | algopy.UInt64], /) -> LogicSig: ...
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@typing.overload
|
|
25
|
+
def logicsig(*, name: str) -> Callable[[Callable[[], bool | algopy.UInt64]], LogicSig]: ...
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def logicsig(
|
|
29
|
+
sub: Callable[[], bool | algopy.UInt64] | None = None, *, name: str | None = None
|
|
30
|
+
) -> algopy.LogicSig | Callable[[Callable[[], bool | algopy.UInt64]], LogicSig]:
|
|
31
|
+
"""Decorator to indicate a function is a logic signature"""
|
|
32
|
+
import algopy
|
|
33
|
+
|
|
34
|
+
def decorator(func: Callable[[], bool | algopy.UInt64]) -> algopy.LogicSig:
|
|
35
|
+
@wraps(func)
|
|
36
|
+
def wrapper() -> bool | algopy.UInt64:
|
|
37
|
+
return func()
|
|
38
|
+
|
|
39
|
+
return algopy.LogicSig(wrapper, name=name)
|
|
40
|
+
|
|
41
|
+
if sub is None:
|
|
42
|
+
return decorator
|
|
43
|
+
else:
|
|
44
|
+
return decorator(sub)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
_T = typing.TypeVar("_T")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TemplateVarGeneric:
|
|
9
|
+
def __getitem__(self, type_: type[_T]) -> typing.Callable[[str], typing.Any]:
|
|
10
|
+
from algopy_testing.context import get_test_context
|
|
11
|
+
|
|
12
|
+
context = get_test_context()
|
|
13
|
+
|
|
14
|
+
def create_template_var(variable_name: str) -> typing.Any:
|
|
15
|
+
if variable_name not in context._template_vars:
|
|
16
|
+
raise ValueError(f"Template variable {variable_name} not found in test context!")
|
|
17
|
+
return context._template_vars[variable_name]
|
|
18
|
+
|
|
19
|
+
return create_template_var
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
TemplateVar: TemplateVarGeneric = TemplateVarGeneric()
|
|
23
|
+
"""Template variables can be used to represent a placeholder for a deploy-time provided value."""
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
from typing import TYPE_CHECKING, TypedDict
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
import algopy
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class _TransactionCoreFields(TypedDict, total=False):
|
|
11
|
+
sender: algopy.Account
|
|
12
|
+
fee: algopy.UInt64
|
|
13
|
+
first_valid: algopy.UInt64
|
|
14
|
+
first_valid_time: algopy.UInt64
|
|
15
|
+
last_valid: algopy.UInt64
|
|
16
|
+
note: algopy.Bytes
|
|
17
|
+
lease: algopy.Bytes
|
|
18
|
+
txn_id: algopy.Bytes
|
|
19
|
+
rekey_to: algopy.Account
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class _TransactionBaseFields(_TransactionCoreFields, total=False):
|
|
23
|
+
type: algopy.TransactionType
|
|
24
|
+
type_bytes: algopy.Bytes
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class _AssetTransferBaseFields(TypedDict, total=False):
|
|
28
|
+
xfer_asset: algopy.Asset
|
|
29
|
+
asset_amount: algopy.UInt64
|
|
30
|
+
asset_sender: algopy.Account
|
|
31
|
+
asset_receiver: algopy.Account
|
|
32
|
+
asset_close_to: algopy.Account
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class _PaymentBaseFields(TypedDict, total=False):
|
|
36
|
+
receiver: algopy.Account
|
|
37
|
+
amount: algopy.UInt64
|
|
38
|
+
close_remainder_to: algopy.Account
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class _AssetFreezeBaseFields(TypedDict, total=False):
|
|
42
|
+
freeze_asset: algopy.Asset
|
|
43
|
+
freeze_account: algopy.Account
|
|
44
|
+
frozen: bool
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class _AssetConfigBaseFields(TypedDict, total=False):
|
|
48
|
+
config_asset: algopy.Asset
|
|
49
|
+
total: algopy.UInt64
|
|
50
|
+
decimals: algopy.UInt64
|
|
51
|
+
default_frozen: bool
|
|
52
|
+
unit_name: algopy.Bytes
|
|
53
|
+
asset_name: algopy.Bytes
|
|
54
|
+
url: algopy.Bytes
|
|
55
|
+
metadata_hash: algopy.Bytes
|
|
56
|
+
manager: algopy.Account
|
|
57
|
+
reserve: algopy.Account
|
|
58
|
+
freeze: algopy.Account
|
|
59
|
+
clawback: algopy.Account
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class _ApplicationCallCoreFields(TypedDict, total=False):
|
|
63
|
+
app_id: algopy.Application
|
|
64
|
+
on_completion: algopy.OnCompleteAction
|
|
65
|
+
num_app_args: algopy.UInt64
|
|
66
|
+
num_accounts: algopy.UInt64
|
|
67
|
+
approval_program: algopy.Bytes
|
|
68
|
+
clear_state_program: algopy.Bytes
|
|
69
|
+
num_assets: algopy.UInt64
|
|
70
|
+
num_apps: algopy.UInt64
|
|
71
|
+
global_num_uint: algopy.UInt64
|
|
72
|
+
global_num_bytes: algopy.UInt64
|
|
73
|
+
local_num_uint: algopy.UInt64
|
|
74
|
+
local_num_bytes: algopy.UInt64
|
|
75
|
+
extra_program_pages: algopy.UInt64
|
|
76
|
+
last_log: algopy.Bytes
|
|
77
|
+
num_approval_program_pages: algopy.UInt64
|
|
78
|
+
num_clear_state_program_pages: algopy.UInt64
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class _ApplicationCallBaseFields(_ApplicationCallCoreFields, total=False):
|
|
82
|
+
app_args: typing.Callable[[algopy.UInt64 | int], algopy.Bytes]
|
|
83
|
+
accounts: typing.Callable[[algopy.UInt64 | int], algopy.Account]
|
|
84
|
+
assets: typing.Callable[[algopy.UInt64 | int], algopy.Asset]
|
|
85
|
+
apps: typing.Callable[[algopy.UInt64 | int], algopy.Application]
|
|
86
|
+
approval_program_pages: typing.Callable[[algopy.UInt64 | int], algopy.Bytes]
|
|
87
|
+
clear_state_program_pages: typing.Callable[[algopy.UInt64 | int], algopy.Bytes]
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class _ApplicationCallFields(_TransactionBaseFields, _ApplicationCallCoreFields, total=False):
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class _KeyRegistrationBaseFields(TypedDict, total=False):
|
|
95
|
+
vote_key: algopy.Bytes
|
|
96
|
+
selection_key: algopy.Bytes
|
|
97
|
+
vote_first: algopy.UInt64
|
|
98
|
+
vote_last: algopy.UInt64
|
|
99
|
+
vote_key_dilution: algopy.UInt64
|
|
100
|
+
non_participation: bool
|
|
101
|
+
state_proof_key: algopy.Bytes
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class _TransactionFields(
|
|
105
|
+
_TransactionBaseFields,
|
|
106
|
+
_PaymentBaseFields,
|
|
107
|
+
_KeyRegistrationBaseFields,
|
|
108
|
+
_AssetConfigBaseFields,
|
|
109
|
+
_AssetTransferBaseFields,
|
|
110
|
+
_AssetFreezeBaseFields,
|
|
111
|
+
_ApplicationCallBaseFields,
|
|
112
|
+
total=False,
|
|
113
|
+
):
|
|
114
|
+
pass
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class AssetTransferFields(_TransactionBaseFields, _AssetTransferBaseFields, total=False):
|
|
118
|
+
pass
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class PaymentFields(_TransactionBaseFields, _PaymentBaseFields, total=False):
|
|
122
|
+
pass
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class AssetFreezeFields(_TransactionBaseFields, _AssetFreezeBaseFields, total=False):
|
|
126
|
+
pass
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class AssetConfigFields(_TransactionBaseFields, _AssetConfigBaseFields, total=False):
|
|
130
|
+
pass
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class ApplicationCallFields(_TransactionBaseFields, _ApplicationCallBaseFields, total=False):
|
|
134
|
+
pass
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class KeyRegistrationFields(_TransactionBaseFields, _KeyRegistrationBaseFields, total=False):
|
|
138
|
+
pass
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
__all__ = [
|
|
142
|
+
"ApplicationCallFields",
|
|
143
|
+
"AssetConfigFields",
|
|
144
|
+
"AssetFreezeFields",
|
|
145
|
+
"AssetTransferFields",
|
|
146
|
+
"KeyRegistrationFields",
|
|
147
|
+
"PaymentFields",
|
|
148
|
+
"_ApplicationCallBaseFields",
|
|
149
|
+
"_ApplicationCallFields",
|
|
150
|
+
"_AssetConfigBaseFields",
|
|
151
|
+
"_AssetFreezeBaseFields",
|
|
152
|
+
"_AssetTransferBaseFields",
|
|
153
|
+
"_KeyRegistrationBaseFields",
|
|
154
|
+
"_PaymentBaseFields",
|
|
155
|
+
"_TransactionBaseFields",
|
|
156
|
+
"_TransactionCoreFields",
|
|
157
|
+
"_TransactionFields",
|
|
158
|
+
]
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
from typing import TYPE_CHECKING, TypedDict, TypeVar
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
import algopy
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
T = TypeVar("T")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TxnFields(TypedDict, total=False):
|
|
14
|
+
sender: algopy.Account
|
|
15
|
+
fee: algopy.UInt64
|
|
16
|
+
first_valid: algopy.UInt64
|
|
17
|
+
first_valid_time: algopy.UInt64
|
|
18
|
+
last_valid: algopy.UInt64
|
|
19
|
+
note: algopy.Bytes
|
|
20
|
+
lease: algopy.Bytes
|
|
21
|
+
receiver: algopy.Account
|
|
22
|
+
amount: algopy.UInt64
|
|
23
|
+
close_remainder_to: algopy.Account
|
|
24
|
+
vote_pk: algopy.Bytes
|
|
25
|
+
selection_pk: algopy.Bytes
|
|
26
|
+
vote_first: algopy.UInt64
|
|
27
|
+
vote_last: algopy.UInt64
|
|
28
|
+
vote_key_dilution: algopy.UInt64
|
|
29
|
+
type: algopy.Bytes
|
|
30
|
+
type_enum: algopy.UInt64
|
|
31
|
+
xfer_asset: algopy.Asset
|
|
32
|
+
asset_amount: algopy.UInt64
|
|
33
|
+
asset_sender: algopy.Account
|
|
34
|
+
asset_receiver: algopy.Account
|
|
35
|
+
asset_close_to: algopy.Account
|
|
36
|
+
group_index: algopy.UInt64
|
|
37
|
+
tx_id: algopy.Bytes
|
|
38
|
+
application_id: algopy.Application
|
|
39
|
+
on_completion: algopy.UInt64
|
|
40
|
+
num_app_args: algopy.UInt64
|
|
41
|
+
num_accounts: algopy.UInt64
|
|
42
|
+
approval_program: algopy.Bytes
|
|
43
|
+
clear_state_program: algopy.Bytes
|
|
44
|
+
rekey_to: algopy.Account
|
|
45
|
+
config_asset: algopy.Asset
|
|
46
|
+
config_asset_total: algopy.UInt64
|
|
47
|
+
config_asset_decimals: algopy.UInt64
|
|
48
|
+
config_asset_default_frozen: bool
|
|
49
|
+
config_asset_unit_name: algopy.Bytes
|
|
50
|
+
config_asset_name: algopy.Bytes
|
|
51
|
+
config_asset_url: algopy.Bytes
|
|
52
|
+
config_asset_metadata_hash: algopy.Bytes
|
|
53
|
+
config_asset_manager: algopy.Account
|
|
54
|
+
config_asset_reserve: algopy.Account
|
|
55
|
+
config_asset_freeze: algopy.Account
|
|
56
|
+
config_asset_clawback: algopy.Account
|
|
57
|
+
freeze_asset: algopy.Asset
|
|
58
|
+
freeze_asset_account: algopy.Account
|
|
59
|
+
freeze_asset_frozen: bool
|
|
60
|
+
num_assets: algopy.UInt64
|
|
61
|
+
num_applications: algopy.UInt64
|
|
62
|
+
global_num_uint: algopy.UInt64
|
|
63
|
+
global_num_byte_slice: algopy.UInt64
|
|
64
|
+
local_num_uint: algopy.UInt64
|
|
65
|
+
local_num_byte_slice: algopy.UInt64
|
|
66
|
+
extra_program_pages: algopy.UInt64
|
|
67
|
+
nonparticipation: bool
|
|
68
|
+
num_logs: algopy.UInt64
|
|
69
|
+
created_asset_id: algopy.Asset
|
|
70
|
+
created_application_id: algopy.Application
|
|
71
|
+
last_log: algopy.Bytes
|
|
72
|
+
state_proof_pk: algopy.Bytes
|
|
73
|
+
num_approval_program_pages: algopy.UInt64
|
|
74
|
+
num_clear_state_program_pages: algopy.UInt64
|
|
75
|
+
application_args: tuple[algopy.Bytes, ...]
|
|
76
|
+
accounts: tuple[algopy.Account, ...]
|
|
77
|
+
assets: tuple[algopy.Asset, ...]
|
|
78
|
+
applications: tuple[algopy.Application, ...]
|
|
79
|
+
logs: tuple[algopy.Bytes, ...]
|
|
80
|
+
approval_program_pages: tuple[algopy.Bytes, ...]
|
|
81
|
+
clear_state_program_pages: tuple[algopy.Bytes, ...]
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class _Txn:
|
|
85
|
+
def _map_fields(self, name: str) -> str:
|
|
86
|
+
field_mapping = {"type": "type_bytes", "type_enum": "type", "application_args": "app_args"}
|
|
87
|
+
return field_mapping.get(name, name)
|
|
88
|
+
|
|
89
|
+
def __getattr__(self, name: str) -> typing.Any:
|
|
90
|
+
from algopy_testing.context import get_test_context
|
|
91
|
+
|
|
92
|
+
context = get_test_context()
|
|
93
|
+
if not context:
|
|
94
|
+
raise ValueError(
|
|
95
|
+
"Test context is not initialized! Use `with algopy_testing_context()` to access "
|
|
96
|
+
"the context manager."
|
|
97
|
+
)
|
|
98
|
+
active_txn = context.get_active_transaction()
|
|
99
|
+
if name in context._txn_fields and context._txn_fields[name] is not None: # type: ignore[literal-required]
|
|
100
|
+
return context._txn_fields[name] # type: ignore[literal-required]
|
|
101
|
+
elif active_txn:
|
|
102
|
+
if context._active_transaction_index is not None and name == "group_index":
|
|
103
|
+
return context._active_transaction_index
|
|
104
|
+
return getattr(active_txn, self._map_fields(name))
|
|
105
|
+
else:
|
|
106
|
+
raise AttributeError(
|
|
107
|
+
f"'Txn' object has no value set for attribute named '{name}'. "
|
|
108
|
+
f"Use `context.patch_txn_fields({name}=your_value)` to set the value "
|
|
109
|
+
"in your test setup."
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
Txn = _Txn()
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
from algopy_testing.utils import as_int64
|
|
6
|
+
|
|
7
|
+
if typing.TYPE_CHECKING:
|
|
8
|
+
from collections.abc import Iterable, Iterator, Reversible
|
|
9
|
+
|
|
10
|
+
import algopy
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class urange: # noqa: N801
|
|
14
|
+
_value: range
|
|
15
|
+
|
|
16
|
+
def __init__(self, *args: int | algopy.UInt64) -> None:
|
|
17
|
+
self._value = range(*[as_int64(arg) for arg in args])
|
|
18
|
+
|
|
19
|
+
def __iter__(self) -> Iterator[algopy.UInt64]:
|
|
20
|
+
import algopy
|
|
21
|
+
|
|
22
|
+
return map(algopy.UInt64, self._value)
|
|
23
|
+
|
|
24
|
+
def __reversed__(self) -> Iterator[algopy.UInt64]:
|
|
25
|
+
import algopy
|
|
26
|
+
|
|
27
|
+
return map(algopy.UInt64, reversed(self._value))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
_T = typing.TypeVar("_T")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def uenumerate(iterable: Iterable[_T]) -> Reversible[tuple[algopy.UInt64, _T]]:
|
|
34
|
+
import algopy
|
|
35
|
+
|
|
36
|
+
return [(algopy.UInt64(i), v) for i, v in enumerate(iterable)]
|