forked from platypush/platypush
Added Procedure.to_dict
method.
Also, LINT+black for the `procedure` module.
This commit is contained in:
parent
a6d6fd4067
commit
3ffaaa0eb9
1 changed files with 168 additions and 83 deletions
|
@ -19,7 +19,7 @@ class Statement(enum.Enum):
|
||||||
RETURN = 'return'
|
RETURN = 'return'
|
||||||
|
|
||||||
|
|
||||||
class Procedure(object):
|
class Procedure:
|
||||||
"""Procedure class. A procedure is a pre-configured list of requests"""
|
"""Procedure class. A procedure is a pre-configured list of requests"""
|
||||||
|
|
||||||
def __init__(self, name, _async, requests, args=None, backend=None):
|
def __init__(self, name, _async, requests, args=None, backend=None):
|
||||||
|
@ -42,7 +42,17 @@ class Procedure(object):
|
||||||
req.backend = self.backend
|
req.backend = self.backend
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def build(cls, name, _async, requests, args=None, backend=None, id=None, procedure_class=None, **kwargs):
|
def build(
|
||||||
|
cls,
|
||||||
|
name,
|
||||||
|
_async,
|
||||||
|
requests,
|
||||||
|
args=None,
|
||||||
|
backend=None,
|
||||||
|
id=None,
|
||||||
|
procedure_class=None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
reqs = []
|
reqs = []
|
||||||
for_count = 0
|
for_count = 0
|
||||||
while_count = 0
|
while_count = 0
|
||||||
|
@ -64,10 +74,11 @@ class Procedure(object):
|
||||||
|
|
||||||
if m:
|
if m:
|
||||||
if_count += 1
|
if_count += 1
|
||||||
if_name = '{}__if_{}'.format(name, if_count)
|
if_name = f'{name}__if_{if_count}'
|
||||||
condition = m.group(2)
|
condition = m.group(2)
|
||||||
|
|
||||||
if_config.put({
|
if_config.put(
|
||||||
|
{
|
||||||
'name': if_name,
|
'name': if_name,
|
||||||
'_async': False,
|
'_async': False,
|
||||||
'requests': request_config[key],
|
'requests': request_config[key],
|
||||||
|
@ -75,14 +86,16 @@ class Procedure(object):
|
||||||
'else_branch': [],
|
'else_branch': [],
|
||||||
'backend': backend,
|
'backend': backend,
|
||||||
'id': id,
|
'id': id,
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if key == 'else':
|
if key == 'else':
|
||||||
if if_config.empty():
|
if if_config.empty():
|
||||||
raise RuntimeError('else statement with no ' +
|
raise RuntimeError(
|
||||||
'associated if in {}'.format(name))
|
f'else statement with no associated if in {name}'
|
||||||
|
)
|
||||||
|
|
||||||
conf = if_config.get()
|
conf = if_config.get()
|
||||||
conf['else_branch'] = request_config[key]
|
conf['else_branch'] = request_config[key]
|
||||||
|
@ -100,19 +113,23 @@ class Procedure(object):
|
||||||
|
|
||||||
if m:
|
if m:
|
||||||
for_count += 1
|
for_count += 1
|
||||||
loop_name = '{}__for_{}'.format(name, for_count)
|
loop_name = f'{name}__for_{for_count}'
|
||||||
|
|
||||||
# A 'for' loop is synchronous. Declare a 'fork' loop if you
|
# A 'for' loop is synchronous. Declare a 'fork' loop if you
|
||||||
# want to process the elements in the iterable in parallel
|
# want to process the elements in the iterable in parallel
|
||||||
_async = True if m.group(1) == 'fork' else False
|
_async = m.group(1) == 'fork'
|
||||||
iterator_name = m.group(2)
|
iterator_name = m.group(2)
|
||||||
iterable = m.group(3)
|
iterable = m.group(3)
|
||||||
|
|
||||||
loop = ForProcedure.build(name=loop_name, _async=_async,
|
loop = ForProcedure.build(
|
||||||
|
name=loop_name,
|
||||||
|
_async=_async,
|
||||||
requests=request_config[key],
|
requests=request_config[key],
|
||||||
backend=backend, id=id,
|
backend=backend,
|
||||||
|
id=id,
|
||||||
iterator_name=iterator_name,
|
iterator_name=iterator_name,
|
||||||
iterable=iterable)
|
iterable=iterable,
|
||||||
|
)
|
||||||
|
|
||||||
reqs.append(loop)
|
reqs.append(loop)
|
||||||
continue
|
continue
|
||||||
|
@ -124,13 +141,17 @@ class Procedure(object):
|
||||||
|
|
||||||
if m:
|
if m:
|
||||||
while_count += 1
|
while_count += 1
|
||||||
loop_name = '{}__while_{}'.format(name, while_count)
|
loop_name = f'{name}__while_{while_count}'
|
||||||
condition = m.group(1).strip()
|
condition = m.group(1).strip()
|
||||||
|
|
||||||
loop = WhileProcedure.build(name=loop_name, _async=False,
|
loop = WhileProcedure.build(
|
||||||
|
name=loop_name,
|
||||||
|
_async=False,
|
||||||
requests=request_config[key],
|
requests=request_config[key],
|
||||||
condition=condition,
|
condition=condition,
|
||||||
backend=backend, id=id)
|
backend=backend,
|
||||||
|
id=id,
|
||||||
|
)
|
||||||
|
|
||||||
reqs.append(loop)
|
reqs.append(loop)
|
||||||
continue
|
continue
|
||||||
|
@ -147,8 +168,14 @@ class Procedure(object):
|
||||||
pending_if = if_config.get()
|
pending_if = if_config.get()
|
||||||
reqs.append(IfProcedure.build(**pending_if))
|
reqs.append(IfProcedure.build(**pending_if))
|
||||||
|
|
||||||
# noinspection PyArgumentList
|
return procedure_class(
|
||||||
return procedure_class(name=name, _async=_async, requests=reqs, args=args, backend=backend, **kwargs)
|
name=name,
|
||||||
|
_async=_async,
|
||||||
|
requests=reqs,
|
||||||
|
args=args,
|
||||||
|
backend=backend,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _find_nearest_loop(stack):
|
def _find_nearest_loop(stack):
|
||||||
|
@ -175,9 +202,9 @@ class Procedure(object):
|
||||||
v = Request.expand_value_from_context(v, **context)
|
v = Request.expand_value_from_context(v, **context)
|
||||||
args[k] = v
|
args[k] = v
|
||||||
context[k] = v
|
context[k] = v
|
||||||
logger.info('Executing procedure {} with arguments {}'.format(self.name, args))
|
logger.info('Executing procedure %s with arguments %s', self.name, args)
|
||||||
else:
|
else:
|
||||||
logger.info('Executing procedure {}'.format(self.name))
|
logger.info('Executing procedure %s', self.name)
|
||||||
|
|
||||||
response = Response()
|
response = Response()
|
||||||
token = Config.get('token')
|
token = Config.get('token')
|
||||||
|
@ -202,10 +229,10 @@ class Procedure(object):
|
||||||
loop._should_continue = True
|
loop._should_continue = True
|
||||||
break
|
break
|
||||||
|
|
||||||
if isinstance(self, LoopProcedure):
|
if isinstance(self, LoopProcedure) and (
|
||||||
if self._should_continue or self._should_break:
|
self._should_continue or self._should_break
|
||||||
|
):
|
||||||
if self._should_continue:
|
if self._should_continue:
|
||||||
# noinspection PyAttributeOutsideInit
|
|
||||||
self._should_continue = False
|
self._should_continue = False
|
||||||
|
|
||||||
break
|
break
|
||||||
|
@ -230,6 +257,14 @@ class Procedure(object):
|
||||||
|
|
||||||
return response or Response()
|
return response or Response()
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return {
|
||||||
|
'name': self.name,
|
||||||
|
'requests': self.requests,
|
||||||
|
'args': self.args,
|
||||||
|
'_async': self._async,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class LoopProcedure(Procedure):
|
class LoopProcedure(Procedure):
|
||||||
"""
|
"""
|
||||||
|
@ -237,7 +272,9 @@ class LoopProcedure(Procedure):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, requests, _async=False, args=None, backend=None):
|
def __init__(self, name, requests, _async=False, args=None, backend=None):
|
||||||
super(). __init__(name=name, _async=_async, requests=requests, args=args, backend=backend)
|
super().__init__(
|
||||||
|
name=name, _async=_async, requests=requests, args=args, backend=backend
|
||||||
|
)
|
||||||
self._should_break = False
|
self._should_break = False
|
||||||
self._should_continue = False
|
self._should_continue = False
|
||||||
|
|
||||||
|
@ -264,36 +301,47 @@ class ForProcedure(LoopProcedure):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, iterator_name, iterable, requests, _async=False, args=None, backend=None):
|
def __init__(
|
||||||
super(). __init__(name=name, _async=_async, requests=requests, args=args, backend=backend)
|
self,
|
||||||
|
name,
|
||||||
|
iterator_name,
|
||||||
|
iterable,
|
||||||
|
requests,
|
||||||
|
_async=False,
|
||||||
|
args=None,
|
||||||
|
backend=None,
|
||||||
|
):
|
||||||
|
super().__init__(
|
||||||
|
name=name, _async=_async, requests=requests, args=args, backend=backend
|
||||||
|
)
|
||||||
self.iterator_name = iterator_name
|
self.iterator_name = iterator_name
|
||||||
self.iterable = iterable
|
self.iterable = iterable
|
||||||
|
|
||||||
def execute(self, _async=None, **context):
|
def execute(self, _async=None, **context):
|
||||||
try:
|
try:
|
||||||
iterable = eval(self.iterable)
|
iterable = eval(self.iterable)
|
||||||
assert hasattr(iterable, '__iter__'), 'Object of type {} is not iterable: {}'.\
|
assert hasattr(
|
||||||
format(type(iterable), iterable)
|
iterable, '__iter__'
|
||||||
|
), f'Object of type {type(iterable)} is not iterable: {iterable}'
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f'Iterable {self.iterable} expansion error: {e}')
|
logger.debug(f'Iterable {self.iterable} expansion error: {e}')
|
||||||
iterable = Request.expand_value_from_context(self.iterable, **context)
|
iterable = Request.expand_value_from_context(self.iterable, **context)
|
||||||
|
|
||||||
response = Response()
|
response = Response()
|
||||||
|
|
||||||
# noinspection DuplicatedCode
|
|
||||||
for item in iterable:
|
for item in iterable:
|
||||||
if self._should_return:
|
if self._should_return:
|
||||||
logger.info('Returning from {}'.format(self.name))
|
logger.info('Returning from %s', self.name)
|
||||||
break
|
break
|
||||||
|
|
||||||
if self._should_continue:
|
if self._should_continue:
|
||||||
self._should_continue = False
|
self._should_continue = False
|
||||||
logger.info('Continuing loop {}'.format(self.name))
|
logger.info('Continuing loop %s', self.name)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if self._should_break:
|
if self._should_break:
|
||||||
self._should_break = False
|
self._should_break = False
|
||||||
logger.info('Breaking loop {}'.format(self.name))
|
logger.info('Breaking loop %s', self.name)
|
||||||
break
|
break
|
||||||
|
|
||||||
context[self.iterator_name] = item
|
context[self.iterator_name] = item
|
||||||
|
@ -327,37 +375,41 @@ class WhileProcedure(LoopProcedure):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, condition, requests, _async=False, args=None, backend=None):
|
def __init__(
|
||||||
super(). __init__(name=name, _async=_async, requests=requests, args=args, backend=backend)
|
self, name, condition, requests, _async=False, args=None, backend=None
|
||||||
|
):
|
||||||
|
super().__init__(
|
||||||
|
name=name, _async=_async, requests=requests, args=args, backend=backend
|
||||||
|
)
|
||||||
self.condition = condition
|
self.condition = condition
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_context(**context):
|
def _get_context(**context):
|
||||||
for (k, v) in context.items():
|
for k, v in context.items():
|
||||||
try:
|
try:
|
||||||
context[k] = eval(v)
|
context[k] = eval(v)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f'Evaluation error for {v}: {e}')
|
logger.debug(f'Evaluation error for {v}: {e}')
|
||||||
if isinstance(v, str):
|
if isinstance(v, str):
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
context[k] = eval('"{}"'.format(re.sub(r'(^|[^\\])"', '\1\\"', v)))
|
context[k] = eval('"' + re.sub(r'(^|[^\\])"', '\1\\"', v) + '"')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning('Could not parse value for context variable {}={}'.format(k, v))
|
logger.warning(
|
||||||
logger.warning('Context: {}'.format(context))
|
'Could not parse value for context variable %s=%s', k, v
|
||||||
|
)
|
||||||
|
logger.warning('Context: %s', context)
|
||||||
logger.exception(e)
|
logger.exception(e)
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
# noinspection DuplicatedCode,PyBroadException
|
|
||||||
def execute(self, _async=None, **context):
|
def execute(self, _async=None, **context):
|
||||||
response = Response()
|
response = Response()
|
||||||
context = self._get_context(**context)
|
context = self._get_context(**context)
|
||||||
for k, v in context.items():
|
for k, v in context.items():
|
||||||
try:
|
try:
|
||||||
exec('{}={}'.format(k, v))
|
exec(f'{k}={v}')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f'Evaluation error: {k}={v}: {e}')
|
logger.debug('Evaluation error: %s=%s: %s', k, v, e)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
condition_true = eval(self.condition)
|
condition_true = eval(self.condition)
|
||||||
|
@ -365,34 +417,32 @@ class WhileProcedure(LoopProcedure):
|
||||||
break
|
break
|
||||||
|
|
||||||
if self._should_return:
|
if self._should_return:
|
||||||
logger.info('Returning from {}'.format(self.name))
|
logger.info('Returning from %s', self.name)
|
||||||
break
|
break
|
||||||
|
|
||||||
if self._should_continue:
|
if self._should_continue:
|
||||||
self._should_continue = False
|
self._should_continue = False
|
||||||
logger.info('Continuing loop {}'.format(self.name))
|
logger.info('Continuing loop %s', self.name)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if self._should_break:
|
if self._should_break:
|
||||||
self._should_break = False
|
self._should_break = False
|
||||||
logger.info('Breaking loop {}'.format(self.name))
|
logger.info('Breaking loop %s', self.name)
|
||||||
break
|
break
|
||||||
|
|
||||||
response = super().execute(**context)
|
response = super().execute(**context)
|
||||||
|
|
||||||
if response.output:
|
if response.output and isinstance(response.output, dict):
|
||||||
if isinstance(response.output, dict):
|
|
||||||
new_context = self._get_context(**response.output)
|
new_context = self._get_context(**response.output)
|
||||||
for k, v in new_context.items():
|
for k, v in new_context.items():
|
||||||
try:
|
try:
|
||||||
exec('{}={}'.format(k, v))
|
exec(f'{k}={v}')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f'Evaluation error: {k}={v}: {e}')
|
logger.debug(f'Evaluation error: {k}={v}: {e}')
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyBroadException
|
|
||||||
class IfProcedure(Procedure):
|
class IfProcedure(Procedure):
|
||||||
"""
|
"""
|
||||||
Models an if-else construct.
|
Models an if-else construct.
|
||||||
|
@ -418,7 +468,17 @@ class IfProcedure(Procedure):
|
||||||
cmd: '/path/turn_off_heating.sh'
|
cmd: '/path/turn_off_heating.sh'
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, condition, requests, else_branch=None, args=None, backend=None, id=None, **kwargs):
|
def __init__(
|
||||||
|
self,
|
||||||
|
name,
|
||||||
|
condition,
|
||||||
|
requests,
|
||||||
|
else_branch=None,
|
||||||
|
args=None,
|
||||||
|
backend=None,
|
||||||
|
id=None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
kwargs['_async'] = False
|
kwargs['_async'] = False
|
||||||
self.condition = condition
|
self.condition = condition
|
||||||
self.else_branch = else_branch
|
self.else_branch = else_branch
|
||||||
|
@ -438,29 +498,54 @@ class IfProcedure(Procedure):
|
||||||
super().__init__(name=name, requests=reqs, args=args, backend=backend, **kwargs)
|
super().__init__(name=name, requests=reqs, args=args, backend=backend, **kwargs)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def build(cls, name, condition, requests, else_branch=None, args=None, backend=None, id=None, **kwargs):
|
def build(
|
||||||
|
cls,
|
||||||
|
name,
|
||||||
|
condition,
|
||||||
|
requests,
|
||||||
|
else_branch=None,
|
||||||
|
args=None,
|
||||||
|
backend=None,
|
||||||
|
id=None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
kwargs['_async'] = False
|
kwargs['_async'] = False
|
||||||
if else_branch:
|
if else_branch:
|
||||||
else_branch = super().build(name=name+'__else', requests=else_branch,
|
else_branch = super().build(
|
||||||
args=args, backend=backend, id=id,
|
name=name + '__else',
|
||||||
procedure_class=Procedure, **kwargs)
|
requests=else_branch,
|
||||||
|
args=args,
|
||||||
|
backend=backend,
|
||||||
|
id=id,
|
||||||
|
procedure_class=Procedure,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
return super().build(name=name, condition=condition, requests=requests,
|
return super().build(
|
||||||
else_branch=else_branch, args=args, backend=backend, id=id,
|
name=name,
|
||||||
**kwargs)
|
condition=condition,
|
||||||
|
requests=requests,
|
||||||
|
else_branch=else_branch,
|
||||||
|
args=args,
|
||||||
|
backend=backend,
|
||||||
|
id=id,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
def execute(self, **context):
|
def execute(self, **context):
|
||||||
for (k, v) in context.items():
|
for k, v in context.items():
|
||||||
try:
|
try:
|
||||||
exec('{}={}'.format(k, v))
|
exec(f'{k}={v}')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f'Evaluation error: {k}={v}: {e}')
|
logger.debug(f'Evaluation error: {k}={v}: {e}')
|
||||||
if isinstance(v, str):
|
if isinstance(v, str):
|
||||||
try:
|
try:
|
||||||
exec('{}="{}"'.format(k, re.sub(r'(^|[^\\])"', '\1\\"', v)))
|
exec('{k}="' + re.sub(r'(^|[^\\])"', '\1\\"', v) + '"')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug('Could not set context variable {}={}: {}'.format(k, v, str(e)))
|
logger.debug(
|
||||||
logger.debug('Context: {}'.format(context))
|
'Could not set context variable %s=%s: %s', k, v, e
|
||||||
|
)
|
||||||
|
logger.debug('Context: %s', context)
|
||||||
|
|
||||||
condition_true = eval(self.condition)
|
condition_true = eval(self.condition)
|
||||||
response = Response()
|
response = Response()
|
||||||
|
|
Loading…
Reference in a new issue