Implemented RunnablePlugin.wait_stop() utility method

This commit is contained in:
Fabio Manganiello 2022-04-05 23:33:02 +02:00
parent 061268cdaf
commit d52ae2fb80
Signed by: blacklight
GPG Key ID: D90FBA7F76362774
3 changed files with 93 additions and 71 deletions

View File

@ -19,12 +19,12 @@ def action(f):
result = f(*args, **kwargs)
if result and isinstance(result, Response):
result.errors = result.errors \
if isinstance(result.errors, list) else [result.errors]
result.errors = (
result.errors if isinstance(result.errors, list) else [result.errors]
)
response = result
elif isinstance(result, tuple) and len(result) == 2:
response.errors = result[1] \
if isinstance(result[1], list) else [result[1]]
response.errors = result[1] if isinstance(result[1], list) else [result[1]]
if len(response.errors) == 1 and response.errors[0] is None:
response.errors = []
@ -39,12 +39,14 @@ def action(f):
return _execute_action
class Plugin(EventGenerator, ExtensionWithManifest): # lgtm [py/missing-call-to-init]
""" Base plugin class """
class Plugin(EventGenerator, ExtensionWithManifest): # lgtm [py/missing-call-to-init]
"""Base plugin class"""
def __init__(self, **kwargs):
super().__init__()
self.logger = logging.getLogger('platypush:plugin:' + get_plugin_name_by_class(self.__class__))
self.logger = logging.getLogger(
'platypush:plugin:' + get_plugin_name_by_class(self.__class__)
)
if 'logging' in kwargs:
self.logger.setLevel(getattr(logging, kwargs['logging'].upper()))
@ -53,8 +55,9 @@ class Plugin(EventGenerator, ExtensionWithManifest): # lgtm [py/missing-call-t
)
def run(self, method, *args, **kwargs):
assert method in self.registered_actions, '{} is not a registered action on {}'.\
format(method, self.__class__.__name__)
assert (
method in self.registered_actions
), '{} is not a registered action on {}'.format(method, self.__class__.__name__)
return getattr(self, method)(*args, **kwargs)
@ -62,6 +65,7 @@ class RunnablePlugin(Plugin):
"""
Class for runnable plugins - i.e. plugins that have a start/stop method and can be started.
"""
def __init__(self, poll_interval: Optional[float] = None, **kwargs):
"""
:param poll_interval: How often the :meth:`.loop` function should be execute (default: None, no pause/interval).
@ -78,6 +82,9 @@ class RunnablePlugin(Plugin):
def should_stop(self):
return self._should_stop.is_set()
def wait_stop(self, timeout=None):
return self._should_stop.wait(timeout)
def start(self):
set_thread_name(self.__class__.__name__)
self._thread = threading.Thread(target=self._runner)

View File

@ -2,7 +2,11 @@ import os
from typing import Sequence, Dict, Tuple, Union, Optional
from platypush.plugins import RunnablePlugin, action
from platypush.schemas.irc import IRCServerSchema, IRCServerStatusSchema, IRCChannelSchema
from platypush.schemas.irc import (
IRCServerSchema,
IRCServerStatusSchema,
IRCChannelSchema,
)
from ._bot import IRCBot
from .. import ChatPlugin
@ -59,29 +63,19 @@ class ChatIrcPlugin(RunnablePlugin, ChatPlugin):
@property
def _bots_by_server(self) -> Dict[str, IRCBot]:
return {
bot.server: bot
for srv, bot in self._bots.items()
}
return {bot.server: bot for srv, bot in self._bots.items()}
@property
def _bots_by_server_and_port(self) -> Dict[Tuple[str, int], IRCBot]:
return {
(bot.server, bot.port): bot
for srv, bot in self._bots.items()
}
return {(bot.server, bot.port): bot for srv, bot in self._bots.items()}
@property
def _bots_by_alias(self) -> Dict[str, IRCBot]:
return {
bot.alias: bot
for srv, bot in self._bots.items()
if bot.alias
}
return {bot.alias: bot for srv, bot in self._bots.items() if bot.alias}
def main(self):
self._connect()
self._should_stop.wait()
self.wait_stop()
def _connect(self):
for srv, bot in self._bots.items():
@ -109,7 +103,11 @@ class ChatIrcPlugin(RunnablePlugin, ChatPlugin):
@action
def send_file(
self, file: str, server: Union[str, Tuple[str, int]], nick: str, bind_address: Optional[str] = None
self,
file: str,
server: Union[str, Tuple[str, int]],
nick: str,
bind_address: Optional[str] = None,
):
"""
Send a file to an IRC user over DCC connection.
@ -127,7 +125,10 @@ class ChatIrcPlugin(RunnablePlugin, ChatPlugin):
@action
def send_message(
self, text: str, server: Union[str, Tuple[str, int]], target: Union[str, Sequence[str]]
self,
text: str,
server: Union[str, Tuple[str, int]],
target: Union[str, Sequence[str]],
):
"""
Send a message to a channel or a nick.
@ -139,15 +140,14 @@ class ChatIrcPlugin(RunnablePlugin, ChatPlugin):
"""
bot = self._get_bot(server)
method = (
bot.connection.privmsg if isinstance(target, str)
bot.connection.privmsg
if isinstance(target, str)
else bot.connection.privmsg_many
)
method(target, text)
@action
def send_notice(
self, text: str, server: Union[str, Tuple[str, int]], target: str
):
def send_notice(self, text: str, server: Union[str, Tuple[str, int]], target: str):
"""
Send a notice to a channel or a nick.
@ -192,22 +192,28 @@ class ChatIrcPlugin(RunnablePlugin, ChatPlugin):
channel_name = channel
channel = bot.channels.get(channel)
assert channel, f'Not connected to channel {channel}'
return IRCChannelSchema().dump({
'is_invite_only': channel.is_invite_only(),
'is_moderated': channel.is_moderated(),
'is_protected': channel.is_protected(),
'is_secret': channel.is_secret(),
'name': channel_name,
'modes': channel.modes,
'opers': list(channel.opers()),
'owners': channel.owners(),
'users': list(channel.users()),
'voiced': list(channel.voiced()),
})
return IRCChannelSchema().dump(
{
'is_invite_only': channel.is_invite_only(),
'is_moderated': channel.is_moderated(),
'is_protected': channel.is_protected(),
'is_secret': channel.is_secret(),
'name': channel_name,
'modes': channel.modes,
'opers': list(channel.opers()),
'owners': channel.owners(),
'users': list(channel.users()),
'voiced': list(channel.voiced()),
}
)
@action
def send_ctcp_message(
self, ctcp_type: str, body: str, server: Union[str, Tuple[str, int]], target: str
self,
ctcp_type: str,
body: str,
server: Union[str, Tuple[str, int]],
target: str,
):
"""
Send a CTCP message to a target.
@ -222,7 +228,7 @@ class ChatIrcPlugin(RunnablePlugin, ChatPlugin):
@action
def send_ctcp_reply(
self, body: str, server: Union[str, Tuple[str, int]], target: str
self, body: str, server: Union[str, Tuple[str, int]], target: str
):
"""
Send a CTCP REPLY command.
@ -235,7 +241,9 @@ class ChatIrcPlugin(RunnablePlugin, ChatPlugin):
bot.connection.ctcp_reply(target, body)
@action
def disconnect(self, server: Union[str, Tuple[str, int]], message: Optional[str] = None):
def disconnect(
self, server: Union[str, Tuple[str, int]], message: Optional[str] = None
):
"""
Disconnect from a server.
@ -246,9 +254,7 @@ class ChatIrcPlugin(RunnablePlugin, ChatPlugin):
bot.connection.disconnect(message or bot.stop_message)
@action
def invite(
self, nick: str, channel: str, server: Union[str, Tuple[str, int]]
):
def invite(self, nick: str, channel: str, server: Union[str, Tuple[str, int]]):
"""
Invite a nick to a channel.
@ -272,7 +278,11 @@ class ChatIrcPlugin(RunnablePlugin, ChatPlugin):
@action
def kick(
self, nick: str, channel: str, server: Union[str, Tuple[str, int]], reason: Optional[str] = None
self,
nick: str,
channel: str,
server: Union[str, Tuple[str, int]],
reason: Optional[str] = None,
):
"""
Kick a nick from a channel.
@ -286,9 +296,7 @@ class ChatIrcPlugin(RunnablePlugin, ChatPlugin):
bot.connection.kick(channel, nick, reason)
@action
def mode(
self, target: str, command: str, server: Union[str, Tuple[str, int]]
):
def mode(self, target: str, command: str, server: Union[str, Tuple[str, int]]):
"""
Send a MODE command on the selected target.
@ -324,8 +332,10 @@ class ChatIrcPlugin(RunnablePlugin, ChatPlugin):
@action
def part(
self, channel: Union[str, Sequence[str]], server: Union[str, Tuple[str, int]],
message: Optional[str] = None
self,
channel: Union[str, Sequence[str]],
server: Union[str, Tuple[str, int]],
message: Optional[str] = None,
):
"""
Parts/exits a channel.
@ -339,9 +349,7 @@ class ChatIrcPlugin(RunnablePlugin, ChatPlugin):
bot.connection.part(channels=channels, message=message or bot.stop_message)
@action
def quit(
self, server: Union[str, Tuple[str, int]], message: Optional[str] = None
):
def quit(self, server: Union[str, Tuple[str, int]], message: Optional[str] = None):
"""
Send a QUIT command.
@ -363,7 +371,12 @@ class ChatIrcPlugin(RunnablePlugin, ChatPlugin):
bot.connection.send_raw(message)
@action
def topic(self, channel: str, server: Union[str, Tuple[str, int]], topic: Optional[str] = None) -> str:
def topic(
self,
channel: str,
server: Union[str, Tuple[str, int]],
topic: Optional[str] = None,
) -> str:
"""
Get/set the topic of an IRC channel.

View File

@ -27,11 +27,11 @@ class GpioPlugin(RunnablePlugin):
"""
def __init__(
self,
pins: Optional[Dict[str, int]] = None,
monitored_pins: Optional[Collection[Union[str, int]]] = None,
mode: str = 'board',
**kwargs
self,
pins: Optional[Dict[str, int]] = None,
monitored_pins: Optional[Collection[Union[str, int]]] = None,
mode: str = 'board',
**kwargs
):
"""
:param mode: Specify ``board`` if you want to use the board PIN numbers,
@ -64,8 +64,9 @@ class GpioPlugin(RunnablePlugin):
self._initialized_pins = {}
self._monitored_pins = monitored_pins or []
self.pins_by_name = pins if pins else {}
self.pins_by_number = {number: name
for (name, number) in self.pins_by_name.items()}
self.pins_by_number = {
number: name for (name, number) in self.pins_by_name.items()
}
def _init_board(self):
import RPi.GPIO as GPIO
@ -98,6 +99,7 @@ class GpioPlugin(RunnablePlugin):
def on_gpio_event(self):
def callback(pin: int):
import RPi.GPIO as GPIO
value = GPIO.input(pin)
pin = self.pins_by_number.get(pin, pin)
get_bus().post(GPIOEvent(pin=pin, value=value))
@ -106,23 +108,23 @@ class GpioPlugin(RunnablePlugin):
def main(self):
import RPi.GPIO as GPIO
if not self._monitored_pins:
return # No need to start the monitor
self._init_board()
monitored_pins = [
self._get_pin_number(pin) for pin in self._monitored_pins
]
monitored_pins = [self._get_pin_number(pin) for pin in self._monitored_pins]
for pin in monitored_pins:
GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.add_event_detect(pin, GPIO.BOTH, callback=self.on_gpio_event())
self._should_stop.wait()
self.wait_stop()
@action
def write(self, pin: Union[int, str], value: Union[int, bool],
name: Optional[str] = None) -> Dict[str, Any]:
def write(
self, pin: Union[int, str], value: Union[int, bool], name: Optional[str] = None
) -> Dict[str, Any]:
"""
Write a byte value to a pin.