Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions zulip_bots/zulip_bots/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import logging
import os
import sys
from pathlib import Path
from typing import Optional

from zulip_bots import finder
Expand Down Expand Up @@ -111,6 +112,28 @@ def exit_gracefully_if_bot_config_file_does_not_exist(bot_config_file: Optional[
sys.exit(1)


def print_bot_not_found_error(bot: str, registry: bool) -> None:
if registry:
print(
f'ERROR: Could not find a bot named "{bot}" in the zulip_bots registry.\n'
"Make sure the package that provides it is installed. Exiting now."
)
return

bundled_bot_path = Path(finder.current_dir, "bots", bot, bot + ".py")
print(
f"""ERROR: Could not load bot module for "{bot}". Looked for:
- a file at {os.path.abspath(bot)}
- {bundled_bot_path} (one of the bots included with zulip_bots)
- a Python module named "{bot}" that can be imported from PYTHONPATH

If you wrote this bot yourself, try passing the path to its .py file, e.g.:
zulip-run-bot path/to/{Path(bot).stem}.py --config-file ~/zuliprc

Exiting now."""
)


def main() -> None:
args = parse_args()

Expand Down Expand Up @@ -156,11 +179,11 @@ def main() -> None:
bot_name = lib_module.__name__
bot_source = "named module"
if args.provision:
print("ERROR: Could not load bot's module for '{}'. Exiting now.")
print(f"ERROR: Could not load bot's module for '{args.bot}'. Exiting now.")
sys.exit(1)

if lib_module is None:
print("ERROR: Could not load bot module. Exiting now.")
print_bot_not_found_error(args.bot, args.registry)
sys.exit(1)

if not args.quiet:
Expand Down
46 changes: 46 additions & 0 deletions zulip_bots/zulip_bots/tests/test_run.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import io
import os
import sys
from contextlib import redirect_stdout
from pathlib import Path
from typing import Optional
from unittest import TestCase, mock
Expand Down Expand Up @@ -127,6 +129,50 @@ def test_run_bot_by_module_name(self, mock_os_path_isfile: mock.Mock) -> None:
zulip_bots.run.main()
mock_import_module.assert_called_once_with(bot_module_name)

@patch("os.path.isfile", return_value=False)
def test_provision_with_bot_module_name_shows_bot_name(
self, mock_os_path_isfile: mock.Mock
) -> None:
mock_bot_module = mock.Mock()
mock_bot_module.__name__ = "bot.module.name"
output = io.StringIO()
with patch("sys.argv", ["zulip-run-bot", "bot.module.name", "--provision"]):
with patch("importlib.import_module", return_value=mock_bot_module):
with redirect_stdout(output), self.assertRaises(SystemExit):
zulip_bots.run.main()

self.assertIn("'bot.module.name'", output.getvalue())
self.assertNotIn("{}", output.getvalue())

@patch("os.path.isfile", return_value=False)
def test_bot_not_found_error_suggests_passing_path(
self, mock_os_path_isfile: mock.Mock
) -> None:
output = io.StringIO()
with patch("sys.argv", ["zulip-run-bot", "mybot", "--config-file", "/path/to/config"]):
with patch("zulip_bots.finder.import_module_by_name", return_value=None):
with redirect_stdout(output), self.assertRaises(SystemExit):
zulip_bots.run.main()

error = output.getvalue()
self.assertIn('Could not load bot module for "mybot"', error)
self.assertIn(str(Path("bots", "mybot", "mybot.py")), error)
self.assertIn("PYTHONPATH", error)
self.assertIn("zulip-run-bot path/to/mybot.py", error)

def test_bot_not_found_in_registry_error(self) -> None:
output = io.StringIO()
with patch(
"sys.argv", ["zulip-run-bot", "mybot", "--config-file", "/path/to/config", "-r"]
):
with patch("zulip_bots.finder.metadata.entry_points", return_value=()):
with redirect_stdout(output), self.assertRaises(SystemExit):
zulip_bots.run.main()

self.assertIn(
'Could not find a bot named "mybot" in the zulip_bots registry', output.getvalue()
)


class TestBotLib(TestCase):
def test_extract_query_without_mention(self) -> None:
Expand Down
Loading