forked from platypush/platypush
[#400] Dynamic logic to infer procedures/hooks arguments.
This allows procedures and event hooks to have more flexible signatures. Along the lines of: ```python @when(SomeEvent) def hook(event): ... @when(SomeOtherEvent) def hook2(): ... ``` Instead of supporting only the full context spec: ```python @when(SomeEvent) def hook(event, **ctx): ... ``` Closes: #400
This commit is contained in:
parent
2ab1743bec
commit
32b8296244
4 changed files with 52 additions and 19 deletions
|
@ -537,7 +537,7 @@ against partial event arguments are also possible, and relational operators are
|
||||||
supported as well. For example:
|
supported as well. For example:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from platypush import hook
|
from platypush import when
|
||||||
from platypush.message.event.sensor import SensorDataChangeEvent
|
from platypush.message.event.sensor import SensorDataChangeEvent
|
||||||
|
|
||||||
@when(SensorDataChangeEvent, data=1):
|
@when(SensorDataChangeEvent, data=1):
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
# A more versatile way to define event hooks than the YAML format of `config.yaml` is through native Python scripts.
|
# A more versatile way to define event hooks than the YAML format of
|
||||||
# You can define hooks as simple Python functions that use the `platypush.event.hook.hook` decorator to specify on
|
# `config.yaml` is through native Python scripts. You can define hooks as simple
|
||||||
# which event type they should be called, and optionally on which event attribute values.
|
# Python functions that use the `platypush.event.hook.hook` decorator to specify
|
||||||
|
# on which event type they should be called, and optionally on which event
|
||||||
|
# attribute values.
|
||||||
#
|
#
|
||||||
# Event hooks should be stored in Python files under `~/.config/platypush/scripts`. All the functions that use the
|
# Event hooks should be stored in Python files under
|
||||||
# @when decorator will automatically be discovered and imported as event hooks into the platform at runtime.
|
# `~/.config/platypush/scripts`. All the functions that use the @when decorator
|
||||||
|
# will automatically be discovered and imported as event hooks into the platform
|
||||||
|
# at runtime.
|
||||||
|
|
||||||
# `run` is a utility function that runs a request by name (e.g. `light.hue.on`).
|
# `run` is a utility function that runs a request by name (e.g. `light.hue.on`).
|
||||||
from platypush import when, run
|
from platypush import when, run
|
||||||
|
@ -16,12 +20,14 @@ from platypush.message.event.assistant import (
|
||||||
|
|
||||||
|
|
||||||
@when(SpeechRecognizedEvent, phrase='play ${title} by ${artist}')
|
@when(SpeechRecognizedEvent, phrase='play ${title} by ${artist}')
|
||||||
def on_music_play_command(event, title=None, artist=None, **context):
|
def on_music_play_command(event, title=None, artist=None):
|
||||||
"""
|
"""
|
||||||
This function will be executed when a SpeechRecognizedEvent with `phrase="play the music"` is triggered.
|
This function will be executed when a SpeechRecognizedEvent with
|
||||||
`event` contains the event object and `context` any key-value info from the running context.
|
`phrase="play the music"` is triggered. `event` contains the event object
|
||||||
Note that in this specific case we can leverage the token-extraction feature of SpeechRecognizedEvent through
|
and `context` any key-value info from the running context. Note that in this
|
||||||
${} that operates on regex-like principles to extract any text that matches the pattern into context variables.
|
specific case we can leverage the token-extraction feature of
|
||||||
|
SpeechRecognizedEvent through ${} that operates on regex-like principles to
|
||||||
|
extract any text that matches the pattern into context variables.
|
||||||
"""
|
"""
|
||||||
results = run(
|
results = run(
|
||||||
'music.mpd.search',
|
'music.mpd.search',
|
||||||
|
@ -31,16 +37,17 @@ def on_music_play_command(event, title=None, artist=None, **context):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
if results:
|
if results and results[0]:
|
||||||
run('music.mpd.play', results[0]['file'])
|
run('music.mpd.play', results[0]['file'])
|
||||||
else:
|
else:
|
||||||
run('tts.say', "I can't find any music matching your query")
|
run('tts.say', "I can't find any music matching your query")
|
||||||
|
|
||||||
|
|
||||||
@when(ConversationStartEvent)
|
@when(ConversationStartEvent)
|
||||||
def on_conversation_start(event, **context):
|
def on_conversation_start():
|
||||||
"""
|
"""
|
||||||
A simple hook that gets invoked when a new conversation starts with a voice assistant and simply pauses the music
|
A simple hook that gets invoked when a new conversation starts with a voice
|
||||||
to make sure that your speech is properly detected.
|
assistant and simply pauses the music to make sure that your speech is
|
||||||
|
properly detected.
|
||||||
"""
|
"""
|
||||||
run('music.mpd.pause_if_playing')
|
run('music.mpd.pause_if_playing')
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import inspect
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from typing import Any, Callable
|
from typing import Any, Callable, Mapping, Sequence, Tuple
|
||||||
|
|
||||||
from platypush.utils.manifest import Manifest
|
from platypush.utils.manifest import Manifest
|
||||||
|
|
||||||
|
@ -10,6 +10,22 @@ from ._types import StoppableThread
|
||||||
logger = logging.getLogger('platypush')
|
logger = logging.getLogger('platypush')
|
||||||
|
|
||||||
|
|
||||||
|
def _build_args(
|
||||||
|
func: Callable, *args, **kwargs
|
||||||
|
) -> Tuple[Sequence[Any], Mapping[str, Any]]:
|
||||||
|
spec = inspect.getfullargspec(func)
|
||||||
|
func_args = args if spec.varargs else args[: len(spec.args)]
|
||||||
|
func_kwargs = (
|
||||||
|
kwargs
|
||||||
|
if spec.varkw
|
||||||
|
else {
|
||||||
|
arg: kwargs[arg] for arg in [*spec.args, *spec.kwonlyargs] if arg in kwargs
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return func_args, func_kwargs
|
||||||
|
|
||||||
|
|
||||||
def exec_wrapper(f: Callable[..., Any], *args, **kwargs):
|
def exec_wrapper(f: Callable[..., Any], *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Utility function that runs a callable with its arguments, wraps its
|
Utility function that runs a callable with its arguments, wraps its
|
||||||
|
@ -17,8 +33,10 @@ def exec_wrapper(f: Callable[..., Any], *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
from platypush import Response
|
from platypush import Response
|
||||||
|
|
||||||
|
func_args, func_kwargs = _build_args(f, *args, **kwargs)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ret = f(*args, **kwargs)
|
ret = f(*func_args, **func_kwargs)
|
||||||
if isinstance(ret, Response):
|
if isinstance(ret, Response):
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
|
@ -179,9 +179,17 @@ class EventHook:
|
||||||
result = self.matches_event(event)
|
result = self.matches_event(event)
|
||||||
|
|
||||||
if result.is_match:
|
if result.is_match:
|
||||||
logger.info('Running hook %s triggered by an event', self.name)
|
logger.info(
|
||||||
|
'Running hook `%s` triggered by a `%s` event',
|
||||||
|
self.name,
|
||||||
|
f'{event.__module__}.{event.__class__.__name__}',
|
||||||
|
)
|
||||||
|
|
||||||
threading.Thread(
|
threading.Thread(
|
||||||
target=_thread_func, name='Event-' + self.name, args=(result,)
|
target=_thread_func,
|
||||||
|
name='Event-' + self.name,
|
||||||
|
args=(result,),
|
||||||
|
daemon=True,
|
||||||
).start()
|
).start()
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue