Skip to content

Commit 32a2787

Browse files
committed
feat: add algopy.public alias for algopy.arc4.abimethod decorator
1 parent a35f340 commit 32a2787

File tree

10 files changed

+40
-31
lines changed

10 files changed

+40
-31
lines changed

docs/coverage.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ See which `algorand-python` stubs are implemented by the `algorand-python-testin
4040
| algopy.ensure_budget | Emulated |
4141
| algopy.log | Emulated |
4242
| algopy.logicsig | Emulated |
43+
| algopy.public | Emulated |
4344
| algopy.size_of | Emulated |
4445
| algopy.subroutine | Native |
4546
| algopy.uenumerate | Native |

docs/index.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class VotingContract(algopy.ARC4Contract):
6262
)
6363
self.voted = algopy.LocalState(algopy.UInt64, key="voted", description="Tracks if an account has voted")
6464
65-
@arc4.abimethod
65+
@algopy.public
6666
def set_topic(self, topic: arc4.String) -> None:
6767
self.topic.value = topic.bytes
6868
@@ -79,7 +79,7 @@ class VotingContract(algopy.ARC4Contract):
7979
self.voted[algopy.Txn.sender] = algopy.UInt64(1)
8080
return arc4.Bool(True)
8181
82-
@arc4.abimethod(readonly=True)
82+
@algopy.public(readonly=True)
8383
def get_votes(self) -> arc4.UInt64:
8484
return arc4.UInt64(self.votes.value)
8585
@@ -141,9 +141,9 @@ This example demonstrates key aspects of testing with `algorand-python-testing`
141141
1. ARC4 Contract Features:
142142

143143
- Use of `algopy.ARC4Contract` as the base class for the contract.
144-
- ABI methods defined using the `@arc4.abimethod` decorator.
144+
- ABI methods defined using the `@arc4.abimethod`, or its alias `@algopy.public`, decorator.
145145
- Use of ARC4-specific types like `arc4.String`, `arc4.Bool`, and `arc4.UInt64`.
146-
- Readonly method annotation with `@arc4.abimethod(readonly=True)`.
146+
- Readonly method annotation with `@arc4.abimethod(readonly=True)` or `@algopy.public(readonly=True)` .
147147

148148
2. Testing ARC4 Contracts:
149149

docs/testing-guide/concepts.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,5 @@ For a full list of all public `algopy` types and their corresponding implementat
6161

6262
## Data Validation
6363

64-
Algorand Python and the puya compiler have functionality to perform validation of transaction inputs via the `--validate-abi-args`, `--validate-abi-return` CLI arguments, `arc4.abimethod(validate_encoding=...)` decorator and `.validate()` methods.
65-
The Algorand Python Testing library does *NOT* implement this validation behaviour, as you should test invalid inputs using an integrated test against a real Algorand network.
64+
Algorand Python and the puya compiler have functionality to perform validation of transaction inputs via the `--validate-abi-args`, `--validate-abi-return` CLI arguments, `arc4.abimethod(validate_encoding=...)` decorator (or its alias, `algopy.public`) and `.validate()` methods.
65+
The Algorand Python Testing library does _NOT_ implement this validation behaviour, as you should test invalid inputs using an integrated test against a real Algorand network.

docs/testing-guide/contract-testing.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Smart Contract Testing
22

3-
This guide provides an overview of how to test smart contracts using the Algorand Python SDK (`algopy`). It covers the basics of testing `ARC4Contract` and `Contract` classes, focusing on the `abimethod` and `baremethod` decorators.
3+
This guide provides an overview of how to test smart contracts using the Algorand Python SDK (`algopy`). It covers the basics of testing `ARC4Contract` and `Contract` classes, focusing on the `baremethod`, `abimethod` and `public` (an alias of `abimethod`) decorators.
44

55
![](https://mermaid.ink/img/pako:eNqVkrFugzAQhl_Fujnp1ImhEiJrJNREWeoOV9sNVsFG9iEVBd69R5w0JE2llsk2n7-7_-AAymsDGewDtpXYrqQT_GyKFwl5vfcBnRZlT5V3IjYYSCjvKKAiCa-JzXfrObyzgTqsxRpVZZ25YOX2nnRrIomCneZzpszLkllktu0f8ratrUKyjFsXCZ1K2gTH7i01_8dGUjOT_55YeLdUFVr3zRunf5b6R5hZoFnBq9cX72_Br_Cj8bl4vJCHaVucvowYxHk5Xg_sfPkY6SbbphDL5dMgQZu29n0U5DMJwzTVGyApySKZKFSNMXKVxPJYYAGNCQ1azX_VYboqgSrTcAcZLzWGDwnSjcxhR37TOwUZhc4sIPhuX0H2jnXkXddqrrCyyKNpTqfjF5m74B8?type=png)
66

@@ -24,7 +24,7 @@ context = ctx_manager.__enter__()
2424

2525
Subclasses of `algopy.ARC4Contract` are **required** to be instantiated with an active test context. As part of instantiation, the test context will automatically create a matching `algopy.Application` object instance.
2626

27-
Within the class implementation, methods decorated with `algopy.arc4.abimethod` and `algopy.arc4.baremethod` will automatically assemble an `algopy.gtxn.ApplicationCallTransaction` to emulate the AVM application call. This behaviour can be overridden by setting the transaction group manually as part of test setup; this is done via implicit invocation of the `algopy_testing.context.any_application()` _value generator_ (refer to the [API](../api.md) for more details).
27+
Within the class implementation, methods decorated with `algopy.arc4.abimethod` (or its alias, `algopy.public`) and `algopy.arc4.baremethod` will automatically assemble an `algopy.gtxn.ApplicationCallTransaction` to emulate the AVM application call. This behaviour can be overridden by setting the transaction group manually as part of test setup; this is done via implicit invocation of the `algopy_testing.context.any_application()` _value generator_ (refer to the [API](../api.md) for more details).
2828

2929
```{testcode}
3030
class SimpleVotingContract(algopy.ARC4Contract):
@@ -42,14 +42,14 @@ class SimpleVotingContract(algopy.ARC4Contract):
4242
self.topic.value = initial_topic
4343
self.votes.value = algopy.UInt64(0)
4444
45-
@algopy.arc4.abimethod
45+
@algopy.public
4646
def vote(self) -> algopy.UInt64:
4747
assert self.voted[algopy.Txn.sender] == algopy.UInt64(0), "Account has already voted"
4848
self.votes.value += algopy.UInt64(1)
4949
self.voted[algopy.Txn.sender] = algopy.UInt64(1)
5050
return self.votes.value
5151
52-
@algopy.arc4.abimethod(readonly=True)
52+
@algopy.public(readonly=True)
5353
def get_votes(self) -> algopy.UInt64:
5454
return self.votes.value
5555
@@ -74,7 +74,7 @@ assert contract.topic.value == initial_topic
7474
assert contract.votes.value == algopy.UInt64(0)
7575
7676
# Act - Vote
77-
# The method `.vote()` is decorated with `algopy.arc4.abimethod`, which means it will assemble a transaction to emulate the AVM application call
77+
# The method `.vote()` is decorated with `algopy.public`, an alias of `algopy.arc4.abimethod`, which means it will assemble a transaction to emulate the AVM application call
7878
result = contract.vote()
7979
8080
# Assert - you can access the corresponding auto generated application call transaction via test context

docs/testing-guide/transactions.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ When testing smart contracts, to stay consistent with AVM, the framework _does n
143143

144144
```{testcode}
145145
class MyContract(algopy.ARC4Contract):
146-
@algopy.arc4.abimethod
146+
@algopy.public
147147
def pay_via_itxn(self, asset: algopy.Asset) -> None:
148148
algopy.itxn.Payment(
149149
receiver=algopy.Txn.sender,
@@ -180,7 +180,7 @@ first_payment_txn = first_itxn_group.payment(0)
180180

181181
In this example, we define a contract method `pay_via_itxn` that creates and submits an inner payment transaction. The test context automatically captures and stores the inner transactions submitted by the contract method.
182182

183-
Note that we don't need to wrap the execution in a `create_group` context manager because the method is decorated with `@algopy.arc4.abimethod`, which automatically creates a transaction group for the method. The `create_group` context manager is only needed when you want to create more complex transaction groups or patch transaction fields for various transaction-related opcodes in AVM.
183+
Note that we don't need to wrap the execution in a `create_group` context manager because the method is decorated with `@algopy.public`, an alias of `@algopy.arc4.abimethod`, which automatically creates a transaction group for the method. The `create_group` context manager is only needed when you want to create more complex transaction groups or patch transaction fields for various transaction-related opcodes in AVM.
184184

185185
To access the submitted inner transactions:
186186

examples/proof_of_attendance/contract.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ def __init__(self) -> None:
88
self.total_attendees = algopy.UInt64(0)
99
self.box_map = algopy.BoxMap(algopy.Bytes, algopy.UInt64)
1010

11-
@algopy.arc4.abimethod(create="require")
11+
@algopy.public(create="require")
1212
def init(self, max_attendees: algopy.UInt64) -> None:
1313
assert algopy.Txn.sender == algopy.Global.creator_address, "Only creator can initialize"
1414
self.max_attendees = max_attendees
1515

16-
@algopy.arc4.abimethod()
16+
@algopy.public()
1717
def confirm_attendance(self) -> None:
1818
assert self.total_attendees < self.max_attendees, "Max attendees reached"
1919

@@ -25,7 +25,7 @@ def confirm_attendance(self) -> None:
2525

2626
algopy.op.Box.put(algopy.Txn.sender.bytes, algopy.op.itob(minted_asset.id))
2727

28-
@algopy.arc4.abimethod()
28+
@algopy.public()
2929
def confirm_attendance_with_box(self) -> None:
3030
assert self.total_attendees < self.max_attendees, "Max attendees reached"
3131

@@ -38,7 +38,7 @@ def confirm_attendance_with_box(self) -> None:
3838

3939
box.value = minted_asset.id
4040

41-
@algopy.arc4.abimethod()
41+
@algopy.public()
4242
def confirm_attendance_with_box_ref(self) -> None:
4343
assert self.total_attendees < self.max_attendees, "Max attendees reached"
4444

@@ -51,7 +51,7 @@ def confirm_attendance_with_box_ref(self) -> None:
5151

5252
box_ref.value = algopy.op.itob(minted_asset.id)
5353

54-
@algopy.arc4.abimethod()
54+
@algopy.public()
5555
def confirm_attendance_with_box_map(self) -> None:
5656
assert self.total_attendees < self.max_attendees, "Max attendees reached"
5757

@@ -63,33 +63,33 @@ def confirm_attendance_with_box_map(self) -> None:
6363

6464
self.box_map[algopy.Txn.sender.bytes] = minted_asset.id
6565

66-
@algopy.arc4.abimethod(readonly=True)
66+
@algopy.public(readonly=True)
6767
def get_poa_id(self) -> algopy.UInt64:
6868
poa_id, exists = algopy.op.Box.get(algopy.Txn.sender.bytes)
6969
assert exists, "POA not found"
7070
return algopy.op.btoi(poa_id)
7171

72-
@algopy.arc4.abimethod(readonly=True)
72+
@algopy.public(readonly=True)
7373
def get_poa_id_with_box(self) -> algopy.UInt64:
7474
box = algopy.Box(algopy.UInt64, key=algopy.Txn.sender.bytes)
7575
poa_id, exists = box.maybe()
7676
assert exists, "POA not found"
7777
return poa_id
7878

79-
@algopy.arc4.abimethod(readonly=True)
79+
@algopy.public(readonly=True)
8080
def get_poa_id_with_box_ref(self) -> algopy.UInt64:
8181
box_ref = algopy.Box(algopy.Bytes, key=algopy.Txn.sender.bytes)
8282
poa_id, exists = box_ref.maybe()
8383
assert exists, "POA not found"
8484
return algopy.op.btoi(poa_id)
8585

86-
@algopy.arc4.abimethod(readonly=True)
86+
@algopy.public(readonly=True)
8787
def get_poa_id_with_box_map(self) -> algopy.UInt64:
8888
poa_id, exists = self.box_map.maybe(algopy.Txn.sender.bytes)
8989
assert exists, "POA not found"
9090
return poa_id
9191

92-
@algopy.arc4.abimethod()
92+
@algopy.public()
9393
def claim_poa(self, opt_in_txn: algopy.gtxn.AssetTransferTransaction) -> None:
9494
poa_id, exists = algopy.op.Box.get(algopy.Txn.sender.bytes)
9595
assert exists, "POA not found, attendance validation failed!"
@@ -108,7 +108,7 @@ def claim_poa(self, opt_in_txn: algopy.gtxn.AssetTransferTransaction) -> None:
108108
algopy.op.btoi(poa_id),
109109
)
110110

111-
@algopy.arc4.abimethod()
111+
@algopy.public()
112112
def claim_poa_with_box(self, opt_in_txn: algopy.gtxn.AssetTransferTransaction) -> None:
113113
box = algopy.Box(algopy.UInt64, key=algopy.Txn.sender.bytes)
114114
poa_id, exists = box.maybe()
@@ -128,7 +128,7 @@ def claim_poa_with_box(self, opt_in_txn: algopy.gtxn.AssetTransferTransaction) -
128128
poa_id,
129129
)
130130

131-
@algopy.arc4.abimethod()
131+
@algopy.public()
132132
def claim_poa_with_box_ref(self, opt_in_txn: algopy.gtxn.AssetTransferTransaction) -> None:
133133
box_ref = algopy.Box(algopy.Bytes, key=algopy.Txn.sender.bytes)
134134
poa_id, exists = box_ref.maybe()
@@ -148,7 +148,7 @@ def claim_poa_with_box_ref(self, opt_in_txn: algopy.gtxn.AssetTransferTransactio
148148
algopy.op.btoi(poa_id),
149149
)
150150

151-
@algopy.arc4.abimethod()
151+
@algopy.public()
152152
def claim_poa_with_box_map(self, opt_in_txn: algopy.gtxn.AssetTransferTransaction) -> None:
153153
poa_id, exists = self.box_map.maybe(algopy.Txn.sender.bytes)
154154
assert exists, "POA not found, attendance validation failed!"

src/_algopy_testing/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# ruff: noqa: I001
12
from _algopy_testing import arc4, gtxn, itxn
23
from _algopy_testing.context import AlgopyTestContext
34
from _algopy_testing.context_helpers.context_storage import algopy_testing_context
@@ -24,6 +25,9 @@
2425
from _algopy_testing.value_generators.arc4 import ARC4ValueGenerator
2526
from _algopy_testing.value_generators.avm import AVMValueGenerator
2627
from _algopy_testing.value_generators.txn import TxnValueGenerator
28+
from _algopy_testing.decorators.arc4 import (
29+
abimethod as public,
30+
)
2731

2832
__all__ = [
2933
"ARC4Contract",
@@ -60,6 +64,7 @@
6064
"gtxn",
6165
"itxn",
6266
"logicsig",
67+
"public",
6368
"subroutine",
6469
"uenumerate",
6570
"urange",

src/algopy/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# ruff: noqa: I001
12
from _algopy_testing.compiled import (
23
CompiledContract,
34
CompiledLogicSig,
@@ -33,6 +34,9 @@
3334
from _algopy_testing.utilities import OpUpFeeSource, ensure_budget, log, size_of
3435

3536
from . import arc4, gtxn, itxn, op
37+
from _algopy_testing.decorators.arc4 import (
38+
abimethod as public,
39+
)
3640

3741
__all__ = [
3842
"ARC4Contract",
@@ -76,6 +80,7 @@
7680
"log",
7781
"logicsig",
7882
"op",
83+
"public",
7984
"size_of",
8085
"subroutine",
8186
"uenumerate",

tests/arc4/test_abi_call.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
import pytest
55
from _algopy_testing import AlgopyTestContext, algopy_testing_context
66
from _algopy_testing.itxn import ApplicationCallInnerTransaction
7-
from algopy import ARC4Contract, arc4
7+
from algopy import ARC4Contract, arc4, public
88
from pytest_mock import MockerFixture
99

1010

1111
class Logger(ARC4Contract):
12-
@arc4.abimethod
12+
@public
1313
def echo(self, value: arc4.String) -> arc4.String:
1414
return "echo: " + value
1515

tests/models/test_contract.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212

1313
class ContractTxnInit(algopy.Contract):
14-
1514
def __init__(self) -> None:
1615
self.arg1 = algopy.Txn.app_args(0) if algopy.Txn.num_app_args else algopy.Bytes()
1716
self.creator = algopy.Txn.sender
@@ -32,14 +31,13 @@ class ContractARC4Create(
3231
local_uints=7,
3332
),
3433
):
35-
3634
def __init__(self) -> None:
3735
self.creator = algopy.Txn.sender
3836
self._name = algopy.String("name")
3937
self._scratch_slots = algopy.UInt64()
4038
self._state_totals = algopy.UInt64()
4139

42-
@algopy.arc4.abimethod(create="require")
40+
@algopy.public(create="require")
4341
def create(self, val: algopy.UInt64) -> None:
4442
self.arg1 = val
4543
assert algopy.Global.current_application_id.global_num_bytes == 4

0 commit comments

Comments
 (0)