Skip to content

Commit

Permalink
Add overriding name to method spec so it comes through in contract js…
Browse files Browse the repository at this point in the history
…on (#550)
  • Loading branch information
barnjamin committed Oct 7, 2022
1 parent 12f2684 commit 33a4e5e
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Fixed
* Erroring on constructing an odd length hex string. ([#539](https://github.com/algorand/pyteal/pull/539))
* Incorrect behavior when overriding a method name ([#550](https://github.com/algorand/pyteal/pull/550))

# 0.18.1

Expand Down
15 changes: 15 additions & 0 deletions docs/abi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,21 @@ Notice that even though the original :code:`get_account_status` function returns

The only exception to this transformation is if the subroutine has no return value. Without a return value, a :code:`ComputedValue` is unnecessary and the subroutine will still return an :code:`Expr` to the caller. In this case, the :code:`@ABIReturnSubroutine` decorator acts identically the :code:`@Subroutine` decorator.

The name of the subroutine constructed by the :code:`@ABIReturnSubroutine` decorator is by default the function name. In order to override the default subroutine name, the decorator :any:`ABIReturnSubroutine.name_override <ABIReturnSubroutine.name_override>` is introduced to construct a subroutine with its name overridden. An example is below:

.. code-block:: python
from pyteal import *
@ABIReturnSubroutine.name_override("increment")
def add_by_one(prev: abi.Uint32, *, output: abi.Uint32) -> Expr:
return output.set(prev.get() + Int(1))
# NOTE! In this case, the `ABIReturnSubroutine` is initialized with a name "increment"
# overriding its original name "add_by_one"
assert add_by_one.method_spec().dictify()["name"] == "increment"
Creating an ARC-4 Program
----------------------------------------------------

Expand Down
2 changes: 1 addition & 1 deletion pyteal/ast/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ def method(
# - CallConfig.Never
# both cases evaluate to False in if statement.
def wrap(_func) -> ABIReturnSubroutine:
wrapped_subroutine = ABIReturnSubroutine(_func)
wrapped_subroutine = ABIReturnSubroutine(_func, overriding_name=name)
call_configs: MethodConfig
if (
no_op is None
Expand Down
37 changes: 37 additions & 0 deletions pyteal/ast/router_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -801,3 +801,40 @@ def clear_state_method():
assemble_helper(actual_clear_state_with_method)
== expected_clear_state_with_method
)


def test_override_names():
r1 = pt.Router("test")

@r1.method(name="handle")
def handle_asa(deposit: pt.abi.AssetTransferTransaction):
"""handles the deposit where the input is an asset transfer"""
return pt.Assert(deposit.get().asset_amount() > pt.Int(0))

@r1.method(name="handle")
def handle_algo(deposit: pt.abi.PaymentTransaction):
"""handles the deposit where the input is a payment"""
return pt.Assert(deposit.get().amount() > pt.Int(0))

ap1, cs1, c1 = r1.compile_program(version=pt.compiler.MAX_PROGRAM_VERSION)
assert len(c1.methods) == 2
for meth in c1.methods:
dmeth = meth.dictify()
assert dmeth["name"] == "handle"

# Confirm an equivalent router definition _without_ `name` overrides produces the same output.
r2 = pt.Router("test")

@r2.method()
def handle(deposit: pt.abi.AssetTransferTransaction):
"""handles the deposit where the input is an asset transfer"""
return pt.Assert(deposit.get().asset_amount() > pt.Int(0))

@r2.method()
def handle(deposit: pt.abi.PaymentTransaction): # noqa: F811
"""handles the deposit where the input is a payment"""
return pt.Assert(deposit.get().amount() > pt.Int(0))

ap2, cs2, c2 = r2.compile_program(version=pt.compiler.MAX_PROGRAM_VERSION)

assert (ap1, cs1, c1) == (ap2, cs2, c2)
11 changes: 11 additions & 0 deletions pyteal/ast/subroutine.py
Original file line number Diff line number Diff line change
Expand Up @@ -519,16 +519,27 @@ def abi_sum(toSum: abi.DynamicArray[abi.Uint64], *, output: abi.Uint64) -> Expr:
def __init__(
self,
fn_implementation: Callable[..., Expr],
/,
*,
overriding_name: Optional[str] = None,
) -> None:
self.output_kwarg_info: Optional[OutputKwArgInfo] = self._get_output_kwarg_info(
fn_implementation
)
self.subroutine = SubroutineDefinition(
fn_implementation,
return_type=TealType.none,
name_str=overriding_name,
has_abi_output=self.output_kwarg_info is not None,
)

@staticmethod
def name_override(name: str) -> Callable[..., "ABIReturnSubroutine"]:
def wrapper(fn_impl: Callable[..., Expr]) -> ABIReturnSubroutine:
return ABIReturnSubroutine(fn_impl, overriding_name=name)

return wrapper

@classmethod
def _get_output_kwarg_info(
cls, fn_implementation: Callable[..., Expr]
Expand Down
18 changes: 18 additions & 0 deletions pyteal/ast/subroutine_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1464,3 +1464,21 @@ def withdraw(amount: pt.abi.Uint64, recipient: pt.abi.Account):
== "An account who will receive the withdrawn Algos. This may or may not be the same as the method call sender."
)
assert "desc" not in mspec_dict["returns"]


def test_override_abi_method_name():
def abi_meth(a: pt.abi.Uint64, b: pt.abi.Uint64, *, output: pt.abi.Uint64):
return output.set(a.get() + b.get())

mspec = ABIReturnSubroutine(abi_meth).method_spec().dictify()
assert mspec["name"] == "abi_meth"

mspec = ABIReturnSubroutine(abi_meth, overriding_name="add").method_spec().dictify()
assert mspec["name"] == "add"

@ABIReturnSubroutine.name_override("overriden_add")
def abi_meth_2(a: pt.abi.Uint64, b: pt.abi.Uint64, *, output: pt.abi.Uint64):
return output.set(a.get() + b.get())

mspec = abi_meth_2.method_spec().dictify()
assert mspec["name"] == "overriden_add"

0 comments on commit 33a4e5e

Please sign in to comment.