platypush/platypush/plugins/variable/__init__.py

206 lines
5.8 KiB
Python
Raw Permalink Normal View History

from functools import wraps
from threading import Event, RLock
from typing import Any, Callable, Collection, Dict, Iterable, Optional, Union
from platypush.entities import EntityManager, get_entities_engine
from platypush.entities.variables import Variable
from platypush.plugins import Plugin, action
# pylint: disable=protected-access
def ensure_initialized(f: Callable[..., Any]):
"""
Ensures that the entities engine has been initialized before
reading/writing the db.
It also performs lazy initialization of the variables in the local cache.
"""
@wraps(f)
def wrapper(*args, **kwargs):
self: VariablePlugin = args[0]
if not self._initialized.is_set():
with self._init_lock:
get_entities_engine(timeout=20)
if not self._initialized.is_set():
self._initialized.set()
with self._db.get_session() as session:
self._db_vars.update(
{
str(var.name): var.value
for var in session.query(Variable).all()
}
)
return f(*args, **kwargs)
return wrapper
class VariablePlugin(Plugin, EntityManager):
"""
This plugin allows you to manipulate context variables that can be
2018-07-20 19:19:55 +02:00
accessed across your tasks. It requires the :mod:`platypush.plugins.db`
and :mod:`platypush.plugins.redis` plugins to be enabled, as the variables
will be stored either persisted on a local database or on the local Redis instance.
"""
2020-09-09 02:14:59 +02:00
def __init__(self, **kwargs):
super().__init__(**kwargs)
self._db_vars: Dict[str, Optional[str]] = {}
""" Local cache for db variables. """
self._initialized = Event()
""" Lazy initialization event for the _db_vars map. """
self._init_lock = RLock()
""" Lock for the _db_vars map initialization. """
@ensure_initialized
@action
def get(self, name: Optional[str] = None, default_value=None):
2018-06-25 19:57:43 +02:00
"""
Get the value of a variable by name from the local db.
2018-06-25 19:57:43 +02:00
:param name: Variable name. If not specified, all the stored variables will be returned.
2018-06-25 19:57:43 +02:00
:param default_value: What will be returned if the variable is not defined (default: None)
:returns: A map in the format ``{"<name>":"<value>"}``
"""
return (
{name: self._db_vars.get(name, default_value)}
if name is not None
else self.status().output
)
@ensure_initialized
@action
def set(self, **kwargs):
2018-06-25 19:57:43 +02:00
"""
Set a variable or a set of variables on the local db.
2018-06-25 19:57:43 +02:00
:param kwargs: Key-value list of variables to set (e.g. ``foo='bar', answer=42``)
"""
self.publish_entities(kwargs)
self._db_vars.update(kwargs)
return kwargs
@ensure_initialized
@action
def delete(self, name: str):
2018-06-25 19:57:43 +02:00
"""
Delete a variable from the database.
Unlike :meth:`.unset`, this method actually deletes the record from the
database instead of setting it to null.
2018-06-25 19:57:43 +02:00
:param name: Name of the variable to remove.
2018-06-25 19:57:43 +02:00
"""
with self._db.get_session() as session:
entity = session.query(Variable).filter(Variable.name == name).first()
if entity is not None:
self._entities.delete(entity.id)
self._db_vars.pop(name, None)
return True
@ensure_initialized
@action
def unset(self, name: str):
"""
Unset a variable by name if it's set on the local db.
Unlike :meth:`.delete`, this method only sets the record to null
instead of removing it from the database.
:param name: Name of the variable to remove.
"""
return self.set(**{name: None})
@ensure_initialized
@action
def mget(self, name: str):
"""
Get the value of a variable by name from Redis.
:param name: Variable name
:returns: A map in the format ``{"<name>":"<value>"}``
"""
return self._redis.mget([name])
@ensure_initialized
@action
def mset(self, **kwargs):
"""
Set a variable or a set of variables on Redis.
:param kwargs: Key-value list of variables to set (e.g. ``foo='bar', answer=42``)
:returns: A map with the set variables
"""
self._redis.mset(**kwargs)
return kwargs
@ensure_initialized
@action
def munset(self, name: str):
"""
Unset a Redis variable by name if it's set
:param name: Name of the variable to remove
"""
return self._redis.delete(name)
2018-09-20 12:49:57 +02:00
@ensure_initialized
@action
def expire(self, name: str, expire: int):
2018-09-20 12:49:57 +02:00
"""
Set a variable expiration on Redis
2018-10-06 23:30:11 +02:00
:param name: Variable name
:param expire: Expiration time in seconds
2018-09-20 12:49:57 +02:00
"""
return self._redis.expire(name, expire)
def transform_entities(
self, entities: Union[dict, Iterable]
) -> Collection[Variable]:
variables = (
[
{
'name': name,
'value': value,
}
for name, value in entities.items()
]
if isinstance(entities, dict)
else entities
)
return super().transform_entities(
[
Variable(id=var['name'], name=var['name'], value=var['value'])
for var in variables
]
)
@ensure_initialized
@action
def status(self, *_, **__):
variables = {
name: value for name, value in self._db_vars.items() if value is not None
}
self.publish_entities(variables)
return variables
# vim:sw=4:ts=4:et: