Пример #1
0
def main():
    args = CmdlineParser().parse_args()

    # Parse environment variables from Dockerfile.
    # If an environment variable is not empty and it's not defined in the arguments, then we'll use the environment
    # variable.
    if args.config_file_name is None and len(
            os.environ.get("CONFIG_FILE_NAME", "")) > 0:
        args.config_file_name = os.environ["CONFIG_FILE_NAME"]
    if args.config_password is None and len(
            os.environ.get("CONFIG_PASSWORD", "")) > 0:
        args.config_password = os.environ["CONFIG_PASSWORD"]

    # If no password is given from the command line, prompt for one.
    secrets_manager_cls = ETHKeyFileSecretManger
    client_config_map = load_client_config_map_from_file()
    if args.config_password is None:
        secrets_manager = login_prompt(secrets_manager_cls,
                                       style=load_style(client_config_map))
        if not secrets_manager:
            return
    else:
        secrets_manager = secrets_manager_cls(args.config_password)

    asyncio.get_event_loop().run_until_complete(
        quick_start(args, secrets_manager))
Пример #2
0
    def __init__(self, input_handler: Callable, bindings: KeyBindings,
                 completer: Completer):
        self.search_field = create_search_field()
        self.input_field = create_input_field(completer=completer)
        self.output_field = create_output_field()
        self.log_field = create_log_field(self.search_field)
        self.layout = generate_layout(self.input_field, self.output_field,
                                      self.log_field, self.search_field)
        # add self.to_stop_config to know if cancel is triggered
        self.to_stop_config: bool = False

        self.bindings = bindings
        self.input_handler = input_handler
        self.input_field.accept_handler = self.accept
        self.app = Application(layout=self.layout,
                               full_screen=True,
                               key_bindings=self.bindings,
                               style=load_style(),
                               mouse_support=True,
                               clipboard=PyperclipClipboard())

        # settings
        self.prompt_text = ">>> "
        self.pending_input = None
        self.input_event = None
        self.hide_input = False
Пример #3
0
    def test_load_style_windows(self, is_windows_mock):
        is_windows_mock.return_value = True

        global_config_map = {}
        global_config_map["top-pane"] = self.ConfigVar("#FAFAFA")
        global_config_map["bottom-pane"] = self.ConfigVar("#FAFAFA")
        global_config_map["output-pane"] = self.ConfigVar("#FAFAFA")
        global_config_map["input-pane"] = self.ConfigVar("#FAFAFA")
        global_config_map["logs-pane"] = self.ConfigVar("#FAFAFA")
        global_config_map["terminal-primary"] = self.ConfigVar("#FCFCFC")

        style = Style.from_dict({
            "output-field": "bg:#ansigray #ansigray",
            "input-field": "bg:#ansigray #ansiwhite",
            "log-field": "bg:#ansigray #ansiwhite",
            "header": "bg:#ansigray #ansiwhite",
            "footer": "bg:#ansigray #ansiwhite",
            "search": "#ansigray",
            "search.current": "#ansigray",
            "primary": "#ansigray",
            "warning": "#ansibrightyellow",
            "error": "#ansired"
        })

        self.assertEqual(style.class_names_and_attrs,
                         load_style(global_config_map).class_names_and_attrs)
Пример #4
0
    def __init__(self, input_handler: Callable, bindings: KeyBindings,
                 completer: Completer):
        use_asyncio_event_loop()
        self.input_field = create_input_field(completer=completer)
        self.output_field = create_output_field()
        self.log_field = create_log_field()
        self.layout = generate_layout(self.input_field, self.output_field,
                                      self.log_field)

        self.bindings = bindings
        self.input_handler = input_handler
        self.input_field.accept_handler = self.accept
        self.app = Application(layout=self.layout,
                               full_screen=True,
                               key_bindings=self.bindings,
                               style=load_style(),
                               mouse_support=True,
                               clipboard=PyperclipClipboard())
        self.log_lines: Deque[str] = deque()
        self.log(HEADER)

        # settings
        self.prompt_text = ">>> "
        self.pending_input = None
        self.input_event = None
        self.hide_input = False
