diff --git a/clive/__private/cli/commands/process/process_account_creation.py b/clive/__private/cli/commands/process/process_account_creation.py index 3df8b1154889eadab67fe34bea3199159403e11f..600ce4b6e75dc5be3a39cc2efd77500ecac30ad3 100644 --- a/clive/__private/cli/commands/process/process_account_creation.py +++ b/clive/__private/cli/commands/process/process_account_creation.py @@ -58,6 +58,10 @@ class ProcessAccountCreation(OperationCommand): def is_active_authority_set(self) -> bool: return self._is_authority_set(self._active_authority) + @property + def is_memo_key_set(self) -> bool: + return self._memo_key is not None + @property def is_owner_authority_set(self) -> bool: return self._is_authority_set(self._owner_authority) @@ -71,9 +75,24 @@ class ProcessAccountCreation(OperationCommand): assert self._memo_key is not None, "Memo key must be specified by user and set with method `set_memo_key`" return self._memo_key - @property - def is_memo_key_set(self) -> bool: - return self._memo_key is not None + def set_keys(self, owner: PublicKey, active: PublicKey, posting: PublicKey, memo: PublicKey) -> None: + for authority_type in ("owner", "active", "posting"): + self._set_threshold(authority_type, DEFAULT_AUTHORITY_THRESHOLD) + self._add_key_authority( + authority_type, + { + "owner": owner, + "active": active, + "posting": posting, + }[authority_type], + DEFAULT_AUTHORITY_WEIGHT, + ) + self._memo_key = memo + + @override + async def validate(self) -> None: + self._validate_all_authorities_are_set() + await super().validate() @override async def fetch_data(self) -> None: @@ -94,27 +113,6 @@ class ProcessAccountCreation(OperationCommand): print_cli(f"Adding account `{self.new_account_name}` to known accounts.") self.profile.accounts.add_known_account(self.new_account_name) - def set_keys(self, owner: PublicKey, active: PublicKey, posting: PublicKey) -> None: - for authority_type in ("owner", "active", "posting"): - self._set_threshold(authority_type, DEFAULT_AUTHORITY_THRESHOLD) - self._add_key_authority( - authority_type, - { - "owner": owner, - "active": active, - "posting": posting, - }[authority_type], - DEFAULT_AUTHORITY_WEIGHT, - ) - - def set_memo_key(self, key: PublicKey) -> None: - self._memo_key = key - - @override - async def validate(self) -> None: - self._validate_all_authorities_are_set() - await super().validate() - def _add_key_authority(self, level: AuthorityLevelRegular, key: PublicKey, weight: int) -> None: self._get_authority(level).key_auths.append((key.value, weight)) diff --git a/clive/__private/cli/common/parameters/argument_related_options.py b/clive/__private/cli/common/parameters/argument_related_options.py index 40537daa3e3f913a5980afe9e4bb22ba0a136cbd..40e89c3d7c797a7b596bd0919103b40d2bd0aefd 100644 --- a/clive/__private/cli/common/parameters/argument_related_options.py +++ b/clive/__private/cli/common/parameters/argument_related_options.py @@ -11,9 +11,8 @@ from typing import TYPE_CHECKING, Any import typer -from clive.__private.cli.common.parameters import options -from clive.__private.cli.common.parameters.modified_param import modified_param -from clive.__private.cli.common.parsers import public_key +from clive.__private.cli.common.parameters import arguments, options +from clive.__private.cli.common.parameters.modified_param import argument_to_option, modified_param from clive.__private.core.constants.cli import LOOK_INTO_ARGUMENT_OPTION_HELP if TYPE_CHECKING: @@ -55,38 +54,10 @@ chain_id = _make_argument_related_option("--chain-id") node_address = _make_argument_related_option("--node-address") -owner_key = _make_argument_related_option( - typer.Option( - None, - "--owner", - parser=public_key, - help="Owner public key that will be set for account.", - ) -) - -active_key = _make_argument_related_option( - typer.Option( - None, - "--active", - parser=public_key, - help="Active public key that will be set for account.", - ) -) - -posting_key = _make_argument_related_option( - typer.Option( - None, - "--posting", - parser=public_key, - help="Posting public key that will be set for account.", - ) -) - -memo_key = _make_argument_related_option( - typer.Option( - None, - "--memo", - parser=public_key, - help="Memo public key that will be set for account.", - ) -) +owner = _make_argument_related_option(argument_to_option(arguments.owner, "--owner")) + +active = _make_argument_related_option(argument_to_option(arguments.active, "--active")) + +posting = _make_argument_related_option(argument_to_option(arguments.posting, "--posting")) + +memo_key = _make_argument_related_option(argument_to_option(arguments.memo_key, "--memo")) diff --git a/clive/__private/cli/common/parameters/arguments.py b/clive/__private/cli/common/parameters/arguments.py index 32445eb4501033c4307ab3e464d444eb3b71db4f..53df6a35d835678be57ae3804926822eeddb1568 100644 --- a/clive/__private/cli/common/parameters/arguments.py +++ b/clive/__private/cli/common/parameters/arguments.py @@ -25,19 +25,19 @@ account_name = modified_param(working_account_template) profile_name = typer.Argument(..., help=stylized_help("The profile to use.", required_as_arg_or_option=True)) -owner_key = typer.Argument( +owner = typer.Argument( None, parser=public_key, help=stylized_help("Owner public key that will be set for account.", required_as_arg_or_option=True), ) -active_key = typer.Argument( +active = typer.Argument( None, parser=public_key, help=stylized_help("Active public key that will be set for account.", required_as_arg_or_option=True), ) -posting_key = typer.Argument( +posting = typer.Argument( None, parser=public_key, help=stylized_help("Posting public key that will be set for account.", required_as_arg_or_option=True), diff --git a/clive/__private/cli/common/parameters/modified_param.py b/clive/__private/cli/common/parameters/modified_param.py index a6b140aa97b9ff27b85df5365b83d3c00b3f1ea7..75e44e32ac432c55b15f1017a4ee88256672dde2 100644 --- a/clive/__private/cli/common/parameters/modified_param.py +++ b/clive/__private/cli/common/parameters/modified_param.py @@ -3,10 +3,73 @@ from __future__ import annotations from copy import deepcopy from typing import TYPE_CHECKING, Any +import typer + if TYPE_CHECKING: from typer.models import ArgumentInfo, OptionInfo +def argument_to_option(argument: typer.models.ArgumentInfo, *param_decls: str) -> Any: # noqa: ANN401 + """ + Transform a Typer Argument into an Option. + + Args: + argument: The typer.Argument(...) object to transform. + *param_decls: Optional names for option. + + Returns: + typer.Option(...) equivalent to the input Argument. + """ + for name in param_decls: + assert name.startswith("-"), "Option name must start with '-'" + # Reuse settings from the original Argument + return typer.Option( + argument.default, + *param_decls, + callback=argument.callback, + metavar=argument.metavar, + expose_value=argument.expose_value, + is_eager=argument.is_eager, + envvar=argument.envvar, + shell_complete=argument.shell_complete, + autocompletion=argument.autocompletion, + default_factory=argument.default_factory, + # Custom type + parser=argument.parser, + # Option + show_default=argument.show_default, + help=argument.help, + hidden=argument.hidden, + show_choices=argument.show_choices, + show_envvar=argument.show_envvar, + # Choice + case_sensitive=argument.case_sensitive, + # Numbers + min=argument.min, + max=argument.max, + clamp=argument.clamp, + # DateTime + formats=argument.formats, + # File + mode=argument.mode, + encoding=argument.encoding, + errors=argument.errors, + lazy=argument.lazy, + atomic=argument.atomic, + # Path + exists=argument.exists, + file_okay=argument.file_okay, + dir_okay=argument.dir_okay, + writable=argument.writable, + readable=argument.readable, + resolve_path=argument.resolve_path, + allow_dash=argument.allow_dash, + path_type=argument.path_type, + # Rich settings + rich_help_panel=argument.rich_help_panel, + ) + + def modified_param(source: OptionInfo | ArgumentInfo, **kwargs: Any) -> Any: # noqa: ANN401 """ Create option/argument based on another option/argument, but with some attributes modified. diff --git a/clive/__private/cli/common/parameters/styling.py b/clive/__private/cli/common/parameters/styling.py index 734534cdabdb2843b8511edbd60cfe8cae7d1226..adcbbb9275bb03f732442479d330073b073a1ae1 100644 --- a/clive/__private/cli/common/parameters/styling.py +++ b/clive/__private/cli/common/parameters/styling.py @@ -37,5 +37,5 @@ def _append_required_as_arg_or_option(help_message: str) -> str: from rich.text import Text # noqa: PLC0415 from typer.rich_utils import STYLE_REQUIRED_LONG # noqa: PLC0415 - postfix = Text(f"[{REQUIRED_AS_ARG_OR_OPTION}]", style=STYLE_REQUIRED_LONG) + postfix = Text(REQUIRED_AS_ARG_OR_OPTION, style=STYLE_REQUIRED_LONG) return f"{help_message} {postfix.markup}" diff --git a/clive/__private/cli/process/main.py b/clive/__private/cli/process/main.py index 54490f1a97dd9fc2de32a381857cc2cafa4d3aa3..14ed64b3299b4dc74cc712451a8051620974d0c2 100644 --- a/clive/__private/cli/process/main.py +++ b/clive/__private/cli/process/main.py @@ -152,13 +152,13 @@ async def process_account_creation( # noqa: PLR0913 creator: str = modified_param(options.working_account_template, param_decls=("--creator",)), new_account_name: str | None = _new_account_name_argument, new_account_name_option: str | None = argument_related_options.new_account_name, - owner: str | None = arguments.owner_key, - active: str | None = arguments.active_key, - posting: str | None = arguments.posting_key, + owner: str | None = arguments.owner, + active: str | None = arguments.active, + posting: str | None = arguments.posting, memo: str | None = arguments.memo_key, - owner_option: str | None = argument_related_options.owner_key, - active_option: str | None = argument_related_options.active_key, - posting_option: str | None = argument_related_options.posting_key, + owner_option: str | None = argument_related_options.owner, + active_option: str | None = argument_related_options.active, + posting_option: str | None = argument_related_options.posting, memo_option: str | None = argument_related_options.memo_key, fee: bool = typer.Option( # noqa: FBT001 default=False, @@ -216,8 +216,8 @@ async def process_account_creation( # noqa: PLR0913 EnsureSingleValue[PublicKey]("owner").of(owner_, owner_option_), EnsureSingleValue[PublicKey]("active").of(active_, active_option_), EnsureSingleValue[PublicKey]("posting").of(posting_, posting_option_), + EnsureSingleValue[PublicKey]("memo").of(memo_, memo_option_), ) - account_creation_command.set_memo_key(EnsureSingleValue[PublicKey]("memo").of(memo_, memo_option_)) await account_creation_command.run() diff --git a/clive/__private/core/constants/cli.py b/clive/__private/core/constants/cli.py index 6ee2698aef4578ebb9f092649d3cbf95bc71cd05..21b0a13d1f70fcb931eaed36b2236938514957af 100644 --- a/clive/__private/core/constants/cli.py +++ b/clive/__private/core/constants/cli.py @@ -6,7 +6,7 @@ HELP_FLAGS: Final[tuple[str, ...]] = ("-h", "--help") PERFORM_WORKING_ACCOUNT_LOAD: Final[str] = "PERFORM_WORKING_ACCOUNT_LOAD" -REQUIRED_AS_ARG_OR_OPTION: Final[str] = "required as argument or option" +REQUIRED_AS_ARG_OR_OPTION: Final[str] = "[required as argument or option]" LOOK_INTO_ARGUMENT_OPTION_HELP: Final[str] = ( "For more help look into argument description. This option takes precedence over positional argument." diff --git a/tests/clive-local-tools/clive_local_tools/cli/chaining/chained_command.py b/tests/clive-local-tools/clive_local_tools/cli/chaining/chained_command.py index a2089ee7df6d4c4d03f0a6efbe639792f42b075f..011580c3b59c8ab6147207838a7b2230a6f76183 100644 --- a/tests/clive-local-tools/clive_local_tools/cli/chaining/chained_command.py +++ b/tests/clive-local-tools/clive_local_tools/cli/chaining/chained_command.py @@ -4,27 +4,53 @@ from typing import TYPE_CHECKING import test_tools as tt -from clive_local_tools.cli.command_options import kwargs_to_cli_options +from clive_local_tools.cli.command_options import build_cli_options, stringify_parameter_value from clive_local_tools.cli.exceptions import CLITestCommandError if TYPE_CHECKING: + from collections.abc import Iterable, Mapping + from click.testing import Result from typer.testing import CliRunner from clive.__private.cli.clive_typer import CliveTyper - from clive_local_tools.cli.command_options import CliOptionT + from clive_local_tools.cli.command_options import CLIArgumentValue, CLIOptionValue class ChainedCommand: - def __init__(self, typer: CliveTyper, runner: CliRunner, command: list[str], **cli_options: CliOptionT) -> None: + def __init__( + self, + typer: CliveTyper, + runner: CliRunner, + command: list[str], + *, + cli_positional_args: Iterable[CLIArgumentValue] | None = None, + cli_named_options: Mapping[str, CLIOptionValue] | None = None, + ) -> None: self.__typer = typer self.__runner = runner - self.__full_command = [*command, *kwargs_to_cli_options(**cli_options)] + positional = ( + [stringify_parameter_value(arg) for arg in cli_positional_args] if cli_positional_args is not None else [] + ) + named = build_cli_options(cli_named_options) if cli_named_options is not None else [] + self.__full_command = [*command, *positional, *named] self.__was_invoked = False - def _add_command_to_chain(self, next_command: str, **cli_options: CliOptionT) -> None: + def _add_command_to_chain( + self, + next_command: str, + *, + cli_positional_args: Iterable[CLIArgumentValue] | None = None, + cli_named_options: Mapping[str, CLIOptionValue] | None = None, + ) -> None: self.__full_command.append(next_command) - self.__full_command.extend(kwargs_to_cli_options(**cli_options)) + + positional = ( + [stringify_parameter_value(arg) for arg in cli_positional_args] if cli_positional_args is not None else [] + ) + named = build_cli_options(cli_named_options) if cli_named_options is not None else [] + self.__full_command.extend(positional) + self.__full_command.extend(named) def fire(self) -> Result: assert self.__was_invoked is False, f"Command '{self.__full_command}' was already invoked." diff --git a/tests/clive-local-tools/clive_local_tools/cli/chaining/update_authority.py b/tests/clive-local-tools/clive_local_tools/cli/chaining/update_authority.py index d56b1d1ccbf76d9e8fe7069065d437f1478dadef..6d0f721231e4118c75d483fdb56442f0dd6be3b4 100644 --- a/tests/clive-local-tools/clive_local_tools/cli/chaining/update_authority.py +++ b/tests/clive-local-tools/clive_local_tools/cli/chaining/update_authority.py @@ -6,13 +6,14 @@ from clive_local_tools.cli.chaining.chained_command import ChainedCommand from clive_local_tools.cli.command_options import extract_params if TYPE_CHECKING: + from collections.abc import Iterable, Mapping from pathlib import Path from typer.testing import CliRunner from clive.__private.cli.clive_typer import CliveTyper from clive.__private.models.schemas import PublicKey - from clive_local_tools.cli.command_options import CliOptionT + from clive_local_tools.cli.command_options import CLIArgumentValue, CLIOptionValue class UpdateAuthority(ChainedCommand): @@ -26,7 +27,7 @@ class UpdateAuthority(ChainedCommand): save_file: Path | None = None, autosign: bool | None = None, ) -> UpdateAuthority: - self._add_command_to_chain("add-key", **extract_params(locals())) + self._add_command_to_chain("add-key", cli_named_options=extract_params(locals())) return self def add_account( # noqa: PLR0913 @@ -39,7 +40,7 @@ class UpdateAuthority(ChainedCommand): save_file: Path | None = None, autosign: bool | None = None, ) -> UpdateAuthority: - self._add_command_to_chain("add-account", **extract_params(locals())) + self._add_command_to_chain("add-account", cli_named_options=extract_params(locals())) return self def remove_key( @@ -51,7 +52,7 @@ class UpdateAuthority(ChainedCommand): save_file: Path | None = None, autosign: bool | None = None, ) -> UpdateAuthority: - self._add_command_to_chain("remove-key", **extract_params(locals())) + self._add_command_to_chain("remove-key", cli_named_options=extract_params(locals())) return self def remove_account( @@ -63,7 +64,7 @@ class UpdateAuthority(ChainedCommand): save_file: Path | None = None, autosign: bool | None = None, ) -> UpdateAuthority: - self._add_command_to_chain("remove-account", **extract_params(locals())) + self._add_command_to_chain("remove-account", cli_named_options=extract_params(locals())) return self def modify_key( # noqa: PLR0913 @@ -76,7 +77,7 @@ class UpdateAuthority(ChainedCommand): save_file: Path | None = None, autosign: bool | None = None, ) -> UpdateAuthority: - self._add_command_to_chain("modify-key", **extract_params(locals())) + self._add_command_to_chain("modify-key", cli_named_options=extract_params(locals())) return self def modify_account( # noqa: PLR0913 @@ -89,23 +90,50 @@ class UpdateAuthority(ChainedCommand): save_file: Path | None = None, autosign: bool | None = None, ) -> UpdateAuthority: - self._add_command_to_chain("modify-account", **extract_params(locals())) + self._add_command_to_chain("modify-account", cli_named_options=extract_params(locals())) return self class UpdateOwnerAuthority(UpdateAuthority): - def __init__(self, typer: CliveTyper, runner: CliRunner, **cli_options: CliOptionT) -> None: + def __init__( + self, + typer: CliveTyper, + runner: CliRunner, + *, + cli_positional_args: Iterable[CLIArgumentValue] | None = None, + cli_named_options: Mapping[str, CLIOptionValue] | None = None, + ) -> None: command = ["process", "update-owner-authority"] - super().__init__(typer, runner, command, **cli_options) + super().__init__( + typer, runner, command, cli_positional_args=cli_positional_args, cli_named_options=cli_named_options + ) class UpdateActiveAuthority(UpdateAuthority): - def __init__(self, typer: CliveTyper, runner: CliRunner, **cli_options: CliOptionT) -> None: + def __init__( + self, + typer: CliveTyper, + runner: CliRunner, + *, + cli_positional_args: Iterable[CLIArgumentValue] | None = None, + cli_named_options: Mapping[str, CLIOptionValue] | None = None, + ) -> None: command = ["process", "update-active-authority"] - super().__init__(typer, runner, command, **cli_options) + super().__init__( + typer, runner, command, cli_positional_args=cli_positional_args, cli_named_options=cli_named_options + ) class UpdatePostingAuthority(UpdateAuthority): - def __init__(self, typer: CliveTyper, runner: CliRunner, **cli_options: CliOptionT) -> None: + def __init__( + self, + typer: CliveTyper, + runner: CliRunner, + *, + cli_positional_args: Iterable[CLIArgumentValue] | None = None, + cli_named_options: Mapping[str, CLIOptionValue] | None = None, + ) -> None: command = ["process", "update-posting-authority"] - super().__init__(typer, runner, command, **cli_options) + super().__init__( + typer, runner, command, cli_positional_args=cli_positional_args, cli_named_options=cli_named_options + ) diff --git a/tests/clive-local-tools/clive_local_tools/cli/cli_tester.py b/tests/clive-local-tools/clive_local_tools/cli/cli_tester.py index 8fd8062b8c61412cca855e516d32a51330c107ec..cc52e3a8a8a93913085cf9ef137d6e30e7889db4 100644 --- a/tests/clive-local-tools/clive_local_tools/cli/cli_tester.py +++ b/tests/clive-local-tools/clive_local_tools/cli/cli_tester.py @@ -10,10 +10,11 @@ from .chaining.update_authority import ( UpdateOwnerAuthority, UpdatePostingAuthority, ) -from .command_options import extract_params, kwargs_to_cli_options, option_to_string +from .command_options import build_cli_options, extract_params, stringify_parameter_value from .exceptions import CLITestCommandError if TYPE_CHECKING: + from collections.abc import Iterable, Mapping from decimal import Decimal from pathlib import Path @@ -24,7 +25,7 @@ if TYPE_CHECKING: from clive.__private.core.types import AlreadySignedMode, AuthorityLevelRegular from clive.__private.core.world import World from clive.__private.models.schemas import PublicKey - from clive_local_tools.cli.command_options import CliOptionT, StringConvertibleOptionTypes + from clive_local_tools.cli.command_options import CLIArgumentValue, CLIOptionValue class CLITester: @@ -83,25 +84,33 @@ class CLITester: raise ValueError(f"Unknown authority type: '{authority}'") def show_owner_authority(self, *, account_name: str | None = None) -> Result: - return self.__invoke_command_with_options(["show", "owner-authority"], account_name=account_name) + return self.__invoke_command_with_options( + ["show", "owner-authority"], cli_named_options=extract_params(locals()) + ) def show_active_authority(self, *, account_name: str | None = None) -> Result: - return self.__invoke_command_with_options(["show", "active-authority"], account_name=account_name) + return self.__invoke_command_with_options( + ["show", "active-authority"], cli_named_options=extract_params(locals()) + ) def show_posting_authority(self, *, account_name: str | None = None) -> Result: - return self.__invoke_command_with_options(["show", "posting-authority"], account_name=account_name) + return self.__invoke_command_with_options( + ["show", "posting-authority"], cli_named_options=extract_params(locals()) + ) def show_memo_key(self, *, account_name: str | None = None) -> Result: - return self.__invoke_command_with_options(["show", "memo-key"], account_name=account_name) + return self.__invoke_command_with_options(["show", "memo-key"], cli_named_options=extract_params(locals())) def show_pending_withdrawals(self, *, account_name: str | None = None) -> Result: - return self.__invoke_command_with_options(["show", "pending", "withdrawals"], account_name=account_name) + return self.__invoke_command_with_options( + ["show", "pending", "withdrawals"], cli_named_options=extract_params(locals()) + ) def show_balances(self, *, account_name: str | None = None) -> Result: - return self.__invoke_command_with_options(["show", "balances"], account_name=account_name) + return self.__invoke_command_with_options(["show", "balances"], cli_named_options=extract_params(locals())) def show_account(self, *, account_name: str | None = None) -> Result: - return self.__invoke_command_with_options(["show", "account"], account_name=account_name) + return self.__invoke_command_with_options(["show", "account"], cli_named_options=extract_params(locals())) def show_accounts(self) -> Result: return self.__invoke_command_with_options(["show", "accounts"]) @@ -119,7 +128,7 @@ class CLITester: return UpdateOwnerAuthority( self.__typer, self.__runner, - **extract_params(locals()), + cli_named_options=extract_params(locals()), ) def process_update_active_authority( # noqa: PLR0913 @@ -135,7 +144,7 @@ class CLITester: return UpdateActiveAuthority( self.__typer, self.__runner, - **extract_params(locals()), + cli_named_options=extract_params(locals()), ) def process_update_posting_authority( # noqa: PLR0913 @@ -151,7 +160,7 @@ class CLITester: return UpdatePostingAuthority( self.__typer, self.__runner, - **extract_params(locals()), + cli_named_options=extract_params(locals()), ) def process_update_memo_key( # noqa: PLR0913 @@ -166,7 +175,7 @@ class CLITester: ) -> Result: return self.__invoke_command_with_options( ["process", "update-memo-key"], - **extract_params(locals()), + cli_named_options=extract_params(locals()), ) def process_savings_deposit( # noqa: PLR0913 @@ -184,7 +193,7 @@ class CLITester: ) -> Result: return self.__invoke_command_with_options( ["process", "savings", "deposit"], - **extract_params(locals()), + cli_named_options=extract_params(locals()), ) def process_savings_withdrawal( # noqa: PLR0913 @@ -203,7 +212,7 @@ class CLITester: ) -> Result: return self.__invoke_command_with_options( ["process", "savings", "withdrawal"], - **extract_params(locals()), + cli_named_options=extract_params(locals()), ) def process_savings_withdrawal_cancel( # noqa: PLR0913 @@ -218,7 +227,7 @@ class CLITester: ) -> Result: return self.__invoke_command_with_options( ["process", "savings", "withdrawal-cancel"], - **extract_params(locals()), + cli_named_options=extract_params(locals()), ) def process_custom_json( # noqa: PLR0913 @@ -235,7 +244,7 @@ class CLITester: ) -> Result: return self.__invoke_command_with_options( ["process", "custom-json"], - **extract_params(locals()), + cli_named_options=extract_params(locals()), ) def process_transaction( # noqa: PLR0913 @@ -252,21 +261,25 @@ class CLITester: ) -> Result: return self.__invoke_command_with_options( ["process", "transaction"], - **extract_params(locals()), + cli_named_options=extract_params(locals()), ) def show_hive_power(self, *, account_name: str | None = None) -> Result: - return self.__invoke_command_with_options(["show", "hive-power"], **extract_params(locals())) + return self.__invoke_command_with_options(["show", "hive-power"], cli_named_options=extract_params(locals())) def show_pending_power_down(self, *, account_name: str | None = None) -> Result: - return self.__invoke_command_with_options(["show", "pending", "power-down"], **extract_params(locals())) + return self.__invoke_command_with_options( + ["show", "pending", "power-down"], cli_named_options=extract_params(locals()) + ) def show_pending_power_ups(self, *, account_name: str | None = None) -> Result: - return self.__invoke_command_with_options(["show", "pending", "power-ups"], **extract_params(locals())) + return self.__invoke_command_with_options( + ["show", "pending", "power-ups"], cli_named_options=extract_params(locals()) + ) def show_pending_removed_delegations(self, *, account_name: str | None = None) -> Result: return self.__invoke_command_with_options( - ["show", "pending", "removed-delegations"], **extract_params(locals()) + ["show", "pending", "removed-delegations"], cli_named_options=extract_params(locals()) ) def process_power_up( # noqa: PLR0913 @@ -281,7 +294,7 @@ class CLITester: force: bool | None = None, autosign: bool | None = None, ) -> Result: - return self.__invoke_command_with_options(["process", "power-up"], **extract_params(locals())) + return self.__invoke_command_with_options(["process", "power-up"], cli_named_options=extract_params(locals())) def process_power_down_start( # noqa: PLR0913 self, @@ -293,7 +306,9 @@ class CLITester: save_file: Path | None = None, autosign: bool | None = None, ) -> Result: - return self.__invoke_command_with_options(["process", "power-down", "start"], **extract_params(locals())) + return self.__invoke_command_with_options( + ["process", "power-down", "start"], cli_named_options=extract_params(locals()) + ) def process_power_down_restart( # noqa: PLR0913 self, @@ -305,7 +320,9 @@ class CLITester: save_file: Path | None = None, autosign: bool | None = None, ) -> Result: - return self.__invoke_command_with_options(["process", "power-down", "restart"], **extract_params(locals())) + return self.__invoke_command_with_options( + ["process", "power-down", "restart"], cli_named_options=extract_params(locals()) + ) def process_power_down_cancel( self, @@ -315,7 +332,9 @@ class CLITester: save_file: Path | None = None, autosign: bool | None = None, ) -> Result: - return self.__invoke_command_with_options(["process", "power-down", "cancel"], **extract_params(locals())) + return self.__invoke_command_with_options( + ["process", "power-down", "cancel"], cli_named_options=extract_params(locals()) + ) def process_delegations_set( # noqa: PLR0913 self, @@ -329,7 +348,9 @@ class CLITester: force: bool | None = None, autosign: bool | None = None, ) -> Result: - return self.__invoke_command_with_options(["process", "delegations", "set"], **extract_params(locals())) + return self.__invoke_command_with_options( + ["process", "delegations", "set"], cli_named_options=extract_params(locals()) + ) def process_delegations_remove( # noqa: PLR0913 self, @@ -341,7 +362,9 @@ class CLITester: save_file: Path | None = None, autosign: bool | None = None, ) -> Result: - return self.__invoke_command_with_options(["process", "delegations", "remove"], **extract_params(locals())) + return self.__invoke_command_with_options( + ["process", "delegations", "remove"], cli_named_options=extract_params(locals()) + ) def process_withdraw_routes_set( # noqa: PLR0913 self, @@ -356,7 +379,9 @@ class CLITester: force: bool | None = None, autosign: bool | None = None, ) -> Result: - return self.__invoke_command_with_options(["process", "withdraw-routes", "set"], **extract_params(locals())) + return self.__invoke_command_with_options( + ["process", "withdraw-routes", "set"], cli_named_options=extract_params(locals()) + ) def process_withdraw_routes_remove( # noqa: PLR0913 self, @@ -368,7 +393,9 @@ class CLITester: save_file: Path | None = None, autosign: bool | None = None, ) -> Result: - return self.__invoke_command_with_options(["process", "withdraw-routes", "remove"], **extract_params(locals())) + return self.__invoke_command_with_options( + ["process", "withdraw-routes", "remove"], cli_named_options=extract_params(locals()) + ) def show_chain(self) -> Result: return self.__invoke_command_with_options(["show", "chain"]) @@ -376,13 +403,16 @@ class CLITester: def __invoke_command_with_options( self, command: list[str], - cli_positional_args: tuple[StringConvertibleOptionTypes, ...] | None = None, + *, + cli_positional_args: Iterable[CLIArgumentValue] | None = None, + cli_named_options: Mapping[str, CLIOptionValue] | None = None, password_stdin: str | None = None, - /, - **cli_named_args: CliOptionT, ) -> Result: - positional = [option_to_string(arg) for arg in cli_positional_args] if cli_positional_args is not None else [] - full_command = [*command, *positional, *kwargs_to_cli_options(**cli_named_args)] + positional = ( + [stringify_parameter_value(arg) for arg in cli_positional_args] if cli_positional_args is not None else [] + ) + named = build_cli_options(cli_named_options) if cli_named_options is not None else [] + full_command = [*command, *positional, *named] return self.invoke_raw_command(full_command, password_stdin) def process_transfer( # noqa: PLR0913 @@ -397,7 +427,7 @@ class CLITester: autosign: bool | None = None, amount: tt.Asset.HiveT | tt.Asset.HbdT, ) -> Result: - return self.__invoke_command_with_options(["process", "transfer"], **extract_params(locals())) + return self.__invoke_command_with_options(["process", "transfer"], cli_named_options=extract_params(locals())) def configure_key_add( self, @@ -405,7 +435,9 @@ class CLITester: key: str, alias: str | None = None, ) -> Result: - return self.__invoke_command_with_options(["configure", "key", "add"], **extract_params(locals())) + return self.__invoke_command_with_options( + ["configure", "key", "add"], cli_named_options=extract_params(locals()) + ) def configure_key_remove( self, @@ -413,14 +445,18 @@ class CLITester: alias: str, from_beekeeper: bool | None = None, ) -> Result: - return self.__invoke_command_with_options(["configure", "key", "remove"], **extract_params(locals())) + return self.__invoke_command_with_options( + ["configure", "key", "remove"], cli_named_options=extract_params(locals()) + ) def configure_node_set( self, *, node_address: str, ) -> Result: - return self.__invoke_command_with_options(["configure", "node", "set"], **extract_params(locals())) + return self.__invoke_command_with_options( + ["configure", "node", "set"], cli_named_options=extract_params(locals()) + ) def unlock( self, @@ -430,41 +466,53 @@ class CLITester: ) -> Result: named_params = locals() named_params.pop("password_stdin") - return self.__invoke_command_with_options(["unlock"], None, password_stdin, **extract_params(named_params)) + return self.__invoke_command_with_options( + ["unlock"], password_stdin=password_stdin, cli_named_options=extract_params(named_params) + ) def lock(self) -> Result: - return self.__invoke_command_with_options(["lock"], **extract_params(locals())) + return self.__invoke_command_with_options(["lock"], cli_named_options=extract_params(locals())) def configure_working_account_switch(self, *, account_name: str) -> Result: return self.__invoke_command_with_options( - ["configure", "working-account", "switch"], **extract_params(locals()) + ["configure", "working-account", "switch"], cli_named_options=extract_params(locals()) ) def configure_tracked_account_add(self, *, account_name: str) -> Result: - return self.__invoke_command_with_options(["configure", "tracked-account", "add"], **extract_params(locals())) + return self.__invoke_command_with_options( + ["configure", "tracked-account", "add"], cli_named_options=extract_params(locals()) + ) def configure_tracked_account_remove(self, *, account_name: str) -> Result: return self.__invoke_command_with_options( - ["configure", "tracked-account", "remove"], **extract_params(locals()) + ["configure", "tracked-account", "remove"], cli_named_options=extract_params(locals()) ) def show_profile(self) -> Result: - return self.__invoke_command_with_options(["show", "profile"], **extract_params(locals())) + return self.__invoke_command_with_options(["show", "profile"], cli_named_options=extract_params(locals())) def show_profiles(self) -> Result: - return self.__invoke_command_with_options(["show", "profiles"], **extract_params(locals())) + return self.__invoke_command_with_options(["show", "profiles"], cli_named_options=extract_params(locals())) def configure_known_account_add(self, *, account_name: str) -> Result: - return self.__invoke_command_with_options(["configure", "known-account", "add"], **extract_params(locals())) + return self.__invoke_command_with_options( + ["configure", "known-account", "add"], cli_named_options=extract_params(locals()) + ) def configure_known_account_remove(self, *, account_name: str) -> Result: - return self.__invoke_command_with_options(["configure", "known-account", "remove"], **extract_params(locals())) + return self.__invoke_command_with_options( + ["configure", "known-account", "remove"], cli_named_options=extract_params(locals()) + ) def configure_known_account_enable(self) -> Result: - return self.__invoke_command_with_options(["configure", "known-account", "enable"], **extract_params(locals())) + return self.__invoke_command_with_options( + ["configure", "known-account", "enable"], cli_named_options=extract_params(locals()) + ) def configure_known_account_disable(self) -> Result: - return self.__invoke_command_with_options(["configure", "known-account", "disable"], **extract_params(locals())) + return self.__invoke_command_with_options( + ["configure", "known-account", "disable"], cli_named_options=extract_params(locals()) + ) def configure_profile_create( self, @@ -475,11 +523,15 @@ class CLITester: named_params = locals() named_params.pop("password_stdin") return self.__invoke_command_with_options( - ["configure", "profile", "create"], None, password_stdin, **extract_params(named_params) + ["configure", "profile", "create"], + password_stdin=password_stdin, + cli_named_options=extract_params(named_params), ) def configure_profile_delete(self, *, profile_name: str, force: bool | None = None) -> Result: - return self.__invoke_command_with_options(["configure", "profile", "delete"], **extract_params(locals())) + return self.__invoke_command_with_options( + ["configure", "profile", "delete"], cli_named_options=extract_params(locals()) + ) def process_proxy_set( # noqa: PLR0913 self, @@ -491,7 +543,9 @@ class CLITester: save_file: Path | None = None, autosign: bool | None = None, ) -> Result: - return self.__invoke_command_with_options(["process", "proxy", "set"], **extract_params(locals())) + return self.__invoke_command_with_options( + ["process", "proxy", "set"], cli_named_options=extract_params(locals()) + ) def process_proxy_clear( self, @@ -502,7 +556,9 @@ class CLITester: save_file: Path | None = None, autosign: bool | None = None, ) -> Result: - return self.__invoke_command_with_options(["process", "proxy", "clear"], **extract_params(locals())) + return self.__invoke_command_with_options( + ["process", "proxy", "clear"], cli_named_options=extract_params(locals()) + ) def process_transfer_schedule_create( # noqa: PLR0913 self, @@ -521,7 +577,7 @@ class CLITester: autosign: bool | None = None, ) -> Result: return self.__invoke_command_with_options( - ["process", "transfer-schedule", "create"], **extract_params(locals()) + ["process", "transfer-schedule", "create"], cli_named_options=extract_params(locals()) ) def process_transfer_schedule_modify( # noqa: PLR0913 @@ -541,7 +597,7 @@ class CLITester: autosign: bool | None = None, ) -> Result: return self.__invoke_command_with_options( - ["process", "transfer-schedule", "modify"], **extract_params(locals()) + ["process", "transfer-schedule", "modify"], cli_named_options=extract_params(locals()) ) def process_transfer_schedule_remove( # noqa: PLR0913 @@ -556,7 +612,7 @@ class CLITester: autosign: bool | None = None, ) -> Result: return self.__invoke_command_with_options( - ["process", "transfer-schedule", "remove"], **extract_params(locals()) + ["process", "transfer-schedule", "remove"], cli_named_options=extract_params(locals()) ) def generate_key_from_seed( @@ -571,15 +627,17 @@ class CLITester: named_params = locals() named_params.pop("password_stdin") return self.__invoke_command_with_options( - ["generate", "key-from-seed"], None, password_stdin, **extract_params(named_params) + ["generate", "key-from-seed"], password_stdin=password_stdin, cli_named_options=extract_params(named_params) ) def generate_public_key(self, *, password_stdin: str | None = None) -> Result: - return self.__invoke_command_with_options(["generate", "public-key"], None, password_stdin) + return self.__invoke_command_with_options(["generate", "public-key"], password_stdin=password_stdin) def generate_random_key(self, *, key_pairs: int | None = None) -> Result: named_params = locals() - return self.__invoke_command_with_options(["generate", "random-key"], **extract_params(named_params)) + return self.__invoke_command_with_options( + ["generate", "random-key"], cli_named_options=extract_params(named_params) + ) def generate_secret_phrase(self) -> Result: return self.__invoke_command_with_options(["generate", "secret-phrase"]) @@ -594,11 +652,13 @@ class CLITester: save_file: Path | None = None, autosign: bool | None = None, ) -> Result: - return self.__invoke_command_with_options(["process", "claim", "new-account-token"], **extract_params(locals())) + return self.__invoke_command_with_options( + ["process", "claim", "new-account-token"], cli_named_options=extract_params(locals()) + ) def process_account_creation( # noqa: PLR0913 self, - *args: StringConvertibleOptionTypes, + *args: CLIArgumentValue, creator: str | None = None, new_account_name: str | None = None, fee: bool | None = None, @@ -613,7 +673,9 @@ class CLITester: autosign: bool | None = None, ) -> Result: return self.__invoke_command_with_options( - ["process", "account-creation"], args, **extract_params(locals(), "args") + ["process", "account-creation"], + cli_positional_args=args, + cli_named_options=extract_params(locals(), "args"), ) def process_update_witness( # noqa: PLR0913 @@ -639,4 +701,6 @@ class CLITester: if use_witness_key is False: named_params.pop("use_witness_key") named_params["use_active_authority"] = True - return self.__invoke_command_with_options(["process", "update-witness"], **extract_params(named_params)) + return self.__invoke_command_with_options( + ["process", "update-witness"], cli_named_options=extract_params(named_params) + ) diff --git a/tests/clive-local-tools/clive_local_tools/cli/command_options.py b/tests/clive-local-tools/clive_local_tools/cli/command_options.py index a1c4e1add3b07c25ffee04096673175a09de3d7b..fbdf11f28f97f9403448ea676c6881416da3b3b5 100644 --- a/tests/clive-local-tools/clive_local_tools/cli/command_options.py +++ b/tests/clive-local-tools/clive_local_tools/cli/command_options.py @@ -3,6 +3,7 @@ from __future__ import annotations from copy import copy, deepcopy from decimal import Decimal from pathlib import Path +from typing import TYPE_CHECKING import test_tools as tt @@ -10,11 +11,15 @@ from clive.__private.models.schemas import PublicKey from .exceptions import UnsupportedOptionError -type StringConvertibleOptionTypes = str | int | Decimal | tt.Asset.AnyT | PublicKey | Path -type CliOptionT = bool | StringConvertibleOptionTypes | list[StringConvertibleOptionTypes] | None +if TYPE_CHECKING: + from collections.abc import Mapping +type CLIParameterValue = str | int | Decimal | tt.Asset.AnyT | PublicKey | Path +type CLIArgumentValue = CLIParameterValue +type CLIOptionValue = bool | CLIParameterValue | list[CLIParameterValue] | None -def option_to_string(value: StringConvertibleOptionTypes) -> str: + +def stringify_parameter_value(value: CLIParameterValue) -> str: if isinstance(value, str): return value if isinstance(value, int): @@ -27,25 +32,25 @@ def option_to_string(value: StringConvertibleOptionTypes) -> str: return value.as_legacy() if isinstance(value, PublicKey): return str(value) - raise UnsupportedOptionError(supported_type=StringConvertibleOptionTypes, actual_type=type(value)) + raise UnsupportedOptionError(supported_type=CLIParameterValue, actual_type=type(value)) -def kwargs_to_cli_options(**cli_options: CliOptionT) -> list[str]: +def build_cli_options(cli_named_options: Mapping[str, CLIOptionValue]) -> list[str]: options: list[str] = [] - for key, value in cli_options.items(): + for key, value in cli_named_options.items(): option_name = key.strip("_").replace("_", "-") if value is True: options.append(f"--{option_name}") elif value is False: options.append(f"--no-{option_name}") elif isinstance(value, list): - options.extend([f"--{option_name}={option_to_string(entry)}" for entry in value]) + options.extend([f"--{option_name}={stringify_parameter_value(entry)}" for entry in value]) elif value is not None: - options.append(f"--{option_name}={option_to_string(value)}") + options.append(f"--{option_name}={stringify_parameter_value(value)}") return options -def extract_params(params: dict[str, CliOptionT], *param_names_to_drop: str) -> dict[str, CliOptionT]: +def extract_params(params: dict[str, CLIOptionValue], *param_names_to_drop: str) -> dict[str, CLIOptionValue]: # TODO: this should be CLITester instancemethod, not global function # deepcopy on self is not possible as it causes CLITester to be copied but it can't be because of Beekeeper copied_params = copy(params)