forked from platypush/platypush
More LINT fixes + refactors
This commit is contained in:
parent
4849e14414
commit
fde834c1b1
5 changed files with 122 additions and 68 deletions
|
@ -1,5 +1,5 @@
|
||||||
import json
|
import json
|
||||||
from typing import Optional
|
from typing import Optional, Union
|
||||||
|
|
||||||
from redis import Redis
|
from redis import Redis
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ class RedisBackend(Backend):
|
||||||
and can't post events or requests to the application bus.
|
and can't post events or requests to the application bus.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, queue='platypush_bus_mq', redis_args=None, *args, **kwargs):
|
def __init__(self, *args, queue='platypush_bus_mq', redis_args=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
:param queue: Queue name to listen on (default: ``platypush_bus_mq``)
|
:param queue: Queue name to listen on (default: ``platypush_bus_mq``)
|
||||||
:type queue: str
|
:type queue: str
|
||||||
|
@ -40,12 +40,21 @@ class RedisBackend(Backend):
|
||||||
self.redis_args = redis_args
|
self.redis_args = redis_args
|
||||||
self.redis: Optional[Redis] = None
|
self.redis: Optional[Redis] = None
|
||||||
|
|
||||||
def send_message(self, msg, queue_name=None, **kwargs):
|
def send_message(
|
||||||
msg = str(msg)
|
self, msg: Union[str, Message], queue_name: Optional[str] = None, **_
|
||||||
if queue_name:
|
):
|
||||||
self.redis.rpush(queue_name, msg)
|
"""
|
||||||
else:
|
Send a message to a Redis queue.
|
||||||
self.redis.rpush(self.queue, msg)
|
|
||||||
|
:param msg: Message to send, as a ``Message`` object or a string.
|
||||||
|
:param queue_name: Queue name to send the message to (default: ``platypush_bus_mq``).
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self.redis:
|
||||||
|
self.logger.warning('The Redis backend is not yet running.')
|
||||||
|
return
|
||||||
|
|
||||||
|
self.redis.rpush(queue_name or self.queue, str(msg))
|
||||||
|
|
||||||
def get_message(self, queue_name=None):
|
def get_message(self, queue_name=None):
|
||||||
queue = queue_name or self.queue
|
queue = queue_name or self.queue
|
||||||
|
@ -60,6 +69,7 @@ class RedisBackend(Backend):
|
||||||
self.logger.debug(str(e))
|
self.logger.debug(str(e))
|
||||||
try:
|
try:
|
||||||
import ast
|
import ast
|
||||||
|
|
||||||
msg = Message.build(ast.literal_eval(msg))
|
msg = Message.build(ast.literal_eval(msg))
|
||||||
except Exception as ee:
|
except Exception as ee:
|
||||||
self.logger.debug(str(ee))
|
self.logger.debug(str(ee))
|
||||||
|
@ -72,7 +82,11 @@ class RedisBackend(Backend):
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
super().run()
|
super().run()
|
||||||
self.logger.info('Initialized Redis backend on queue {} with arguments {}'.format(self.queue, self.redis_args))
|
self.logger.info(
|
||||||
|
'Initialized Redis backend on queue %s with arguments %s',
|
||||||
|
self.queue,
|
||||||
|
self.redis_args,
|
||||||
|
)
|
||||||
|
|
||||||
with Redis(**self.redis_args) as self.redis:
|
with Redis(**self.redis_args) as self.redis:
|
||||||
while not self.should_stop():
|
while not self.should_stop():
|
||||||
|
@ -81,7 +95,7 @@ class RedisBackend(Backend):
|
||||||
if not msg:
|
if not msg:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.logger.info('Received message on the Redis backend: {}'.format(msg))
|
self.logger.info('Received message on the Redis backend: %s', msg)
|
||||||
self.on_message(msg)
|
self.on_message(msg)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.exception(e)
|
self.logger.exception(e)
|
||||||
|
|
|
@ -1,48 +1,55 @@
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
|
from typing import Optional
|
||||||
from redis import Redis
|
|
||||||
|
|
||||||
from platypush.bus import Bus
|
from platypush.bus import Bus
|
||||||
from platypush.config import Config
|
|
||||||
from platypush.message import Message
|
from platypush.message import Message
|
||||||
|
|
||||||
logger = logging.getLogger('platypush:bus:redis')
|
logger = logging.getLogger('platypush:bus:redis')
|
||||||
|
|
||||||
|
|
||||||
class RedisBus(Bus):
|
class RedisBus(Bus):
|
||||||
""" Overrides the in-process in-memory local bus with a Redis bus """
|
"""
|
||||||
|
Overrides the in-process in-memory local bus with a Redis bus
|
||||||
|
"""
|
||||||
|
|
||||||
_DEFAULT_REDIS_QUEUE = 'platypush/bus'
|
_DEFAULT_REDIS_QUEUE = 'platypush/bus'
|
||||||
|
|
||||||
def __init__(self, *args, on_message=None, redis_queue=None, **kwargs):
|
def __init__(self, *args, on_message=None, redis_queue=None, **kwargs):
|
||||||
|
from platypush.utils import get_redis
|
||||||
|
|
||||||
super().__init__(on_message=on_message)
|
super().__init__(on_message=on_message)
|
||||||
|
self.redis = get_redis(*args, **kwargs)
|
||||||
if not args and not kwargs:
|
|
||||||
kwargs = (Config.get('backend.redis') or {}).get('redis_args', {})
|
|
||||||
|
|
||||||
self.redis = Redis(*args, **kwargs)
|
|
||||||
self.redis_args = kwargs
|
self.redis_args = kwargs
|
||||||
self.redis_queue = redis_queue or self._DEFAULT_REDIS_QUEUE
|
self.redis_queue = redis_queue or self._DEFAULT_REDIS_QUEUE
|
||||||
self.on_message = on_message
|
self.on_message = on_message
|
||||||
self.thread_id = threading.get_ident()
|
self.thread_id = threading.get_ident()
|
||||||
|
|
||||||
def get(self):
|
def get(self) -> Optional[Message]:
|
||||||
""" Reads one message from the Redis queue """
|
"""
|
||||||
|
Reads one message from the Redis queue
|
||||||
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if self.should_stop():
|
if self.should_stop():
|
||||||
return
|
return None
|
||||||
|
|
||||||
msg = self.redis.blpop(self.redis_queue, timeout=1)
|
msg = self.redis.blpop(self.redis_queue, timeout=1)
|
||||||
if not msg or msg[1] is None:
|
if not msg or msg[1] is None:
|
||||||
return
|
return None
|
||||||
|
|
||||||
msg = msg[1].decode('utf-8')
|
msg = msg[1].decode('utf-8')
|
||||||
return Message.build(msg)
|
return Message.build(msg)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(e)
|
logger.exception(e)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
def post(self, msg):
|
def post(self, msg):
|
||||||
""" Sends a message to the Redis queue """
|
"""
|
||||||
|
Sends a message to the Redis queue
|
||||||
|
"""
|
||||||
|
|
||||||
return self.redis.rpush(self.redis_queue, str(msg))
|
return self.redis.rpush(self.redis_queue, str(msg))
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
|
|
@ -2,6 +2,7 @@ import asyncio
|
||||||
import importlib
|
import importlib
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from dataclasses import dataclass, field
|
||||||
from threading import RLock
|
from threading import RLock
|
||||||
from typing import Optional, Any
|
from typing import Optional, Any
|
||||||
|
|
||||||
|
@ -11,36 +12,62 @@ from ..utils import get_enabled_plugins
|
||||||
|
|
||||||
logger = logging.getLogger('platypush:context')
|
logger = logging.getLogger('platypush:context')
|
||||||
|
|
||||||
# Map: backend_name -> backend_instance
|
|
||||||
backends = {}
|
|
||||||
|
|
||||||
# Map: plugin_name -> plugin_instance
|
@dataclass
|
||||||
plugins = {}
|
class Context:
|
||||||
|
"""
|
||||||
|
Data class to hold the context of the application.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# backend_name -> backend_instance
|
||||||
|
backends: dict = field(default_factory=dict)
|
||||||
|
# plugin_name -> plugin_instance
|
||||||
|
plugins: dict = field(default_factory=dict)
|
||||||
|
# Reference to the main application bus
|
||||||
|
bus: Optional[Bus] = None
|
||||||
|
|
||||||
|
|
||||||
|
_ctx = Context()
|
||||||
|
|
||||||
|
# # Map: backend_name -> backend_instance
|
||||||
|
# backends = {}
|
||||||
|
|
||||||
|
# # Map: plugin_name -> plugin_instance
|
||||||
|
# plugins = {}
|
||||||
|
|
||||||
# Map: plugin_name -> init_lock to make sure that a plugin isn't initialized
|
# Map: plugin_name -> init_lock to make sure that a plugin isn't initialized
|
||||||
# multiple times
|
# multiple times
|
||||||
plugins_init_locks = {}
|
plugins_init_locks = {}
|
||||||
|
|
||||||
# Reference to the main application bus
|
# Reference to the main application bus
|
||||||
main_bus = None
|
# main_bus = None
|
||||||
|
|
||||||
|
|
||||||
|
def get_context() -> Context:
|
||||||
|
"""
|
||||||
|
Get the current application context.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return _ctx
|
||||||
|
|
||||||
|
|
||||||
def register_backends(bus=None, global_scope=False, **kwargs):
|
def register_backends(bus=None, global_scope=False, **kwargs):
|
||||||
"""Initialize the backend objects based on the configuration and returns
|
"""
|
||||||
a name -> backend_instance map.
|
Initialize the backend objects based on the configuration and returns a
|
||||||
|
name -> backend_instance map.
|
||||||
|
|
||||||
Params:
|
Params:
|
||||||
bus -- If specific (it usually should), the messages processed by the
|
bus -- If specific (it usually should), the messages processed by the
|
||||||
backends will be posted on this bus.
|
backends will be posted on this bus.
|
||||||
|
kwargs -- Any additional key-value parameters required to initialize
|
||||||
kwargs -- Any additional key-value parameters required to initialize the backends
|
the backends
|
||||||
"""
|
"""
|
||||||
|
|
||||||
global main_bus
|
|
||||||
if bus:
|
if bus:
|
||||||
main_bus = bus
|
_ctx.bus = bus
|
||||||
|
|
||||||
if global_scope:
|
if global_scope:
|
||||||
global backends
|
backends = _ctx.backends
|
||||||
else:
|
else:
|
||||||
backends = {}
|
backends = {}
|
||||||
|
|
||||||
|
@ -57,13 +84,16 @@ def register_backends(bus=None, global_scope=False, **kwargs):
|
||||||
b = getattr(module, cls_name)(bus=bus, **cfg, **kwargs)
|
b = getattr(module, cls_name)(bus=bus, **cfg, **kwargs)
|
||||||
backends[name] = b
|
backends[name] = b
|
||||||
except AttributeError as e:
|
except AttributeError as e:
|
||||||
logger.warning('No such class in {}: {}'.format(module.__name__, cls_name))
|
logger.warning('No such class in %s: %s', module.__name__, cls_name)
|
||||||
raise RuntimeError(e)
|
raise RuntimeError(e) from e
|
||||||
|
|
||||||
return backends
|
return backends
|
||||||
|
|
||||||
|
|
||||||
def register_plugins(bus=None):
|
def register_plugins(bus=None):
|
||||||
|
"""
|
||||||
|
Register and start all the ``RunnablePlugin`` configured implementations.
|
||||||
|
"""
|
||||||
from ..plugins import RunnablePlugin
|
from ..plugins import RunnablePlugin
|
||||||
|
|
||||||
for plugin in get_enabled_plugins().values():
|
for plugin in get_enabled_plugins().values():
|
||||||
|
@ -75,27 +105,25 @@ def register_plugins(bus=None):
|
||||||
def get_backend(name):
|
def get_backend(name):
|
||||||
"""Returns the backend instance identified by name if it exists"""
|
"""Returns the backend instance identified by name if it exists"""
|
||||||
|
|
||||||
global backends
|
return _ctx.backends.get(name)
|
||||||
return backends.get(name)
|
|
||||||
|
|
||||||
|
|
||||||
def get_plugin(plugin_name, reload=False):
|
def get_plugin(plugin_name, reload=False):
|
||||||
"""Registers a plugin instance by name if not registered already, or
|
"""
|
||||||
returns the registered plugin instance"""
|
Registers a plugin instance by name if not registered already, or returns
|
||||||
global plugins
|
the registered plugin instance.
|
||||||
global plugins_init_locks
|
"""
|
||||||
|
|
||||||
if plugin_name not in plugins_init_locks:
|
if plugin_name not in plugins_init_locks:
|
||||||
plugins_init_locks[plugin_name] = RLock()
|
plugins_init_locks[plugin_name] = RLock()
|
||||||
|
|
||||||
if plugin_name in plugins and not reload:
|
if plugin_name in _ctx.plugins and not reload:
|
||||||
return plugins[plugin_name]
|
return _ctx.plugins[plugin_name]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
plugin = importlib.import_module('platypush.plugins.' + plugin_name)
|
plugin = importlib.import_module('platypush.plugins.' + plugin_name)
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
logger.warning('No such plugin: {}'.format(plugin_name))
|
logger.warning('No such plugin: %s', plugin_name)
|
||||||
raise RuntimeError(e)
|
raise RuntimeError(e) from e
|
||||||
|
|
||||||
# e.g. plugins.music.mpd main class: MusicMpdPlugin
|
# e.g. plugins.music.mpd main class: MusicMpdPlugin
|
||||||
cls_name = ''
|
cls_name = ''
|
||||||
|
@ -120,30 +148,34 @@ def get_plugin(plugin_name, reload=False):
|
||||||
try:
|
try:
|
||||||
plugin_class = getattr(plugin, cls_name)
|
plugin_class = getattr(plugin, cls_name)
|
||||||
except AttributeError as e:
|
except AttributeError as e:
|
||||||
logger.warning(
|
logger.warning('No such class in %s: %s [error: %s]', plugin_name, cls_name, e)
|
||||||
'No such class in {}: {} [error: {}]'.format(plugin_name, cls_name, str(e))
|
raise RuntimeError(e) from e
|
||||||
)
|
|
||||||
raise RuntimeError(e)
|
|
||||||
|
|
||||||
with plugins_init_locks[plugin_name]:
|
with plugins_init_locks[plugin_name]:
|
||||||
if plugins.get(plugin_name) and not reload:
|
if _ctx.plugins.get(plugin_name) and not reload:
|
||||||
return plugins[plugin_name]
|
return _ctx.plugins[plugin_name]
|
||||||
plugins[plugin_name] = plugin_class(**plugin_conf)
|
_ctx.plugins[plugin_name] = plugin_class(**plugin_conf)
|
||||||
|
|
||||||
return plugins[plugin_name]
|
return _ctx.plugins[plugin_name]
|
||||||
|
|
||||||
|
|
||||||
def get_bus() -> Bus:
|
def get_bus() -> Bus:
|
||||||
global main_bus
|
"""
|
||||||
if main_bus:
|
Get or register the main application bus.
|
||||||
return main_bus
|
"""
|
||||||
|
|
||||||
from platypush.bus.redis import RedisBus
|
from platypush.bus.redis import RedisBus
|
||||||
|
|
||||||
return RedisBus()
|
if _ctx.bus:
|
||||||
|
return _ctx.bus
|
||||||
|
|
||||||
|
_ctx.bus = RedisBus()
|
||||||
|
return _ctx.bus
|
||||||
|
|
||||||
|
|
||||||
def get_or_create_event_loop():
|
def get_or_create_event_loop() -> asyncio.AbstractEventLoop:
|
||||||
|
"""
|
||||||
|
Get or create a new event loop
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
except (DeprecationWarning, RuntimeError):
|
except (DeprecationWarning, RuntimeError):
|
||||||
|
|
|
@ -71,7 +71,7 @@ class RedisPlugin(Plugin):
|
||||||
try:
|
try:
|
||||||
return self._get_redis().mset(**kwargs)
|
return self._get_redis().mset(**kwargs)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
# XXX commit https://github.com/andymccurdy/redis-py/commit/90a52dd5de111f0053bb3ebaa7c78f73a82a1e3e
|
# Commit https://github.com/andymccurdy/redis-py/commit/90a52dd5de111f0053bb3ebaa7c78f73a82a1e3e
|
||||||
# broke back-compatibility with the previous way of passing
|
# broke back-compatibility with the previous way of passing
|
||||||
# key-value pairs to mset directly on kwargs. This try-catch block
|
# key-value pairs to mset directly on kwargs. This try-catch block
|
||||||
# is to support things on all the redis-py versions
|
# is to support things on all the redis-py versions
|
||||||
|
|
|
@ -525,7 +525,7 @@ def get_enabled_plugins() -> dict:
|
||||||
return plugins
|
return plugins
|
||||||
|
|
||||||
|
|
||||||
def get_redis() -> Redis:
|
def get_redis(*args, **kwargs) -> Redis:
|
||||||
"""
|
"""
|
||||||
Get a Redis client on the basis of the Redis configuration.
|
Get a Redis client on the basis of the Redis configuration.
|
||||||
|
|
||||||
|
@ -537,13 +537,14 @@ def get_redis() -> Redis:
|
||||||
"""
|
"""
|
||||||
from platypush.config import Config
|
from platypush.config import Config
|
||||||
|
|
||||||
return Redis(
|
if not (args or kwargs):
|
||||||
**(
|
kwargs = (
|
||||||
(Config.get('backend.redis') or {}).get('redis_args', {})
|
(Config.get('backend.redis') or {}).get('redis_args', {})
|
||||||
or Config.get('redis')
|
or Config.get('redis')
|
||||||
or {}
|
or {}
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
return Redis(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def to_datetime(t: Union[str, int, float, datetime.datetime]) -> datetime.datetime:
|
def to_datetime(t: Union[str, int, float, datetime.datetime]) -> datetime.datetime:
|
||||||
|
|
Loading…
Reference in a new issue