Пример #5
0
    def test_load_style_unix(self, is_windows_mock):
        is_windows_mock.return_value = False

        global_config_map = {}
        global_config_map["top-pane"] = self.ConfigVar("#FAFAFA")
        global_config_map["bottom-pane"] = self.ConfigVar("#FAFAFA")
        global_config_map["output-pane"] = self.ConfigVar("#FAFAFA")
        global_config_map["input-pane"] = self.ConfigVar("#FAFAFA")
        global_config_map["logs-pane"] = self.ConfigVar("#FAFAFA")
        global_config_map["terminal-primary"] = self.ConfigVar("#FCFCFC")

        style = Style.from_dict({
            "output-field": "bg:#FAFAFA #FCFCFC",
            "input-field": "bg:#FAFAFA #FFFFFF",
            "log-field": "bg:#FAFAFA #FFFFFF",
            "header": "bg:#FAFAFA #AAAAAA",
            "footer": "bg:#FAFAFA #AAAAAA",
            "search": "bg:#000000 #93C36D",
            "search.current": "bg:#000000 #1CD085",
            "primary": "#FCFCFC",
            "warning": "#93C36D",
            "error": "#F5634A"
        })

        self.assertEqual(style.class_names_and_attrs,
                         load_style(global_config_map).class_names_and_attrs)
Пример #6
0
def main():
    chdir_to_data_directory()
    secrets_manager_cls = ETHKeyFileSecretManger
    ev_loop: asyncio.AbstractEventLoop = asyncio.get_event_loop()
    client_config_map = load_client_config_map_from_file()
    if login_prompt(secrets_manager_cls, style=load_style(client_config_map)):
        ev_loop.run_until_complete(main_async(client_config_map))
 async def run(self):
     self.app = Application(layout=self.layout,
                            full_screen=True,
                            key_bindings=self.bindings,
                            style=load_style(),
                            mouse_support=True,
                            clipboard=PyperclipClipboard())
     await self.app.run_async()
Пример #8
0
 async def run(self):
     self.app = Application(layout=self.layout,
                            full_screen=True,
                            key_bindings=self.bindings,
                            style=load_style(),
                            mouse_support=True,
                            clipboard=PyperclipClipboard())
     await self.app.run_async(pre_run=self.did_start_ui)
     self._stdout_redirect_context.close()
Пример #9
0
    def __init__(self) -> None:
        super().__init__()
        self.html_tag_css_style_map: Dict[str, str] = {style: css for style, css in load_style().style_rules}
        self.html_tag_css_style_map.update({
            style: config.value
            for style, config in color_config_map.items()
            if style not in self.html_tag_css_style_map.keys()
        })

        # Maps specific text to its corresponding UI styles
        self.text_style_tag_map: Dict[str, str] = text_ui_style
Пример #10
0
    def __init__(self, client_config_map: ClientConfigAdapter) -> None:
        super().__init__()
        self.html_tag_css_style_map: Dict[str, str] = {
            style: css for style, css in load_style(client_config_map).style_rules
        }
        self.html_tag_css_style_map.update({
            ti.attr: ti.value
            for ti in client_config_map.color.traverse()
            if ti.attr not in self.html_tag_css_style_map
        })

        # Maps specific text to its corresponding UI styles
        self.text_style_tag_map: Dict[str, str] = text_ui_style
Пример #11
0
    def test_load_style_unix(self, is_windows_mock):
        is_windows_mock.return_value = False

        global_config_map = ClientConfigMap()
        global_config_map.color.top_pane = "#FAFAFA"
        global_config_map.color.bottom_pane = "#FAFAFA"
        global_config_map.color.output_pane = "#FAFAFA"
        global_config_map.color.input_pane = "#FAFAFA"
        global_config_map.color.logs_pane = "#FAFAFA"
        global_config_map.color.terminal_primary = "#FCFCFC"

        global_config_map.color.primary_label = "#5FFFD7"
        global_config_map.color.secondary_label = "#FFFFFF"
        global_config_map.color.success_label = "#5FFFD7"
        global_config_map.color.warning_label = "#FFFF00"
        global_config_map.color.info_label = "#5FD7FF"
        global_config_map.color.error_label = "#FF0000"

        adapter = ClientConfigAdapter(global_config_map)

        style = Style.from_dict({
            "output_field": "bg:#FAFAFA #FCFCFC",
            "input_field": "bg:#FAFAFA #FFFFFF",
            "log_field": "bg:#FAFAFA #FFFFFF",
            "header": "bg:#FAFAFA #AAAAAA",
            "footer": "bg:#FAFAFA #AAAAAA",
            "search": "bg:#000000 #93C36D",
            "search.current": "bg:#000000 #1CD085",
            "primary": "#FCFCFC",
            "warning": "#93C36D",
            "error": "#F5634A",
            "tab_button.focused": "bg:#FCFCFC #FAFAFA",
            "tab_button": "bg:#FFFFFF #FAFAFA",
            "dialog": "bg:#171E2B",
            "dialog frame.label": "bg:#FCFCFC #000000",
            "dialog.body": "bg:#000000 #FCFCFC",
            "dialog shadow": "bg:#171E2B",
            "button": "bg:#000000",
            "text-area": "bg:#000000 #FCFCFC",
            # Label bg and font color
            "primary_label": "bg:#5FFFD7 #FAFAFA",
            "secondary_label": "bg:#FFFFFF #FAFAFA",
            "success_label": "bg:#5FFFD7 #FAFAFA",
            "warning_label": "bg:#FFFF00 #FAFAFA",
            "info_label": "bg:#5FD7FF #FAFAFA",
            "error_label": "bg:#FF0000 #FAFAFA",
        })

        self.assertEqual(style.class_names_and_attrs,
                         load_style(adapter).class_names_and_attrs)
