Skip to content

Commit

Permalink
Merge branch 'release/v2.0.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
excalq committed Jan 3, 2023
2 parents 9bf39f2 + e9b8597 commit e90337d
Show file tree
Hide file tree
Showing 69 changed files with 2,552 additions and 7,523 deletions.
1 change: 1 addition & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ jobs:
- checkout
- run: pip install -r requirements.txt
- run: black --check .
- run: mypy algosdk
- run: pytest tests/unit_tests
integration-test:
parameters:
Expand Down
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
# Changelog

# v2.0.0

## What's Changed
### Breaking Changes

* Remove v1 algod API (`algosdk/algod.py`) due to API end-of-life (2022-12-01). Instead, use v2 algod API (`algosdk/v2client/algod.py`).
* Remove `algosdk.future` package. Move package contents to `algosdk`.
* Remove `encoding.future_msgpack_decode` method in favor of `encoding.msgpack_decode` method.
* Remove `cost` field in `DryrunTxnResult` in favor of 2 fields: `budget-added` and `budget-consumed`. `cost` can be derived by `budget-consumed - budget-added`.
* Remove `mnemonic.to_public_key` in favor of `account.address_from_private_key`.
* Remove logicsig templates, `algosdk/data/langspec.json` and all methods in `logic` depending on it.

### Bugfixes
* Fix: populate_foreign_array offset logic by @jgomezst in https://github.com/algorand/py-algorand-sdk/pull/406

### Enhancements
* v2: Breaking changes from v1 to v2.0.0 by @ahangsu in https://github.com/algorand/py-algorand-sdk/pull/415
* v2: Delete more references to `langspec` by @algochoi in https://github.com/algorand/py-algorand-sdk/pull/426
* LogicSig: Add LogicSig usage disclaimer by @michaeldiamant in https://github.com/algorand/py-algorand-sdk/pull/424
* Infrastructure: Only package `algosdk` in `setup.py` by @algochoi in https://github.com/algorand/py-algorand-sdk/pull/428
* Tests: Introduce type linting with mypy by @jdtzmn in https://github.com/algorand/py-algorand-sdk/pull/397


# v1.20.2

## What's Changed
Expand Down
3 changes: 2 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
include algosdk/data/langspec.json
global-include *.pyi
global-include *.typed
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ Format code:

* `black .`

Lint types:

* `mypy algosdk`

## Quick start

Here's a simple example you can run without a node.
Expand Down
3 changes: 0 additions & 3 deletions algosdk/__init__.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
from . import abi
from . import account
from . import algod
from . import auction
from . import constants
from . import dryrun_results
from . import encoding
from . import error
from . import future
from . import kmd
from . import logic
from . import mnemonic
from . import template
from . import transaction
from . import util
from . import v2client
Expand Down
11 changes: 5 additions & 6 deletions algosdk/abi/address_type.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Union
from typing import Union, cast

from algosdk.abi.base_type import ABIType
from algosdk.abi.byte_type import ByteType
Expand Down Expand Up @@ -53,15 +53,16 @@ def encode(self, value: Union[str, bytes]) -> bytes:
value = encoding.decode_address(value)
except Exception as e:
raise error.ABIEncodingError(
"cannot encode the following address: {}".format(value)
f"cannot encode the following address: {value!r}"
) from e
elif (
not (isinstance(value, bytes) or isinstance(value, bytearray))
or len(value) != 32
):
raise error.ABIEncodingError(
"cannot encode the following public key: {}".format(value)
f"cannot encode the following public key: {value!r}"
)
value = cast(bytes, value)
return bytes(value)

