-
Notifications
You must be signed in to change notification settings - Fork 131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Convenience wrapper for subroutine calls #405
Conversation
I changed the name to the decorator to the (IMO) more appropriate Ideas for further improvement: support writing the ABI types directly as type annotations in the original function and use the internal |
Added inlining of "subroutines" (technically not really subroutines any more) to better support the workflow I was doing manually before, where I would write functions to generate Now the This allows for testing things in isolation by treating all functions as subroutines, even if they are actually just meant to be inlined as part of more complex logic. I think it is relatively safe, since (as far as I'm aware) the PyTeal compiler does not do too much optimization? For example: @CallableSubroutine(
input_types=[TealType.uint64],
output_type=TealType.uint64,
inline=True
)
def square_subr(x: Expr) -> Expr:
return x ** Int(2)
@CallableSubroutine(
input_types=[TealType.uint64],
output_type=TealType.uint64,
)
def pow4_subr(x: Expr) -> Expr:
return square_subr(square_subr(x))
# yep, squaring works
assert square_subr(5) == 5 ** 2
# squaring twice works too, amazing!
assert pow4_subr(5) == 5 ** 4
# inner "subroutine" was inlined!
print(pow4_subr.report(5))
# step | PC# | L# | Teal | Scratch | Stack
# --------+-------+------+------------------------+-----------+----------------------
# 1 | 1 | 1 | #pragma version 6 | | []
# 2 | 4 | 2 | intcblock 2 | | []
# 3 | 7 | 3 | txna ApplicationArgs 0 | | [0x05]
# 4 | 8 | 4 | btoi | | [5]
# 5 | 20 | 12 | label1: | | [5]
# 6 | 22 | 13 | store 0 | 0->5 | []
# 7 | 24 | 14 | load 0 | | [5]
# 8 | 25 | 15 | intc_0 // 2 | | [5, 2]
# 9 | 26 | 16 | exp | | [25]
# 10 | 27 | 17 | intc_0 // 2 | | [25, 2]
# 11 | 28 | 18 | exp | | [625]
# 12 | 11 | 5 | callsub label1 | | [625]
# 13 | 13 | 6 | store 1 | 1->625 | []
# 14 | 15 | 7 | load 1 | | [625]
# 15 | 16 | 8 | itob | | [0x0000000000000271]
# 16 | 17 | 9 | log | | []
# 17 | 19 | 10 | load 1 | | [625]
# 18 | 29 | 19 | retsub | | [625]
# =============== This workflow may be too implicit for a lot of people though. |
Hi there, I spoke to @tzaffi on the graviton repo who suggested I make a pull request here. I wrote a
CallableSubroutine
decorator to make directly testing subroutine calls a bit more convenient for me personally. It doesn't add any new functionality, it is just a way to skip along some steps in case you only care about quickly comparing some outputs.Example use:
I removed a lot of the things I had initially implemented locally, since I was unaware of how much work had been done on the
feature/abi
branch on PyTeal integration. I tried to strip the thing whole thing down a bit, but I use something like this in my own testing and found it super convenient, since it makes it pretty easy to write unit tests the way you would for any other Python project.It's pretty hacky still and I'm sure it could be polished a lot by incorporating all the work happening on this branch, but it's been suitable for what I need and thought others might find it useful too. Hopefully I didn't miss something like this already existing in the repo, otherwise feel free to close!
It'd be pretty nice to infer the (Python-native) return types based on the ABI types too, but I only needed it for some numeric calculations and didn't want to spend too much time in case nobody else would want this. Otherwise happy to incorporate feedback. :)