Merge branch '341/procedure-entities' into 341/procedure-entities-ui

This commit is contained in:
Fabio Manganiello 2024-09-10 22:55:53 +02:00
commit 152c2020de
Signed by untrusted user: blacklight
GPG key ID: D90FBA7F76362774
3 changed files with 61 additions and 14 deletions

View file

@ -64,12 +64,13 @@ class Request(Message):
msg = super().parse(msg) msg = super().parse(msg)
args = { args = {
'target': msg.get('target', Config.get('device_id')), 'target': msg.get('target', Config.get('device_id')),
'action': msg['action'], 'action': msg.get('action', msg.get('name')),
'args': msg.get('args', {}), 'args': msg.get('args', {}),
'id': msg['id'] if 'id' in msg else cls._generate_id(), 'id': msg['id'] if 'id' in msg else cls._generate_id(),
'timestamp': msg['_timestamp'] if '_timestamp' in msg else time.time(), 'timestamp': msg['_timestamp'] if '_timestamp' in msg else time.time(),
} }
assert args.get('action'), 'No action specified in the request'
if 'origin' in msg: if 'origin' in msg:
args['origin'] = msg['origin'] args['origin'] = msg['origin']
if 'token' in msg: if 'token' in msg:

View file

@ -286,9 +286,10 @@ class ProceduresPlugin(RunnablePlugin, ProcedureEntityManager):
@classmethod @classmethod
def _serialize_action(cls, data: Union[Iterable, Dict]) -> Union[Dict, List]: def _serialize_action(cls, data: Union[Iterable, Dict]) -> Union[Dict, List]:
if isinstance(data, dict): if isinstance(data, dict):
if data.get('action'): name = data.get('action', data.get('name'))
if name:
return { return {
'action': data['action'], 'action': name,
**({'args': data['args']} if data.get('args') else {}), **({'args': data['args']} if data.get('args') else {}),
} }

View file

@ -1,10 +1,11 @@
import enum import enum
import logging import logging
import re import re
from dataclasses import dataclass
from functools import wraps from functools import wraps
from queue import LifoQueue from queue import LifoQueue
from typing import Optional from typing import Any, Optional
from ..common import exec_wrapper from ..common import exec_wrapper
from ..config import Config from ..config import Config
@ -14,7 +15,7 @@ from ..message.response import Response
logger = logging.getLogger('platypush') logger = logging.getLogger('platypush')
class Statement(enum.Enum): class StatementType(enum.Enum):
""" """
Enumerates the possible statements in a procedure. Enumerates the possible statements in a procedure.
""" """
@ -24,6 +25,45 @@ class Statement(enum.Enum):
RETURN = 'return' RETURN = 'return'
@dataclass
class Statement:
"""
Models a statement in a procedure.
"""
type: StatementType
argument: Optional[str] = None
@classmethod
def build(cls, statement: str):
"""
Builds a statement from a string.
"""
m = re.match(r'\s*return\s*(.*)\s*', statement, re.IGNORECASE)
if m:
return ReturnStatement(argument=m.group(1))
return cls(StatementType(statement.lower()))
def run(self, *_, **__) -> Optional[Any]:
"""
Executes the statement.
"""
@dataclass
class ReturnStatement(Statement):
"""
Models a return statement in a procedure.
"""
type: StatementType = StatementType.RETURN
def run(self, *_, **context):
return Request.expand_value_from_context(self.argument, **context)
class Procedure: class Procedure:
"""Procedure class. A procedure is a pre-configured list of requests""" """Procedure class. A procedure is a pre-configured list of requests"""
@ -55,7 +95,6 @@ class Procedure:
requests, requests,
args=None, args=None,
backend=None, backend=None,
id=None, # pylint: disable=redefined-builtin
procedure_class=None, procedure_class=None,
**kwargs, **kwargs,
): ):
@ -66,11 +105,20 @@ class Procedure:
if_config = LifoQueue() if_config = LifoQueue()
procedure_class = procedure_class or cls procedure_class = procedure_class or cls
key = None key = None
kwargs.pop('id', None)
for request_config in requests: for request_config in requests:
# Check if it's a break/continue/return statement # Check if it's a break/continue/return statement
if isinstance(request_config, str): if isinstance(request_config, str):
reqs.append(Statement(request_config)) reqs.append(Statement.build(request_config))
continue
# Check if it's a return statement with a value
if (
len(request_config.keys()) == 1
and list(request_config.keys())[0] == 'return'
):
reqs.append(ReturnStatement(argument=request_config['return']))
continue continue
# Check if this request is an if-else # Check if this request is an if-else
@ -91,7 +139,6 @@ class Procedure:
'condition': condition, 'condition': condition,
'else_branch': [], 'else_branch': [],
'backend': backend, 'backend': backend,
'id': id,
} }
) )
@ -132,7 +179,6 @@ class Procedure:
_async=_async, _async=_async,
requests=request_config[key], requests=request_config[key],
backend=backend, backend=backend,
id=id,
iterator_name=iterator_name, iterator_name=iterator_name,
iterable=iterable, iterable=iterable,
) )
@ -156,14 +202,12 @@ class Procedure:
requests=request_config[key], requests=request_config[key],
condition=condition, condition=condition,
backend=backend, backend=backend,
id=id,
) )
reqs.append(loop) reqs.append(loop)
continue continue
request_config['origin'] = Config.get('device_id') request_config['origin'] = Config.get('device_id')
request_config['id'] = id
if 'target' not in request_config: if 'target' not in request_config:
request_config['target'] = request_config['origin'] request_config['target'] = request_config['origin']
@ -222,15 +266,16 @@ class Procedure:
continue continue
if isinstance(request, Statement): if isinstance(request, Statement):
if request == Statement.RETURN: if isinstance(request, ReturnStatement):
response = Response(output=request.run(**context))
self._should_return = True self._should_return = True
for proc in __stack__: for proc in __stack__:
proc._should_return = True # pylint: disable=protected-access proc._should_return = True # pylint: disable=protected-access
break break
if request in [Statement.BREAK, Statement.CONTINUE]: if request.type in [StatementType.BREAK, StatementType.CONTINUE]:
loop = self._find_nearest_loop(__stack__) loop = self._find_nearest_loop(__stack__)
if request == Statement.BREAK: if request == StatementType.BREAK:
loop._should_break = True # pylint: disable=protected-access loop._should_break = True # pylint: disable=protected-access
else: else:
loop._should_continue = True # pylint: disable=protected-access loop._should_continue = True # pylint: disable=protected-access