def decode(self, bytestring: Union[bytearray, bytes]) -> str:
Expand All @@ -82,9 +83,7 @@ def decode(self, bytestring: Union[bytearray, bytes]) -> str:
or len(bytestring) != 32
):
raise error.ABIEncodingError(
"address string must be in bytes and correspond to a byte[32]: {}".format(
bytestring
)
f"address string must be in bytes and correspond to a byte[32]: {bytestring!r}"
)
# Return the base32 encoded address string
return encoding.encode_address(bytestring)
4 changes: 1 addition & 3 deletions algosdk/abi/array_dynamic_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,7 @@ def encode(self, value_array: Union[List[Any], bytes, bytearray]) -> bytes:
or isinstance(value_array, bytearray)
) and not isinstance(self.child_type, ByteType):
raise error.ABIEncodingError(
"cannot pass in bytes when the type of the array is not ByteType: {}".format(
value_array
)
f"cannot pass in bytes when the type of the array is not ByteType: {value_array!r}"
)
converted_tuple = self._to_tuple_type(len(value_array))
length_to_encode = len(converted_tuple.child_types).to_bytes(
Expand Down
4 changes: 1 addition & 3 deletions algosdk/abi/array_static_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,7 @@ def encode(self, value_array: Union[List[Any], bytes, bytearray]) -> bytes:
or isinstance(value_array, bytearray)
) and not isinstance(self.child_type, ByteType):
raise error.ABIEncodingError(
"cannot pass in bytes when the type of the array is not ByteType: {}".format(
value_array
)
f"cannot pass in bytes when the type of the array is not ByteType: {value_array!r}"
)
converted_tuple = self._to_tuple_type()
return converted_tuple.encode(value_array)
Expand Down
11 changes: 4 additions & 7 deletions algosdk/abi/base_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ def from_string(s: str) -> "ABIType":
elif s.endswith("]"):
matches = re.search(STATIC_ARRAY_REGEX, s)
try:
static_length = int(matches.group(2))
array_type = ABIType.from_string(matches.group(1))
static_length = int(matches.group(2)) # type: ignore[union-attr] # we allow attribute errors to be caught
array_type = ABIType.from_string(matches.group(1)) # type: ignore[union-attr] # we allow attribute errors to be caught
return ArrayStaticType(array_type, static_length)
except Exception as e:
raise error.ABITypeError(
Expand All @@ -103,8 +103,8 @@ def from_string(s: str) -> "ABIType":
elif s.startswith("ufixed"):
matches = re.search(UFIXED_REGEX, s)
try:
bit_size = int(matches.group(1))
precision = int(matches.group(2))
bit_size = int(matches.group(1)) # type: ignore[union-attr] # we allow attribute errors to be caught
precision = int(matches.group(2)) # type: ignore[union-attr] # we allow attribute errors to be caught
return UfixedType(bit_size, precision)
except Exception as e:
raise error.ABITypeError(
Expand All @@ -124,9 +124,6 @@ def from_string(s: str) -> "ABIType":
if isinstance(tup, str):
tt = ABIType.from_string(tup)
tuple_list.append(tt)
elif isinstance(tup, list):
tts = [ABIType.from_string(t_) for t_ in tup]
tuple_list.append(tts)
else:
raise error.ABITypeError(
"cannot convert {} to an ABI type".format(tup)
Expand Down
6 changes: 2 additions & 4 deletions algosdk/abi/bool_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,13 @@ def decode(self, bytestring: Union[bytes, bytearray]) -> bool:
or len(bytestring) != 1
):
raise error.ABIEncodingError(
"value string must be in bytes and correspond to a bool: {}".format(
bytestring
)
f"value string must be in bytes and correspond to a bool: {bytestring!r}"
)
if bytestring == b"\x80":
return True
elif bytestring == b"\x00":
return False
else:
raise error.ABIEncodingError(
"boolean value could not be decoded: {}".format(bytestring)
f"boolean value could not be decoded: {bytestring!r}"
)
8 changes: 3 additions & 5 deletions algosdk/abi/byte_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ def encode(self, value: int) -> bytes:
)
return bytes([value])

def decode(self, bytestring: Union[bytes, bytearray]) -> bytes:
def decode(self, bytestring: Union[bytes, bytearray]) -> int:
"""
Decodes a bytestring to a single byte.
Args:
bytestring (bytes | bytearray): bytestring to be decoded
Returns:
bytes: byte of the encoded bytestring
int: byte value of the encoded bytestring
"""
if (
not (
Expand All @@ -60,8 +60,6 @@ def decode(self, bytestring: Union[bytes, bytearray]) -> bytes:
or len(bytestring) != 1
):
raise error.ABIEncodingError(
"value string must be in bytes and correspond to a byte: {}".format(
bytestring
)
f"value string must be in bytes and correspond to a byte: {bytestring!r}"
)
return bytestring[0]
36 changes: 26 additions & 10 deletions algosdk/abi/contract.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
import json
from typing import Dict, List, Union
from typing import Dict, List, Union, Optional, TypedDict

from algosdk.abi.method import Method, get_method_by_name
from algosdk.abi.method import Method, MethodDict, get_method_by_name


class NetworkInfoDict(TypedDict):
appID: int


# In Python 3.11+ the following classes should be combined using `NotRequired`
class ContractDict_Optional(TypedDict, total=False):
desc: str


class ContractDict(ContractDict_Optional):
name: str
methods: List[MethodDict]
networks: Dict[str, NetworkInfoDict]


class Contract:
Expand All @@ -20,8 +35,8 @@ def __init__(
self,
name: str,
methods: List[Method],
desc: str = None,
networks: Dict[str, "NetworkInfo"] = None,
desc: Optional[str] = None,
networks: Optional[Dict[str, "NetworkInfo"]] = None,
) -> None:
self.name = name
self.methods = methods
Expand All @@ -43,11 +58,12 @@ def from_json(resp: Union[str, bytes, bytearray]) -> "Contract":
d = json.loads(resp)
return Contract.undictify(d)

def dictify(self) -> dict:
d = {}
d["name"] = self.name
d["methods"] = [m.dictify() for m in self.methods]
d["networks"] = {k: v.dictify() for k, v in self.networks.items()}
def dictify(self) -> ContractDict:
d: ContractDict = {
"name": self.name,
"methods": [m.dictify() for m in self.methods],
"networks": {k: v.dictify() for k, v in self.networks.items()},
}
if self.desc is not None:
d["desc"] = self.desc
return d
Expand Down Expand Up @@ -84,7 +100,7 @@ def __eq__(self, o: object) -> bool:
return False
return self.app_id == o.app_id

def dictify(self) -> dict:
def dictify(self) -> NetworkInfoDict:
return {"appID": self.app_id}

@staticmethod
Expand Down
24 changes: 17 additions & 7 deletions algosdk/abi/interface.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import json
from typing import List, Union
from typing import List, Union, Optional, TypedDict

from algosdk.abi.method import Method, get_method_by_name
from algosdk.abi.method import Method, MethodDict, get_method_by_name

# In Python 3.11+ the following classes should be combined using `NotRequired`
class InterfaceDict_Optional(TypedDict, total=False):
desc: str


class InterfaceDict(InterfaceDict_Optional):
name: str
methods: List[MethodDict]


class Interface:
Expand All @@ -15,7 +24,7 @@ class Interface:
"""

def __init__(
self, name: str, methods: List[Method], desc: str = None
self, name: str, methods: List[Method], desc: Optional[str] = None
) -> None:
self.name = name
self.methods = methods
Expand All @@ -35,10 +44,11 @@ def from_json(resp: Union[str, bytes, bytearray]) -> "Interface":
d = json.loads(resp)
return Interface.undictify(d)

def dictify(self) -> dict:
d = {}
d["name"] = self.name
d["methods"] = [m.dictify() for m in self.methods]
def dictify(self) -> InterfaceDict:
d: InterfaceDict = {
"name": self.name,
"methods": [m.dictify() for m in self.methods],
}
if self.desc:
d["desc"] = self.desc
return d
Expand Down
36 changes: 25 additions & 11 deletions algosdk/abi/method.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import json
from typing import List, Union
from typing import List, Union, Optional, TypedDict

from Cryptodome.Hash import SHA512

from algosdk import abi, constants, error

# In Python 3.11+ the following classes should be combined using `NotRequired`
class MethodDict_Optional(TypedDict, total=False):
desc: str


class MethodDict(MethodDict_Optional):
name: str
args: List[dict]
returns: dict


class Method:
"""
Expand All @@ -23,7 +33,7 @@ def __init__(
name: str,
args: List["Argument"],
returns: "Returns",
desc: str = None,
desc: Optional[str] = None,
) -> None:
self.name = name
self.args = args
Expand Down Expand Up @@ -108,11 +118,12 @@ def from_signature(s: str) -> "Method":
return_type = Returns(tokens[-1])
return Method(name=tokens[0], args=argument_list, returns=return_type)

def dictify(self) -> dict:
d = {}
d["name"] = self.name
d["args"] = [arg.dictify() for arg in self.args]
d["returns"] = self.returns.dictify()
def dictify(self) -> MethodDict:
d: MethodDict = {
"name": self.name,
"args": [arg.dictify() for arg in self.args],
"returns": self.returns.dictify(),
}
if self.desc:
d["desc"] = self.desc
return d
Expand Down Expand Up @@ -156,12 +167,15 @@ class Argument:
"""

def __init__(
self, arg_type: str, name: str = None, desc: str = None
self,
arg_type: str,
name: Optional[str] = None,
desc: Optional[str] = None,
) -> None:
if abi.is_abi_transaction_type(arg_type) or abi.is_abi_reference_type(
arg_type
):
self.type = arg_type
self.type: Union[str, abi.ABIType] = arg_type
else:
# If the type cannot be parsed into an ABI type, it will error
self.type = abi.ABIType.from_string(arg_type)
Expand Down Expand Up @@ -208,9 +222,9 @@ class Returns:
# Represents a void return.
VOID = "void"

def __init__(self, arg_type: str, desc: str = None) -> None:
def __init__(self, arg_type: str, desc: Optional[str] = None) -> None:
if arg_type == "void":
self.type = self.VOID
self.type: Union[str, abi.ABIType] = self.VOID
else:
# If the type cannot be parsed into an ABI type, it will error.
self.type = abi.ABIType.from_string(arg_type)
Expand Down
2 changes: 1 addition & 1 deletion algosdk/abi/transaction.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Any

from algosdk import constants
from algosdk.future.transaction import Transaction
from algosdk.transaction import Transaction


class ABITransactionType:
Expand Down

0 comments on commit e90337d

Please sign in to comment.