Skip to content

Commit

Permalink
Make cli more inclusive (#413)
Browse files Browse the repository at this point in the history
* Add exit command

* Add help command

* Update usage messages and add custom version option

* Update version message

* Fix var name
  • Loading branch information
falvaradorodriguez authored Jun 13, 2024
1 parent 29be0a2 commit bebbba2
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 5 deletions.
37 changes: 35 additions & 2 deletions src/safe_cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from safe_cli.argparse_validators import check_ethereum_address
from safe_cli.operators import (
SafeCliTerminationException,
SafeOperator,
SafeServiceNotAvailable,
SafeTxServiceOperator,
Expand Down Expand Up @@ -45,12 +46,21 @@ def __init__(self, safe_address: ChecksumAddress, node_url: str, history: bool):

def print_startup_info(self):
print_formatted_text(text2art("Safe CLI")) # Print fancy text
print_formatted_text(HTML(f"<b><ansigreen>Version {VERSION}</ansigreen></b>"))
print_formatted_text(HTML(f"<b>Version: {VERSION}</b>"))
print_formatted_text(
HTML("<b><ansigreen>Loading Safe information...</ansigreen></b>")
)
self.safe_operator.print_info()

print_formatted_text(
HTML("\nUse the <b>tab key</b> to show options in interactive mode.")
)
print_formatted_text(
HTML(
"The <b>help</b> command displays all available options and the <b>exit</b> command terminates the safe-cli."
)
)

def get_prompt_text(self):
mode: Optional[str] = "blockchain"
if isinstance(self.prompt_parser.safe_operator, SafeTxServiceOperator):
Expand Down Expand Up @@ -111,6 +121,8 @@ def loop(self):
new_operator.refresh_safe_cli_info() # ClI info needs to be initialized
else:
self.prompt_parser.process_command(command)
except SafeCliTerminationException:
break
except EOFError:
break
except KeyboardInterrupt:
Expand All @@ -119,8 +131,29 @@ def loop(self):
pass


def get_usage_msg():
return """
safe-cli [-h] [--history] [--get-safes-from-owner] address node_url
Examples:
safe-cli 0x0000000000000000000000000000000000000000 https://sepolia.drpc.org
safe-cli --get-safes-from-owner 0x0000000000000000000000000000000000000000 https://sepolia.drpc.org
safe-cli --history 0x0000000000000000000000000000000000000000 https://sepolia.drpc.org
safe-cli --history --get-safes-from-owner 0x0000000000000000000000000000000000000000 https://sepolia.drpc.org
"""


def build_safe_cli() -> Optional[SafeCli]:
parser = argparse.ArgumentParser()
parser = argparse.ArgumentParser(usage=get_usage_msg())
parser.add_argument(
"-v",
"--version",
action="version",
version=f"Safe CLI v{VERSION}",
help="Show program's version number and exit.",
)

parser.add_argument(
"address",
help="The address of the Safe, or an owner address if --get-safes-from-owner is specified.",
Expand Down
3 changes: 2 additions & 1 deletion src/safe_cli/operators/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from .enums import SafeOperatorMode
from .exceptions import SafeServiceNotAvailable
from .exceptions import SafeCliTerminationException, SafeServiceNotAvailable
from .safe_operator import SafeOperator
from .safe_tx_service_operator import SafeTxServiceOperator

Expand All @@ -8,4 +8,5 @@
"SafeOperatorMode",
"SafeServiceNotAvailable",
"SafeTxServiceOperator",
"SafeCliTerminationException",
]
4 changes: 4 additions & 0 deletions src/safe_cli/operators/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,7 @@ class SafeServiceNotAvailable(SafeOperatorException):

class HardwareWalletException(SafeOperatorException):
pass


class SafeCliTerminationException(SafeOperatorException):
pass
29 changes: 29 additions & 0 deletions src/safe_cli/prompt_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import functools

from prompt_toolkit import HTML, print_formatted_text
from prompt_toolkit.formatted_text import html

from gnosis.safe.api import SafeAPIException

Expand All @@ -25,6 +26,7 @@
NotEnoughSignatures,
NotEnoughTokenToSend,
SafeAlreadyUpdatedException,
SafeCliTerminationException,
SafeOperatorException,
SafeVersionNotSupportedException,
SameFallbackHandlerException,
Expand All @@ -33,6 +35,7 @@
ThresholdLimitException,
)
from .operators.safe_operator import SafeOperator
from .safe_completer_constants import meta, safe_commands_arguments


def safe_exception(function):
Expand Down Expand Up @@ -319,6 +322,24 @@ def remove_delegate(args):
def remove_proposed_transaction(args):
safe_operator.remove_proposed_transaction(args.safe_tx_hash)

def list_commands(args):
print_formatted_text(
HTML("<b>The following commands can be used:</b>"), end="\n\n"
)
for key in sorted(safe_commands_arguments.keys()):
print_formatted_text(
HTML(
f"<b>{key} {html.html_escape(safe_commands_arguments.get(key))}</b>"
)
)
print_formatted_text(HTML("&#9;"), meta.get(key, ""))
print_formatted_text(
HTML("\nUse the <b>tab key</b> to show options in interactive mode.")
)

def terminate_cli(args):
raise SafeCliTerminationException()

# Cli owners
parser_show_cli_owners = subparsers.add_parser("show_cli_owners")
parser_show_cli_owners.set_defaults(func=show_cli_owners)
Expand Down Expand Up @@ -542,4 +563,12 @@ def remove_proposed_transaction(args):
"safe_tx_hash", type=check_keccak256_hash
)

