Skip to content
Closed
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
32 changes: 32 additions & 0 deletions zulip_botserver/tests/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import unittest
from .server_test_lib import BotServerTestCase
import json
from importlib import import_module
from types import ModuleType

from zulip_botserver import server
from zulip_botserver.input_parameters import parse_args
Expand Down Expand Up @@ -177,5 +179,35 @@ def test_read_config_file(self) -> None:
}
assert json.dumps(bot_conf2, sort_keys=True) == json.dumps(expected_config2, sort_keys=True)

def test_load_lib_modules(self) -> None:
# This testcase requires hardcoded paths, which here is a good thing so if we ever
# restructure zulip_bots, this test would fail and we would also update Botserver
# at the same time.
helloworld = import_module('zulip_bots.bots.{bot}.{bot}'.format(bot='helloworld'))
root_dir = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '../../'))
# load valid module name
module = server.load_lib_modules(['helloworld'])['helloworld']
assert module == helloworld

# load valid file path
path = os.path.join(root_dir, 'zulip_bots/zulip_bots/bots/{bot}/{bot}.py'.format(bot='helloworld'))
module = server.load_lib_modules([path])[path]
assert module.__name__ == 'custom_bot_module'
assert module.__file__ == path
assert isinstance(module, ModuleType)

# load invalid module name
with self.assertRaisesRegexp(SystemExit, # type: ignore
'Error: Bot "botserver-test-case-random-bot" doesn\'t exist. '
'Please make sure you have set up the botserverrc file correctly.'):
module = server.load_lib_modules(['botserver-test-case-random-bot'])['botserver-test-case-random-bot']

# load invalid file path
with self.assertRaisesRegexp(SystemExit, # type: ignore
'Error: Bot "{}/zulip_bots/zulip_bots/bots/helloworld.py" doesn\'t exist. '
'Please make sure you have set up the botserverrc file correctly.'.format(root_dir)):
path = os.path.join(root_dir, 'zulip_bots/zulip_bots/bots/{bot}.py'.format(bot='helloworld'))
module = server.load_lib_modules([path])[path]

if __name__ == '__main__':
unittest.main()
17 changes: 15 additions & 2 deletions zulip_botserver/zulip_botserver/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
import json
import os
import sys
import importlib.util

from configparser import MissingSectionHeaderError, NoOptionError
from flask import Flask, request
from importlib import import_module
from typing import Any, Dict, Union, List, Optional
from types import ModuleType
from werkzeug.exceptions import BadRequest, Unauthorized

from zulip import Client
Expand Down Expand Up @@ -68,13 +70,24 @@ def parse_config_file(config_file_path: str) -> configparser.ConfigParser:
parser.read(config_file_path)
return parser

def load_module_from_file(file_path: str) -> ModuleType:
# Wrapper around importutil; see https://stackoverflow.com/a/67692/3909240.
spec = importlib.util.spec_from_file_location("custom_bot_module", file_path)
lib_module = importlib.util.module_from_spec(spec)
assert spec is not None
assert spec.loader is not None
spec.loader.exec_module(lib_module)
return lib_module

def load_lib_modules(available_bots: List[str]) -> Dict[str, Any]:
bots_lib_module = {}
for bot in available_bots:
try:
module_name = 'zulip_bots.bots.{bot}.{bot}'.format(bot=bot)
lib_module = import_module(module_name)
if bot.endswith('.py') and os.path.isfile(bot):
lib_module = load_module_from_file(bot)
else:
module_name = 'zulip_bots.bots.{bot}.{bot}'.format(bot=bot)
lib_module = import_module(module_name)
bots_lib_module[bot] = lib_module
except ImportError:
error_message = ("Error: Bot \"{}\" doesn't exist. Please make sure "
Expand Down