Philipp Hörist pushed to branch master at gajim / gajim
Commits: 18ac2084 by lovetox at 2022-08-20T23:36:28+02:00 refactor: Commands: Raise singals instead of exceptions
- Raise signals instead of exceptions - ChatStack: Connect directly to command signals - Add /help command
- - - - -
3 changed files:
- gajim/common/commands.py - gajim/gtk/chat_stack.py - gajim/gtk/message_actions_box.py
Changes:
===================================== gajim/common/commands.py ===================================== @@ -52,12 +52,15 @@ def split_argument_string(string: str) -> list[str]: return result
-def get_usage_from_command(cmd: argparse.ArgumentParser) -> str: +def get_usage_from_command(cmd: argparse.ArgumentParser, + command_name: str + ) -> str: + with io.StringIO() as output: cmd.print_usage(file=output) usage = output.getvalue()
- usage = usage.split('[-h] ')[1] + usage = usage.split(f'ChatCommands {command_name}')[1] return usage.strip()
@@ -69,7 +72,7 @@ class CommandError(Exception): pass
-class CommandNotFoundError(Exception): +class CommandFailed(Exception): pass
@@ -82,7 +85,7 @@ class ChatCommands(Observable): def __init__(self) -> None: Observable.__init__(self) self._parser = ArgumentParser(prog='ChatCommands') - self._sub_parser = self._parser.add_subparsers() + self._sub_parser = self._parser.add_subparsers(title='Commands') self._commands: dict[str, tuple[list[str], str]] = {}
self._create_commands() @@ -95,6 +98,16 @@ def get_commands(self, used_in: str) -> list[tuple[str, str]]: commands.sort(key=operator.itemgetter(0)) return commands
+ def _generate_help(self, used_in: str) -> str: + help_text = 'Commands:\n\n' + commands = self.get_commands(used_in) + for command in commands: + name, usage = command + if name == 'help': + continue + help_text += f' {name:<15} {usage}\n' + return help_text + def make_parser(self, command_name: str, callback: Callable[..., Any], @@ -104,9 +117,11 @@ def make_parser(self, it with the command name. '''
- parser = self._sub_parser.add_parser(command_name, **kwargs) - parser.set_defaults(command_name=command_name) - self.connect(command_name, callback) + parser = self._sub_parser.add_parser(command_name, + add_help=False, + **kwargs) + parser.set_defaults(command_name=command_name, + exec=callback) return parser
def add_command(self, @@ -115,7 +130,7 @@ def add_command(self, cmd: argparse.ArgumentParser ) -> None:
- usage = get_usage_from_command(cmd) + usage = get_usage_from_command(cmd, command_name) self._commands[command_name] = (used_in, usage)
def parse(self, type_: str, arg_string: str) -> None: @@ -124,13 +139,33 @@ def parse(self, type_: str, arg_string: str) -> None: command_name = arg_list[0] command = self._commands.get(command_name) if command is None or type_ not in command[0]: - raise CommandNotFoundError(_('Unknown command: %s' % command_name)) + self.notify('command-not-found', + _('Unknown command: %s' % command_name)) + raise CommandFailed + + try: + args = self._parser.parse_args(arg_list) + except ArgumentParserError as error: + self.notify('command-error', str(error)) + raise CommandFailed + + args.used_in = type_ + + try: + result = args.exec(args) + except CommandError as error: + self.notify('command-error', str(error)) + raise CommandFailed
- args = self._parser.parse_args(arg_list) + if result is None: + result = _('Command executed successfully')
- self.notify(args.command_name, args) + self.notify('command-result', result)
def _create_commands(self) -> None: + parser = self.make_parser('help', self._help_command) + self.add_command('help', ['chat', 'groupchat', 'pm'], parser) + parser = self.make_parser('status', self._status_command) parser.add_argument('status', choices=['online', 'away', 'xa', 'dnd']) @@ -164,12 +199,10 @@ def _create_commands(self) -> None: choices=['moderator', 'participant', 'visitor']) self.add_command('role', ['groupchat'], parser)
- def _status_command(self, - chat_commands: Any, - signal_name: str, - args: Any - ) -> None: + def _help_command(self, args: Any) -> str: + return self._generate_help(args.used_in)
+ def _status_command(self, args: Any) -> None: for client in app.get_clients(): if not app.settings.get_account_setting(client.account, 'sync_with_global_status'): @@ -189,12 +222,7 @@ def _check_if_joined(self) -> GroupchatContact: 'joined this group chat')) return contact
- def _invite_command(self, - chat_commands: Any, - signal_name: str, - args: Any - ) -> None: - + def _invite_command(self, args: Any) -> None: contact = self._check_if_joined()
try: @@ -242,20 +270,10 @@ def _change_affiliation(self, {jid: {'affiliation': affiliation, 'reason': reason}})
- def _ban_command(self, - chat_commands: Any, - signal_name: str, - args: Any - ) -> None: - + def _ban_command(self, args: Any) -> None: self._change_affiliation(args.who, 'outcast', args.reason)
- def _affiliate_command(self, - chat_commands: Any, - signal_name: str, - args: Any - ) -> None: - + def _affiliate_command(self, args: Any) -> None: self._change_affiliation(args.who, args.affiliation, None)
def _change_role(self, nick: str, role: str, reason: Optional[str]) -> None: @@ -278,18 +296,8 @@ def _change_role(self, nick: str, role: str, reason: Optional[str]) -> None: role, reason)
- def _kick_command(self, - chat_commands: Any, - signal_name: str, - args: Any - ) -> None: - + def _kick_command(self, args: Any) -> None: self._change_role(args.who, 'none', args.reason)
- def _role_command(self, - chat_commands: Any, - signal_name: str, - args: Any - ) -> None: - + def _role_command(self, args: Any) -> None: self._change_role(args.who, args.role, None)
===================================== gajim/gtk/chat_stack.py ===================================== @@ -34,6 +34,7 @@ from gajim.common import events from gajim.common import helpers from gajim.common import preview_helpers +from gajim.common.commands import ChatCommands from gajim.common.i18n import _ from gajim.common.const import CallType from gajim.common.modules.contacts import BareContact @@ -76,10 +77,10 @@ def __init__(self): self._chat_banner = ChatBanner() self._chat_control = ChatControl() self._message_action_box = MessageActionsBox() - self._message_action_box.connect('command-error', - self._on_command_error) - self._message_action_box.connect('command-not-found', - self._on_command_not_found) + + app.commands.connect('command-error', self._on_command_signal) + app.commands.connect('command-not-found', self._on_command_signal) + app.commands.connect('command-result', self._on_command_signal)
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) box.add(self._chat_banner) @@ -741,18 +742,14 @@ def clear(self) -> None: self._message_action_box.clear() self._chat_control.clear()
- def _on_command_error(self, - message_actions_box: MessageActionsBox, - error: str) -> None: - - self._chat_control.add_command_output(error, True) - - def _on_command_not_found(self, - message_actions_box: MessageActionsBox, - error: str - ) -> None: + def _on_command_signal(self, + chat_commands: ChatCommands, + signal_name: str, + text: str + ) -> None:
- self._chat_control.add_command_output(error, True) + is_error = signal_name != 'command-result' + self._chat_control.add_command_output(text, is_error)
class ChatPlaceholderBox(Gtk.Box):
===================================== gajim/gtk/message_actions_box.py ===================================== @@ -37,9 +37,7 @@ from gajim.common import app from gajim.common import events from gajim.common import ged -from gajim.common.commands import ArgumentParserError -from gajim.common.commands import CommandError -from gajim.common.commands import CommandNotFoundError +from gajim.common.commands import CommandFailed from gajim.common.i18n import _ from gajim.common.client import Client from gajim.common.const import SimpleClientState @@ -67,12 +65,6 @@
class MessageActionsBox(Gtk.Grid, ged.EventHelper): - - __gsignals__ = { - 'command-error': (GObject.SignalFlags.RUN_FIRST, None, (str,)), - 'command-not-found': (GObject.SignalFlags.RUN_FIRST, None, (str,)) - } - def __init__(self) -> None: Gtk.Grid.__init__(self) ged.EventHelper.__init__(self) @@ -544,10 +536,8 @@ def _on_msg_textview_key_press_event(self, not message.startswith('/me ')): try: app.commands.parse(self._contact.type_string, message) - except (ArgumentParserError, CommandError) as error: - self.emit('command-error', error) - except CommandNotFoundError as error: - self.emit('command-not-found', error) + except CommandFailed: + pass else: self.msg_textview.clear() return True
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/commit/18ac20841f531939e0ab8d1e25711be04...