Skip to content

Commit 634a729

Browse files
authored
Merge pull request CloudBotIRC#193 from linuxdaemon/gonzobot+clean-up-func-launches
Refactor hook launching system
2 parents 10e0917 + 479f54b commit 634a729

File tree

5 files changed

+50
-50
lines changed

5 files changed

+50
-50
lines changed

cloudbot/event.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,12 @@ def is_nick_valid(self, nick):
372372
"""
373373
return self.conn.is_nick_valid(nick)
374374

375+
def __getitem__(self, item):
376+
try:
377+
return getattr(self, item)
378+
except AttributeError:
379+
raise KeyError(item)
380+
375381
if sys.version_info < (3, 7, 0):
376382
# noinspection PyCompatibility
377383
@asyncio.coroutine

cloudbot/plugin.py

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from cloudbot.event import Event, PostHookEvent
1919
from cloudbot.hook import Priority, Action
2020
from cloudbot.util import database, async_util
21+
from cloudbot.util.func_utils import call_with_args
2122

2223
logger = logging.getLogger("cloudbot")
2324

@@ -411,39 +412,15 @@ def _log_hook(self, hook):
411412
logger.info("Loaded {}".format(hook))
412413
logger.debug("Loaded {}".format(repr(hook)))
413414

414-
def _prepare_parameters(self, hook, event):
415-
"""
416-
Prepares arguments for the given hook
417-
418-
:type hook: cloudbot.plugin.Hook
419-
:type event: cloudbot.event.Event
420-
:rtype: list
421-
"""
422-
parameters = []
423-
for required_arg in hook.required_args:
424-
if hasattr(event, required_arg):
425-
value = getattr(event, required_arg)
426-
parameters.append(value)
427-
else:
428-
logger.error("Plugin {} asked for invalid argument '{}', cancelling execution!"
429-
.format(hook.description, required_arg))
430-
logger.debug("Valid arguments are: {} ({})".format(dir(event), event))
431-
return None
432-
return parameters
433-
434415
def _execute_hook_threaded(self, hook, event):
435416
"""
436417
:type hook: Hook
437418
:type event: cloudbot.event.Event
438419
"""
439420
event.prepare_threaded()
440421

441-
parameters = self._prepare_parameters(hook, event)
442-
if parameters is None:
443-
return None
444-
445422
try:
446-
return hook.function(*parameters)
423+
return call_with_args(hook.function, event)
447424
finally:
448425
event.close_threaded()
449426

@@ -455,12 +432,8 @@ def _execute_hook_sync(self, hook, event):
455432
"""
456433
yield from event.prepare()
457434

458-
parameters = self._prepare_parameters(hook, event)
459-
if parameters is None:
460-
return None
461-
462435
try:
463-
return (yield from hook.function(*parameters))
436+
return (yield from call_with_args(hook.function, event))
464437
finally:
465438
yield from event.close()
466439

cloudbot/util/async_util.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
"""
44

55
import asyncio
6-
76
import sys
87
from functools import partial
98

9+
from cloudbot.util.func_utils import call_with_args
10+
1011

1112
def wrap_future(fut, *, loop=None):
1213
"""
@@ -33,6 +34,19 @@ def run_func(loop, func, *args, **kwargs):
3334
return (yield from loop.run_in_executor(None, part))
3435

3536

37+
@asyncio.coroutine
38+
def run_func_with_args(loop, func, arg_data, executor=None):
39+
if asyncio.iscoroutine(func):
40+
raise TypeError('A coroutine function or a normal, non-async callable are required')
41+
42+
if asyncio.iscoroutinefunction(func):
43+
coro = call_with_args(func, arg_data)
44+
else:
45+
coro = loop.run_in_executor(executor, call_with_args, func, arg_data)
46+
47+
return (yield from coro)
48+
49+
3650
def run_coroutine_threadsafe(coro, loop):
3751
"""
3852
Runs a coroutine in a threadsafe manner

cloudbot/util/func_utils.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import inspect
2+
3+
4+
class ParameterError(Exception):
5+
def __init__(self, name, valid_args):
6+
self.__init__(name, list(valid_args))
7+
8+
def __str__(self):
9+
return "'{}' is not a valid parameter, valid parameters are: {}".format(self.args[0], self.args[1])
10+
11+
12+
def call_with_args(func, arg_data):
13+
sig = inspect.signature(func)
14+
try:
15+
args = [arg_data[key] for key in sig.parameters.keys() if not key.startswith('_')]
16+
except KeyError as e:
17+
raise ParameterError(e.args[0], arg_data.keys()) from e
18+
19+
return func(*args)

plugins/core/cap.py

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import asyncio
2-
import inspect
2+
from collections import ChainMap
33
from functools import partial
44

55
from cloudbot import hook
@@ -56,24 +56,12 @@ def _decorate(func):
5656
def _launch_handler(subcmd, event, **kwargs):
5757
subcmd = subcmd.upper()
5858
kwargs["subcmd"] = subcmd
59-
handler = HANDLERS.get(subcmd)
60-
if handler:
61-
sig = inspect.signature(handler)
62-
args = []
63-
64-
for arg in sig.parameters.keys():
65-
if arg in kwargs:
66-
args.append(kwargs[arg])
67-
else:
68-
try:
69-
value = getattr(event, arg)
70-
except AttributeError:
71-
event.logger.warning("CAP subcommand handler requested unknown argument: %s", arg)
72-
return
73-
else:
74-
args.append(value)
75-
76-
yield from async_util.run_func(event.loop, handler, *args)
59+
try:
60+
handler = HANDLERS[subcmd]
61+
except LookupError:
62+
return
63+
64+
yield from async_util.run_func_with_args(event.loop, handler, ChainMap(event, kwargs))
7765

7866

7967
@_subcmd_handler("LS")

0 commit comments

Comments
 (0)