Пример #12
0
    def test_load_style_windows(self, is_windows_mock):
        is_windows_mock.return_value = True

        global_config_map = ClientConfigMap()
        global_config_map.color.top_pane = "#FAFAFA"
        global_config_map.color.bottom_pane = "#FAFAFA"
        global_config_map.color.output_pane = "#FAFAFA"
        global_config_map.color.input_pane = "#FAFAFA"
        global_config_map.color.logs_pane = "#FAFAFA"
        global_config_map.color.terminal_primary = "#FCFCFC"

        global_config_map.color.primary_label = "#5FFFD7"
        global_config_map.color.secondary_label = "#FFFFFF"
        global_config_map.color.success_label = "#5FFFD7"
        global_config_map.color.warning_label = "#FFFF00"
        global_config_map.color.info_label = "#5FD7FF"
        global_config_map.color.error_label = "#FF0000"

        adapter = ClientConfigAdapter(global_config_map)

        style = Style.from_dict({
            "output_field": "bg:#ansiwhite #ansiwhite",
            "input_field": "bg:#ansiwhite #ansiwhite",
            "log_field": "bg:#ansiwhite #ansiwhite",
            "header": "bg:#ansiwhite #ansiwhite",
            "footer": "bg:#ansiwhite #ansiwhite",
            "search": "#ansiwhite",
            "search.current": "#ansiwhite",
            "primary": "#ansiwhite",
            "warning": "#ansibrightyellow",
            "error": "#ansired",
            "tab_button.focused": "bg:#ansiwhite #ansiwhite",
            "tab_button": "bg:#ansiwhite #ansiwhite",
            "dialog": "bg:#ansigreen",
            "dialog frame.label": "bg:#ansiwhite #ansiblack",
            "dialog.body": "bg:#ansiblack #ansiwhite",
            "dialog shadow": "bg:#ansigreen",
            "button": "bg:#ansigreen",
            "text-area": "bg:#ansiblack #ansiwhite",
            # Label bg and font color
            "primary_label": "bg:#ansicyan #ansiwhite",
            "secondary_label": "bg:#ansiwhite #ansiwhite",
            "success_label": "bg:#ansicyan #ansiwhite",
            "warning_label": "bg:#ansiyellow #ansiwhite",
            "info_label": "bg:#ansicyan #ansiwhite",
            "error_label": "bg:#ansired #ansiwhite",
        })

        self.assertEqual(style.class_names_and_attrs,
                         load_style(adapter).class_names_and_attrs)
    def test_login_success(
        self,
        new_password_required_mock: MagicMock,
        login_mock: MagicMock,
        input_dialog_mock: MagicMock,
        message_dialog_mock: MagicMock,
    ):
        new_password_required_mock.return_value = False
        run_mock = MagicMock()
        run_mock.run.return_value = self.password
        input_dialog_mock.return_value = run_mock
        login_mock.return_value = True

        self.assertTrue(login_prompt(ETHKeyFileSecretManger, style=load_style(self.client_config_map)))
        self.assertEqual(1, len(login_mock.mock_calls))
        message_dialog_mock.assert_not_called()
    def test_login_error_retries(
        self,
        new_password_required_mock: MagicMock,
        login_mock: MagicMock,
        input_dialog_mock: MagicMock,
        message_dialog_mock: MagicMock,
    ):
        new_password_required_mock.return_value = False
        run_mock = MagicMock()
        run_mock.run.return_value = "somePassword"
        input_dialog_mock.return_value = run_mock
        message_dialog_mock.return_value = run_mock
        login_mock.side_effect = [False, True]

        self.assertTrue(login_prompt(ETHKeyFileSecretManger, style=load_style(self.client_config_map)))
        self.assertEqual(2, len(login_mock.mock_calls))
        message_dialog_mock.assert_called()
