[procedure] Added support for custom values on the return statement.

This enables constructs like this in procedures:

```yaml
- return

- return 1

- return: ${output}
```
This commit is contained in:
Fabio Manganiello 2024-09-10 19:55:26 +02:00
parent 946c7b1783
commit 1e9f7fb2c6
Signed by untrusted user: blacklight
GPG key ID: D90FBA7F76362774

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"""
@ -70,7 +110,15 @@ class Procedure:
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
@ -218,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