[#341] Backend implementation of the new procedure entities architecture.

This commit is contained in:
Fabio Manganiello 2024-08-25 16:06:56 +02:00
parent 1ee8055597
commit 99909c73ab
8 changed files with 138 additions and 10 deletions

View file

@ -272,6 +272,7 @@ class Config:
@property
def _core_plugins(self) -> Dict[str, dict]:
return {
'procedures': {},
'variable': {},
}

View file

@ -1,7 +0,0 @@
class ProceduresManager:
"""
This class is responsible for managing the procedures as native entities.
"""
def __init__(self):
self.procedures = {}

View file

@ -0,0 +1,29 @@
from abc import ABC, abstractmethod
from typing import Callable, Dict, Union
from platypush.config import Config
from . import EntityManager
class ProcedureEntityManager(EntityManager, ABC):
"""
Base class for integrations that can run and manage procedures.
"""
@abstractmethod
def exec(self, procedure: str, *args, **kwargs):
"""
Run a procedure.
:param procedure: Procedure to run, by name.
:param args: Arguments to pass to the procedure.
:param kwargs: Keyword arguments to pass to the procedure.
"""
raise NotImplementedError()
@property
def _all_procedures(self) -> Dict[str, Union[dict, Callable]]:
"""
:return: All the procedures that can be run by this entity manager.
"""
return Config.get_procedures()

View file

@ -28,9 +28,13 @@ if not is_defined('procedure'):
id = Column(
Integer, ForeignKey('entity.id', ondelete='CASCADE'), primary_key=True
)
name = Column(String, unique=True, nullable=False)
args = Column(JSON, nullable=False, default=[])
type = Column(Enum('python', 'config', name='procedure_type'), nullable=False)
procedure_type = Column(
Enum('python', 'config', name='procedure_type'), nullable=False
)
module = Column(String)
source = Column(String)
line = Column(Integer)
__table_args__ = {'keep_existing': True}
__mapper_args__ = {

View file

@ -12,6 +12,7 @@ from platypush.common.db import override_definitions
from platypush.common.reflection import Integration, Message as MessageMetadata
from platypush.config import Config
from platypush.plugins import Plugin, action
from platypush.plugins.procedure import ProcedureEncoder
from platypush.message import Message
from platypush.message.event import Event
from platypush.message.response import Response
@ -20,7 +21,6 @@ from platypush.utils.mock import auto_mocks
from platypush.utils.manifest import Manifest, Manifests, PackageManagers
from ._cache import Cache
from ._serialize import ProcedureEncoder
class InspectPlugin(Plugin):

View file

@ -0,0 +1,94 @@
import json
from dataclasses import dataclass
from typing import Callable, Collection, Optional, Union
from platypush.entities.managers.procedures import ProcedureEntityManager
from platypush.entities.procedures import Procedure
from platypush.plugins import RunnablePlugin, action
from platypush.utils import run
from ._serialize import ProcedureEncoder
@dataclass
class _ProcedureWrapper:
name: str
obj: Union[dict, Callable]
class ProceduresPlugin(RunnablePlugin, ProcedureEntityManager):
"""
Utility plugin to run and store procedures as native entities.
"""
@action
def exec(self, procedure: str, *args, **kwargs):
return run(f'procedure.{procedure}', *args, **kwargs)
def _convert_procedure(self, name: str, proc: Union[dict, Callable]) -> Procedure:
metadata = self._serialize_procedure(proc, name=name)
return Procedure(
id=name,
name=name,
plugin=self,
procedure_type=metadata['type'],
module=metadata.get('module'),
source=metadata.get('source'),
line=metadata.get('line'),
args=metadata.get('args', []),
)
@action
def status(self, *_, **__):
"""
:return: The serialized configured procedures. Format:
.. code-block:: json
{
"procedure_name": {
"type": "python",
"module": "module_name",
"source": "/path/to/source.py",
"line": 42,
"args": ["arg1", "arg2"]
}
}
"""
self.publish_entities(self._get_wrapped_procedures())
return self._get_serialized_procedures()
def transform_entities(
self, entities: Collection[_ProcedureWrapper], **_
) -> Collection[Procedure]:
return [
self._convert_procedure(name=proc.name, proc=proc.obj) for proc in entities
]
def _get_wrapped_procedures(self) -> Collection[_ProcedureWrapper]:
return [
_ProcedureWrapper(name=name, obj=proc)
for name, proc in self._all_procedures.items()
]
@staticmethod
def _serialize_procedure(
proc: Union[dict, Callable], name: Optional[str] = None
) -> dict:
ret = json.loads(json.dumps(proc, cls=ProcedureEncoder))
if name:
ret['name'] = name
return ret
def _get_serialized_procedures(self) -> dict:
return {
name: self._serialize_procedure(proc, name=name)
for name, proc in self._all_procedures.items()
}
def main(self, *_, **__):
while not self.should_stop():
self.publish_entities(self._get_wrapped_procedures())
self.wait_stop()

View file

@ -0,0 +1,7 @@
{
"manifest": {
"package": "platypush.plugins.procedure",
"type": "plugin",
"events": []
}
}