From d8813d1ec0d77a1182d95c11bcbe6d4d577e1d10 Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Wed, 28 Feb 2024 15:17:21 +1000 Subject: [PATCH 01/37] feat: add compile py command - Compile Python to TEAL with Puyapy --- src/algokit/cli/__init__.py | 2 ++ src/algokit/cli/compile.py | 50 +++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 src/algokit/cli/compile.py diff --git a/src/algokit/cli/__init__.py b/src/algokit/cli/__init__.py index 2eaee190..f7e95d74 100644 --- a/src/algokit/cli/__init__.py +++ b/src/algokit/cli/__init__.py @@ -1,6 +1,7 @@ import click from algokit.cli.bootstrap import bootstrap_group +from algokit.cli.compile import compile_group from algokit.cli.completions import completions_group from algokit.cli.config import config_group from algokit.cli.deploy import deploy_command @@ -49,3 +50,4 @@ def algokit(*, skip_version_check: bool) -> None: algokit.add_command(deploy_command) algokit.add_command(dispenser_group) algokit.add_command(task_group) +algokit.add_command(compile_group) diff --git a/src/algokit/cli/compile.py b/src/algokit/cli/compile.py new file mode 100644 index 00000000..98288ba7 --- /dev/null +++ b/src/algokit/cli/compile.py @@ -0,0 +1,50 @@ +import logging + +import click + +from algokit.core.proc import run +from algokit.core.utils import find_valid_pipx_command + +logger = logging.getLogger(__name__) + + +@click.group("compile") +def compile_group() -> None: + """Compile smart contracts to TEAL""" + + +@compile_group.command( + "py", + short_help="Compile Python to TEAL with Puyapy", + context_settings={ + "ignore_unknown_options": True, + }, +) +@click.option( + "-v", + "--version", + "version", + required=False, + default=None, + help=("Puyapy compiler version. " "Default to latest"), +) +@click.argument("puya_args", nargs=-1, type=click.UNPROCESSED) +def compile_py_command(version: str | None, puya_args: list[str]) -> None: + """ + Compile Python contract(s) with Puyapy + """ + + pipx_command = find_valid_pipx_command( + "Unable to find pipx install so that `Puyapy` compiler can be installed; " + "please install pipx via https://pypa.github.io/pipx/ " + "and then try `algokit compile py ...` again." + ) + run( + [ + *pipx_command, + "run", + "puya" if version is None else f"puya=={version}", + *puya_args, + ], + bad_return_code_error_message=("Puyapy failed to compile the contract(s)"), + ) From 2b94a388ce2b082ad2477cb116e2b6a20ca824ac Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Wed, 28 Feb 2024 16:49:02 +1000 Subject: [PATCH 02/37] chore: fix PuyaPy casing --- src/algokit/cli/compile.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/algokit/cli/compile.py b/src/algokit/cli/compile.py index 98288ba7..8fa928aa 100644 --- a/src/algokit/cli/compile.py +++ b/src/algokit/cli/compile.py @@ -15,7 +15,7 @@ def compile_group() -> None: @compile_group.command( "py", - short_help="Compile Python to TEAL with Puyapy", + short_help="Compile Python to TEAL with PuyaPy", context_settings={ "ignore_unknown_options": True, }, @@ -26,16 +26,16 @@ def compile_group() -> None: "version", required=False, default=None, - help=("Puyapy compiler version. " "Default to latest"), + help=("PuyaPy compiler version. " "Default to latest"), ) @click.argument("puya_args", nargs=-1, type=click.UNPROCESSED) def compile_py_command(version: str | None, puya_args: list[str]) -> None: """ - Compile Python contract(s) with Puyapy + Compile Python contract(s) with PuyaPy """ pipx_command = find_valid_pipx_command( - "Unable to find pipx install so that `Puyapy` compiler can be installed; " + "Unable to find pipx install so that `PuyaPy` compiler can be installed; " "please install pipx via https://pypa.github.io/pipx/ " "and then try `algokit compile py ...` again." ) @@ -46,5 +46,5 @@ def compile_py_command(version: str | None, puya_args: list[str]) -> None: "puya" if version is None else f"puya=={version}", *puya_args, ], - bad_return_code_error_message=("Puyapy failed to compile the contract(s)"), + bad_return_code_error_message=("PuyaPy failed to compile the contract(s)"), ) From cd966fa7aa6f6a84e0641c5a4a16a622501eefbe Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Wed, 28 Feb 2024 17:00:15 +1000 Subject: [PATCH 03/37] chore: draft docs --- docs/features/compile.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 docs/features/compile.md diff --git a/docs/features/compile.md b/docs/features/compile.md new file mode 100644 index 00000000..eb637237 --- /dev/null +++ b/docs/features/compile.md @@ -0,0 +1,21 @@ +# AlgoKit Compile + +The AlgoKit Compile feature allows you to compile your smart contracts to TEAL. + +## Functionality + +The AlgoKit Compile feature allows you to compile your smart contracts to TEAL. + +Supported languages are: + +- Python is compiled to TEAL with [PuyaPy](https://github.com/algorandfoundation/puya). When using this feature, all PuyaPy options are supported. + +# Examples + +For example, to compile a Python smart contract and output to a directory, run + +``` +algokit compile py hello_world/contract.py --out-dir hello_world/out +``` + +Any compilation errors will be logged to the command line output. From ddf4d5fe6910203f077cb28679d2e93ef1a70fa9 Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Wed, 28 Feb 2024 17:17:44 +1000 Subject: [PATCH 04/37] chore: generate docs --- docs/cli/index.md | 114 ++++++++++++++++++++++++++++++---------------- 1 file changed, 74 insertions(+), 40 deletions(-) diff --git a/docs/cli/index.md b/docs/cli/index.md index 288da442..a202696b 100644 --- a/docs/cli/index.md +++ b/docs/cli/index.md @@ -18,64 +18,70 @@ - [--interactive, --non-interactive, --ci](#--interactive---non-interactive---ci-1) - [npm](#npm) - [poetry](#poetry) + - [compile](#compile) + - [py](#py) + - [Options](#options-4) + - [-v, --version ](#-v---version-) + - [Arguments](#arguments) + - [PUYA_ARGS](#puya_args) - [completions](#completions) - [install](#install) - - [Options](#options-4) + - [Options](#options-5) - [--shell ](#--shell-) - [uninstall](#uninstall) - - [Options](#options-5) + - [Options](#options-6) - [--shell ](#--shell--1) - [config](#config) - [version-prompt](#version-prompt) - - [Arguments](#arguments) + - [Arguments](#arguments-1) - [ENABLE](#enable) - [deploy](#deploy) - - [Options](#options-6) + - [Options](#options-7) - [-C, --command ](#-c---command-) - [--interactive, --non-interactive, --ci](#--interactive---non-interactive---ci-2) - [-P, --path ](#-p---path-) - [--deployer ](#--deployer-) - [--dispenser ](#--dispenser-) - - [Arguments](#arguments-1) + - [Arguments](#arguments-2) - [ENVIRONMENT_NAME](#environment_name) - [dispenser](#dispenser) - [fund](#fund) - - [Options](#options-7) + - [Options](#options-8) - [-r, --receiver ](#-r---receiver-) - [-a, --amount ](#-a---amount-) - [--whole-units](#--whole-units) - [limit](#limit) - - [Options](#options-8) + - [Options](#options-9) - [--whole-units](#--whole-units-1) - [login](#login) - - [Options](#options-9) + - [Options](#options-10) - [--ci](#--ci) - [-o, --output ](#-o---output-) - [-f, --file ](#-f---file-) - [logout](#logout) - [refund](#refund) - - [Options](#options-10) + - [Options](#options-11) - [-t, --txID ](#-t---txid-) - [doctor](#doctor) - - [Options](#options-11) + - [Options](#options-12) - [-c, --copy-to-clipboard](#-c---copy-to-clipboard) - [explore](#explore) - - [Arguments](#arguments-2) + - [Arguments](#arguments-3) - [NETWORK](#network) - [generate](#generate) - [client](#client) - - [Options](#options-12) + - [Options](#options-13) - [-o, --output ](#-o---output--1) - [-l, --language ](#-l---language-) - - [Arguments](#arguments-3) + - [Arguments](#arguments-4) - [APP_SPEC_PATH_OR_DIR](#app_spec_path_or_dir) - [goal](#goal) - - [Options](#options-13) + - [Options](#options-14) - [--console](#--console) - - [Arguments](#arguments-4) + - [Arguments](#arguments-5) - [GOAL_ARGS](#goal_args) - [init](#init) - - [Options](#options-14) + - [Options](#options-15) - [-n, --name ](#-n---name-) - [-t, --template ](#-t---template-) - [--template-url ](#--template-url-) @@ -91,33 +97,33 @@ - [console](#console) - [explore](#explore-1) - [logs](#logs) - - [Options](#options-15) + - [Options](#options-16) - [--follow, -f](#--follow--f) - [--tail ](#--tail-) - [reset](#reset) - - [Options](#options-16) + - [Options](#options-17) - [--update, --no-update](#--update---no-update) - [start](#start) - - [Options](#options-17) + - [Options](#options-18) - [-n, --name ](#-n---name--1) - [status](#status) - [stop](#stop) - [task](#task) - [analyze](#analyze) - - [Options](#options-18) + - [Options](#options-19) - [-r, --recursive](#-r---recursive) - [--force](#--force-1) - [--diff](#--diff) - [-o, --output ](#-o---output--2) - [-e, --exclude ](#-e---exclude-) - - [Arguments](#arguments-5) + - [Arguments](#arguments-6) - [INPUT_PATHS](#input_paths) - [ipfs](#ipfs) - - [Options](#options-19) + - [Options](#options-20) - [-f, --file ](#-f---file--1) - [-n, --name ](#-n---name--2) - [mint](#mint) - - [Options](#options-20) + - [Options](#options-21) - [--creator ](#--creator-) - [-n, --name ](#-n---name--3) - [-u, --unit ](#-u---unit-) @@ -129,37 +135,37 @@ - [--nft, --ft](#--nft---ft) - [-n, --network ](#-n---network-) - [nfd-lookup](#nfd-lookup) - - [Options](#options-21) + - [Options](#options-22) - [-o, --output ](#-o---output--3) - - [Arguments](#arguments-6) + - [Arguments](#arguments-7) - [VALUE](#value) - [opt-in](#opt-in) - - [Options](#options-22) + - [Options](#options-23) - [-a, --account ](#-a---account-) - [-n, --network ](#-n---network--1) - - [Arguments](#arguments-7) + - [Arguments](#arguments-8) - [ASSET_IDS](#asset_ids) - [opt-out](#opt-out) - - [Options](#options-23) + - [Options](#options-24) - [-a, --account ](#-a---account--1) - [--all](#--all) - [-n, --network ](#-n---network--2) - - [Arguments](#arguments-8) + - [Arguments](#arguments-9) - [ASSET_IDS](#asset_ids-1) - [send](#send) - - [Options](#options-24) + - [Options](#options-25) - [-f, --file ](#-f---file--2) - [-t, --transaction ](#-t---transaction-) - [-n, --network ](#-n---network--3) - [sign](#sign) - - [Options](#options-25) + - [Options](#options-26) - [-a, --account ](#-a---account--2) - [-f, --file ](#-f---file--3) - [-t, --transaction ](#-t---transaction--1) - [-o, --output ](#-o---output--4) - [--force](#--force-2) - [transfer](#transfer) - - [Options](#options-26) + - [Options](#options-27) - [-s, --sender ](#-s---sender-) - [-r, --receiver ](#-r---receiver--1) - [--asset, --id ](#--asset---id-) @@ -167,28 +173,28 @@ - [--whole-units](#--whole-units-2) - [-n, --network ](#-n---network--4) - [vanity-address](#vanity-address) - - [Options](#options-27) + - [Options](#options-28) - [-m, --match ](#-m---match-) - [-o, --output ](#-o---output--5) - [-a, --alias ](#-a---alias-) - [--file-path ](#--file-path-) - [-f, --force](#-f---force) - - [Arguments](#arguments-9) + - [Arguments](#arguments-10) - [KEYWORD](#keyword) - [wallet](#wallet) - - [Options](#options-28) + - [Options](#options-29) - [-a, --address ](#-a---address-) - [-m, --mnemonic](#-m---mnemonic) - [-f, --force](#-f---force-1) - - [Arguments](#arguments-10) - - [ALIAS_NAME](#alias_name) - [Arguments](#arguments-11) + - [ALIAS_NAME](#alias_name) + - [Arguments](#arguments-12) - [ALIAS](#alias) - - [Options](#options-29) + - [Options](#options-30) - [-f, --force](#-f---force-2) - - [Arguments](#arguments-12) + - [Arguments](#arguments-13) - [ALIAS](#alias-1) - - [Options](#options-30) + - [Options](#options-31) - [-f, --force](#-f---force-3) # algokit @@ -278,6 +284,34 @@ Installs Python Poetry (if not present) and runs poetry install in the current w algokit bootstrap poetry [OPTIONS] ``` +## compile + +Compile smart contracts to TEAL + +```shell +algokit compile [OPTIONS] COMMAND [ARGS]... +``` + +### py + +Compile Python contract(s) with PuyaPy + +```shell +algokit compile py [OPTIONS] [PUYA_ARGS]... +``` + +### Options + + +### -v, --version +PuyaPy compiler version. Default to latest + +### Arguments + + +### PUYA_ARGS +Optional argument(s) + ## completions Install and Uninstall AlgoKit shell integrations. From a6ca2162a3a212879b7a76ef9a03bc95622570c8 Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Wed, 28 Feb 2024 17:51:01 +1000 Subject: [PATCH 05/37] chore: test and document --- src/algokit/cli/compile.py | 1 + tests/test_root.test_help.approved.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/src/algokit/cli/compile.py b/src/algokit/cli/compile.py index 8fa928aa..09d116e9 100644 --- a/src/algokit/cli/compile.py +++ b/src/algokit/cli/compile.py @@ -16,6 +16,7 @@ def compile_group() -> None: @compile_group.command( "py", short_help="Compile Python to TEAL with PuyaPy", + help="Compile Python to TEAL with PuyaPy, review https://github.com/algorandfoundation/puya for usage", context_settings={ "ignore_unknown_options": True, }, diff --git a/tests/test_root.test_help.approved.txt b/tests/test_root.test_help.approved.txt index 7968b1a9..3111610c 100644 --- a/tests/test_root.test_help.approved.txt +++ b/tests/test_root.test_help.approved.txt @@ -16,6 +16,7 @@ Options: Commands: bootstrap Bootstrap local dependencies in an AlgoKit project; run from project root directory. + compile Compile smart contracts to TEAL completions Install and Uninstall AlgoKit shell integrations. config Configure AlgoKit settings. deploy Deploy smart contracts from AlgoKit compliant repository. From 7120d1e40b4d0ae961ad1dc16bced0d4dbcc26f9 Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Wed, 28 Feb 2024 17:53:50 +1000 Subject: [PATCH 06/37] chore: docs --- docs/cli/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cli/index.md b/docs/cli/index.md index a202696b..ab0a2f91 100644 --- a/docs/cli/index.md +++ b/docs/cli/index.md @@ -294,7 +294,7 @@ algokit compile [OPTIONS] COMMAND [ARGS]... ### py -Compile Python contract(s) with PuyaPy +Compile Python to TEAL with PuyaPy, review [https://github.com/algorandfoundation/puya](https://github.com/algorandfoundation/puya) for usage ```shell algokit compile py [OPTIONS] [PUYA_ARGS]... From 3ce4a79d778d2c7684997d1944dae5416d92cb2b Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Thu, 29 Feb 2024 14:04:48 +1000 Subject: [PATCH 07/37] chore: improve detect puyapy version --- src/algokit/cli/compile.py | 17 +++++------- src/algokit/core/compile.py | 53 +++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 11 deletions(-) create mode 100644 src/algokit/core/compile.py diff --git a/src/algokit/cli/compile.py b/src/algokit/cli/compile.py index 09d116e9..8eaefdf6 100644 --- a/src/algokit/cli/compile.py +++ b/src/algokit/cli/compile.py @@ -2,15 +2,15 @@ import click +from algokit.core.compile import find_valid_puyapy_command from algokit.core.proc import run -from algokit.core.utils import find_valid_pipx_command logger = logging.getLogger(__name__) @click.group("compile") def compile_group() -> None: - """Compile smart contracts to TEAL""" + """Compile high level language smart contracts to TEAL""" @compile_group.command( @@ -32,19 +32,14 @@ def compile_group() -> None: @click.argument("puya_args", nargs=-1, type=click.UNPROCESSED) def compile_py_command(version: str | None, puya_args: list[str]) -> None: """ - Compile Python contract(s) with PuyaPy + Compile Python contract(s) to TEAL with PuyaPy """ - pipx_command = find_valid_pipx_command( - "Unable to find pipx install so that `PuyaPy` compiler can be installed; " - "please install pipx via https://pypa.github.io/pipx/ " - "and then try `algokit compile py ...` again." - ) + puya_command = find_valid_puyapy_command(version) + run( [ - *pipx_command, - "run", - "puya" if version is None else f"puya=={version}", + *puya_command, *puya_args, ], bad_return_code_error_message=("PuyaPy failed to compile the contract(s)"), diff --git a/src/algokit/core/compile.py b/src/algokit/core/compile.py new file mode 100644 index 00000000..e5580479 --- /dev/null +++ b/src/algokit/core/compile.py @@ -0,0 +1,53 @@ +from collections.abc import Iterator + +from algokit.core.proc import run +from algokit.core.utils import extract_version_triple, find_valid_pipx_command + + +def find_valid_puyapy_command(version: str | None) -> list[str]: + puya_version_triple = extract_version_triple(version) if version is not None else None + + for puyapy_command in _get_candidates_puyapy_commands(): + try: + puyapy_version_result = run([*puyapy_command, "--version"]) + except OSError: + pass # in case of path/permission issues, go to next candidate + else: + version_output_triple = extract_version_triple(puyapy_version_result.output) + if version is None or version_output_triple == puya_version_triple: + return puyapy_command + + pipx_command = find_valid_pipx_command( + "Unable to find pipx install so that `PuyaPy` compiler can be installed; " + "please install pipx via https://pypa.github.io/pipx/ " + "and then try `algokit compile py ...` again." + ) + if puya_version_triple is None: + _install_puyapy_with_pipx(pipx_command) + return ["puyapy"] + else: + return [ + *pipx_command, + "run", + "puya" if puya_version_triple is None else f"puya=={puya_version_triple}", + ] + + +def _install_puyapy_with_pipx(pipx_command: list[str]) -> None: + run( + [ + *pipx_command, + "install", + "puya", + ], + bad_return_code_error_message=( + "Unable to install puya via pipx; please install puya manually and try `algokit compile py ...` again." + ), + ) + + +def _get_candidates_puyapy_commands() -> Iterator[list[str]]: + # when puya is installed at the project level + yield ["poetry", "run", "puyapy"] + # when puya is installed at the global level + yield ["puyapy"] From e6a7780d9b3cf28607ddb0a5c680e07cda6fb749 Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Thu, 29 Feb 2024 14:24:31 +1000 Subject: [PATCH 08/37] chore: docs --- docs/cli/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cli/index.md b/docs/cli/index.md index ab0a2f91..26c91b5c 100644 --- a/docs/cli/index.md +++ b/docs/cli/index.md @@ -286,7 +286,7 @@ algokit bootstrap poetry [OPTIONS] ## compile -Compile smart contracts to TEAL +Compile high level language smart contracts to TEAL ```shell algokit compile [OPTIONS] COMMAND [ARGS]... From c14ed797f3eb2ebd549c8c06ee2cab79ba3f1add Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Thu, 29 Feb 2024 14:28:33 +1000 Subject: [PATCH 09/37] chore: fix test --- tests/test_root.test_help.approved.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_root.test_help.approved.txt b/tests/test_root.test_help.approved.txt index 3111610c..69904174 100644 --- a/tests/test_root.test_help.approved.txt +++ b/tests/test_root.test_help.approved.txt @@ -16,7 +16,7 @@ Options: Commands: bootstrap Bootstrap local dependencies in an AlgoKit project; run from project root directory. - compile Compile smart contracts to TEAL + compile Compile high level language smart contracts to TEAL completions Install and Uninstall AlgoKit shell integrations. config Configure AlgoKit settings. deploy Deploy smart contracts from AlgoKit compliant repository. From 5af9e0b39b0ca7dedaba01da583454fd992e2d31 Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Thu, 29 Feb 2024 15:52:08 +1000 Subject: [PATCH 10/37] chore: clean up --- src/algokit/cli/compile.py | 2 +- src/algokit/core/compile.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/algokit/cli/compile.py b/src/algokit/cli/compile.py index 8eaefdf6..59aa6b7d 100644 --- a/src/algokit/cli/compile.py +++ b/src/algokit/cli/compile.py @@ -27,7 +27,7 @@ def compile_group() -> None: "version", required=False, default=None, - help=("PuyaPy compiler version. " "Default to latest"), + help=("PuyaPy compiler version. Default to latest"), ) @click.argument("puya_args", nargs=-1, type=click.UNPROCESSED) def compile_py_command(version: str | None, puya_args: list[str]) -> None: diff --git a/src/algokit/core/compile.py b/src/algokit/core/compile.py index e5580479..5ed86a76 100644 --- a/src/algokit/core/compile.py +++ b/src/algokit/core/compile.py @@ -14,7 +14,7 @@ def find_valid_puyapy_command(version: str | None) -> list[str]: pass # in case of path/permission issues, go to next candidate else: version_output_triple = extract_version_triple(puyapy_version_result.output) - if version is None or version_output_triple == puya_version_triple: + if puya_version_triple is None or version_output_triple == puya_version_triple: return puyapy_command pipx_command = find_valid_pipx_command( From 1641ff1bbebeb78c07fe41ed031035aec04ee2eb Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Thu, 29 Feb 2024 17:34:11 +1000 Subject: [PATCH 11/37] chore: bug and docs --- docs/cli/index.md | 2 +- src/algokit/cli/compile.py | 2 +- src/algokit/core/compile.py | 47 +++++++++++++++++++++++++++---------- 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/docs/cli/index.md b/docs/cli/index.md index 26c91b5c..9b8be9f0 100644 --- a/docs/cli/index.md +++ b/docs/cli/index.md @@ -304,7 +304,7 @@ algokit compile py [OPTIONS] [PUYA_ARGS]... ### -v, --version -PuyaPy compiler version. Default to latest +PuyaPy compiler version, for example, 0.6.0. Default to latest ### Arguments diff --git a/src/algokit/cli/compile.py b/src/algokit/cli/compile.py index 59aa6b7d..4b8dce11 100644 --- a/src/algokit/cli/compile.py +++ b/src/algokit/cli/compile.py @@ -27,7 +27,7 @@ def compile_group() -> None: "version", required=False, default=None, - help=("PuyaPy compiler version. Default to latest"), + help=("PuyaPy compiler version, for example, 0.6.0. Default to latest"), ) @click.argument("puya_args", nargs=-1, type=click.UNPROCESSED) def compile_py_command(version: str | None, puya_args: list[str]) -> None: diff --git a/src/algokit/core/compile.py b/src/algokit/core/compile.py index 5ed86a76..f742ffc3 100644 --- a/src/algokit/core/compile.py +++ b/src/algokit/core/compile.py @@ -5,16 +5,23 @@ def find_valid_puyapy_command(version: str | None) -> list[str]: - puya_version_triple = extract_version_triple(version) if version is not None else None + return ( + _find_puya_command_with_version_specifier(version) + if version is not None + else _find_puya_command_without_version_specifier() + ) + +def _find_puya_command_with_version_specifier(version: str) -> list[str]: for puyapy_command in _get_candidates_puyapy_commands(): try: puyapy_version_result = run([*puyapy_command, "--version"]) except OSError: pass # in case of path/permission issues, go to next candidate else: - version_output_triple = extract_version_triple(puyapy_version_result.output) - if puya_version_triple is None or version_output_triple == puya_version_triple: + if puyapy_version_result.exit_code == 0 and ( + extract_version_triple(version) == extract_version_triple(puyapy_version_result.output) + ): return puyapy_command pipx_command = find_valid_pipx_command( @@ -22,15 +29,31 @@ def find_valid_puyapy_command(version: str | None) -> list[str]: "please install pipx via https://pypa.github.io/pipx/ " "and then try `algokit compile py ...` again." ) - if puya_version_triple is None: - _install_puyapy_with_pipx(pipx_command) - return ["puyapy"] - else: - return [ - *pipx_command, - "run", - "puya" if puya_version_triple is None else f"puya=={puya_version_triple}", - ] + + return [ + *pipx_command, + "run", + f"puya=={version}", + ] + + +def _find_puya_command_without_version_specifier() -> list[str]: + for puyapy_command in _get_candidates_puyapy_commands(): + try: + puyapy_help_result = run([*puyapy_command, "-h"]) + except OSError: + pass # in case of path/permission issues, go to next candidate + else: + if puyapy_help_result.exit_code == 0: + return puyapy_command + + pipx_command = find_valid_pipx_command( + "Unable to find pipx install so that `PuyaPy` compiler can be installed; " + "please install pipx via https://pypa.github.io/pipx/ " + "and then try `algokit compile py ...` again." + ) + _install_puyapy_with_pipx(pipx_command) + return ["puyapy"] def _install_puyapy_with_pipx(pipx_command: list[str]) -> None: From b1fb1b84d59e0716b1fa8574028e5465629c412d Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Thu, 29 Feb 2024 17:37:21 +1000 Subject: [PATCH 12/37] chore: code comment --- src/algokit/core/compile.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/algokit/core/compile.py b/src/algokit/core/compile.py index f742ffc3..b5a01fd5 100644 --- a/src/algokit/core/compile.py +++ b/src/algokit/core/compile.py @@ -5,14 +5,14 @@ def find_valid_puyapy_command(version: str | None) -> list[str]: - return ( - _find_puya_command_with_version_specifier(version) - if version is not None - else _find_puya_command_without_version_specifier() - ) + return _find_puya_command_at_version(version) if version is not None else _find_puya_command() -def _find_puya_command_with_version_specifier(version: str) -> list[str]: +def _find_puya_command_at_version(version: str) -> list[str]: + """ + Find puya command with a specific version. + If the puya version isn't installed, install it with pipx run. + """ for puyapy_command in _get_candidates_puyapy_commands(): try: puyapy_version_result = run([*puyapy_command, "--version"]) @@ -37,7 +37,11 @@ def _find_puya_command_with_version_specifier(version: str) -> list[str]: ] -def _find_puya_command_without_version_specifier() -> list[str]: +def _find_puya_command() -> list[str]: + """ + Find puya command. + If puya isn't installed, install the latest version with pipx. + """ for puyapy_command in _get_candidates_puyapy_commands(): try: puyapy_help_result = run([*puyapy_command, "-h"]) From 01f0015e84ff6cc3a3749f3ccdb979ca7b3988c5 Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Thu, 29 Feb 2024 19:02:01 +1000 Subject: [PATCH 13/37] chore: first test --- tests/compile/__init__.py | 0 tests/compile/hello_world_contract.py | 0 tests/compile/test_py.py | 37 +++++++++++++++++++ .../test_py.test_compile_py_help.approved.txt | 9 +++++ ...puyapy_not_installed_anywhere.approved.txt | 10 +++++ 5 files changed, 56 insertions(+) create mode 100644 tests/compile/__init__.py create mode 100644 tests/compile/hello_world_contract.py create mode 100644 tests/compile/test_py.py create mode 100644 tests/compile/test_py.test_compile_py_help.approved.txt create mode 100644 tests/compile/test_py.test_puyapy_not_installed_anywhere.approved.txt diff --git a/tests/compile/__init__.py b/tests/compile/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/compile/hello_world_contract.py b/tests/compile/hello_world_contract.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/compile/test_py.py b/tests/compile/test_py.py new file mode 100644 index 00000000..4a62e328 --- /dev/null +++ b/tests/compile/test_py.py @@ -0,0 +1,37 @@ +from pathlib import Path + +import pytest +from pytest_mock import MockerFixture + +from tests.utils.approvals import verify +from tests.utils.click_invoker import invoke +from tests.utils.proc_mock import ProcMock + + +@pytest.fixture() +def hello_world_contract_path() -> Path: + return Path(__file__).parent / "hello_world_contract.py" + + +def test_compile_py_help() -> None: + result = invoke("compile py -h") + + assert result.exit_code == 0 + verify(result.output) + + +def test_puyapy_not_installed_anywhere(hello_world_contract_path: Path, mocker: MockerFixture) -> None: + proc_mock = ProcMock() + proc_mock.should_bad_exit_on(["poetry", "run", "puyapy", "-h"], exit_code=1, output=["Puyapy not found"]) + proc_mock.should_bad_exit_on(["puyapy", "-h"], exit_code=1, output=["Puyapy not found"]) + + proc_mock.set_output(["pipx", "--version"], ["1.0.0"]) + proc_mock.set_output(["pipx", "install", "puya"], ["Puyapy is installed"]) + proc_mock.set_output(["puyapy", str(hello_world_contract_path)], ["Done"]) + + mocker.patch("algokit.core.proc.Popen").side_effect = proc_mock.popen + + result = invoke(f"compile py {hello_world_contract_path}") + + assert result.exit_code == 0 + verify(result.output) diff --git a/tests/compile/test_py.test_compile_py_help.approved.txt b/tests/compile/test_py.test_compile_py_help.approved.txt new file mode 100644 index 00000000..e669c4ba --- /dev/null +++ b/tests/compile/test_py.test_compile_py_help.approved.txt @@ -0,0 +1,9 @@ +Usage: algokit compile py [OPTIONS] [PUYA_ARGS]... + + Compile Python to TEAL with PuyaPy, review + https://github.com/algorandfoundation/puya for usage + +Options: + -v, --version TEXT PuyaPy compiler version, for example, 0.6.0. Default to + latest + -h, --help Show this message and exit. diff --git a/tests/compile/test_py.test_puyapy_not_installed_anywhere.approved.txt b/tests/compile/test_py.test_puyapy_not_installed_anywhere.approved.txt new file mode 100644 index 00000000..af51f1f4 --- /dev/null +++ b/tests/compile/test_py.test_puyapy_not_installed_anywhere.approved.txt @@ -0,0 +1,10 @@ +DEBUG: Running 'poetry run puyapy -h' in '{current_working_directory}' +DEBUG: poetry: Puyapy not found +DEBUG: Running 'puyapy -h' in '{current_working_directory}' +DEBUG: puyapy: Puyapy not found +DEBUG: Running 'pipx --version' in '{current_working_directory}' +DEBUG: pipx: 1.0.0 +DEBUG: Running 'pipx install puya' in '{current_working_directory}' +DEBUG: pipx: Puyapy is installed +DEBUG: Running 'puyapy {current_working_directory}/tests/compile/hello_world_contract.py' in '{current_working_directory}' +DEBUG: puyapy: Done From 61ac65bfbbea4595402d6ee9dbb50696c0da32cd Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Thu, 29 Feb 2024 23:26:53 +1000 Subject: [PATCH 14/37] chore: fix tests --- tests/compile/test_py.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/compile/test_py.py b/tests/compile/test_py.py index 4a62e328..e8241312 100644 --- a/tests/compile/test_py.py +++ b/tests/compile/test_py.py @@ -8,6 +8,10 @@ from tests.utils.proc_mock import ProcMock +def _normalize_path(path: Path) -> str: + return str(path.absolute()).replace("\\", r"\\") + + @pytest.fixture() def hello_world_contract_path() -> Path: return Path(__file__).parent / "hello_world_contract.py" @@ -27,7 +31,7 @@ def test_puyapy_not_installed_anywhere(hello_world_contract_path: Path, mocker: proc_mock.set_output(["pipx", "--version"], ["1.0.0"]) proc_mock.set_output(["pipx", "install", "puya"], ["Puyapy is installed"]) - proc_mock.set_output(["puyapy", str(hello_world_contract_path)], ["Done"]) + proc_mock.set_output(["puyapy", _normalize_path(hello_world_contract_path)], ["Done"]) mocker.patch("algokit.core.proc.Popen").side_effect = proc_mock.popen From c60fa7d76eeeadd4a35eb5738d9adb98ce72c42d Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Thu, 29 Feb 2024 23:32:19 +1000 Subject: [PATCH 15/37] chore: fix tests --- tests/compile/test_py.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/compile/test_py.py b/tests/compile/test_py.py index e8241312..b3d55341 100644 --- a/tests/compile/test_py.py +++ b/tests/compile/test_py.py @@ -35,7 +35,7 @@ def test_puyapy_not_installed_anywhere(hello_world_contract_path: Path, mocker: mocker.patch("algokit.core.proc.Popen").side_effect = proc_mock.popen - result = invoke(f"compile py {hello_world_contract_path}") + result = invoke(f"compile py {_normalize_path(hello_world_contract_path)}") assert result.exit_code == 0 verify(result.output) From 24470b63d780c571083ab2fc911ceacc7cb2b9af Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Thu, 29 Feb 2024 23:43:50 +1000 Subject: [PATCH 16/37] chore: fix tests --- tests/compile/test_py.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/compile/test_py.py b/tests/compile/test_py.py index b3d55341..20d2f9d7 100644 --- a/tests/compile/test_py.py +++ b/tests/compile/test_py.py @@ -31,7 +31,7 @@ def test_puyapy_not_installed_anywhere(hello_world_contract_path: Path, mocker: proc_mock.set_output(["pipx", "--version"], ["1.0.0"]) proc_mock.set_output(["pipx", "install", "puya"], ["Puyapy is installed"]) - proc_mock.set_output(["puyapy", _normalize_path(hello_world_contract_path)], ["Done"]) + proc_mock.set_output(["puyapy", str(hello_world_contract_path)], ["Done"]) mocker.patch("algokit.core.proc.Popen").side_effect = proc_mock.popen From 71c3bd0ef662efd99eb6467f6f244d611167730c Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Fri, 1 Mar 2024 08:59:31 +1000 Subject: [PATCH 17/37] chore: tests --- tests/compile/test_py.py | 30 ++++++++++++++++++- ..._puyapy_is_installed_globally.approved.txt | 6 ++++ ...uyapy_is_installed_in_project.approved.txt | 4 +++ ...py_is_not_installed_anywhere.approved.txt} | 0 4 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 tests/compile/test_py.test_puyapy_is_installed_globally.approved.txt create mode 100644 tests/compile/test_py.test_puyapy_is_installed_in_project.approved.txt rename tests/compile/{test_py.test_puyapy_not_installed_anywhere.approved.txt => test_py.test_puyapy_is_not_installed_anywhere.approved.txt} (100%) diff --git a/tests/compile/test_py.py b/tests/compile/test_py.py index 20d2f9d7..51b472f6 100644 --- a/tests/compile/test_py.py +++ b/tests/compile/test_py.py @@ -24,7 +24,7 @@ def test_compile_py_help() -> None: verify(result.output) -def test_puyapy_not_installed_anywhere(hello_world_contract_path: Path, mocker: MockerFixture) -> None: +def test_puyapy_is_not_installed_anywhere(hello_world_contract_path: Path, mocker: MockerFixture) -> None: proc_mock = ProcMock() proc_mock.should_bad_exit_on(["poetry", "run", "puyapy", "-h"], exit_code=1, output=["Puyapy not found"]) proc_mock.should_bad_exit_on(["puyapy", "-h"], exit_code=1, output=["Puyapy not found"]) @@ -39,3 +39,31 @@ def test_puyapy_not_installed_anywhere(hello_world_contract_path: Path, mocker: assert result.exit_code == 0 verify(result.output) + + +def test_puyapy_is_installed_in_project(hello_world_contract_path: Path, mocker: MockerFixture) -> None: + proc_mock = ProcMock() + proc_mock.set_output(["poetry", "run", "puyapy", "-h"], output=["Puyapy help"]) + proc_mock.set_output(["poetry", "run", "puyapy", str(hello_world_contract_path)], ["Done"]) + + mocker.patch("algokit.core.proc.Popen").side_effect = proc_mock.popen + + result = invoke(f"compile py {_normalize_path(hello_world_contract_path)}") + + assert result.exit_code == 0 + verify(result.output) + + +def test_puyapy_is_installed_globally(hello_world_contract_path: Path, mocker: MockerFixture) -> None: + proc_mock = ProcMock() + proc_mock.should_bad_exit_on(["poetry", "run", "puyapy", "-h"], exit_code=1, output=["Puyapy not found"]) + + proc_mock.set_output(["puyapy", "-h"], output=["Puyapy help"]) + proc_mock.set_output(["puyapy", str(hello_world_contract_path)], ["Done"]) + + mocker.patch("algokit.core.proc.Popen").side_effect = proc_mock.popen + + result = invoke(f"compile py {_normalize_path(hello_world_contract_path)}") + + assert result.exit_code == 0 + verify(result.output) diff --git a/tests/compile/test_py.test_puyapy_is_installed_globally.approved.txt b/tests/compile/test_py.test_puyapy_is_installed_globally.approved.txt new file mode 100644 index 00000000..02041bce --- /dev/null +++ b/tests/compile/test_py.test_puyapy_is_installed_globally.approved.txt @@ -0,0 +1,6 @@ +DEBUG: Running 'poetry run puyapy -h' in '{current_working_directory}' +DEBUG: poetry: Puyapy not found +DEBUG: Running 'puyapy -h' in '{current_working_directory}' +DEBUG: puyapy: Puyapy help +DEBUG: Running 'puyapy {current_working_directory}/tests/compile/hello_world_contract.py' in '{current_working_directory}' +DEBUG: puyapy: Done diff --git a/tests/compile/test_py.test_puyapy_is_installed_in_project.approved.txt b/tests/compile/test_py.test_puyapy_is_installed_in_project.approved.txt new file mode 100644 index 00000000..cbadef7a --- /dev/null +++ b/tests/compile/test_py.test_puyapy_is_installed_in_project.approved.txt @@ -0,0 +1,4 @@ +DEBUG: Running 'poetry run puyapy -h' in '{current_working_directory}' +DEBUG: poetry: Puyapy help +DEBUG: Running 'poetry run puyapy {current_working_directory}/tests/compile/hello_world_contract.py' in '{current_working_directory}' +DEBUG: poetry: Done diff --git a/tests/compile/test_py.test_puyapy_not_installed_anywhere.approved.txt b/tests/compile/test_py.test_puyapy_is_not_installed_anywhere.approved.txt similarity index 100% rename from tests/compile/test_py.test_puyapy_not_installed_anywhere.approved.txt rename to tests/compile/test_py.test_puyapy_is_not_installed_anywhere.approved.txt From 6d8dde490687620738ac9eed9e9c64c98bba700e Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Fri, 1 Mar 2024 09:18:43 +1000 Subject: [PATCH 18/37] chore: test --- tests/compile/test_py.py | 20 +++++++++++++++++++ ...yapy_version_is_not_installed.approved.txt | 8 ++++++++ 2 files changed, 28 insertions(+) create mode 100644 tests/compile/test_py.test_specificed_puyapy_version_is_not_installed.approved.txt diff --git a/tests/compile/test_py.py b/tests/compile/test_py.py index 51b472f6..4a2103c3 100644 --- a/tests/compile/test_py.py +++ b/tests/compile/test_py.py @@ -30,6 +30,7 @@ def test_puyapy_is_not_installed_anywhere(hello_world_contract_path: Path, mocke proc_mock.should_bad_exit_on(["puyapy", "-h"], exit_code=1, output=["Puyapy not found"]) proc_mock.set_output(["pipx", "--version"], ["1.0.0"]) + proc_mock.set_output(["pipx", "install", "puya"], ["Puyapy is installed"]) proc_mock.set_output(["puyapy", str(hello_world_contract_path)], ["Done"]) @@ -41,6 +42,25 @@ def test_puyapy_is_not_installed_anywhere(hello_world_contract_path: Path, mocke verify(result.output) +def test_specificed_puyapy_version_is_not_installed(hello_world_contract_path: Path, mocker: MockerFixture) -> None: + current_version = "1.0.0" + target_version = "1.1.0" + + proc_mock = ProcMock() + proc_mock.set_output(["poetry", "run", "puyapy", "--version"], output=[current_version]) + proc_mock.should_bad_exit_on(["puyapy", "--version"], exit_code=1, output=["Puyapy not found"]) + + proc_mock.set_output(["pipx", "--version"], ["1.0.0"]) + proc_mock.set_output(["pipx", "run", f"puya=={target_version}", str(hello_world_contract_path)], ["Done"]) + + mocker.patch("algokit.core.proc.Popen").side_effect = proc_mock.popen + + result = invoke(f"compile py {_normalize_path(hello_world_contract_path)} --version {target_version}") + + assert result.exit_code == 0 + verify(result.output) + + def test_puyapy_is_installed_in_project(hello_world_contract_path: Path, mocker: MockerFixture) -> None: proc_mock = ProcMock() proc_mock.set_output(["poetry", "run", "puyapy", "-h"], output=["Puyapy help"]) diff --git a/tests/compile/test_py.test_specificed_puyapy_version_is_not_installed.approved.txt b/tests/compile/test_py.test_specificed_puyapy_version_is_not_installed.approved.txt new file mode 100644 index 00000000..234a4de3 --- /dev/null +++ b/tests/compile/test_py.test_specificed_puyapy_version_is_not_installed.approved.txt @@ -0,0 +1,8 @@ +DEBUG: Running 'poetry run puyapy --version' in '{current_working_directory}' +DEBUG: poetry: 1.0.0 +DEBUG: Running 'puyapy --version' in '{current_working_directory}' +DEBUG: puyapy: Puyapy not found +DEBUG: Running 'pipx --version' in '{current_working_directory}' +DEBUG: pipx: 1.0.0 +DEBUG: Running 'pipx run puya==1.1.0 {current_working_directory}/tests/compile/hello_world_contract.py' in '{current_working_directory}' +DEBUG: pipx: Done From e1836191a66b4f579cf551ffdab6137f249c09f9 Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Fri, 1 Mar 2024 09:23:59 +1000 Subject: [PATCH 19/37] chore: turns out I don't need this --- tests/compile/hello_world_contract.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 tests/compile/hello_world_contract.py diff --git a/tests/compile/hello_world_contract.py b/tests/compile/hello_world_contract.py deleted file mode 100644 index e69de29b..00000000 From 50d9ef2b2c2b8276a2483beeff54bc74c74ef988 Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Fri, 1 Mar 2024 16:36:33 +1000 Subject: [PATCH 20/37] chore: move --version flag to the group --- docs/cli/index.md | 14 +++++++------- src/algokit/cli/compile.py | 39 ++++++++++++++++++++++++++------------ 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/docs/cli/index.md b/docs/cli/index.md index 9b8be9f0..b6b94e5e 100644 --- a/docs/cli/index.md +++ b/docs/cli/index.md @@ -19,9 +19,9 @@ - [npm](#npm) - [poetry](#poetry) - [compile](#compile) - - [py](#py) - [Options](#options-4) - [-v, --version ](#-v---version-) + - [py](#py) - [Arguments](#arguments) - [PUYA_ARGS](#puya_args) - [completions](#completions) @@ -292,6 +292,12 @@ Compile high level language smart contracts to TEAL algokit compile [OPTIONS] COMMAND [ARGS]... ``` +### Options + + +### -v, --version +Compiler version, for example, 1.0.0. If the version isn't specified, AlgoKit will check if the compiler is installed locally, and execute that. If the compiler is not found, it will install the latest version. If the version is specified, AlgoKit will check if the local compiler's version satisfies, and execute that. Otherwise, AlgoKit will install the specifed compiler version. + ### py Compile Python to TEAL with PuyaPy, review [https://github.com/algorandfoundation/puya](https://github.com/algorandfoundation/puya) for usage @@ -300,12 +306,6 @@ Compile Python to TEAL with PuyaPy, review [https://github.com/algorandfoundatio algokit compile py [OPTIONS] [PUYA_ARGS]... ``` -### Options - - -### -v, --version -PuyaPy compiler version, for example, 0.6.0. Default to latest - ### Arguments diff --git a/src/algokit/cli/compile.py b/src/algokit/cli/compile.py index 4b8dce11..5913d6b7 100644 --- a/src/algokit/cli/compile.py +++ b/src/algokit/cli/compile.py @@ -9,8 +9,25 @@ @click.group("compile") -def compile_group() -> None: +@click.option( + "-v", + "--version", + "version", + required=False, + default=None, + help=( + "Compiler version, for example, 1.0.0. " + "If the version isn't specified, AlgoKit will check if the compiler is installed locally, and execute that. " + "If the compiler is not found, it will install the latest version. " + "If the version is specified, AlgoKit will check if the local compiler's version satisfies, and execute that. " + "Otherwise, AlgoKit will install the specifed compiler version." + ), +) +@click.pass_context +def compile_group(context: click.Context, version: str | None) -> None: """Compile high level language smart contracts to TEAL""" + context.ensure_object(dict) + context.obj["version"] = version @compile_group.command( @@ -20,27 +37,25 @@ def compile_group() -> None: context_settings={ "ignore_unknown_options": True, }, -) -@click.option( - "-v", - "--version", - "version", - required=False, - default=None, - help=("PuyaPy compiler version, for example, 0.6.0. Default to latest"), + add_help_option=False, ) @click.argument("puya_args", nargs=-1, type=click.UNPROCESSED) -def compile_py_command(version: str | None, puya_args: list[str]) -> None: +@click.pass_context +def compile_py_command(context: click.Context, puya_args: list[str]) -> None: """ Compile Python contract(s) to TEAL with PuyaPy """ + version = str(context.obj["version"]) if context.obj["version"] else None puya_command = find_valid_puyapy_command(version) - run( + run_result = run( [ *puya_command, *puya_args, ], - bad_return_code_error_message=("PuyaPy failed to compile the contract(s)"), ) + click.echo(run_result.output) + + if run_result.exit_code != 0: + raise click.exceptions.Exit(run_result.exit_code) From b4f5d59ae5f0c45c4e1e6350067e107755d11f58 Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Fri, 1 Mar 2024 16:45:28 +1000 Subject: [PATCH 21/37] chore: fix the tests --- tests/compile/test_py.py | 8 ++++++-- .../test_py.test_compile_py_help.approved.txt | 14 +++++--------- ....test_puyapy_is_installed_globally.approved.txt | 1 + ...est_puyapy_is_installed_in_project.approved.txt | 1 + ...t_puyapy_is_not_installed_anywhere.approved.txt | 1 + ...ed_puyapy_version_is_not_installed.approved.txt | 1 + 6 files changed, 15 insertions(+), 11 deletions(-) diff --git a/tests/compile/test_py.py b/tests/compile/test_py.py index 4a2103c3..f10dda43 100644 --- a/tests/compile/test_py.py +++ b/tests/compile/test_py.py @@ -17,7 +17,11 @@ def hello_world_contract_path() -> Path: return Path(__file__).parent / "hello_world_contract.py" -def test_compile_py_help() -> None: +def test_compile_py_help(mocker: MockerFixture) -> None: + proc_mock = ProcMock() + proc_mock.set_output(["poetry", "run", "puyapy", "-h"], output=["Puyapy help"]) + + mocker.patch("algokit.core.proc.Popen").side_effect = proc_mock.popen result = invoke("compile py -h") assert result.exit_code == 0 @@ -55,7 +59,7 @@ def test_specificed_puyapy_version_is_not_installed(hello_world_contract_path: P mocker.patch("algokit.core.proc.Popen").side_effect = proc_mock.popen - result = invoke(f"compile py {_normalize_path(hello_world_contract_path)} --version {target_version}") + result = invoke(f"compile --version {target_version} py {_normalize_path(hello_world_contract_path)}") assert result.exit_code == 0 verify(result.output) diff --git a/tests/compile/test_py.test_compile_py_help.approved.txt b/tests/compile/test_py.test_compile_py_help.approved.txt index e669c4ba..17b77cdc 100644 --- a/tests/compile/test_py.test_compile_py_help.approved.txt +++ b/tests/compile/test_py.test_compile_py_help.approved.txt @@ -1,9 +1,5 @@ -Usage: algokit compile py [OPTIONS] [PUYA_ARGS]... - - Compile Python to TEAL with PuyaPy, review - https://github.com/algorandfoundation/puya for usage - -Options: - -v, --version TEXT PuyaPy compiler version, for example, 0.6.0. Default to - latest - -h, --help Show this message and exit. +DEBUG: Running 'poetry run puyapy -h' in '{current_working_directory}' +DEBUG: poetry: Puyapy help +DEBUG: Running 'poetry run puyapy -h' in '{current_working_directory}' +DEBUG: poetry: Puyapy help +Puyapy help diff --git a/tests/compile/test_py.test_puyapy_is_installed_globally.approved.txt b/tests/compile/test_py.test_puyapy_is_installed_globally.approved.txt index 02041bce..fad9bfbd 100644 --- a/tests/compile/test_py.test_puyapy_is_installed_globally.approved.txt +++ b/tests/compile/test_py.test_puyapy_is_installed_globally.approved.txt @@ -4,3 +4,4 @@ DEBUG: Running 'puyapy -h' in '{current_working_directory}' DEBUG: puyapy: Puyapy help DEBUG: Running 'puyapy {current_working_directory}/tests/compile/hello_world_contract.py' in '{current_working_directory}' DEBUG: puyapy: Done +Done diff --git a/tests/compile/test_py.test_puyapy_is_installed_in_project.approved.txt b/tests/compile/test_py.test_puyapy_is_installed_in_project.approved.txt index cbadef7a..5a24e571 100644 --- a/tests/compile/test_py.test_puyapy_is_installed_in_project.approved.txt +++ b/tests/compile/test_py.test_puyapy_is_installed_in_project.approved.txt @@ -2,3 +2,4 @@ DEBUG: Running 'poetry run puyapy -h' in '{current_working_directory}' DEBUG: poetry: Puyapy help DEBUG: Running 'poetry run puyapy {current_working_directory}/tests/compile/hello_world_contract.py' in '{current_working_directory}' DEBUG: poetry: Done +Done diff --git a/tests/compile/test_py.test_puyapy_is_not_installed_anywhere.approved.txt b/tests/compile/test_py.test_puyapy_is_not_installed_anywhere.approved.txt index af51f1f4..5db9082e 100644 --- a/tests/compile/test_py.test_puyapy_is_not_installed_anywhere.approved.txt +++ b/tests/compile/test_py.test_puyapy_is_not_installed_anywhere.approved.txt @@ -8,3 +8,4 @@ DEBUG: Running 'pipx install puya' in '{current_working_directory}' DEBUG: pipx: Puyapy is installed DEBUG: Running 'puyapy {current_working_directory}/tests/compile/hello_world_contract.py' in '{current_working_directory}' DEBUG: puyapy: Done +Done diff --git a/tests/compile/test_py.test_specificed_puyapy_version_is_not_installed.approved.txt b/tests/compile/test_py.test_specificed_puyapy_version_is_not_installed.approved.txt index 234a4de3..d8f49061 100644 --- a/tests/compile/test_py.test_specificed_puyapy_version_is_not_installed.approved.txt +++ b/tests/compile/test_py.test_specificed_puyapy_version_is_not_installed.approved.txt @@ -6,3 +6,4 @@ DEBUG: Running 'pipx --version' in '{current_working_directory}' DEBUG: pipx: 1.0.0 DEBUG: Running 'pipx run puya==1.1.0 {current_working_directory}/tests/compile/hello_world_contract.py' in '{current_working_directory}' DEBUG: pipx: Done +Done From ace7e4aa8648304e01a5652d50b5792c721158bf Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Mon, 4 Mar 2024 09:23:31 +1000 Subject: [PATCH 22/37] chore: disable compile_group command --- docs/cli/index.md | 114 +++++++++---------------- pyproject.toml | 2 +- src/algokit/cli/__init__.py | 4 +- tests/test_root.test_help.approved.txt | 1 - 4 files changed, 43 insertions(+), 78 deletions(-) diff --git a/docs/cli/index.md b/docs/cli/index.md index b6b94e5e..288da442 100644 --- a/docs/cli/index.md +++ b/docs/cli/index.md @@ -18,70 +18,64 @@ - [--interactive, --non-interactive, --ci](#--interactive---non-interactive---ci-1) - [npm](#npm) - [poetry](#poetry) - - [compile](#compile) - - [Options](#options-4) - - [-v, --version ](#-v---version-) - - [py](#py) - - [Arguments](#arguments) - - [PUYA_ARGS](#puya_args) - [completions](#completions) - [install](#install) - - [Options](#options-5) + - [Options](#options-4) - [--shell ](#--shell-) - [uninstall](#uninstall) - - [Options](#options-6) + - [Options](#options-5) - [--shell ](#--shell--1) - [config](#config) - [version-prompt](#version-prompt) - - [Arguments](#arguments-1) + - [Arguments](#arguments) - [ENABLE](#enable) - [deploy](#deploy) - - [Options](#options-7) + - [Options](#options-6) - [-C, --command ](#-c---command-) - [--interactive, --non-interactive, --ci](#--interactive---non-interactive---ci-2) - [-P, --path ](#-p---path-) - [--deployer ](#--deployer-) - [--dispenser ](#--dispenser-) - - [Arguments](#arguments-2) + - [Arguments](#arguments-1) - [ENVIRONMENT_NAME](#environment_name) - [dispenser](#dispenser) - [fund](#fund) - - [Options](#options-8) + - [Options](#options-7) - [-r, --receiver ](#-r---receiver-) - [-a, --amount ](#-a---amount-) - [--whole-units](#--whole-units) - [limit](#limit) - - [Options](#options-9) + - [Options](#options-8) - [--whole-units](#--whole-units-1) - [login](#login) - - [Options](#options-10) + - [Options](#options-9) - [--ci](#--ci) - [-o, --output ](#-o---output-) - [-f, --file ](#-f---file-) - [logout](#logout) - [refund](#refund) - - [Options](#options-11) + - [Options](#options-10) - [-t, --txID ](#-t---txid-) - [doctor](#doctor) - - [Options](#options-12) + - [Options](#options-11) - [-c, --copy-to-clipboard](#-c---copy-to-clipboard) - [explore](#explore) - - [Arguments](#arguments-3) + - [Arguments](#arguments-2) - [NETWORK](#network) - [generate](#generate) - [client](#client) - - [Options](#options-13) + - [Options](#options-12) - [-o, --output ](#-o---output--1) - [-l, --language ](#-l---language-) - - [Arguments](#arguments-4) + - [Arguments](#arguments-3) - [APP_SPEC_PATH_OR_DIR](#app_spec_path_or_dir) - [goal](#goal) - - [Options](#options-14) + - [Options](#options-13) - [--console](#--console) - - [Arguments](#arguments-5) + - [Arguments](#arguments-4) - [GOAL_ARGS](#goal_args) - [init](#init) - - [Options](#options-15) + - [Options](#options-14) - [-n, --name ](#-n---name-) - [-t, --template ](#-t---template-) - [--template-url ](#--template-url-) @@ -97,33 +91,33 @@ - [console](#console) - [explore](#explore-1) - [logs](#logs) - - [Options](#options-16) + - [Options](#options-15) - [--follow, -f](#--follow--f) - [--tail ](#--tail-) - [reset](#reset) - - [Options](#options-17) + - [Options](#options-16) - [--update, --no-update](#--update---no-update) - [start](#start) - - [Options](#options-18) + - [Options](#options-17) - [-n, --name ](#-n---name--1) - [status](#status) - [stop](#stop) - [task](#task) - [analyze](#analyze) - - [Options](#options-19) + - [Options](#options-18) - [-r, --recursive](#-r---recursive) - [--force](#--force-1) - [--diff](#--diff) - [-o, --output ](#-o---output--2) - [-e, --exclude ](#-e---exclude-) - - [Arguments](#arguments-6) + - [Arguments](#arguments-5) - [INPUT_PATHS](#input_paths) - [ipfs](#ipfs) - - [Options](#options-20) + - [Options](#options-19) - [-f, --file ](#-f---file--1) - [-n, --name ](#-n---name--2) - [mint](#mint) - - [Options](#options-21) + - [Options](#options-20) - [--creator ](#--creator-) - [-n, --name ](#-n---name--3) - [-u, --unit ](#-u---unit-) @@ -135,37 +129,37 @@ - [--nft, --ft](#--nft---ft) - [-n, --network ](#-n---network-) - [nfd-lookup](#nfd-lookup) - - [Options](#options-22) + - [Options](#options-21) - [-o, --output ](#-o---output--3) - - [Arguments](#arguments-7) + - [Arguments](#arguments-6) - [VALUE](#value) - [opt-in](#opt-in) - - [Options](#options-23) + - [Options](#options-22) - [-a, --account ](#-a---account-) - [-n, --network ](#-n---network--1) - - [Arguments](#arguments-8) + - [Arguments](#arguments-7) - [ASSET_IDS](#asset_ids) - [opt-out](#opt-out) - - [Options](#options-24) + - [Options](#options-23) - [-a, --account ](#-a---account--1) - [--all](#--all) - [-n, --network ](#-n---network--2) - - [Arguments](#arguments-9) + - [Arguments](#arguments-8) - [ASSET_IDS](#asset_ids-1) - [send](#send) - - [Options](#options-25) + - [Options](#options-24) - [-f, --file ](#-f---file--2) - [-t, --transaction ](#-t---transaction-) - [-n, --network ](#-n---network--3) - [sign](#sign) - - [Options](#options-26) + - [Options](#options-25) - [-a, --account ](#-a---account--2) - [-f, --file ](#-f---file--3) - [-t, --transaction ](#-t---transaction--1) - [-o, --output ](#-o---output--4) - [--force](#--force-2) - [transfer](#transfer) - - [Options](#options-27) + - [Options](#options-26) - [-s, --sender ](#-s---sender-) - [-r, --receiver ](#-r---receiver--1) - [--asset, --id ](#--asset---id-) @@ -173,28 +167,28 @@ - [--whole-units](#--whole-units-2) - [-n, --network ](#-n---network--4) - [vanity-address](#vanity-address) - - [Options](#options-28) + - [Options](#options-27) - [-m, --match ](#-m---match-) - [-o, --output ](#-o---output--5) - [-a, --alias ](#-a---alias-) - [--file-path ](#--file-path-) - [-f, --force](#-f---force) - - [Arguments](#arguments-10) + - [Arguments](#arguments-9) - [KEYWORD](#keyword) - [wallet](#wallet) - - [Options](#options-29) + - [Options](#options-28) - [-a, --address ](#-a---address-) - [-m, --mnemonic](#-m---mnemonic) - [-f, --force](#-f---force-1) - - [Arguments](#arguments-11) + - [Arguments](#arguments-10) - [ALIAS_NAME](#alias_name) - - [Arguments](#arguments-12) + - [Arguments](#arguments-11) - [ALIAS](#alias) - - [Options](#options-30) + - [Options](#options-29) - [-f, --force](#-f---force-2) - - [Arguments](#arguments-13) + - [Arguments](#arguments-12) - [ALIAS](#alias-1) - - [Options](#options-31) + - [Options](#options-30) - [-f, --force](#-f---force-3) # algokit @@ -284,34 +278,6 @@ Installs Python Poetry (if not present) and runs poetry install in the current w algokit bootstrap poetry [OPTIONS] ``` -## compile - -Compile high level language smart contracts to TEAL - -```shell -algokit compile [OPTIONS] COMMAND [ARGS]... -``` - -### Options - - -### -v, --version -Compiler version, for example, 1.0.0. If the version isn't specified, AlgoKit will check if the compiler is installed locally, and execute that. If the compiler is not found, it will install the latest version. If the version is specified, AlgoKit will check if the local compiler's version satisfies, and execute that. Otherwise, AlgoKit will install the specifed compiler version. - -### py - -Compile Python to TEAL with PuyaPy, review [https://github.com/algorandfoundation/puya](https://github.com/algorandfoundation/puya) for usage - -```shell -algokit compile py [OPTIONS] [PUYA_ARGS]... -``` - -### Arguments - - -### PUYA_ARGS -Optional argument(s) - ## completions Install and Uninstall AlgoKit shell integrations. diff --git a/pyproject.toml b/pyproject.toml index 2ced74c5..3c0cff19 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -161,7 +161,7 @@ markers = [ "mock_platform_system", "pyinstaller_binary_tests" ] -addopts = "-m 'not pyinstaller_binary_tests'" # by default, exclude pyinstaller_binary_tests +addopts = "-m 'not pyinstaller_binary_tests' --ignore=tests/compile" # by default, exclude pyinstaller_binary_tests [tool.mypy] files = ["src", "tests"] exclude = ["dist"] diff --git a/src/algokit/cli/__init__.py b/src/algokit/cli/__init__.py index f7e95d74..22987589 100644 --- a/src/algokit/cli/__init__.py +++ b/src/algokit/cli/__init__.py @@ -1,7 +1,6 @@ import click from algokit.cli.bootstrap import bootstrap_group -from algokit.cli.compile import compile_group from algokit.cli.completions import completions_group from algokit.cli.config import config_group from algokit.cli.deploy import deploy_command @@ -50,4 +49,5 @@ def algokit(*, skip_version_check: bool) -> None: algokit.add_command(deploy_command) algokit.add_command(dispenser_group) algokit.add_command(task_group) -algokit.add_command(compile_group) +# TODO enable pytest for tests/compile after adding this back +# algokit.add_command(compile_group) # noqa: ERA001 diff --git a/tests/test_root.test_help.approved.txt b/tests/test_root.test_help.approved.txt index 69904174..7968b1a9 100644 --- a/tests/test_root.test_help.approved.txt +++ b/tests/test_root.test_help.approved.txt @@ -16,7 +16,6 @@ Options: Commands: bootstrap Bootstrap local dependencies in an AlgoKit project; run from project root directory. - compile Compile high level language smart contracts to TEAL completions Install and Uninstall AlgoKit shell integrations. config Configure AlgoKit settings. deploy Deploy smart contracts from AlgoKit compliant repository. From fab33151d1763c9e87dd89164f5e64fabc50e962 Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Mon, 4 Mar 2024 20:00:25 +1000 Subject: [PATCH 23/37] chore: add the compile group back, hidden from users --- pyproject.toml | 2 +- src/algokit/cli/__init__.py | 4 ++-- src/algokit/cli/compile.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 3c0cff19..2ced74c5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -161,7 +161,7 @@ markers = [ "mock_platform_system", "pyinstaller_binary_tests" ] -addopts = "-m 'not pyinstaller_binary_tests' --ignore=tests/compile" # by default, exclude pyinstaller_binary_tests +addopts = "-m 'not pyinstaller_binary_tests'" # by default, exclude pyinstaller_binary_tests [tool.mypy] files = ["src", "tests"] exclude = ["dist"] diff --git a/src/algokit/cli/__init__.py b/src/algokit/cli/__init__.py index 22987589..f7e95d74 100644 --- a/src/algokit/cli/__init__.py +++ b/src/algokit/cli/__init__.py @@ -1,6 +1,7 @@ import click from algokit.cli.bootstrap import bootstrap_group +from algokit.cli.compile import compile_group from algokit.cli.completions import completions_group from algokit.cli.config import config_group from algokit.cli.deploy import deploy_command @@ -49,5 +50,4 @@ def algokit(*, skip_version_check: bool) -> None: algokit.add_command(deploy_command) algokit.add_command(dispenser_group) algokit.add_command(task_group) -# TODO enable pytest for tests/compile after adding this back -# algokit.add_command(compile_group) # noqa: ERA001 +algokit.add_command(compile_group) diff --git a/src/algokit/cli/compile.py b/src/algokit/cli/compile.py index 5913d6b7..4064014e 100644 --- a/src/algokit/cli/compile.py +++ b/src/algokit/cli/compile.py @@ -8,7 +8,7 @@ logger = logging.getLogger(__name__) -@click.group("compile") +@click.group("compile", hidden=True) @click.option( "-v", "--version", From 2f4ad48dcae9f346118dfa0099b1e277a521cccc Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Mon, 4 Mar 2024 20:01:54 +1000 Subject: [PATCH 24/37] chore: delete doc --- docs/features/compile.md | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 docs/features/compile.md diff --git a/docs/features/compile.md b/docs/features/compile.md deleted file mode 100644 index eb637237..00000000 --- a/docs/features/compile.md +++ /dev/null @@ -1,21 +0,0 @@ -# AlgoKit Compile - -The AlgoKit Compile feature allows you to compile your smart contracts to TEAL. - -## Functionality - -The AlgoKit Compile feature allows you to compile your smart contracts to TEAL. - -Supported languages are: - -- Python is compiled to TEAL with [PuyaPy](https://github.com/algorandfoundation/puya). When using this feature, all PuyaPy options are supported. - -# Examples - -For example, to compile a Python smart contract and output to a directory, run - -``` -algokit compile py hello_world/contract.py --out-dir hello_world/out -``` - -Any compilation errors will be logged to the command line output. From 3f043d94792b4d7fad1bbc20e11bc51830fae914 Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Mon, 4 Mar 2024 23:20:58 +1000 Subject: [PATCH 25/37] chore: pr feedback --- src/algokit/cli/compile.py | 5 ++ tests/compile/conftest.py | 27 ++++++++ tests/compile/test_py.py | 68 +++++++++++++++---- ...test_py.test_invalid_contract.approved.txt | 67 ++++++++++++++++++ ..._contract.contract.clear.teal.approved.txt | 0 ..._puyapy_is_installed_globally.approved.txt | 2 +- ...uyapy_is_installed_in_project.approved.txt | 2 +- ...apy_is_not_installed_anywhere.approved.txt | 2 +- ...yapy_version_is_not_installed.approved.txt | 2 +- ...ntract.contract.approval.teal.approved.txt | 17 +++++ ..._contract.contract.clear.teal.approved.txt | 7 ++ 11 files changed, 180 insertions(+), 19 deletions(-) create mode 100644 tests/compile/conftest.py create mode 100644 tests/compile/test_py.test_invalid_contract.approved.txt create mode 100644 tests/compile/test_py.test_invalid_contract.contract.clear.teal.approved.txt create mode 100644 tests/compile/test_py.test_valid_contract.contract.approval.teal.approved.txt create mode 100644 tests/compile/test_py.test_valid_contract.contract.clear.teal.approved.txt diff --git a/src/algokit/cli/compile.py b/src/algokit/cli/compile.py index 4064014e..f78527d8 100644 --- a/src/algokit/cli/compile.py +++ b/src/algokit/cli/compile.py @@ -58,4 +58,9 @@ def compile_py_command(context: click.Context, puya_args: list[str]) -> None: click.echo(run_result.output) if run_result.exit_code != 0: + click.secho( + "An error occurred during compile. Ensure supplied files are valid PuyaPy code before retrying.", + err=True, + fg="red", + ) raise click.exceptions.Exit(run_result.exit_code) diff --git a/tests/compile/conftest.py b/tests/compile/conftest.py new file mode 100644 index 00000000..44dfde6b --- /dev/null +++ b/tests/compile/conftest.py @@ -0,0 +1,27 @@ +VALID_PUYA_CONTRACT_FILE_CONTENT = """ +from puyapy import Contract, Txn, log + + +class HelloWorldContract(Contract): + def approval_program(self) -> bool: + name = Txn.application_args(0) + log(b"Hello, " + name) + return True + + def clear_state_program(self) -> bool: + return True +""" + +INVALID_PUYA_CONTRACT_FILE_CONTENT = """ +from puyapy import Contract, Txn, log + + +class HelloWorldContract(Contract): + def approval_program(self) -> bool: + name = Txn.application_args_invalid(0) + log(b"Hello, " + name) + return True + + def clear_state_program(self) -> bool: + return True +""" diff --git a/tests/compile/test_py.py b/tests/compile/test_py.py index f10dda43..665dbceb 100644 --- a/tests/compile/test_py.py +++ b/tests/compile/test_py.py @@ -1,9 +1,12 @@ +import os from pathlib import Path import pytest +from approvaltests.namer import NamerFactory from pytest_mock import MockerFixture -from tests.utils.approvals import verify +from tests.compile.conftest import INVALID_PUYA_CONTRACT_FILE_CONTENT, VALID_PUYA_CONTRACT_FILE_CONTENT +from tests.utils.approvals import normalize_path, verify from tests.utils.click_invoker import invoke from tests.utils.proc_mock import ProcMock @@ -13,8 +16,18 @@ def _normalize_path(path: Path) -> str: @pytest.fixture() -def hello_world_contract_path() -> Path: - return Path(__file__).parent / "hello_world_contract.py" +def dummy_contract_path() -> Path: + return Path(__file__).parent / "dummy_contract.py" + + +@pytest.fixture(autouse=True) +def cwd(tmp_path_factory: pytest.TempPathFactory) -> Path: + return tmp_path_factory.mktemp("cwd", numbered=True) + + +@pytest.fixture() +def output_path(cwd: Path) -> Path: + return cwd / "output" def test_compile_py_help(mocker: MockerFixture) -> None: @@ -28,7 +41,7 @@ def test_compile_py_help(mocker: MockerFixture) -> None: verify(result.output) -def test_puyapy_is_not_installed_anywhere(hello_world_contract_path: Path, mocker: MockerFixture) -> None: +def test_puyapy_is_not_installed_anywhere(dummy_contract_path: Path, mocker: MockerFixture) -> None: proc_mock = ProcMock() proc_mock.should_bad_exit_on(["poetry", "run", "puyapy", "-h"], exit_code=1, output=["Puyapy not found"]) proc_mock.should_bad_exit_on(["puyapy", "-h"], exit_code=1, output=["Puyapy not found"]) @@ -36,17 +49,17 @@ def test_puyapy_is_not_installed_anywhere(hello_world_contract_path: Path, mocke proc_mock.set_output(["pipx", "--version"], ["1.0.0"]) proc_mock.set_output(["pipx", "install", "puya"], ["Puyapy is installed"]) - proc_mock.set_output(["puyapy", str(hello_world_contract_path)], ["Done"]) + proc_mock.set_output(["puyapy", str(dummy_contract_path)], ["Done"]) mocker.patch("algokit.core.proc.Popen").side_effect = proc_mock.popen - result = invoke(f"compile py {_normalize_path(hello_world_contract_path)}") + result = invoke(f"compile py {_normalize_path(dummy_contract_path)}") assert result.exit_code == 0 verify(result.output) -def test_specificed_puyapy_version_is_not_installed(hello_world_contract_path: Path, mocker: MockerFixture) -> None: +def test_specificed_puyapy_version_is_not_installed(dummy_contract_path: Path, mocker: MockerFixture) -> None: current_version = "1.0.0" target_version = "1.1.0" @@ -55,39 +68,64 @@ def test_specificed_puyapy_version_is_not_installed(hello_world_contract_path: P proc_mock.should_bad_exit_on(["puyapy", "--version"], exit_code=1, output=["Puyapy not found"]) proc_mock.set_output(["pipx", "--version"], ["1.0.0"]) - proc_mock.set_output(["pipx", "run", f"puya=={target_version}", str(hello_world_contract_path)], ["Done"]) + proc_mock.set_output(["pipx", "run", f"puya=={target_version}", str(dummy_contract_path)], ["Done"]) mocker.patch("algokit.core.proc.Popen").side_effect = proc_mock.popen - result = invoke(f"compile --version {target_version} py {_normalize_path(hello_world_contract_path)}") + result = invoke(f"compile --version {target_version} py {_normalize_path(dummy_contract_path)}") assert result.exit_code == 0 verify(result.output) -def test_puyapy_is_installed_in_project(hello_world_contract_path: Path, mocker: MockerFixture) -> None: +def test_puyapy_is_installed_in_project(dummy_contract_path: Path, mocker: MockerFixture) -> None: proc_mock = ProcMock() proc_mock.set_output(["poetry", "run", "puyapy", "-h"], output=["Puyapy help"]) - proc_mock.set_output(["poetry", "run", "puyapy", str(hello_world_contract_path)], ["Done"]) + proc_mock.set_output(["poetry", "run", "puyapy", str(dummy_contract_path)], ["Done"]) mocker.patch("algokit.core.proc.Popen").side_effect = proc_mock.popen - result = invoke(f"compile py {_normalize_path(hello_world_contract_path)}") + result = invoke(f"compile py {_normalize_path(dummy_contract_path)}") assert result.exit_code == 0 verify(result.output) -def test_puyapy_is_installed_globally(hello_world_contract_path: Path, mocker: MockerFixture) -> None: +def test_puyapy_is_installed_globally(dummy_contract_path: Path, mocker: MockerFixture) -> None: proc_mock = ProcMock() proc_mock.should_bad_exit_on(["poetry", "run", "puyapy", "-h"], exit_code=1, output=["Puyapy not found"]) proc_mock.set_output(["puyapy", "-h"], output=["Puyapy help"]) - proc_mock.set_output(["puyapy", str(hello_world_contract_path)], ["Done"]) + proc_mock.set_output(["puyapy", str(dummy_contract_path)], ["Done"]) mocker.patch("algokit.core.proc.Popen").side_effect = proc_mock.popen - result = invoke(f"compile py {_normalize_path(hello_world_contract_path)}") + result = invoke(f"compile py {_normalize_path(dummy_contract_path)}") assert result.exit_code == 0 verify(result.output) + + +def test_valid_contract(cwd: Path, output_path: Path) -> None: + contract_path = cwd / "contract.py" + contract_path.write_text(VALID_PUYA_CONTRACT_FILE_CONTENT) + result = invoke(f"compile py {_normalize_path(contract_path)} --out-dir {_normalize_path(output_path)}") + + assert result.exit_code == 0 + + for d, __, files in os.walk(output_path): + for file in files: + content = (d / Path(file)).read_text() + normalize_content = normalize_path(content, str(cwd), "{temp_output_directory}") + verify(normalize_content, options=NamerFactory.with_parameters(file)) + + +def test_invalid_contract(cwd: Path, output_path: Path) -> None: + contract_path = cwd / "contract.py" + contract_path.write_text(INVALID_PUYA_CONTRACT_FILE_CONTENT) + result = invoke(f"compile py {_normalize_path(contract_path)} --out-dir {_normalize_path(output_path)}") + + assert result.exit_code == 1 + + normalize_output = normalize_path(result.output, str(cwd), "{temp_output_directory}") + verify(normalize_output) diff --git a/tests/compile/test_py.test_invalid_contract.approved.txt b/tests/compile/test_py.test_invalid_contract.approved.txt new file mode 100644 index 00000000..33cf4c7c --- /dev/null +++ b/tests/compile/test_py.test_invalid_contract.approved.txt @@ -0,0 +1,67 @@ +DEBUG: Running 'poetry run puyapy -h' in '{current_working_directory}' +DEBUG: poetry: usage: puya [-h] [-O {0,1,2}] [--output-teal | --no-output-teal] +DEBUG: poetry: [--output-arc32 | --no-output-arc32] [--out-dir OUT_DIR] +DEBUG: poetry: [--log-level {notset,debug,info,warning,error,fatal}] [-g {0,1,2}] +DEBUG: poetry: [--output-awst | --no-output-awst] +DEBUG: poetry: [--output-ssa-ir | --no-output-ssa-ir] +DEBUG: poetry: [--output-optimization-ir | --no-output-optimization-ir] +DEBUG: poetry: [--output-destructured-ir | --no-output-destructured-ir] +DEBUG: poetry: [--output-memory-ir | --no-output-memory-ir] +DEBUG: poetry: [--target-avm-version {10}] +DEBUG: poetry: [--locals-coalescing-strategy {root_operand,root_operand_excluding_args,aggressive}] +DEBUG: poetry: PATH [PATH ...] +DEBUG: poetry: +DEBUG: poetry: positional arguments: +DEBUG: poetry: PATH +DEBUG: poetry: +DEBUG: poetry: options: +DEBUG: poetry: -h, --help show this help message and exit +DEBUG: poetry: -O {0,1,2}, --optimization-level {0,1,2} +DEBUG: poetry: set optimization level (default: 1) +DEBUG: poetry: --output-teal, --no-output-teal +DEBUG: poetry: Output TEAL (default: True) +DEBUG: poetry: --output-arc32, --no-output-arc32 +DEBUG: poetry: Output ARC32 application.json (default: True) +DEBUG: poetry: --out-dir OUT_DIR path for outputting artefacts (default: False) +DEBUG: poetry: --log-level {notset,debug,info,warning,error,fatal} +DEBUG: poetry: Minimum level to log to console (default: info) +DEBUG: poetry: -g {0,1,2}, --debug-level {0,1,2} +DEBUG: poetry: debug information level (default: 1) +DEBUG: poetry: --output-awst, --no-output-awst +DEBUG: poetry: output parsed result of AST (default: False) +DEBUG: poetry: --output-ssa-ir, --no-output-ssa-ir +DEBUG: poetry: output IR in SSA form (default: False) +DEBUG: poetry: --output-optimization-ir, --no-output-optimization-ir +DEBUG: poetry: output IR after each optimization (default: False) +DEBUG: poetry: --output-destructured-ir, --no-output-destructured-ir +DEBUG: poetry: output IR after SSA destructuring and before codegen +DEBUG: poetry: (default: False) +DEBUG: poetry: --output-memory-ir, --no-output-memory-ir +DEBUG: poetry: output MIR before lowering to TealOps (default: False) +DEBUG: poetry: --target-avm-version {10} +DEBUG: poetry: --locals-coalescing-strategy {root_operand,root_operand_excluding_args,aggressive} +DEBUG: poetry: Strategy choice for out-of-ssa local variable +DEBUG: poetry: coalescing. The best choice for your app is best +DEBUG: poetry: determined through experimentation (default: +DEBUG: poetry: root_operand) +DEBUG: Running 'poetry run puyapy {temp_output_directory}/contract.py --out-dir {temp_output_directory}/output' in '{current_working_directory}' +DEBUG: poetry: {temp_output_directory}/contract.py:7 error: "type[Txn]" has no attribute "application_args_invalid" [attr-defined] +DEBUG: poetry: name = Txn.application_args_invalid(0) +DEBUG: poetry: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ +DEBUG: poetry: {temp_output_directory}/contract.py:7 error: Expression has type "Any" [misc] +DEBUG: poetry: name = Txn.application_args_invalid(0) +DEBUG: poetry: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +DEBUG: poetry: {temp_output_directory}/contract.py:8 error: Expression has type "Any" [misc] +DEBUG: poetry: log(b"Hello, " + name) +DEBUG: poetry: ^~~~~~~~~~~~~~~~~ +{temp_output_directory}/contract.py:7 error: "type[Txn]" has no attribute "application_args_invalid" [attr-defined] + name = Txn.application_args_invalid(0) + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ +{temp_output_directory}/contract.py:7 error: Expression has type "Any" [misc] + name = Txn.application_args_invalid(0) + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +{temp_output_directory}/contract.py:8 error: Expression has type "Any" [misc] + log(b"Hello, " + name) + ^~~~~~~~~~~~~~~~~ + +An error occurred during compile. Ensure supplied files are valid PuyaPy code before retrying. diff --git a/tests/compile/test_py.test_invalid_contract.contract.clear.teal.approved.txt b/tests/compile/test_py.test_invalid_contract.contract.clear.teal.approved.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/compile/test_py.test_puyapy_is_installed_globally.approved.txt b/tests/compile/test_py.test_puyapy_is_installed_globally.approved.txt index fad9bfbd..ba256b52 100644 --- a/tests/compile/test_py.test_puyapy_is_installed_globally.approved.txt +++ b/tests/compile/test_py.test_puyapy_is_installed_globally.approved.txt @@ -2,6 +2,6 @@ DEBUG: Running 'poetry run puyapy -h' in '{current_working_directory}' DEBUG: poetry: Puyapy not found DEBUG: Running 'puyapy -h' in '{current_working_directory}' DEBUG: puyapy: Puyapy help -DEBUG: Running 'puyapy {current_working_directory}/tests/compile/hello_world_contract.py' in '{current_working_directory}' +DEBUG: Running 'puyapy {current_working_directory}/tests/compile/dummy_contract.py' in '{current_working_directory}' DEBUG: puyapy: Done Done diff --git a/tests/compile/test_py.test_puyapy_is_installed_in_project.approved.txt b/tests/compile/test_py.test_puyapy_is_installed_in_project.approved.txt index 5a24e571..c2963d12 100644 --- a/tests/compile/test_py.test_puyapy_is_installed_in_project.approved.txt +++ b/tests/compile/test_py.test_puyapy_is_installed_in_project.approved.txt @@ -1,5 +1,5 @@ DEBUG: Running 'poetry run puyapy -h' in '{current_working_directory}' DEBUG: poetry: Puyapy help -DEBUG: Running 'poetry run puyapy {current_working_directory}/tests/compile/hello_world_contract.py' in '{current_working_directory}' +DEBUG: Running 'poetry run puyapy {current_working_directory}/tests/compile/dummy_contract.py' in '{current_working_directory}' DEBUG: poetry: Done Done diff --git a/tests/compile/test_py.test_puyapy_is_not_installed_anywhere.approved.txt b/tests/compile/test_py.test_puyapy_is_not_installed_anywhere.approved.txt index 5db9082e..9eb04315 100644 --- a/tests/compile/test_py.test_puyapy_is_not_installed_anywhere.approved.txt +++ b/tests/compile/test_py.test_puyapy_is_not_installed_anywhere.approved.txt @@ -6,6 +6,6 @@ DEBUG: Running 'pipx --version' in '{current_working_directory}' DEBUG: pipx: 1.0.0 DEBUG: Running 'pipx install puya' in '{current_working_directory}' DEBUG: pipx: Puyapy is installed -DEBUG: Running 'puyapy {current_working_directory}/tests/compile/hello_world_contract.py' in '{current_working_directory}' +DEBUG: Running 'puyapy {current_working_directory}/tests/compile/dummy_contract.py' in '{current_working_directory}' DEBUG: puyapy: Done Done diff --git a/tests/compile/test_py.test_specificed_puyapy_version_is_not_installed.approved.txt b/tests/compile/test_py.test_specificed_puyapy_version_is_not_installed.approved.txt index d8f49061..0cf292cd 100644 --- a/tests/compile/test_py.test_specificed_puyapy_version_is_not_installed.approved.txt +++ b/tests/compile/test_py.test_specificed_puyapy_version_is_not_installed.approved.txt @@ -4,6 +4,6 @@ DEBUG: Running 'puyapy --version' in '{current_working_directory}' DEBUG: puyapy: Puyapy not found DEBUG: Running 'pipx --version' in '{current_working_directory}' DEBUG: pipx: 1.0.0 -DEBUG: Running 'pipx run puya==1.1.0 {current_working_directory}/tests/compile/hello_world_contract.py' in '{current_working_directory}' +DEBUG: Running 'pipx run puya==1.1.0 {current_working_directory}/tests/compile/dummy_contract.py' in '{current_working_directory}' DEBUG: pipx: Done Done diff --git a/tests/compile/test_py.test_valid_contract.contract.approval.teal.approved.txt b/tests/compile/test_py.test_valid_contract.contract.approval.teal.approved.txt new file mode 100644 index 00000000..22a8f6dd --- /dev/null +++ b/tests/compile/test_py.test_valid_contract.contract.approval.teal.approved.txt @@ -0,0 +1,17 @@ +#pragma version 10 + +contract.HelloWorldContract.approval_program: + // {temp_output_directory}/contract.py:8 + // log(b"Hello, " + name) + byte "Hello, " + // {temp_output_directory}/contract.py:7 + // name = Txn.application_args(0) + txna ApplicationArgs 0 + // {temp_output_directory}/contract.py:8 + // log(b"Hello, " + name) + concat + log + // {temp_output_directory}/contract.py:9 + // return True + int 1 + return diff --git a/tests/compile/test_py.test_valid_contract.contract.clear.teal.approved.txt b/tests/compile/test_py.test_valid_contract.contract.clear.teal.approved.txt new file mode 100644 index 00000000..ce638ea7 --- /dev/null +++ b/tests/compile/test_py.test_valid_contract.contract.clear.teal.approved.txt @@ -0,0 +1,7 @@ +#pragma version 10 + +contract.HelloWorldContract.clear_state_program: + // {temp_output_directory}/contract.py:12 + // return True + int 1 + return From 78a1b87d19022a5c4fc75c83f729f30209d46e1c Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Mon, 4 Mar 2024 23:27:45 +1000 Subject: [PATCH 26/37] chore: install puya during CI --- .github/workflows/build-python.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-python.yaml b/.github/workflows/build-python.yaml index 73412f88..0ced580a 100644 --- a/.github/workflows/build-python.yaml +++ b/.github/workflows/build-python.yaml @@ -24,7 +24,10 @@ jobs: - name: Install dependencies # TODO: remove fixed pipx dependency once 3.12 compatibility is addressed # track here -> https://github.com/crytic/tealer/pull/209 - run: poetry install --no-interaction && pipx install tealer==0.1.2 + run: | + poetry install --no-interaction + pipx install tealer==0.1.2 + pipx install puya - name: pytest shell: bash From b350faa1a80fca390388480dc48dd59cd8733a7f Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Tue, 5 Mar 2024 08:37:49 +1000 Subject: [PATCH 27/37] Don't check for PuyaPy outputs --- tests/compile/test_py.py | 18 ++--- ...test_py.test_invalid_contract.approved.txt | 67 ------------------- ..._contract.contract.clear.teal.approved.txt | 0 ...ntract.contract.approval.teal.approved.txt | 17 ----- ..._contract.contract.clear.teal.approved.txt | 7 -- 5 files changed, 6 insertions(+), 103 deletions(-) delete mode 100644 tests/compile/test_py.test_invalid_contract.approved.txt delete mode 100644 tests/compile/test_py.test_invalid_contract.contract.clear.teal.approved.txt delete mode 100644 tests/compile/test_py.test_valid_contract.contract.approval.teal.approved.txt delete mode 100644 tests/compile/test_py.test_valid_contract.contract.clear.teal.approved.txt diff --git a/tests/compile/test_py.py b/tests/compile/test_py.py index 665dbceb..6ba7b403 100644 --- a/tests/compile/test_py.py +++ b/tests/compile/test_py.py @@ -1,12 +1,10 @@ -import os from pathlib import Path import pytest -from approvaltests.namer import NamerFactory from pytest_mock import MockerFixture from tests.compile.conftest import INVALID_PUYA_CONTRACT_FILE_CONTENT, VALID_PUYA_CONTRACT_FILE_CONTENT -from tests.utils.approvals import normalize_path, verify +from tests.utils.approvals import verify from tests.utils.click_invoker import invoke from tests.utils.proc_mock import ProcMock @@ -111,21 +109,17 @@ def test_valid_contract(cwd: Path, output_path: Path) -> None: contract_path.write_text(VALID_PUYA_CONTRACT_FILE_CONTENT) result = invoke(f"compile py {_normalize_path(contract_path)} --out-dir {_normalize_path(output_path)}") + # Only check for the exit code, don't check the results from PuyaPy assert result.exit_code == 0 - for d, __, files in os.walk(output_path): - for file in files: - content = (d / Path(file)).read_text() - normalize_content = normalize_path(content, str(cwd), "{temp_output_directory}") - verify(normalize_content, options=NamerFactory.with_parameters(file)) - def test_invalid_contract(cwd: Path, output_path: Path) -> None: contract_path = cwd / "contract.py" contract_path.write_text(INVALID_PUYA_CONTRACT_FILE_CONTENT) result = invoke(f"compile py {_normalize_path(contract_path)} --out-dir {_normalize_path(output_path)}") + # Only check for the exit code and the error message from AlgoKit CLI assert result.exit_code == 1 - - normalize_output = normalize_path(result.output, str(cwd), "{temp_output_directory}") - verify(normalize_output) + result.output.endswith( + "An error occurred during compile. Ensure supplied files are valid PuyaPy code before retrying." + ) diff --git a/tests/compile/test_py.test_invalid_contract.approved.txt b/tests/compile/test_py.test_invalid_contract.approved.txt deleted file mode 100644 index 33cf4c7c..00000000 --- a/tests/compile/test_py.test_invalid_contract.approved.txt +++ /dev/null @@ -1,67 +0,0 @@ -DEBUG: Running 'poetry run puyapy -h' in '{current_working_directory}' -DEBUG: poetry: usage: puya [-h] [-O {0,1,2}] [--output-teal | --no-output-teal] -DEBUG: poetry: [--output-arc32 | --no-output-arc32] [--out-dir OUT_DIR] -DEBUG: poetry: [--log-level {notset,debug,info,warning,error,fatal}] [-g {0,1,2}] -DEBUG: poetry: [--output-awst | --no-output-awst] -DEBUG: poetry: [--output-ssa-ir | --no-output-ssa-ir] -DEBUG: poetry: [--output-optimization-ir | --no-output-optimization-ir] -DEBUG: poetry: [--output-destructured-ir | --no-output-destructured-ir] -DEBUG: poetry: [--output-memory-ir | --no-output-memory-ir] -DEBUG: poetry: [--target-avm-version {10}] -DEBUG: poetry: [--locals-coalescing-strategy {root_operand,root_operand_excluding_args,aggressive}] -DEBUG: poetry: PATH [PATH ...] -DEBUG: poetry: -DEBUG: poetry: positional arguments: -DEBUG: poetry: PATH -DEBUG: poetry: -DEBUG: poetry: options: -DEBUG: poetry: -h, --help show this help message and exit -DEBUG: poetry: -O {0,1,2}, --optimization-level {0,1,2} -DEBUG: poetry: set optimization level (default: 1) -DEBUG: poetry: --output-teal, --no-output-teal -DEBUG: poetry: Output TEAL (default: True) -DEBUG: poetry: --output-arc32, --no-output-arc32 -DEBUG: poetry: Output ARC32 application.json (default: True) -DEBUG: poetry: --out-dir OUT_DIR path for outputting artefacts (default: False) -DEBUG: poetry: --log-level {notset,debug,info,warning,error,fatal} -DEBUG: poetry: Minimum level to log to console (default: info) -DEBUG: poetry: -g {0,1,2}, --debug-level {0,1,2} -DEBUG: poetry: debug information level (default: 1) -DEBUG: poetry: --output-awst, --no-output-awst -DEBUG: poetry: output parsed result of AST (default: False) -DEBUG: poetry: --output-ssa-ir, --no-output-ssa-ir -DEBUG: poetry: output IR in SSA form (default: False) -DEBUG: poetry: --output-optimization-ir, --no-output-optimization-ir -DEBUG: poetry: output IR after each optimization (default: False) -DEBUG: poetry: --output-destructured-ir, --no-output-destructured-ir -DEBUG: poetry: output IR after SSA destructuring and before codegen -DEBUG: poetry: (default: False) -DEBUG: poetry: --output-memory-ir, --no-output-memory-ir -DEBUG: poetry: output MIR before lowering to TealOps (default: False) -DEBUG: poetry: --target-avm-version {10} -DEBUG: poetry: --locals-coalescing-strategy {root_operand,root_operand_excluding_args,aggressive} -DEBUG: poetry: Strategy choice for out-of-ssa local variable -DEBUG: poetry: coalescing. The best choice for your app is best -DEBUG: poetry: determined through experimentation (default: -DEBUG: poetry: root_operand) -DEBUG: Running 'poetry run puyapy {temp_output_directory}/contract.py --out-dir {temp_output_directory}/output' in '{current_working_directory}' -DEBUG: poetry: {temp_output_directory}/contract.py:7 error: "type[Txn]" has no attribute "application_args_invalid" [attr-defined] -DEBUG: poetry: name = Txn.application_args_invalid(0) -DEBUG: poetry: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ -DEBUG: poetry: {temp_output_directory}/contract.py:7 error: Expression has type "Any" [misc] -DEBUG: poetry: name = Txn.application_args_invalid(0) -DEBUG: poetry: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -DEBUG: poetry: {temp_output_directory}/contract.py:8 error: Expression has type "Any" [misc] -DEBUG: poetry: log(b"Hello, " + name) -DEBUG: poetry: ^~~~~~~~~~~~~~~~~ -{temp_output_directory}/contract.py:7 error: "type[Txn]" has no attribute "application_args_invalid" [attr-defined] - name = Txn.application_args_invalid(0) - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ -{temp_output_directory}/contract.py:7 error: Expression has type "Any" [misc] - name = Txn.application_args_invalid(0) - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -{temp_output_directory}/contract.py:8 error: Expression has type "Any" [misc] - log(b"Hello, " + name) - ^~~~~~~~~~~~~~~~~ - -An error occurred during compile. Ensure supplied files are valid PuyaPy code before retrying. diff --git a/tests/compile/test_py.test_invalid_contract.contract.clear.teal.approved.txt b/tests/compile/test_py.test_invalid_contract.contract.clear.teal.approved.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/compile/test_py.test_valid_contract.contract.approval.teal.approved.txt b/tests/compile/test_py.test_valid_contract.contract.approval.teal.approved.txt deleted file mode 100644 index 22a8f6dd..00000000 --- a/tests/compile/test_py.test_valid_contract.contract.approval.teal.approved.txt +++ /dev/null @@ -1,17 +0,0 @@ -#pragma version 10 - -contract.HelloWorldContract.approval_program: - // {temp_output_directory}/contract.py:8 - // log(b"Hello, " + name) - byte "Hello, " - // {temp_output_directory}/contract.py:7 - // name = Txn.application_args(0) - txna ApplicationArgs 0 - // {temp_output_directory}/contract.py:8 - // log(b"Hello, " + name) - concat - log - // {temp_output_directory}/contract.py:9 - // return True - int 1 - return diff --git a/tests/compile/test_py.test_valid_contract.contract.clear.teal.approved.txt b/tests/compile/test_py.test_valid_contract.contract.clear.teal.approved.txt deleted file mode 100644 index ce638ea7..00000000 --- a/tests/compile/test_py.test_valid_contract.contract.clear.teal.approved.txt +++ /dev/null @@ -1,7 +0,0 @@ -#pragma version 10 - -contract.HelloWorldContract.clear_state_program: - // {temp_output_directory}/contract.py:12 - // return True - int 1 - return From 550465d1d98d0088a669921225c1c9253924a730 Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Tue, 5 Mar 2024 13:35:40 +1000 Subject: [PATCH 28/37] chore: only run puyapy tests for python 3.12 --- .github/workflows/build-python.yaml | 4 ++++ tests/compile/test_py.py | 3 +++ 2 files changed, 7 insertions(+) diff --git a/.github/workflows/build-python.yaml b/.github/workflows/build-python.yaml index 0ced580a..dab45504 100644 --- a/.github/workflows/build-python.yaml +++ b/.github/workflows/build-python.yaml @@ -29,6 +29,10 @@ jobs: pipx install tealer==0.1.2 pipx install puya + - name: Install PuyaPy + if: ${{ matrix.python == '3.12' }} + run: pipx install puya + - name: pytest shell: bash if: ${{ !(matrix.python == '3.12' && matrix.os == 'ubuntu-latest') }} diff --git a/tests/compile/test_py.py b/tests/compile/test_py.py index 6ba7b403..e36d77a0 100644 --- a/tests/compile/test_py.py +++ b/tests/compile/test_py.py @@ -1,3 +1,4 @@ +import sys from pathlib import Path import pytest @@ -8,6 +9,8 @@ from tests.utils.click_invoker import invoke from tests.utils.proc_mock import ProcMock +pytestmark = pytest.mark.skipif(sys.version_info < (3, 12), reason="requires python3.12 or higher") + def _normalize_path(path: Path) -> str: return str(path.absolute()).replace("\\", r"\\") From 2e1e02b9df534a7be3ac076c4387b3c1eb1eafd8 Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Tue, 5 Mar 2024 13:38:53 +1000 Subject: [PATCH 29/37] chore: oops --- .github/workflows/build-python.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/build-python.yaml b/.github/workflows/build-python.yaml index dab45504..9774d2d6 100644 --- a/.github/workflows/build-python.yaml +++ b/.github/workflows/build-python.yaml @@ -24,10 +24,7 @@ jobs: - name: Install dependencies # TODO: remove fixed pipx dependency once 3.12 compatibility is addressed # track here -> https://github.com/crytic/tealer/pull/209 - run: | - poetry install --no-interaction - pipx install tealer==0.1.2 - pipx install puya + run: poetry install --no-interaction && pipx install tealer==0.1.2 - name: Install PuyaPy if: ${{ matrix.python == '3.12' }} From 6386367fa2480f91236531ddbc5f1afda7fa302a Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Tue, 5 Mar 2024 14:44:18 +1000 Subject: [PATCH 30/37] chore: run tests with --no-color flag --- tests/compile/test_py.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/compile/test_py.py b/tests/compile/test_py.py index e36d77a0..9983e1ad 100644 --- a/tests/compile/test_py.py +++ b/tests/compile/test_py.py @@ -110,7 +110,7 @@ def test_puyapy_is_installed_globally(dummy_contract_path: Path, mocker: MockerF def test_valid_contract(cwd: Path, output_path: Path) -> None: contract_path = cwd / "contract.py" contract_path.write_text(VALID_PUYA_CONTRACT_FILE_CONTENT) - result = invoke(f"compile py {_normalize_path(contract_path)} --out-dir {_normalize_path(output_path)}") + result = invoke(f"--no-color compile py {_normalize_path(contract_path)} --out-dir {_normalize_path(output_path)}") # Only check for the exit code, don't check the results from PuyaPy assert result.exit_code == 0 @@ -119,7 +119,7 @@ def test_valid_contract(cwd: Path, output_path: Path) -> None: def test_invalid_contract(cwd: Path, output_path: Path) -> None: contract_path = cwd / "contract.py" contract_path.write_text(INVALID_PUYA_CONTRACT_FILE_CONTENT) - result = invoke(f"compile py {_normalize_path(contract_path)} --out-dir {_normalize_path(output_path)}") + result = invoke(f"--no-color compile py {_normalize_path(contract_path)} --out-dir {_normalize_path(output_path)}") # Only check for the exit code and the error message from AlgoKit CLI assert result.exit_code == 1 From 1da08b1e6d5c2ba6e13356b5db04cdc6a21c1129 Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Tue, 5 Mar 2024 15:06:42 +1000 Subject: [PATCH 31/37] chore: fix tests --- tests/compile/test_py.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/compile/test_py.py b/tests/compile/test_py.py index 9983e1ad..e5b60214 100644 --- a/tests/compile/test_py.py +++ b/tests/compile/test_py.py @@ -1,3 +1,4 @@ +import os import sys from pathlib import Path @@ -108,18 +109,25 @@ def test_puyapy_is_installed_globally(dummy_contract_path: Path, mocker: MockerF def test_valid_contract(cwd: Path, output_path: Path) -> None: + # Set NO_COLOR to 1 to avoid requirements for colorama on Windows + os.environ["NO_COLOR"] = "1" + contract_path = cwd / "contract.py" contract_path.write_text(VALID_PUYA_CONTRACT_FILE_CONTENT) - result = invoke(f"--no-color compile py {_normalize_path(contract_path)} --out-dir {_normalize_path(output_path)}") + + result = invoke(f"compile py {_normalize_path(contract_path)} --out-dir {_normalize_path(output_path)}") # Only check for the exit code, don't check the results from PuyaPy assert result.exit_code == 0 def test_invalid_contract(cwd: Path, output_path: Path) -> None: + # Set NO_COLOR to 1 to avoid requirements for colorama on Windows + os.environ["NO_COLOR"] = "1" + contract_path = cwd / "contract.py" contract_path.write_text(INVALID_PUYA_CONTRACT_FILE_CONTENT) - result = invoke(f"--no-color compile py {_normalize_path(contract_path)} --out-dir {_normalize_path(output_path)}") + result = invoke(f"compile py {_normalize_path(contract_path)} --out-dir {_normalize_path(output_path)}") # Only check for the exit code and the error message from AlgoKit CLI assert result.exit_code == 1 From 3d9ed2b7000d5dec76f1d8df3d544c40aedc9fe0 Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Tue, 5 Mar 2024 15:37:55 +1000 Subject: [PATCH 32/37] chore: only skip the e2e tests --- tests/compile/test_py.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/compile/test_py.py b/tests/compile/test_py.py index e5b60214..a9effa0b 100644 --- a/tests/compile/test_py.py +++ b/tests/compile/test_py.py @@ -10,8 +10,6 @@ from tests.utils.click_invoker import invoke from tests.utils.proc_mock import ProcMock -pytestmark = pytest.mark.skipif(sys.version_info < (3, 12), reason="requires python3.12 or higher") - def _normalize_path(path: Path) -> str: return str(path.absolute()).replace("\\", r"\\") @@ -108,6 +106,7 @@ def test_puyapy_is_installed_globally(dummy_contract_path: Path, mocker: MockerF verify(result.output) +@pytest.mark.skipif(sys.version_info < (3, 12), reason="PuyaPy requires python3.12 or higher") def test_valid_contract(cwd: Path, output_path: Path) -> None: # Set NO_COLOR to 1 to avoid requirements for colorama on Windows os.environ["NO_COLOR"] = "1" @@ -121,6 +120,7 @@ def test_valid_contract(cwd: Path, output_path: Path) -> None: assert result.exit_code == 0 +@pytest.mark.skipif(sys.version_info < (3, 12), reason="PuyaPy requires python3.12 or higher") def test_invalid_contract(cwd: Path, output_path: Path) -> None: # Set NO_COLOR to 1 to avoid requirements for colorama on Windows os.environ["NO_COLOR"] = "1" From 28085ece5af5041c099abfbc1448ec3f6cd5240d Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Wed, 6 Mar 2024 14:11:40 +1000 Subject: [PATCH 33/37] chore: address PR feedback --- src/algokit/cli/compile.py | 2 +- src/algokit/core/{compile.py => compile/py.py} | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/algokit/core/{compile.py => compile/py.py} (93%) diff --git a/src/algokit/cli/compile.py b/src/algokit/cli/compile.py index f78527d8..27482c70 100644 --- a/src/algokit/cli/compile.py +++ b/src/algokit/cli/compile.py @@ -2,7 +2,7 @@ import click -from algokit.core.compile import find_valid_puyapy_command +from algokit.core.compile.py import find_valid_puyapy_command from algokit.core.proc import run logger = logging.getLogger(__name__) diff --git a/src/algokit/core/compile.py b/src/algokit/core/compile/py.py similarity index 93% rename from src/algokit/core/compile.py rename to src/algokit/core/compile/py.py index b5a01fd5..e4e4b12c 100644 --- a/src/algokit/core/compile.py +++ b/src/algokit/core/compile/py.py @@ -25,7 +25,7 @@ def _find_puya_command_at_version(version: str) -> list[str]: return puyapy_command pipx_command = find_valid_pipx_command( - "Unable to find pipx install so that `PuyaPy` compiler can be installed; " + "Unable to find pipx install so that the `PuyaPy` compiler can be installed; " "please install pipx via https://pypa.github.io/pipx/ " "and then try `algokit compile py ...` again." ) @@ -52,7 +52,7 @@ def _find_puya_command() -> list[str]: return puyapy_command pipx_command = find_valid_pipx_command( - "Unable to find pipx install so that `PuyaPy` compiler can be installed; " + "Unable to find pipx install so that the `PuyaPy` compiler can be installed; " "please install pipx via https://pypa.github.io/pipx/ " "and then try `algokit compile py ...` again." ) From 6043f6f1668d8e0ea4e078fe7b9d5393615448aa Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Mon, 11 Mar 2024 14:22:01 +1000 Subject: [PATCH 34/37] chore: support "compile python" and "compile py" --- src/algokit/cli/compile.py | 19 ++++++++++++++++--- src/algokit/core/compile/{py.py => python.py} | 0 tests/compile/{test_py.py => test_python.py} | 12 ++++++------ ..._python.test_compile_py_help.approved.txt} | 0 ...puyapy_is_installed_globally.approved.txt} | 0 ...yapy_is_installed_in_project.approved.txt} | 0 ...py_is_not_installed_anywhere.approved.txt} | 0 ...apy_version_is_not_installed.approved.txt} | 0 8 files changed, 22 insertions(+), 9 deletions(-) rename src/algokit/core/compile/{py.py => python.py} (100%) rename tests/compile/{test_py.py => test_python.py} (90%) rename tests/compile/{test_py.test_compile_py_help.approved.txt => test_python.test_compile_py_help.approved.txt} (100%) rename tests/compile/{test_py.test_puyapy_is_installed_globally.approved.txt => test_python.test_puyapy_is_installed_globally.approved.txt} (100%) rename tests/compile/{test_py.test_puyapy_is_installed_in_project.approved.txt => test_python.test_puyapy_is_installed_in_project.approved.txt} (100%) rename tests/compile/{test_py.test_puyapy_is_not_installed_anywhere.approved.txt => test_python.test_puyapy_is_not_installed_anywhere.approved.txt} (100%) rename tests/compile/{test_py.test_specificed_puyapy_version_is_not_installed.approved.txt => test_python.test_specificed_puyapy_version_is_not_installed.approved.txt} (100%) diff --git a/src/algokit/cli/compile.py b/src/algokit/cli/compile.py index 27482c70..2a34fb12 100644 --- a/src/algokit/cli/compile.py +++ b/src/algokit/cli/compile.py @@ -2,13 +2,26 @@ import click -from algokit.core.compile.py import find_valid_puyapy_command +from algokit.core.compile.python import find_valid_puyapy_command from algokit.core.proc import run logger = logging.getLogger(__name__) -@click.group("compile", hidden=True) +class CompileGroup(click.Group): + def get_command(self, ctx: click.Context, cmd_name: str) -> click.Command | None: + rv = click.Group.get_command(self, ctx, cmd_name) + if rv is not None: + return rv + + command_dict = {"py": "python"} + if cmd_name in command_dict: + return click.Group.get_command(self, ctx, command_dict[cmd_name]) + + return None + + +@click.group("compile", cls=CompileGroup, hidden=True) @click.option( "-v", "--version", @@ -31,7 +44,7 @@ def compile_group(context: click.Context, version: str | None) -> None: @compile_group.command( - "py", + "python", short_help="Compile Python to TEAL with PuyaPy", help="Compile Python to TEAL with PuyaPy, review https://github.com/algorandfoundation/puya for usage", context_settings={ diff --git a/src/algokit/core/compile/py.py b/src/algokit/core/compile/python.py similarity index 100% rename from src/algokit/core/compile/py.py rename to src/algokit/core/compile/python.py diff --git a/tests/compile/test_py.py b/tests/compile/test_python.py similarity index 90% rename from tests/compile/test_py.py rename to tests/compile/test_python.py index a9effa0b..2139ae16 100644 --- a/tests/compile/test_py.py +++ b/tests/compile/test_python.py @@ -35,7 +35,7 @@ def test_compile_py_help(mocker: MockerFixture) -> None: proc_mock.set_output(["poetry", "run", "puyapy", "-h"], output=["Puyapy help"]) mocker.patch("algokit.core.proc.Popen").side_effect = proc_mock.popen - result = invoke("compile py -h") + result = invoke("compile python -h") assert result.exit_code == 0 verify(result.output) @@ -53,7 +53,7 @@ def test_puyapy_is_not_installed_anywhere(dummy_contract_path: Path, mocker: Moc mocker.patch("algokit.core.proc.Popen").side_effect = proc_mock.popen - result = invoke(f"compile py {_normalize_path(dummy_contract_path)}") + result = invoke(f"compile python {_normalize_path(dummy_contract_path)}") assert result.exit_code == 0 verify(result.output) @@ -85,7 +85,7 @@ def test_puyapy_is_installed_in_project(dummy_contract_path: Path, mocker: Mocke mocker.patch("algokit.core.proc.Popen").side_effect = proc_mock.popen - result = invoke(f"compile py {_normalize_path(dummy_contract_path)}") + result = invoke(f"compile python {_normalize_path(dummy_contract_path)}") assert result.exit_code == 0 verify(result.output) @@ -100,7 +100,7 @@ def test_puyapy_is_installed_globally(dummy_contract_path: Path, mocker: MockerF mocker.patch("algokit.core.proc.Popen").side_effect = proc_mock.popen - result = invoke(f"compile py {_normalize_path(dummy_contract_path)}") + result = invoke(f"compile python {_normalize_path(dummy_contract_path)}") assert result.exit_code == 0 verify(result.output) @@ -114,7 +114,7 @@ def test_valid_contract(cwd: Path, output_path: Path) -> None: contract_path = cwd / "contract.py" contract_path.write_text(VALID_PUYA_CONTRACT_FILE_CONTENT) - result = invoke(f"compile py {_normalize_path(contract_path)} --out-dir {_normalize_path(output_path)}") + result = invoke(f"compile python {_normalize_path(contract_path)} --out-dir {_normalize_path(output_path)}") # Only check for the exit code, don't check the results from PuyaPy assert result.exit_code == 0 @@ -127,7 +127,7 @@ def test_invalid_contract(cwd: Path, output_path: Path) -> None: contract_path = cwd / "contract.py" contract_path.write_text(INVALID_PUYA_CONTRACT_FILE_CONTENT) - result = invoke(f"compile py {_normalize_path(contract_path)} --out-dir {_normalize_path(output_path)}") + result = invoke(f"compile python {_normalize_path(contract_path)} --out-dir {_normalize_path(output_path)}") # Only check for the exit code and the error message from AlgoKit CLI assert result.exit_code == 1 diff --git a/tests/compile/test_py.test_compile_py_help.approved.txt b/tests/compile/test_python.test_compile_py_help.approved.txt similarity index 100% rename from tests/compile/test_py.test_compile_py_help.approved.txt rename to tests/compile/test_python.test_compile_py_help.approved.txt diff --git a/tests/compile/test_py.test_puyapy_is_installed_globally.approved.txt b/tests/compile/test_python.test_puyapy_is_installed_globally.approved.txt similarity index 100% rename from tests/compile/test_py.test_puyapy_is_installed_globally.approved.txt rename to tests/compile/test_python.test_puyapy_is_installed_globally.approved.txt diff --git a/tests/compile/test_py.test_puyapy_is_installed_in_project.approved.txt b/tests/compile/test_python.test_puyapy_is_installed_in_project.approved.txt similarity index 100% rename from tests/compile/test_py.test_puyapy_is_installed_in_project.approved.txt rename to tests/compile/test_python.test_puyapy_is_installed_in_project.approved.txt diff --git a/tests/compile/test_py.test_puyapy_is_not_installed_anywhere.approved.txt b/tests/compile/test_python.test_puyapy_is_not_installed_anywhere.approved.txt similarity index 100% rename from tests/compile/test_py.test_puyapy_is_not_installed_anywhere.approved.txt rename to tests/compile/test_python.test_puyapy_is_not_installed_anywhere.approved.txt diff --git a/tests/compile/test_py.test_specificed_puyapy_version_is_not_installed.approved.txt b/tests/compile/test_python.test_specificed_puyapy_version_is_not_installed.approved.txt similarity index 100% rename from tests/compile/test_py.test_specificed_puyapy_version_is_not_installed.approved.txt rename to tests/compile/test_python.test_specificed_puyapy_version_is_not_installed.approved.txt From 636384e2e21772271cadffd70944bf5b4540d550 Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Mon, 11 Mar 2024 21:37:04 +1000 Subject: [PATCH 35/37] chore: show all sub commands --- src/algokit/cli/compile.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/algokit/cli/compile.py b/src/algokit/cli/compile.py index 2a34fb12..1977d1ed 100644 --- a/src/algokit/cli/compile.py +++ b/src/algokit/cli/compile.py @@ -1,4 +1,6 @@ import logging +from collections.abc import MutableMapping, Sequence +from typing import Any import click @@ -9,17 +11,29 @@ class CompileGroup(click.Group): + def __init__( + self, + name: str | None = None, + commands: MutableMapping[str, click.Command] | Sequence[click.Command] | None = None, + **attrs: Any, + ) -> None: + super().__init__(name, commands, **attrs) + self.command_dict = {"py": "python"} + def get_command(self, ctx: click.Context, cmd_name: str) -> click.Command | None: rv = click.Group.get_command(self, ctx, cmd_name) if rv is not None: return rv - - command_dict = {"py": "python"} - if cmd_name in command_dict: - return click.Group.get_command(self, ctx, command_dict[cmd_name]) + if cmd_name in self.command_dict: + return click.Group.get_command(self, ctx, self.command_dict[cmd_name]) return None + def list_commands(self, ctx: click.Context) -> list[str]: + predefined_command_names = super().list_commands(ctx) + + return sorted(predefined_command_names + list(self.command_dict.keys())) + @click.group("compile", cls=CompileGroup, hidden=True) @click.option( From 5ae5fc427c4c9ce794e4b01bc0d2047b4d8bf71e Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Mon, 11 Mar 2024 22:52:54 +1000 Subject: [PATCH 36/37] chore: PR feedback --- src/algokit/cli/compile.py | 37 ++++++------------------------------- 1 file changed, 6 insertions(+), 31 deletions(-) diff --git a/src/algokit/cli/compile.py b/src/algokit/cli/compile.py index 1977d1ed..8aea9026 100644 --- a/src/algokit/cli/compile.py +++ b/src/algokit/cli/compile.py @@ -1,6 +1,4 @@ import logging -from collections.abc import MutableMapping, Sequence -from typing import Any import click @@ -10,32 +8,7 @@ logger = logging.getLogger(__name__) -class CompileGroup(click.Group): - def __init__( - self, - name: str | None = None, - commands: MutableMapping[str, click.Command] | Sequence[click.Command] | None = None, - **attrs: Any, - ) -> None: - super().__init__(name, commands, **attrs) - self.command_dict = {"py": "python"} - - def get_command(self, ctx: click.Context, cmd_name: str) -> click.Command | None: - rv = click.Group.get_command(self, ctx, cmd_name) - if rv is not None: - return rv - if cmd_name in self.command_dict: - return click.Group.get_command(self, ctx, self.command_dict[cmd_name]) - - return None - - def list_commands(self, ctx: click.Context) -> list[str]: - predefined_command_names = super().list_commands(ctx) - - return sorted(predefined_command_names + list(self.command_dict.keys())) - - -@click.group("compile", cls=CompileGroup, hidden=True) +@click.group("compile", hidden=True) @click.option( "-v", "--version", @@ -57,10 +30,8 @@ def compile_group(context: click.Context, version: str | None) -> None: context.obj["version"] = version -@compile_group.command( +@click.command( "python", - short_help="Compile Python to TEAL with PuyaPy", - help="Compile Python to TEAL with PuyaPy, review https://github.com/algorandfoundation/puya for usage", context_settings={ "ignore_unknown_options": True, }, @@ -91,3 +62,7 @@ def compile_py_command(context: click.Context, puya_args: list[str]) -> None: fg="red", ) raise click.exceptions.Exit(run_result.exit_code) + + +compile_group.add_command(compile_py_command, "python") +compile_group.add_command(compile_py_command, "py") From 51a79ab64dc15dd97cae54f260f0f714d261b8a2 Mon Sep 17 00:00:00 2001 From: Patrick Dinh Date: Mon, 11 Mar 2024 22:54:34 +1000 Subject: [PATCH 37/37] chore: clean up --- src/algokit/cli/compile.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/algokit/cli/compile.py b/src/algokit/cli/compile.py index 8aea9026..b5df7671 100644 --- a/src/algokit/cli/compile.py +++ b/src/algokit/cli/compile.py @@ -31,7 +31,6 @@ def compile_group(context: click.Context, version: str | None) -> None: @click.command( - "python", context_settings={ "ignore_unknown_options": True, },