Пример #15
0
 async def _config_single_key_legacy(
     self,  # type: HummingbotApplication
     key: str,
     input_value: Any,
 ):  # pragma: no cover
     config_var, config_map, file_path = None, None, None
     if self.strategy_config_map is not None and key in self.strategy_config_map:
         config_map = self.strategy_config_map
         file_path = STRATEGIES_CONF_DIR_PATH / self.strategy_file_name
     config_var = config_map[key]
     if input_value is None:
         self.notify(
             "Please follow the prompt to complete configurations: ")
     if config_var.key == "inventory_target_base_pct":
         await self.asset_ratio_maintenance_prompt_legacy(
             config_map, input_value)
     elif config_var.key == "inventory_price":
         await self.inventory_price_prompt_legacy(config_map, input_value)
     else:
         await self.prompt_a_config_legacy(config_var,
                                           input_value=input_value,
                                           assign_default=False)
     if self.app.to_stop_config:
         self.app.to_stop_config = False
         return
     missings = missing_required_configs_legacy(config_map)
     if missings:
         self.notify(
             "\nThere are other configuration required, please follow the prompt to complete them."
         )
     missings = await self._prompt_missing_configs(config_map)
     save_to_yml_legacy(str(file_path), config_map)
     self.notify("\nNew configuration saved:")
     self.notify(f"{key}: {str(config_var.value)}")
     self.app.app.style = load_style(self.client_config_map)
     for config in missings:
         self.notify(f"{config.key}: {str(config.value)}")
     if (isinstance(self.strategy, PureMarketMakingStrategy)
             or isinstance(self.strategy, PerpetualMarketMakingStrategy)):
         updated = ConfigCommand.update_running_mm(self.strategy, key,
                                                   config_var.value)
         if updated:
             self.notify(
                 f"\nThe current {self.strategy_name} strategy has been updated "
                 f"to reflect the new configuration.")
Пример #16
0
    def test_load_style_windows(self, is_windows_mock):
        is_windows_mock.return_value = True

        global_config_map = {}
        global_config_map["top-pane"] = self.ConfigVar("#FAFAFA")
        global_config_map["bottom-pane"] = self.ConfigVar("#FAFAFA")
        global_config_map["output-pane"] = self.ConfigVar("#FAFAFA")
        global_config_map["input-pane"] = self.ConfigVar("#FAFAFA")
        global_config_map["logs-pane"] = self.ConfigVar("#FAFAFA")
        global_config_map["terminal-primary"] = self.ConfigVar("#FCFCFC")

        global_config_map["primary-label"] = self.ConfigVar("#5FFFD7")
        global_config_map["secondary-label"] = self.ConfigVar("#FFFFFF")
        global_config_map["success-label"] = self.ConfigVar("#5FFFD7")
        global_config_map["warning-label"] = self.ConfigVar("#FFFF00")
        global_config_map["info-label"] = self.ConfigVar("#5FD7FF")
        global_config_map["error-label"] = self.ConfigVar("#FF0000")

        style = Style.from_dict({
            "output-field": "bg:#ansiwhite #ansiwhite",
            "input-field": "bg:#ansiwhite #ansiwhite",
            "log-field": "bg:#ansiwhite #ansiwhite",
            "header": "bg:#ansiwhite #ansiwhite",
            "footer": "bg:#ansiwhite #ansiwhite",
            "search": "#ansiwhite",
            "search.current": "#ansiwhite",
            "primary": "#ansiwhite",
            "warning": "#ansibrightyellow",
            "error": "#ansired",
            "tab_button.focused": "bg:#ansiwhite #ansiwhite",
            "tab_button": "bg:#ansiwhite #ansiwhite",
            # Label bg and font color
            "primary-label": "bg:#ansicyan #ansiwhite",
            "secondary-label": "bg:#ansiwhite #ansiwhite",
            "success-label": "bg:#ansicyan #ansiwhite",
            "warning-label": "bg:#ansiyellow #ansiwhite",
            "info-label": "bg:#ansicyan #ansiwhite",
            "error-label": "bg:#ansired #ansiwhite",
        })

        self.assertEqual(style.class_names_and_attrs,
                         load_style(global_config_map).class_names_and_attrs)
