Skip to content
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

Add set on address and string, change array init to accept typespec #289

Merged
merged 31 commits into from
May 4, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
efbe1e5
adding string and address types
barnjamin Apr 14, 2022
7db17ca
fix type spec, added get and thoughts on set
barnjamin Apr 14, 2022
4055424
move address to its own file, starting to add tests
barnjamin Apr 15, 2022
12a36a6
adding tests for string
barnjamin Apr 15, 2022
8a54f14
Adding slicing for a string
barnjamin Apr 15, 2022
af7967d
removed unused tests
barnjamin Apr 15, 2022
0dcc792
merged up
barnjamin Apr 15, 2022
41b10c3
merged up
barnjamin Apr 15, 2022
3e7fdfd
cr
barnjamin Apr 15, 2022
6f4a540
Merge branch 'strings' into slicey
barnjamin Apr 16, 2022
bc95806
add util function to prefix strlen
barnjamin Apr 16, 2022
9c3b5a3
fix set on address
barnjamin Apr 17, 2022
190ec16
merge feature/abi
barnjamin Apr 21, 2022
9a33018
cr from original strings pr
barnjamin Apr 21, 2022
7964aa4
maybe fix linter stuff
barnjamin Apr 21, 2022
86199ef
pass type spec to init for tuple, static array, and dynamic array
barnjamin Apr 22, 2022
c0e921e
merged feature/abi
barnjamin Apr 22, 2022
c5815a4
address mypy issues
barnjamin Apr 22, 2022
2cf6699
fmt
barnjamin Apr 22, 2022
50848df
adding tests for string set
barnjamin Apr 27, 2022
044a76c
starting to try slice testing
barnjamin Apr 27, 2022
e0f3147
remove slicing for now
barnjamin Apr 27, 2022
85cbdad
adding tests for address set
barnjamin Apr 27, 2022
50068c9
make linter happy
barnjamin Apr 27, 2022
5baead2
Merge branch 'feature/abi' into slicey
barnjamin Apr 28, 2022
c5b13c4
adding handlers for other cases, adding testing for new handlers
barnjamin Apr 28, 2022
d4b6ecf
change typespec checking
barnjamin Apr 28, 2022
58ccd76
use match for typechecks
barnjamin Apr 29, 2022
a8f8d29
adding sequence check from collections package
barnjamin Apr 29, 2022
017a7ff
partial cr
barnjamin May 2, 2022
dcf504b
use IntEnum, replace hardcoded 32 with enum value
barnjamin May 4, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions pyteal/ast/abi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
from pyteal.ast.abi.address import (
AddressTypeSpec,
Address,
ADDRESS_LENGTH_BYTES,
ADDRESS_LENGTH_STR,
AddressLength,
)
from pyteal.ast.abi.type import TypeSpec, BaseType, ComputedValue
from pyteal.ast.abi.bool import BoolTypeSpec, Bool
Expand Down Expand Up @@ -43,8 +42,7 @@
"StringTypeSpec",
"Address",
"AddressTypeSpec",
"ADDRESS_LENGTH_BYTES",
"ADDRESS_LENGTH_STR",
"AddressLength",
"TypeSpec",
"BaseType",
"ComputedValue",
Expand Down
57 changes: 38 additions & 19 deletions pyteal/ast/abi/address.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import Union, Sequence, TypeVar
from enum import Enum
from typing import Union, Sequence, Literal
from collections.abc import Sequence as CollectionSequence

from pyteal.errors import TealInputError
Expand All @@ -7,19 +8,21 @@
from pyteal.ast.addr import Addr
from pyteal.ast.abi.type import ComputedValue, BaseType
from pyteal.ast.abi.array_static import StaticArray, StaticArrayTypeSpec
from pyteal.ast.abi.uint import ByteTypeSpec
from pyteal.ast.abi.uint import ByteTypeSpec, Byte
from pyteal.ast.expr import Expr

ADDRESS_LENGTH_STR = 58
ADDRESS_LENGTH_BYTES = 32

T = TypeVar("T", bound=BaseType)
N = TypeVar("N", bound=int)
class AddressLength(Enum):
String = 58
Bytes = 32


AddressLength.__module__ = "pyteal"


class AddressTypeSpec(StaticArrayTypeSpec):
def __init__(self) -> None:
super().__init__(ByteTypeSpec(), ADDRESS_LENGTH_BYTES)
super().__init__(ByteTypeSpec(), AddressLength.Bytes.value)

def new_instance(self) -> "Address":
return Address()
Expand All @@ -34,7 +37,7 @@ def __eq__(self, other: object) -> bool:
AddressTypeSpec.__module__ = "pyteal"


class Address(StaticArray):
class Address(StaticArray[Byte, Literal[32]]):
def __init__(self) -> None:
super().__init__(AddressTypeSpec())

Expand All @@ -47,9 +50,9 @@ def get(self) -> Expr:
def set(
self,
value: Union[
Sequence[T],
StaticArray[T, N],
ComputedValue[StaticArray[T, N]],
Sequence[Byte],
StaticArray[Byte, Literal[32]],
ComputedValue[StaticArray[Byte, Literal[32]]],
"Address",
str,
bytes,
Expand All @@ -59,42 +62,58 @@ def set(

match value:
case ComputedValue():
if value.produced_type_spec() == AddressTypeSpec():
pts = value.produced_type_spec()
if pts == AddressTypeSpec() or pts == StaticArrayTypeSpec(
ByteTypeSpec(), AddressLength.Bytes.value
):
return value.store_into(self)

raise TealInputError(
f"Got ComputedValue with type spec {value.produced_type_spec()}, expected AddressTypeSpec"
f"Got ComputedValue with type spec {pts}, expected AddressTypeSpec or StaticArray[Byte, Literal[32]]"
)
case BaseType():
if (
value.type_spec() == AddressTypeSpec()
or value.type_spec()
== StaticArrayTypeSpec(ByteTypeSpec(), ADDRESS_LENGTH_BYTES)
== StaticArrayTypeSpec(ByteTypeSpec(), AddressLength.Bytes.value)
):
return self.stored_value.store(value.stored_value.load())

raise TealInputError(
f"Got {value} with type spec {value.type_spec()}, expected AddressTypeSpec"
)
case str():
if len(value) == ADDRESS_LENGTH_STR:
if len(value) == AddressLength.String.value:
return self.stored_value.store(Addr(value))
raise TealInputError(
f"Got string with length {len(value)}, expected {ADDRESS_LENGTH_STR}"
f"Got string with length {len(value)}, expected {AddressLength.String.value}"
)
case bytes():
if len(value) == ADDRESS_LENGTH_BYTES:
if len(value) == AddressLength.Bytes.value:
return self.stored_value.store(Bytes(value))
raise TealInputError(
f"Got bytes with length {len(value)}, expected {ADDRESS_LENGTH_BYTES}"
f"Got bytes with length {len(value)}, expected {AddressLength.Bytes.value}"
)
case Expr():
return self.stored_value.store(value)
case CollectionSequence():
# TODO: mypy thinks its possible for the type of `value` here to be
# Union[Sequence[Byte], str, bytes] even though we check above?
if isinstance(value, str) or isinstance(value, bytes):
return
return super().set(value)

# TODO: wdyt? something like this maybe should be in utils? or just manually update the args each time?
# from inspect import signature
# from typing import get_args
# sig = signature(self.set)
# expected = ", ".join([repr(a) for a in get_args(sig.parameters['value'].annotation)])
# raise TealInputError(
# f"Got {type(value)}, expected {expected}"
# )

raise TealInputError(
f"Got {type(value)}, expected StaticArray, ComputedValue, String, str, bytes, Expr"
f"Got {type(value)}, expected Sequence, StaticArray, ComputedValue, Address, str, bytes, Expr"
)


Expand Down
52 changes: 26 additions & 26 deletions pyteal/ast/abi/address_test.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import pytest
import pyteal as pt
from pyteal import abi
from pyteal.ast.abi.address import ADDRESS_LENGTH_BYTES

from pyteal.ast.global_ import Global, GlobalField
from pyteal.ast.abi.type_test import ContainerType
from pyteal.ast.abi.util import substringForDecoding

Expand All @@ -20,7 +18,7 @@ def test_AddressTypeSpec_is_dynamic():


def test_AddressTypeSpec_byte_length_static():
assert (abi.AddressTypeSpec()).byte_length_static() == ADDRESS_LENGTH_BYTES
assert (abi.AddressTypeSpec()).byte_length_static() == abi.AddressLength.Bytes.value


def test_AddressTypeSpec_new_instance():
Expand Down Expand Up @@ -52,7 +50,7 @@ def test_Address_encode():


def test_Address_decode():
address = bytes([0] * ADDRESS_LENGTH_BYTES)
address = bytes([0] * abi.AddressLength.Bytes.value)
encoded = pt.Bytes(address)

for startIndex in (None, pt.Int(0)):
Expand Down Expand Up @@ -107,32 +105,31 @@ def test_Address_get():


def test_Address_set_StaticArray():
sa = abi.StaticArray(
abi.StaticArrayTypeSpec(abi.ByteTypeSpec(), ADDRESS_LENGTH_BYTES)
value_to_set = abi.StaticArray(
abi.StaticArrayTypeSpec(abi.ByteTypeSpec(), abi.AddressLength.Bytes.value)
)
for value_to_set in (sa,):
value = abi.Address()
expr = value.set(value_to_set)
assert expr.type_of() == pt.TealType.none
assert not expr.has_return()
value = abi.Address()
expr = value.set(value_to_set)
assert expr.type_of() == pt.TealType.none
assert not expr.has_return()

expected = pt.TealSimpleBlock(
[
pt.TealOp(None, pt.Op.load, value_to_set.stored_value.slot),
pt.TealOp(None, pt.Op.store, value.stored_value.slot),
]
)
expected = pt.TealSimpleBlock(
[
pt.TealOp(None, pt.Op.load, value_to_set.stored_value.slot),
pt.TealOp(None, pt.Op.store, value.stored_value.slot),
]
)

actual, _ = expr.__teal__(options)
actual.addIncoming()
actual = pt.TealBlock.NormalizeBlocks(actual)
actual, _ = expr.__teal__(options)
actual.addIncoming()
actual = pt.TealBlock.NormalizeBlocks(actual)

with pt.TealComponent.Context.ignoreExprEquality():
assert actual == expected
with pt.TealComponent.Context.ignoreExprEquality():
assert actual == expected

with pytest.raises(pt.TealInputError):
bogus = abi.StaticArray(abi.StaticArrayTypeSpec(abi.ByteTypeSpec(), 10))
value.set(bogus)
with pytest.raises(pt.TealInputError):
bogus = abi.StaticArray(abi.StaticArrayTypeSpec(abi.ByteTypeSpec(), 10))
value.set(bogus)


def test_Address_set_str():
Expand Down Expand Up @@ -184,9 +181,12 @@ def test_Address_set_bytes():
with pytest.raises(pt.TealInputError):
value.set(bytes(16))

with pytest.raises(pt.TealInputError):
value.set(16)


def test_Address_set_expr():
for value_to_set in [Global(GlobalField.zero_address)]:
for value_to_set in [pt.Global(pt.GlobalField.zero_address)]:
value = abi.Address()
expr = value.set(value_to_set)
assert expr.type_of() == pt.TealType.none
Expand Down
4 changes: 2 additions & 2 deletions pyteal/ast/abi/array_dynamic.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

class DynamicArrayTypeSpec(ArrayTypeSpec[T]):
def new_instance(self) -> "DynamicArray[T]":
return DynamicArray(DynamicArrayTypeSpec(self.value_type_spec()))
return DynamicArray(self)

def is_length_dynamic(self) -> bool:
return True
Expand All @@ -47,7 +47,7 @@ def __str__(self) -> str:
class DynamicArray(Array[T]):
"""The class that represents ABI dynamic array type."""

def __init__(self, array_type_spec: DynamicArrayTypeSpec) -> None:
def __init__(self, array_type_spec: DynamicArrayTypeSpec[T]) -> None:
super().__init__(array_type_spec)

def type_spec(self) -> DynamicArrayTypeSpec[T]:
Expand Down
6 changes: 2 additions & 4 deletions pyteal/ast/abi/array_static.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ def __init__(self, value_type_spec: TypeSpec, array_length: int) -> None:
self.array_length: Final = array_length

def new_instance(self) -> "StaticArray[T, N]":
return StaticArray(
StaticArrayTypeSpec(self.value_type_spec(), self.length_static())
)
return StaticArray(self)

def length_static(self) -> int:
"""Get the size of this static array type.
Expand Down Expand Up @@ -74,7 +72,7 @@ def __str__(self) -> str:
class StaticArray(Array[T], Generic[T, N]):
"""The class that represents ABI static array type."""

def __init__(self, array_type_spec: StaticArrayTypeSpec) -> None:
def __init__(self, array_type_spec: StaticArrayTypeSpec[T, N]) -> None:
super().__init__(array_type_spec)

def type_spec(self) -> StaticArrayTypeSpec[T, N]:
Expand Down
57 changes: 23 additions & 34 deletions pyteal/ast/abi/tuple.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ def length_static(self) -> int:
return len(self.value_specs)

def new_instance(self) -> "Tuple":
return Tuple(TupleTypeSpec(*self.value_specs))
return Tuple(self)

def is_dynamic(self) -> bool:
return any(type_spec.is_dynamic() for type_spec in self.value_type_specs())
Expand Down Expand Up @@ -336,6 +336,13 @@ def store_into(self, output: BaseType) -> Expr:
# sizes.


def _tuple_raise_arg_mismatch(expected: int, typespec: TupleTypeSpec):
if len(typespec.value_specs) != expected:
raise TealInputError(
f"Expected TupleTypeSpec with {expected} elements, Got {len(typespec.value_specs)}"
)


class Tuple0(Tuple):
"""A Tuple with 0 values."""

Expand All @@ -351,8 +358,9 @@ def __init__(self) -> None:
class Tuple1(Tuple, Generic[T1]):
"""A Tuple with 1 value."""

def __init__(self, value1_type_spec: TypeSpec) -> None:
super().__init__(TupleTypeSpec(value1_type_spec))
def __init__(self, value_type_spec: TupleTypeSpec) -> None:
_tuple_raise_arg_mismatch(1, value_type_spec)
super().__init__(value_type_spec)


Tuple1.__module__ = "pyteal"
Expand All @@ -363,8 +371,9 @@ def __init__(self, value1_type_spec: TypeSpec) -> None:
class Tuple2(Tuple, Generic[T1, T2]):
"""A Tuple with 2 values."""

def __init__(self, value1_type_spec: TypeSpec, value2_type_spec: TypeSpec) -> None:
super().__init__(TupleTypeSpec(value1_type_spec, value2_type_spec))
def __init__(self, value_type_spec: TupleTypeSpec) -> None:
_tuple_raise_arg_mismatch(2, value_type_spec)
super().__init__(value_type_spec)


Tuple2.__module__ = "pyteal"
Expand All @@ -377,13 +386,10 @@ class Tuple3(Tuple, Generic[T1, T2, T3]):

def __init__(
self,
value1_type_spec: TypeSpec,
value2_type_spec: TypeSpec,
value3_type_spec: TypeSpec,
value_type_spec: TupleTypeSpec,
) -> None:
super().__init__(
TupleTypeSpec(value1_type_spec, value2_type_spec, value3_type_spec)
)
_tuple_raise_arg_mismatch(3, value_type_spec)
super().__init__(value_type_spec)


Tuple3.__module__ = "pyteal"
Expand All @@ -396,16 +402,10 @@ class Tuple4(Tuple, Generic[T1, T2, T3, T4]):

def __init__(
self,
value1_type_spec: TypeSpec,
value2_type_spec: TypeSpec,
value3_type_spec: TypeSpec,
value4_type_spec: TypeSpec,
value_type_spec: TupleTypeSpec,
) -> None:
super().__init__(
TupleTypeSpec(
value1_type_spec, value2_type_spec, value3_type_spec, value4_type_spec
)
)
_tuple_raise_arg_mismatch(4, value_type_spec)
super().__init__(value_type_spec)


Tuple4.__module__ = "pyteal"
Expand All @@ -418,21 +418,10 @@ class Tuple5(Tuple, Generic[T1, T2, T3, T4, T5]):

def __init__(
self,
value1_type_spec: TypeSpec,
value2_type_spec: TypeSpec,
value3_type_spec: TypeSpec,
value4_type_spec: TypeSpec,
value5_type_spec: TypeSpec,
value_type_spec: TupleTypeSpec,
) -> None:
super().__init__(
TupleTypeSpec(
value1_type_spec,
value2_type_spec,
value3_type_spec,
value4_type_spec,
value5_type_spec,
)
)
_tuple_raise_arg_mismatch(5, value_type_spec)
super().__init__(value_type_spec)


Tuple5.__module__ = "pyteal"