Skip to content

Commit 58bfbd4

Browse files
authored
Merge pull request #55 from algorandfoundation/feat/fixed-bytes
feat: add fixed size variant of bytes as a separate `FixedBytes` type
2 parents 6011f65 + 9886083 commit 58bfbd4

File tree

112 files changed

+2390
-1692
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

112 files changed

+2390
-1692
lines changed

docs/coverage.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
See which `algorand-python` stubs are implemented by the `algorand-python-testing` library. See the [Concepts](testing-guide/concepts.md#types-of-algopy-stub-implementations) section for more details on the implementation categories. Refer to the [`algorand-python` stubs API](api.md) for the full list of stubs for which the `algorand-python-testing` library provides implementations referenced in the table below.
44

55
| Name | Implementation type |
6-
|---------------------------------------------|---------------------|
6+
| ------------------------------------------- | ------------------- |
77
| algopy.Account | Emulated |
88
| algopy.Application | Emulated |
99
| algopy.Asset | Emulated |
@@ -18,6 +18,7 @@ See which `algorand-python` stubs are implemented by the `algorand-python-testin
1818
| algopy.CompiledLogicSig | Mockable |
1919
| algopy.Contract | Emulated |
2020
| algopy.FixedArray | Native |
21+
| algopy.FixedBytes | Native |
2122
| algopy.Global | Emulated |
2223
| algopy.GlobalState | Emulated |
2324
| algopy.ImmutableArray | Native |

docs/testing-guide/avm-types.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,22 @@ random_bytes = context.any.bytes()
5050
random_bytes = context.any.bytes(length=32)
5151
```
5252

53+
## FixedBytes
54+
55+
```{testcode}
56+
import typing
57+
58+
# Direct instantiation
59+
bytes_value = algopy.FixedBytes[typing.Literal[16]](b"Hello, Algorand!")
60+
61+
62+
# Instantiate test context
63+
...
64+
65+
# Generate random byte sequences of length 32
66+
random_bytes = context.any.fixed_bytes(algopy.FixedBytes[typing.Literal[32]])
67+
```
68+
5369
## String
5470

5571
```{testcode}

src/_algopy_testing/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
uenumerate,
2020
urange,
2121
)
22-
from _algopy_testing.primitives import BigUInt, Bytes, String, UInt64
22+
from _algopy_testing.primitives import BigUInt, Bytes, FixedBytes, String, UInt64
2323
from _algopy_testing.state import Box, BoxMap, BoxRef, GlobalState, LocalState
2424
from _algopy_testing.value_generators.arc4 import ARC4ValueGenerator
2525
from _algopy_testing.value_generators.avm import AVMValueGenerator
@@ -39,6 +39,7 @@
3939
"BoxRef",
4040
"Bytes",
4141
"Contract",
42+
"FixedBytes",
4243
"GlobalState",
4344
"ITxnGroupLoader",
4445
"ITxnLoader",

src/_algopy_testing/decorators/arc4.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from _algopy_testing.constants import ALWAYS_APPROVE_TEAL_PROGRAM, ARC4_RETURN_PREFIX
1313
from _algopy_testing.context_helpers import lazy_context
1414
from _algopy_testing.enums import OnCompleteAction
15-
from _algopy_testing.primitives import BigUInt, Bytes, String, UInt64
15+
from _algopy_testing.primitives import BigUInt, Bytes, FixedBytes, String, UInt64
1616

1717
_P = typing.ParamSpec("_P")
1818
_R = typing.TypeVar("_R")
@@ -418,6 +418,8 @@ def _type_to_arc4( # noqa: PLR0912 PLR0911
418418
return "uint512"
419419
if issubclass(annotation, Bytes):
420420
return "byte[]"
421+
if issubclass(annotation, FixedBytes):
422+
return f"byte[{annotation._length}]"
421423
if issubclass(annotation, bool):
422424
return "bool"
423425
raise TypeError(f"type not a valid ARC4 type: {annotation}")

src/_algopy_testing/primitives/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
)
1010
from _algopy_testing.primitives.biguint import BigUInt
1111
from _algopy_testing.primitives.bytes import Bytes
12+
from _algopy_testing.primitives.fixed_bytes import FixedBytes
1213
from _algopy_testing.primitives.string import String
1314
from _algopy_testing.primitives.uint64 import UInt64
1415

@@ -17,6 +18,7 @@
1718
"BigUInt",
1819
"Bytes",
1920
"FixedArray",
21+
"FixedBytes",
2022
"ImmutableArray",
2123
"ImmutableFixedArray",
2224
"ReferenceArray",

src/_algopy_testing/primitives/bytes.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
if TYPE_CHECKING:
88
from collections.abc import Iterator
99

10+
from _algopy_testing.primitives.fixed_bytes import FixedBytes
11+
1012

1113
from itertools import zip_longest
1214

@@ -27,7 +29,7 @@ def __init__(self, value: bytes = b"") -> None:
2729
check_type(value, bytes)
2830
self.value = as_bytes(value)
2931

30-
def __contains__(self, item: Bytes | bytes) -> bool:
32+
def __contains__(self, item: Bytes | FixedBytes | bytes) -> bool: # type: ignore[type-arg]
3133
item_bytes = as_bytes(item)
3234
return item_bytes in self.value
3335

@@ -43,11 +45,8 @@ def __bool__(self) -> bool:
4345
def __add__(self, other: Bytes | bytes) -> Bytes:
4446
"""Concatenate Bytes with another Bytes or bytes literal e.g. `Bytes(b"Hello ")
4547
+ b"World"`."""
46-
if isinstance(other, Bytes):
47-
return _checked_result(self.value + other.value, "+")
48-
else:
49-
result = self.value + as_bytes(other)
50-
return _checked_result(result, "+")
48+
result = self.value + as_bytes(other)
49+
return _checked_result(result, "+")
5150

5251
def __radd__(self, other: bytes) -> Bytes:
5352
"""Concatenate Bytes with another Bytes or bytes literal e.g. `b"Hello " +
@@ -70,7 +69,7 @@ def __getitem__(
7069
self, index: UInt64 | int | slice
7170
) -> Bytes: # maps to substring/substring3 if slice, extract/extract3 otherwise?
7271
"""Returns a Bytes containing a single byte if indexed with UInt64 or int
73-
otherwise the substring o bytes described by the slice."""
72+
otherwise the substring of bytes described by the slice."""
7473
if isinstance(index, slice):
7574
return Bytes(self.value[index])
7675
else:
@@ -181,7 +180,7 @@ def _checked_result(result: bytes, op: str) -> Bytes:
181180
"""Ensures `result` is a valid Bytes value.
182181
183182
Raises:
184-
ArithmeticError: If `result` of `op` is out of bounds
183+
OverflowError: If `result` of `op` is out of bounds
185184
"""
186185
if len(result) > MAX_BYTES_SIZE:
187186
raise OverflowError(f"{op} overflows")

0 commit comments

Comments
 (0)