# List all command options
parser_help = subparsers.add_parser("help")
parser_help.set_defaults(func=list_commands)

# Terminate safe cli
parser_exit = subparsers.add_parser("exit")
parser_exit.set_defaults(func=terminate_cli)

return prompt_parser
6 changes: 6 additions & 0 deletions src/safe_cli/safe_completer_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
"blockchain": "",
"tx-service": "",
"drain": "<address>",
"help": "",
"exit": "",
}

safe_commands = list(safe_commands_arguments.keys())
Expand Down Expand Up @@ -215,4 +217,8 @@
"drain": HTML(
"Command <b>drain</b> will try to send all assets ether and ERC20 to a check-summed account"
),
"help": HTML(
"Command <b>help</b> will display the command options available for use in safe-cli"
),
"exit": HTML("Command <b>exit</b> will terminate the safe-cli process"),
}
21 changes: 19 additions & 2 deletions src/safe_cli/safe_creator.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,32 @@
)
from safe_cli.utils import yes_or_no_question

from . import VERSION
from .argparse_validators import (
check_ethereum_address,
check_positive_integer,
check_private_key,
)


def get_usage_msg():
return """
safe-creator [-h] [-v] [--threshold THRESHOLD] [--owners OWNERS [OWNERS ...]] [--safe-contract SAFE_CONTRACT] [--proxy-factory PROXY_FACTORY] [--callback-handler CALLBACK_HANDLER] [--salt-nonce SALT_NONCE] [--without-events] node_url private_key
Example:
safe-creator https://sepolia.drpc.org 0000000000000000000000000000000000000000000000000000000000000000
"""


def setup_argument_parser():
parser = argparse.ArgumentParser()
parser = argparse.ArgumentParser(usage=get_usage_msg())
parser.add_argument(
"-v",
"--version",
action="version",
version=f"Safe Creator v{VERSION}",
help="Show program's version number and exit.",
)
parser.add_argument("node_url", help="Ethereum node url")
parser.add_argument(
"private_key", help="Deployer private_key", type=check_private_key
Expand Down Expand Up @@ -88,8 +105,8 @@ def setup_argument_parser():

def main(*args, **kwargs) -> EthereumTxSent:
parser = setup_argument_parser()
print_formatted_text(text2art("Safe Creator")) # Print fancy text
args = parser.parse_args()
print_formatted_text(text2art("Safe Creator")) # Print fancy text
node_url: URI = args.node_url
account: LocalAccount = Account.from_key(args.private_key)
owners: List[str] = args.owners if args.owners else [account.address]
Expand Down
5 changes: 5 additions & 0 deletions tests/test_safe_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from gnosis.safe import Safe

from safe_cli.operators import SafeCliTerminationException
from safe_cli.operators.safe_operator import SafeOperator
from safe_cli.prompt_parser import PromptParser

Expand Down Expand Up @@ -74,6 +75,10 @@ def test_safe_cli_happy_path(self):
)
self.assertEqual(safe.retrieve_owners(), [self.ethereum_test_account.address])

# Terminate cli
with self.assertRaises(SafeCliTerminationException):
prompt_parser.process_command("exit")


if __name__ == "__main__":
unittest.main()

0 comments on commit bebbba2

Please sign in to comment.