Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 4 additions & 2 deletions pyteal/ast/abi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@
from pyteal.ast.abi.array_base import ArrayTypeSpec, Array, ArrayElement
from pyteal.ast.abi.array_static import StaticArrayTypeSpec, StaticArray
from pyteal.ast.abi.array_dynamic import DynamicArrayTypeSpec, DynamicArray
from pyteal.ast.abi.util import type_spec_from_annotation

from pyteal.ast.abi.method_return import MethodReturn
from pyteal.ast.abi.util import type_spec_from_annotation, make

__all__ = [
"String",
Expand Down Expand Up @@ -76,6 +77,7 @@
"StaticArray",
"DynamicArrayTypeSpec",
"DynamicArray",
"type_spec_from_annotation",
"MethodReturn",
"type_spec_from_annotation",
"make",
]
31 changes: 29 additions & 2 deletions pyteal/ast/abi/util.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from typing import Any, Literal, get_origin, get_args
from typing import TypeVar, Any, Literal, get_origin, get_args, cast

from pyteal.errors import TealInputError
from pyteal.ast.expr import Expr
from pyteal.ast.int import Int
from pyteal.ast.substring import Extract, Substring, Suffix
from pyteal.ast.abi.type import TypeSpec
from pyteal.ast.abi.type import TypeSpec, BaseType


def substringForDecoding(
Expand Down Expand Up @@ -205,3 +205,30 @@ def type_spec_from_annotation(annotation: Any) -> TypeSpec:
return TupleTypeSpec(*(type_spec_from_annotation(arg) for arg in args))

raise TypeError("Unknown annotation origin: {}".format(origin))


T = TypeVar("T", bound=BaseType)


def make(t: type[T]) -> T:
"""Create a new instance of an ABI type. The type to create is determined by the input argument,
which must be a fully-specified type's class. Fully-specified means that every generic argument
is given a value.

For example:
.. code-block:: python

# both of these are equivalent
a = abi.make(abi.Tuple2[abi.Uint64, abi.StaticArray[abi.Bool, Literal[8]]])
b = abi.TupleTypeSpec(abi.Uint64TypeSpec(), abi.StaticArrayTypeSpec(abi.BoolTypeSpec(), 8))

This is purely a convenience method over instantiating the type directly, which can be cumbersome
due to the lengthy TypeSpec class names.

Args:
t: A fully-specified subclass of abi.BaseType.

Returns:
A new instance of the given type class.
"""
return cast(T, type_spec_from_annotation(t).new_instance())
10 changes: 10 additions & 0 deletions pyteal/ast/abi/util_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,3 +303,13 @@ def test_type_spec_from_annotation_is_exhaustive():
except TypeError as e:
# if subclass is generic, we should get an error that is NOT "Unknown annotation origin"
assert "Unknown annotation origin" not in str(e)


def test_make():
actual = abi.make(abi.Tuple2[abi.Uint64, abi.StaticArray[abi.Bool, Literal[8]]])
expected_type_spec = abi.TupleTypeSpec(
abi.Uint64TypeSpec(), abi.StaticArrayTypeSpec(abi.BoolTypeSpec(), 8)
)

assert actual.type_spec() == expected_type_spec
assert type(actual) is abi.Tuple