Welcome to the Algorand Python Testing library! This guide will help you get started with the project and contribute to its development.
Our development environment relies on the following tools:
- Python 3.12
- Hatch: A modern, extensible Python project manager.
- pre-commit: A framework for managing and maintaining code quality.
Here are some common commands you will use with Hatch:
- Pre-commit checks:
hatch run pre_commit - Run tests:
hatch run tests - Build project:
hatch build - Open shell:
hatch shell - Reset environments:
hatch env prune
- Regenerate typed clients for example contracts:
hatch run refresh_test_artifacts - Coverage check of existing progress on implementing AlgoPy Stubs:
hatch run check_stubs_cov
Execute pre-commit install to ensure auto run of hatch against src and examples folders
Examples folder uses a dedicated 'venv.examples' virtual environment managed by Hatch that simulates a user environment with both algorand-python and algorand-python-testing installed explicitly. This is useful for testing new features or bug fixes in the testing library.
- Pre-commit checks against examples:
hatch run examples:pre-commit
Project relies on python-semantic-release for release automation.
Releases are triggered by a workflow_dispatch event on the main branch with prerelease set to accordingly using the .github/workflows/cd.yaml file.
The canonical definition of the algopy API comes from the alogrand-python package which consists only of typing information defined in the algopy-stubs module.
In this library the testing implementation should follow the "shape" of the API defined in these stubs. In particular, types, methods and attributes should reflect what is described in the stubs.
However, implementation specific details can diverge from what is described in the stubs. For example methods marked as @staticmethod in the stubs do not need to be static methods in the implementation.
Implementation types may provide more methods or properties than described in the stubs, the stubs are just the minimal set of what should be defined.
The algopy_testing module contains the implementation of the stubs and additional parts of the testing framework.
The algopy module is used to alias the relevant algopy_testing implementations into the correct namespaces defined in the stubs.
Implementations within algopy_testing should use algopy_testing types as much as possible, except for typing annotations which should use the algopy equivalents instead. A rule of thumb to follow is
withing the algopy_testing module, import algopy should only be used for type definitions e.g.
import typing
if typing.TYPE_CHECKING:
import algopy
def do_something_with_bytes(a: algopy.Bytes) -> algopy.Bytes:
...Instance checks should be done via the algopy_testing namespace, this will ensure any additional attributes on the implementation will be type checked correctly e.g.
import algopy_testing
def do_something(value: object) -> None:
if isinstance(value, algopy_testing.Bytes):
raw_bytes: bytes = value.value
...Occasionally this will require silencing Mypy warnings, within the scope of the implementation code this is considered acceptable.
import typing
import algopy_testing
if typing.TYPE_CHECKING:
import algopy
def get_some_bytes() -> algopy.Bytes:
value = algopy_testing.Bytes(b"42")
# in the following statement, the return type warning is silenced as
# algopy.Bytes is an alias of algopy_testing.Bytes
return value # type: ignore[return-value]