• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

safe-global / safe-cli / 12011247560

25 Nov 2024 01:38PM CUT coverage: 88.612%. Remained the same
12011247560

Pull #469

github

web-flow
Merge d6e230fe3 into 0659e6cdb
Pull Request #469: Bump typer from 0.13.0 to 0.13.1

221 of 262 branches covered (84.35%)

Branch coverage included in aggregate %.

2868 of 3224 relevant lines covered (88.96%)

2.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.32
/src/safe_cli/safe_cli.py
1
import argparse
3✔
2
import os
3✔
3
import sys
3✔
4
from typing import Optional
3✔
5

6
from eth_typing import ChecksumAddress
3✔
7
from prompt_toolkit import HTML, PromptSession, print_formatted_text
3✔
8
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
3✔
9
from prompt_toolkit.history import FileHistory
3✔
10
from prompt_toolkit.lexers import PygmentsLexer
3✔
11

12
from .operators import (
3✔
13
    SafeCliTerminationException,
14
    SafeOperator,
15
    SafeServiceNotAvailable,
16
    SafeTxServiceOperator,
17
)
18
from .prompt_parser import PromptParser
3✔
19
from .safe_completer import SafeCompleter
3✔
20
from .safe_lexer import SafeLexer
3✔
21

22

23
class SafeCli:
3✔
24
    def __init__(self, safe_address: ChecksumAddress, node_url: str, history: bool):
3✔
25
        """
26
        :param safe_address: Safe address
27
        :param node_url: Ethereum RPC url
28
        :param history: If `True` keep command history, otherwise history is not kept after closing the CLI
29
        """
30
        self.safe_address = safe_address
3✔
31
        self.node_url = node_url
3✔
32
        if history:
3✔
33
            self.session = PromptSession(
3✔
34
                history=FileHistory(os.path.join(sys.path[0], ".history"))
35
            )
36
        else:
37
            self.session = PromptSession()
×
38
        self.safe_operator = SafeOperator(safe_address, node_url)
3✔
39
        self.prompt_parser = PromptParser(self.safe_operator)
3✔
40

41
    def print_startup_info(self):
3✔
42
        print_formatted_text(
3✔
43
            HTML("<b><ansigreen>Loading Safe information...</ansigreen></b>")
44
        )
45
        self.safe_operator.print_info()
3✔
46

47
        print_formatted_text(
3✔
48
            HTML("\nUse the <b>tab key</b> to show options in interactive mode.")
49
        )
50
        print_formatted_text(
3✔
51
            HTML(
52
                "The <b>help</b> command displays all available options and the <b>exit</b> command terminates the safe-cli."
53
            )
54
        )
55

56
    def get_prompt_text(self):
3✔
57
        mode: Optional[str] = "blockchain"
3✔
58
        if isinstance(self.prompt_parser.safe_operator, SafeTxServiceOperator):
3✔
59
            mode = "tx-service"
×
60

61
        return HTML(
3✔
62
            f"<bold><ansiblue>{mode} > {self.safe_address}</ansiblue><ansired> > </ansired></bold>"
63
        )
64

65
    def get_bottom_toolbar(self):
3✔
66
        return HTML(
×
67
            f'<b><style fg="ansiyellow">network={self.safe_operator.network.name} '
68
            f"{self.safe_operator.safe_cli_info}</style></b>"
69
        )
70

71
    def parse_operator_mode(self, command: str) -> Optional[SafeOperator]:
3✔
72
        """
73
        Parse operator mode to switch between blockchain (default) and tx-service
74
        :param command:
75
        :return: SafeOperator if detected
76
        """
77
        split_command = command.split()
3✔
78
        try:
3✔
79
            if (split_command[0]) == "tx-service":
3✔
80
                print_formatted_text(
3✔
81
                    HTML("<b><ansigreen>Sending txs to tx service</ansigreen></b>")
82
                )
83
                return SafeTxServiceOperator(self.safe_address, self.node_url)
3✔
84
            elif split_command[0] == "blockchain":
3✔
85
                print_formatted_text(
×
86
                    HTML("<b><ansigreen>Sending txs to blockchain</ansigreen></b>")
87
                )
88
                return self.safe_operator
×
89
        except SafeServiceNotAvailable:
3✔
90
            print_formatted_text(
3✔
91
                HTML("<b><ansired>Mode not supported on this network</ansired></b>")
92
            )
93

94
    def get_command(self) -> str:
3✔
95
        return self.session.prompt(
3✔
96
            self.get_prompt_text,
97
            auto_suggest=AutoSuggestFromHistory(),
98
            bottom_toolbar=self.get_bottom_toolbar,
99
            lexer=PygmentsLexer(SafeLexer),
100
            completer=SafeCompleter(),
101
        )
102

103
    def loop(self):
3✔
104
        while True:
3✔
105
            try:
3✔
106
                command = self.get_command()
3✔
107
                if not command.strip():
3✔
108
                    continue
×
109

110
                new_operator = self.parse_operator_mode(command)
3✔
111
                if new_operator:
3✔
112
                    self.prompt_parser = PromptParser(new_operator)
×
113
                    new_operator.refresh_safe_cli_info()  # ClI info needs to be initialized
×
114
                else:
115
                    self.prompt_parser.process_command(command)
3✔
116
            except SafeCliTerminationException:
3✔
117
                break
3✔
118
            except EOFError:
3✔
119
                break
3✔
120
            except KeyboardInterrupt:
3✔
121
                continue
×
122
            except (argparse.ArgumentError, argparse.ArgumentTypeError, SystemExit):
3✔
123
                pass
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc