forked from platypush/platypush
If a request on a RunnablePlugin throws an exception then we should also restart the plugin upon reload
Plus some Black/LINT chores
This commit is contained in:
parent
c32142c8b5
commit
55671f4aff
1 changed files with 97 additions and 44 deletions
|
@ -12,8 +12,12 @@ from platypush.config import Config
|
||||||
from platypush.context import get_plugin
|
from platypush.context import get_plugin
|
||||||
from platypush.message import Message
|
from platypush.message import Message
|
||||||
from platypush.message.response import Response
|
from platypush.message.response import Response
|
||||||
from platypush.utils import get_hash, get_module_and_method_from_action, get_redis_queue_name_by_message, \
|
from platypush.utils import (
|
||||||
is_functional_procedure
|
get_hash,
|
||||||
|
get_module_and_method_from_action,
|
||||||
|
get_redis_queue_name_by_message,
|
||||||
|
is_functional_procedure,
|
||||||
|
)
|
||||||
|
|
||||||
logger = logging.getLogger('platypush')
|
logger = logging.getLogger('platypush')
|
||||||
|
|
||||||
|
@ -21,8 +25,17 @@ logger = logging.getLogger('platypush')
|
||||||
class Request(Message):
|
class Request(Message):
|
||||||
"""Request message class"""
|
"""Request message class"""
|
||||||
|
|
||||||
def __init__(self, target, action, origin=None, id=None, backend=None,
|
def __init__(
|
||||||
args=None, token=None, timestamp=None):
|
self,
|
||||||
|
target,
|
||||||
|
action,
|
||||||
|
origin=None,
|
||||||
|
id=None,
|
||||||
|
backend=None,
|
||||||
|
args=None,
|
||||||
|
token=None,
|
||||||
|
timestamp=None,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Params:
|
Params:
|
||||||
target -- Target node [Str]
|
target -- Target node [Str]
|
||||||
|
@ -48,9 +61,13 @@ class Request(Message):
|
||||||
@classmethod
|
@classmethod
|
||||||
def build(cls, msg):
|
def build(cls, msg):
|
||||||
msg = super().parse(msg)
|
msg = super().parse(msg)
|
||||||
args = {'target': msg.get('target', Config.get('device_id')), 'action': msg['action'],
|
args = {
|
||||||
'args': msg.get('args', {}), 'id': msg['id'] if 'id' in msg else cls._generate_id(),
|
'target': msg.get('target', Config.get('device_id')),
|
||||||
'timestamp': msg['_timestamp'] if '_timestamp' in msg else time.time()}
|
'action': msg['action'],
|
||||||
|
'args': msg.get('args', {}),
|
||||||
|
'id': msg['id'] if 'id' in msg else cls._generate_id(),
|
||||||
|
'timestamp': msg['_timestamp'] if '_timestamp' in msg else time.time(),
|
||||||
|
}
|
||||||
|
|
||||||
if 'origin' in msg:
|
if 'origin' in msg:
|
||||||
args['origin'] = msg['origin']
|
args['origin'] = msg['origin']
|
||||||
|
@ -61,7 +78,7 @@ class Request(Message):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _generate_id():
|
def _generate_id():
|
||||||
_id = ''
|
_id = ''
|
||||||
for i in range(0, 16):
|
for _ in range(0, 16):
|
||||||
_id += '%.2x' % random.randint(0, 255)
|
_id += '%.2x' % random.randint(0, 255)
|
||||||
return _id
|
return _id
|
||||||
|
|
||||||
|
@ -84,9 +101,14 @@ class Request(Message):
|
||||||
|
|
||||||
return proc_config(*args, **kwargs)
|
return proc_config(*args, **kwargs)
|
||||||
|
|
||||||
proc = Procedure.build(name=proc_name, requests=proc_config['actions'],
|
proc = Procedure.build(
|
||||||
_async=proc_config['_async'], args=self.args,
|
name=proc_name,
|
||||||
backend=self.backend, id=self.id)
|
requests=proc_config['actions'],
|
||||||
|
_async=proc_config['_async'],
|
||||||
|
args=self.args,
|
||||||
|
backend=self.backend,
|
||||||
|
id=self.id,
|
||||||
|
)
|
||||||
|
|
||||||
return proc.execute(*args, **kwargs)
|
return proc.execute(*args, **kwargs)
|
||||||
|
|
||||||
|
@ -112,7 +134,7 @@ class Request(Message):
|
||||||
|
|
||||||
if isinstance(value, str):
|
if isinstance(value, str):
|
||||||
value = self.expand_value_from_context(value, **context)
|
value = self.expand_value_from_context(value, **context)
|
||||||
elif isinstance(value, dict) or isinstance(value, list):
|
elif isinstance(value, (dict, list)):
|
||||||
self._expand_context(event_args=value, **context)
|
self._expand_context(event_args=value, **context)
|
||||||
|
|
||||||
event_args[key] = value
|
event_args[key] = value
|
||||||
|
@ -132,7 +154,11 @@ class Request(Message):
|
||||||
try:
|
try:
|
||||||
exec('{}="{}"'.format(k, re.sub(r'(^|[^\\])"', '\1\\"', v)))
|
exec('{}="{}"'.format(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(
|
||||||
|
'Could not set context variable {}={}: {}'.format(
|
||||||
|
k, v, str(e)
|
||||||
|
)
|
||||||
|
)
|
||||||
logger.debug('Context: {}'.format(context))
|
logger.debug('Context: {}'.format(context))
|
||||||
|
|
||||||
parsed_value = ''
|
parsed_value = ''
|
||||||
|
@ -152,7 +178,7 @@ class Request(Message):
|
||||||
|
|
||||||
if callable(context_value):
|
if callable(context_value):
|
||||||
context_value = context_value()
|
context_value = context_value()
|
||||||
if isinstance(context_value, range) or isinstance(context_value, tuple):
|
if isinstance(context_value, (range, tuple)):
|
||||||
context_value = [*context_value]
|
context_value = [*context_value]
|
||||||
if isinstance(context_value, datetime.date):
|
if isinstance(context_value, datetime.date):
|
||||||
context_value = context_value.isoformat()
|
context_value = context_value.isoformat()
|
||||||
|
@ -162,7 +188,7 @@ class Request(Message):
|
||||||
|
|
||||||
parsed_value += prefix + (
|
parsed_value += prefix + (
|
||||||
json.dumps(context_value)
|
json.dumps(context_value)
|
||||||
if isinstance(context_value, list) or isinstance(context_value, dict)
|
if isinstance(context_value, (list, dict))
|
||||||
else str(context_value)
|
else str(context_value)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
@ -205,6 +231,9 @@ class Request(Message):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _thread_func(_n_tries, errors=None):
|
def _thread_func(_n_tries, errors=None):
|
||||||
|
from platypush.context import get_bus
|
||||||
|
from platypush.plugins import RunnablePlugin
|
||||||
|
|
||||||
response = None
|
response = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -221,11 +250,15 @@ class Request(Message):
|
||||||
return response
|
return response
|
||||||
else:
|
else:
|
||||||
action = self.expand_value_from_context(self.action, **context)
|
action = self.expand_value_from_context(self.action, **context)
|
||||||
(module_name, method_name) = get_module_and_method_from_action(action)
|
(module_name, method_name) = get_module_and_method_from_action(
|
||||||
|
action
|
||||||
|
)
|
||||||
plugin = get_plugin(module_name)
|
plugin = get_plugin(module_name)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(e)
|
logger.exception(e)
|
||||||
msg = 'Uncaught pre-processing exception from action [{}]: {}'.format(self.action, str(e))
|
msg = 'Uncaught pre-processing exception from action [{}]: {}'.format(
|
||||||
|
self.action, str(e)
|
||||||
|
)
|
||||||
logger.warning(msg)
|
logger.warning(msg)
|
||||||
response = Response(output=None, errors=[msg])
|
response = Response(output=None, errors=[msg])
|
||||||
self._send_response(response)
|
self._send_response(response)
|
||||||
|
@ -243,24 +276,37 @@ class Request(Message):
|
||||||
response = plugin.run(method_name, args)
|
response = plugin.run(method_name, args)
|
||||||
|
|
||||||
if not response:
|
if not response:
|
||||||
logger.warning('Received null response from action {}'.format(action))
|
logger.warning(
|
||||||
|
'Received null response from action {}'.format(action)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
if response.is_error():
|
if response.is_error():
|
||||||
logger.warning(('Response processed with errors from ' +
|
logger.warning(
|
||||||
'action {}: {}').format(
|
(
|
||||||
action, str(response)))
|
'Response processed with errors from ' + 'action {}: {}'
|
||||||
|
).format(action, str(response))
|
||||||
|
)
|
||||||
elif not response.disable_logging:
|
elif not response.disable_logging:
|
||||||
logger.info('Processed response from action {}: {}'.
|
logger.info(
|
||||||
format(action, str(response)))
|
'Processed response from action {}: {}'.format(
|
||||||
|
action, str(response)
|
||||||
|
)
|
||||||
|
)
|
||||||
except (AssertionError, TimeoutError) as e:
|
except (AssertionError, TimeoutError) as e:
|
||||||
plugin.logger.exception(e)
|
plugin.logger.exception(e)
|
||||||
logger.warning('{} from action [{}]: {}'.format(type(e), action, str(e)))
|
logger.warning(
|
||||||
|
'{} from action [{}]: {}'.format(type(e), action, str(e))
|
||||||
|
)
|
||||||
response = Response(output=None, errors=[str(e)])
|
response = Response(output=None, errors=[str(e)])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Retry mechanism
|
# Retry mechanism
|
||||||
plugin.logger.exception(e)
|
plugin.logger.exception(e)
|
||||||
logger.warning(('Uncaught exception while processing response ' +
|
logger.warning(
|
||||||
'from action [{}]: {}').format(action, str(e)))
|
(
|
||||||
|
'Uncaught exception while processing response '
|
||||||
|
+ 'from action [{}]: {}'
|
||||||
|
).format(action, str(e))
|
||||||
|
)
|
||||||
|
|
||||||
errors = errors or []
|
errors = errors or []
|
||||||
if str(e) not in errors:
|
if str(e) not in errors:
|
||||||
|
@ -269,16 +315,20 @@ class Request(Message):
|
||||||
response = Response(output=None, errors=errors)
|
response = Response(output=None, errors=errors)
|
||||||
if _n_tries - 1 > 0:
|
if _n_tries - 1 > 0:
|
||||||
logger.info('Reloading plugin {} and retrying'.format(module_name))
|
logger.info('Reloading plugin {} and retrying'.format(module_name))
|
||||||
get_plugin(module_name, reload=True)
|
plugin = get_plugin(module_name, reload=True)
|
||||||
|
if isinstance(plugin, RunnablePlugin):
|
||||||
|
plugin.bus = get_bus()
|
||||||
|
plugin.start()
|
||||||
|
|
||||||
response = _thread_func(_n_tries=_n_tries - 1, errors=errors)
|
response = _thread_func(_n_tries=_n_tries - 1, errors=errors)
|
||||||
finally:
|
finally:
|
||||||
self._send_response(response)
|
self._send_response(response)
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
token_hash = Config.get('token_hash')
|
stored_token_hash = Config.get('token_hash')
|
||||||
|
token = getattr(self, 'token', '')
|
||||||
if token_hash:
|
if stored_token_hash and get_hash(token) != stored_token_hash:
|
||||||
if self.token is None or get_hash(self.token) != token_hash:
|
|
||||||
raise PermissionError()
|
raise PermissionError()
|
||||||
|
|
||||||
if _async:
|
if _async:
|
||||||
|
@ -292,7 +342,8 @@ class Request(Message):
|
||||||
the message into a UTF-8 JSON string
|
the message into a UTF-8 JSON string
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return json.dumps({
|
return json.dumps(
|
||||||
|
{
|
||||||
'type': 'request',
|
'type': 'request',
|
||||||
'target': self.target,
|
'target': self.target,
|
||||||
'action': self.action,
|
'action': self.action,
|
||||||
|
@ -301,6 +352,8 @@ class Request(Message):
|
||||||
'id': self.id if hasattr(self, 'id') else None,
|
'id': self.id if hasattr(self, 'id') else None,
|
||||||
'token': self.token if hasattr(self, 'token') else None,
|
'token': self.token if hasattr(self, 'token') else None,
|
||||||
'_timestamp': self.timestamp,
|
'_timestamp': self.timestamp,
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
Loading…
Reference in a new issue