diff options
| author | 2019-12-22 07:27:27 +0000 | |
|---|---|---|
| committer | 2019-12-22 07:27:27 +0000 | |
| commit | d00c59e6bc48b66cf0daa077d3ba3ce382e60ce1 (patch) | |
| tree | a2ee8ec901a5e35aaf6a25ffc2918c04a990393b /http2irc.py | |
| parent | Add support for transformation/translation modules that do arbitrary request ... (diff) | |
| signature | ||
Add a way to pass additional arguments into the module
Diffstat (limited to 'http2irc.py')
| -rw-r--r-- | http2irc.py | 32 |
1 files changed, 23 insertions, 9 deletions
diff --git a/http2irc.py b/http2irc.py index 7a09642..bdeffe4 100644 --- a/http2irc.py +++ b/http2irc.py @@ -116,11 +116,16 @@ class Config(dict): raise InvalidConfig(f'Invalid map key {key!r}') if not isinstance(map_, collections.abc.Mapping): raise InvalidConfig(f'Invalid map for {key!r}') - if any(x not in ('webpath', 'ircchannel', 'auth', 'module') for x in map_): + if any(x not in ('webpath', 'ircchannel', 'auth', 'module', 'moduleargs') for x in map_): raise InvalidConfig(f'Unknown key(s) found in map {key!r}') #TODO: Check values if 'module' in map_ and not os.path.isfile(map_['module']): raise InvalidConfig(f'Module {map_["module"]!r} in map {key!r} is not a file') + if 'moduleargs' in map_: + if not isinstance(map_['moduleargs'], list): + raise InvalidConfig(f'Invalid module args for {key!r}: not an array') + if 'module' not in map_: + raise InvalidConfig(f'Module args cannot be specified without a module for {key!r}') # Default values finalObj = {'logging': {'level': 'INFO', 'format': '{asctime} {levelname} {message}'}, 'irc': {'host': 'irc.hackint.org', 'port': 6697, 'ssl': 'yes', 'nick': 'h2ibot', 'real': 'I am an http2irc bot.', 'certfile': None, 'certkeyfile': None}, 'web': {'host': '127.0.0.1', 'port': 8080}, 'maps': {}} @@ -135,11 +140,19 @@ class Config(dict): map_['auth'] = False if 'module' not in map_: map_['module'] = None + if 'moduleargs' not in map_: + map_['moduleargs'] = [] # Load modules - modulePaths = {map_['module'] for map_ in obj['maps'].values() if 'module' in map_ and map_['module'] is not None} + modulePaths = {} # path: str -> (extraargs: int, key: str) + for key, map_ in obj['maps'].items(): + if map_['module'] is not None: + if map_['module'] not in modulePaths: + modulePaths[map_['module']] = (len(map_['moduleargs']), key) + elif modulePaths[map_['module']][0] != len(map_['moduleargs']): + raise InvalidConfig(f'Module {map_["module"]!r} process function extra argument inconsistency between {key!r} and {modulePaths[map_["module"]][1]!r}') modules = {} # path: str -> module: module - for i, path in enumerate(modulePaths): + for i, (path, (extraargs, _)) in enumerate(modulePaths.items()): try: # Build a name that is virtually guaranteed to be unique across a process. # Although importlib does not seem to perform any caching as of CPython 3.8, this is not guaranteed by spec. @@ -152,8 +165,9 @@ class Config(dict): raise InvalidConfig(f'Module {path!r} does not have a process function') if not inspect.iscoroutinefunction(module.process): raise InvalidConfig(f'Module {path!r} process attribute is not a coroutine function') - if len(inspect.signature(module.process).parameters) != 1: - raise InvalidConfig(f'Module {path!r} process function does not take exactly 1 parameter') + nargs = len(inspect.signature(module.process).parameters) + if nargs != 1 + extraargs: + raise InvalidConfig(f'Module {path!r} process function takes {nargs} parameter{"s" if nargs > 1 else ""}, not {1 + extraargs}') modules[path] = module # Replace module value in maps @@ -401,7 +415,7 @@ class WebServer: self.messageQueue = messageQueue self.config = config - self._paths = {} # '/path' => ('#channel', auth, module) where auth is either False (no authentication) or the HTTP header value for basic auth + self._paths = {} # '/path' => ('#channel', auth, module, moduleargs) where auth is either False (no authentication) or the HTTP header value for basic auth self._app = aiohttp.web.Application() self._app.add_routes([aiohttp.web.post('/{path:.+}', self.post)]) @@ -410,7 +424,7 @@ class WebServer: self._configChanged = asyncio.Event() def update_config(self, config): - self._paths = {map_['webpath']: (map_['ircchannel'], f'Basic {base64.b64encode(map_["auth"].encode("utf-8")).decode("utf-8")}' if map_['auth'] else False, map_['module']) for map_ in config['maps'].values()} + self._paths = {map_['webpath']: (map_['ircchannel'], f'Basic {base64.b64encode(map_["auth"].encode("utf-8")).decode("utf-8")}' if map_['auth'] else False, map_['module'], map_['moduleargs']) for map_ in config['maps'].values()} needRebind = self.config['web'] != config['web'] self.config = config if needRebind: @@ -431,7 +445,7 @@ class WebServer: async def post(self, request): logging.info(f'Received request for {request.path!r}') try: - channel, auth, module = self._paths[request.path] + channel, auth, module, moduleargs = self._paths[request.path] except KeyError: logging.info(f'Bad request: no path {request.path!r}') raise aiohttp.web.HTTPNotFound() @@ -442,7 +456,7 @@ class WebServer: raise aiohttp.web.HTTPForbidden() if module is not None: try: - message = await module.process(request) + message = await module.process(request, *moduleargs) except aiohttp.web.HTTPException as e: raise e except Exception as e: |