Пример #17
0
    def test_load_style_unix(self, is_windows_mock):
        is_windows_mock.return_value = False

        global_config_map = {}
        global_config_map["top-pane"] = self.ConfigVar("#FAFAFA")
        global_config_map["bottom-pane"] = self.ConfigVar("#FAFAFA")
        global_config_map["output-pane"] = self.ConfigVar("#FAFAFA")
        global_config_map["input-pane"] = self.ConfigVar("#FAFAFA")
        global_config_map["logs-pane"] = self.ConfigVar("#FAFAFA")
        global_config_map["terminal-primary"] = self.ConfigVar("#FCFCFC")

        global_config_map["primary-label"] = self.ConfigVar("#5FFFD7")
        global_config_map["secondary-label"] = self.ConfigVar("#FFFFFF")
        global_config_map["success-label"] = self.ConfigVar("#5FFFD7")
        global_config_map["warning-label"] = self.ConfigVar("#FFFF00")
        global_config_map["info-label"] = self.ConfigVar("#5FD7FF")
        global_config_map["error-label"] = self.ConfigVar("#FF0000")

        style = Style.from_dict({
            "output-field": "bg:#FAFAFA #FCFCFC",
            "input-field": "bg:#FAFAFA #FFFFFF",
            "log-field": "bg:#FAFAFA #FFFFFF",
            "header": "bg:#FAFAFA #AAAAAA",
            "footer": "bg:#FAFAFA #AAAAAA",
            "search": "bg:#000000 #93C36D",
            "search.current": "bg:#000000 #1CD085",
            "primary": "#FCFCFC",
            "warning": "#93C36D",
            "error": "#F5634A",
            "tab_button.focused": "bg:#FCFCFC #FAFAFA",
            "tab_button": "bg:#FFFFFF #FAFAFA",
            # Label bg and font color
            "primary-label": "bg:#5FFFD7 #FAFAFA",
            "secondary-label": "bg:#FFFFFF #FAFAFA",
            "success-label": "bg:#5FFFD7 #FAFAFA",
            "warning-label": "bg:#FFFF00 #FAFAFA",
            "info-label": "bg:#5FD7FF #FAFAFA",
            "error-label": "bg:#FF0000 #FAFAFA",
        })

        self.assertEqual(style.class_names_and_attrs,
                         load_style(global_config_map).class_names_and_attrs)
Пример #18
0
    def __init__(self, input_handler: Callable, bindings: KeyBindings,
                 completer: Completer):
        self.search_field = create_search_field()
        self.input_field = create_input_field(completer=completer)
        self.output_field = create_output_field()
        self.log_field = create_log_field(self.search_field)
        self.timer = create_timer()
        self.process_usage = create_process_monitor()
        self.trade_monitor = create_trade_monitor()
        self.layout = generate_layout(self.input_field, self.output_field,
                                      self.log_field, self.search_field,
                                      self.timer, self.process_usage,
                                      self.trade_monitor)
        # add self.to_stop_config to know if cancel is triggered
        self.to_stop_config: bool = False

        self.live_updates = False
        self.bindings = bindings
        self.input_handler = input_handler
        self.input_field.accept_handler = self.accept
        self.app = Application(layout=self.layout,
                               full_screen=True,
                               key_bindings=self.bindings,
                               style=load_style(),
                               mouse_support=True,
                               clipboard=PyperclipClipboard())

        # settings
        self.prompt_text = ">>> "
        self.pending_input = None
        self.input_event = None
        self.hide_input = False

        # start ui tasks
        loop = asyncio.get_event_loop()
        loop.create_task(start_timer(self.timer))
        loop.create_task(start_process_monitor(self.process_usage))
        loop.create_task(start_trade_monitor(self.trade_monitor))
