From 3448d9c9c191f7a4672ccfe557b5b4ca10e62356 Mon Sep 17 00:00:00 2001 From: novokrest Date: Wed, 30 May 2018 23:26:28 +0300 Subject: [PATCH] zulip-run-bot: Add the ability to run a bot by the module name. --- zulip_bots/zulip_bots/finder.py | 16 ++++++++-- zulip_bots/zulip_bots/run.py | 41 +++++++++++++++---------- zulip_bots/zulip_bots/tests/test_run.py | 14 ++++++++- 3 files changed, 51 insertions(+), 20 deletions(-) diff --git a/zulip_bots/zulip_bots/finder.py b/zulip_bots/zulip_bots/finder.py index 7c5ae20..ff7063e 100644 --- a/zulip_bots/zulip_bots/finder.py +++ b/zulip_bots/zulip_bots/finder.py @@ -23,12 +23,22 @@ def import_module_from_source(path: Text, name: Text) -> Any: return module -def resolve_bot_path(name: Text) -> Tuple[Text, Text]: +def import_module_by_name(name: Text) -> Any: + import importlib + try: + return importlib.import_module(name) + except ModuleNotFoundError: + return None + +def resolve_bot_path(name: Text) -> Optional[Tuple[Text, Text]]: if os.path.isfile(name): bot_path = os.path.abspath(name) bot_name = splitext(basename(bot_path))[0] + return (bot_path, bot_name) else: - bot_path = os.path.abspath(os.path.join(current_dir, 'bots', name, name + '.py')) bot_name = name + bot_path = os.path.abspath(os.path.join(current_dir, 'bots', bot_name, bot_name + '.py')) + if os.path.isfile(bot_path): + return (bot_path, bot_name) - return (bot_path, bot_name) + return None diff --git a/zulip_bots/zulip_bots/run.py b/zulip_bots/zulip_bots/run.py index 140f220..450264e 100755 --- a/zulip_bots/zulip_bots/run.py +++ b/zulip_bots/zulip_bots/run.py @@ -81,25 +81,34 @@ def exit_gracefully_if_bot_config_file_does_not_exist(bot_config_file: str) -> N def main() -> None: args = parse_args() - bot_path, bot_name = finder.resolve_bot_path(args.bot) - sys.path.insert(0, os.path.dirname(bot_path)) + result = finder.resolve_bot_path(args.bot) + if result: + bot_path, bot_name = result + sys.path.insert(0, os.path.dirname(bot_path)) - if args.provision: - provision_bot(os.path.dirname(bot_path), args.force) + if args.provision: + provision_bot(os.path.dirname(bot_path), args.force) - try: - lib_module = finder.import_module_from_source(bot_path, bot_name) - except ImportError as e: - req_path = os.path.join(os.path.dirname(bot_path), "requirements.txt") - with open(req_path) as fp: - deps_list = fp.read() + try: + lib_module = finder.import_module_from_source(bot_path, bot_name) + except ImportError as e: + req_path = os.path.join(os.path.dirname(bot_path), "requirements.txt") + with open(req_path) as fp: + deps_list = fp.read() - dep_err_msg = ("ERROR: The following dependencies for the {bot_name} bot are not installed:\n\n" - "{deps_list}\n" - "If you'd like us to install these dependencies, run:\n" - " zulip-run-bot {bot_name} --provision") - print(dep_err_msg.format(bot_name=bot_name, deps_list=deps_list)) - sys.exit(1) + dep_err_msg = ("ERROR: The following dependencies for the {bot_name} bot are not installed:\n\n" + "{deps_list}\n" + "If you'd like us to install these dependencies, run:\n" + " zulip-run-bot {bot_name} --provision") + print(dep_err_msg.format(bot_name=bot_name, deps_list=deps_list)) + sys.exit(1) + else: + lib_module = finder.import_module_by_name(args.bot) + if lib_module: + bot_name = lib_module.__name__ + if args.provision: + print("ERROR: Could not load bot's module for '{}'. Exiting now.") + sys.exit(1) if lib_module is None: print("ERROR: Could not load bot module. Exiting now.") diff --git a/zulip_bots/zulip_bots/tests/test_run.py b/zulip_bots/zulip_bots/tests/test_run.py index 3343fe6..2a9c8d7 100644 --- a/zulip_bots/zulip_bots/tests/test_run.py +++ b/zulip_bots/zulip_bots/tests/test_run.py @@ -42,7 +42,7 @@ class TestDefaultArguments(TestCase): quiet=False) def test_adding_bot_parent_dir_to_sys_path_when_bot_name_specified(self) -> None: - bot_name = 'any_bot_name' + bot_name = 'helloworld' # existing bot's name expected_bot_dir_path = os.path.join( os.path.dirname(zulip_bots.run.__file__), 'bots', @@ -66,6 +66,18 @@ class TestDefaultArguments(TestCase): self.assertIn(bot_dir_path, sys.path) + @patch('os.path.isfile', return_value=False) + def test_run_bot_by_module_name(self, mock_os_path_isfile: mock.Mock) -> None: + bot_module_name = 'bot.module.name' + mock_bot_module = mock.Mock() + mock_bot_module.__name__ = bot_module_name + with patch('sys.argv', ['zulip-run-bot', 'bot.module.name', '--config-file', '/path/to/config']): + with patch('importlib.import_module', return_value=mock_bot_module) as mock_import_module: + with patch('zulip_bots.run.run_message_handler_for_bot'): + with patch('zulip_bots.run.exit_gracefully_if_zulip_config_file_does_not_exist'): + zulip_bots.run.main() + mock_import_module.assert_called_once_with(bot_module_name) + class TestBotLib(TestCase): def test_extract_query_without_mention(self) -> None: