Better management of the context and support for proper expression expansion from the context in the action execution through eval

This commit is contained in:
Fabio Manganiello 2018-01-07 23:31:19 +01:00
parent 2871583c75
commit 252f503e4d
4 changed files with 37 additions and 27 deletions

View file

@ -97,10 +97,10 @@ class Event(Message):
'phrase': 'Hey dude turn on the living room lights' 'phrase': 'Hey dude turn on the living room lights'
} }
- self._matches_argument(argname='phrase', condition_value='Turn on the $lights lights') - self._matches_argument(argname='phrase', condition_value='Turn on the ${lights} lights')
will return EventMatchResult(is_match=True, parsed_args={ 'lights': 'living room' }) will return EventMatchResult(is_match=True, parsed_args={ 'lights': 'living room' })
- self._matches_argument(argname='phrase', condition_value='Turn off the $lights lights') - self._matches_argument(argname='phrase', condition_value='Turn off the ${lights} lights')
will return EventMatchResult(is_match=False, parsed_args={}) will return EventMatchResult(is_match=False, parsed_args={})
""" """
@ -117,15 +117,14 @@ class Event(Message):
condition_tokens.pop(0) condition_tokens.pop(0)
result.score += 1 result.score += 1
elif re.search(condition_token, event_token): elif re.search(condition_token, event_token):
# The only supported regex-match as of now is the equivalent of m = re.search('({})'.format(condition_token), event_token)
# the maybe operator. if m.group(1):
# e.g. "turn on (the)? lights" would match both "turn on the lights" event_tokens.pop(0)
# and "turn on lights". In such a case, we just consume the result.score += 1
# condition token and proceed forward. TODO add a more
# sophisticated regex-match handling
condition_tokens.pop(0) condition_tokens.pop(0)
else: else:
m = re.match('[^\\\]*\$([\w\d_-]+)', condition_token) m = re.match('[^\\\]*\${(.+?)}', condition_token)
if m: if m:
argname = m.group(1) argname = m.group(1)
if argname not in result.parsed_args: if argname not in result.parsed_args:

View file

@ -88,22 +88,33 @@ class Request(Message):
return event_args return event_args
def _expand_value_from_context(self, value, **context): @classmethod
def _expand_value_from_context(cls, value, **context):
parsed_value = '' parsed_value = ''
while value: while value:
m = re.match('([^\\\]*)\$([\w\d_-]+)(.*)', value) m = re.match('([^\$]*)(\${\s*(.+?)\s*})(.*)', value)
if m: if m and not m.group(1).endswith('\\'):
context_argname = m.group(2) prefix = m.group(1); expr = m.group(2);
value = m.group(3) inner_expr = m.group(3); value = m.group(4)
m = re.match('([^.\[\]()]+)(.*)', inner_expr)
context_argname = m.group(1)
path = m.group(2)
if context_argname in context: if context_argname in context:
parsed_value += m.group(1) + context[context_argname] try:
else: context_value = eval("context['{}']{}".format(
parsed_value += m.group(1) + '$' + m.group(2) context_argname, path if path else ''))
except: context_value = expr
parsed_value += prefix + str(context_value)
else: parsed_value += prefix + expr
else: else:
parsed_value += value parsed_value += value
value = '' value = ''
return parsed_value try: return json.loads(parsed_value)
except ValueError as e: return parsed_value
def _send_response(self, response): def _send_response(self, response):
@ -126,16 +137,14 @@ class Request(Message):
context -- Key-valued context. Example: context -- Key-valued context. Example:
context = (group_name='Kitchen lights') context = (group_name='Kitchen lights')
request.args: request.args:
- group: $group_name # will be expanded as "Kitchen lights") - group: ${group_name} # will be expanded as "Kitchen lights")
""" """
def _thread_func(n_tries): def _thread_func(n_tries):
if self.action.startswith('procedure.'): if self.action.startswith('procedure.'):
try: response = self._execute_procedure(n_tries=n_tries)
response = self._execute_procedure(n_tries=n_tries) self._send_response(response)
finally: return response
self._send_response(response)
return response
else: else:
(module_name, method_name) = get_module_and_method_from_action(self.action) (module_name, method_name) = get_module_and_method_from_action(self.action)
plugin = get_plugin(module_name) plugin = get_plugin(module_name)

View file

@ -54,8 +54,10 @@ class Procedure(object):
request.execute(n_tries, async=True, **context) request.execute(n_tries, async=True, **context)
else: else:
response = request.execute(n_tries, async=False, **context) response = request.execute(n_tries, async=False, **context)
context = { k:v for (k,v) in response.output.items() } \
if isinstance(response.output, dict) else {} if isinstance(response.output, dict):
for (k,v) in response.output.items():
context[k] = v
context['output'] = response.output context['output'] = response.output
context['errors'] = response.errors context['errors'] = response.errors

View file

@ -9,7 +9,7 @@ class TestEventParse(unittest.TestCase):
def setUp(self): def setUp(self):
self.condition = EventCondition.build({ self.condition = EventCondition.build({
'type': 'platypush.message.event.ping.PingEvent', 'type': 'platypush.message.event.ping.PingEvent',
'message': 'This is (the)? answer: $answer' 'message': 'This is (the)? answer: ${answer}'
}) })
def test_event_parse(self): def test_event_parse(self):