Пример #19
0
    async def _config_single_key(
            self,  # type: HummingbotApplication
            key: str,
            input_value):
        """
        Configure a single variable only.
        Prompt the user to finish all configurations if there are remaining empty configs at the end.
        """

        self.placeholder_mode = True
        self.app.hide_input = True

        try:
            if (not isinstance(self.strategy_config_map,
                               (type(None), ClientConfigAdapter))
                    and key in self.strategy_config_map):
                await self._config_single_key_legacy(key, input_value)
            else:
                client_config_key = key in self.client_config_map.config_paths(
                )
                if client_config_key:
                    config_map = self.client_config_map
                    file_path = CLIENT_CONFIG_PATH
                elif self.strategy is not None:
                    self.notify(
                        "Configuring the strategy while it is running is not currently supported."
                    )
                    return
                else:
                    config_map = self.strategy_config_map
                    file_path = STRATEGIES_CONF_DIR_PATH / self.strategy_file_name
                if input_value is None:
                    self.notify(
                        "Please follow the prompt to complete configurations: "
                    )
                if key == "inventory_target_base_pct":
                    await self.asset_ratio_maintenance_prompt(
                        config_map, input_value)
                elif key == "inventory_price":
                    await self.inventory_price_prompt(config_map, input_value)
                else:
                    await self.prompt_a_config(config_map,
                                               key,
                                               input_value,
                                               assign_default=False)
                if self.app.to_stop_config:
                    self.app.to_stop_config = False
                    return
                save_to_yml(file_path, config_map)
                self.notify("\nNew configuration saved.")
                if client_config_key:
                    self.list_client_configs()
                else:
                    self.list_strategy_configs()
                self.app.app.style = load_style(self.client_config_map)
        except asyncio.TimeoutError:
            self.logger().error("Prompt timeout")
        except Exception as err:
            self.logger().error(str(err), exc_info=True)
        finally:
            self.app.hide_input = False
            self.placeholder_mode = False
            self.app.change_prompt(prompt=">>> ")
Пример #20
0
    async def _config_single_key(
            self,  # type: HummingbotApplication
            key: str,
            input_value):
        """
        Configure a single variable only.
        Prompt the user to finish all configurations if there are remaining empty configs at the end.
        """

        self.placeholder_mode = True
        self.app.hide_input = True

        try:
            config_var, config_map, file_path = None, None, None
            if key in global_config_map:
                config_map = global_config_map
                file_path = GLOBAL_CONFIG_PATH
            elif self.strategy_config_map is not None and key in self.strategy_config_map:
                config_map = self.strategy_config_map
                file_path = join(CONF_FILE_PATH, self.strategy_file_name)
            config_var = config_map[key]
            if input_value is None:
                self._notify(
                    "Please follow the prompt to complete configurations: ")
            if config_var.key == "inventory_target_base_pct":
                await self.asset_ratio_maintenance_prompt(
                    config_map, input_value)
            elif config_var.key == "inventory_price":
                await self.inventory_price_prompt(config_map, input_value)
            else:
                await self.prompt_a_config(config_var,
                                           input_value=input_value,
                                           assign_default=False)
            if self.app.to_stop_config:
                self.app.to_stop_config = False
                return
            await self.update_all_secure_configs()
            missings = missing_required_configs(config_map)
            if missings:
                self._notify(
                    "\nThere are other configuration required, please follow the prompt to complete them."
                )
            missings = await self._prompt_missing_configs(config_map)
            save_to_yml(file_path, config_map)
            self._notify("\nNew configuration saved:")
            self._notify(f"{key}: {str(config_var.value)}")
            self.app.app.style = load_style()
            for config in missings:
                self._notify(f"{config.key}: {str(config.value)}")
            if isinstance(self.strategy, PureMarketMakingStrategy) or \
               isinstance(self.strategy, PerpetualMarketMakingStrategy):
                updated = ConfigCommand.update_running_mm(
                    self.strategy, key, config_var.value)
                if updated:
                    self._notify(
                        f"\nThe current {self.strategy_name} strategy has been updated "
                        f"to reflect the new configuration.")
        except asyncio.TimeoutError:
            self.logger().error("Prompt timeout")
        except Exception as err:
            self.logger().error(str(err), exc_info=True)
        finally:
            self.app.hide_input = False
            self.placeholder_mode = False
            self.app.change_prompt(prompt=">>> ")