Merge pull request #747 from YunoHost/python-hooks-reloaded

Python hooks reloaded
This commit is contained in:
Alexandre Aubin 2019-08-31 04:08:52 +02:00 committed by GitHub
commit a208763d0c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -25,8 +25,11 @@
"""
import os
import re
import sys
import tempfile
import mimetypes
from glob import iglob
from importlib import import_module
from moulinette import m18n, msettings
from yunohost.utils.error import YunohostError
@ -179,7 +182,7 @@ def hook_list(action, list_by='name', show_info=False):
def _append_folder(d, folder):
# Iterate over and add hook from a folder
for f in os.listdir(folder + action):
if f[0] == '.' or f[-1] == '~':
if f[0] == '.' or f[-1] == '~' or f.endswith(".pyc"):
continue
path = '%s%s/%s' % (folder, action, f)
priority, name = _extract_filename_parts(f)
@ -311,7 +314,6 @@ def hook_exec(path, args=None, raise_on_error=False, no_trace=False,
user -- User with which to run the command
"""
from moulinette.utils.process import call_async_output
# Validate hook path
if path[0] != '/':
@ -319,6 +321,38 @@ def hook_exec(path, args=None, raise_on_error=False, no_trace=False,
if not os.path.isfile(path):
raise YunohostError('file_does_not_exist', path=path)
# Define output loggers and call command
loggers = (
lambda l: logger.debug(l.rstrip()+"\r"),
lambda l: logger.warning(l.rstrip()),
lambda l: logger.info(l.rstrip())
)
# Check the type of the hook (bash by default)
# For now we support only python and bash hooks.
hook_type = mimetypes.MimeTypes().guess_type(path)[0]
if hook_type == 'text/x-python':
returncode, returndata = _hook_exec_python(path, args, env, loggers)
else:
returncode, returndata = _hook_exec_bash(path, args, no_trace, chdir, env, user, return_format, loggers)
# Check and return process' return code
if returncode is None:
if raise_on_error:
raise YunohostError('hook_exec_not_terminated', path=path)
else:
logger.error(m18n.n('hook_exec_not_terminated', path=path))
return 1, {}
elif raise_on_error and returncode != 0:
raise YunohostError('hook_exec_failed', path=path)
return returncode, returndata
def _hook_exec_bash(path, args, no_trace, chdir, env, user, return_format, loggers):
from moulinette.utils.process import call_async_output
# Construct command variables
cmd_args = ''
if args and isinstance(args, list):
@ -369,33 +403,13 @@ def hook_exec(path, args=None, raise_on_error=False, no_trace=False,
else:
logger.debug(m18n.n('executing_script', script=path))
# Define output callbacks and call command
callbacks = (
lambda l: logger.debug(l.rstrip()+"\r"),
lambda l: logger.warning(l.rstrip()),
)
if stdinfo:
callbacks = (callbacks[0], callbacks[1],
lambda l: logger.info(l.rstrip()))
logger.debug("About to run the command '%s'" % command)
returncode = call_async_output(
command, callbacks, shell=False, cwd=chdir,
command, loggers, shell=False, cwd=chdir,
stdinfo=stdinfo
)
# Check and return process' return code
if returncode is None:
if raise_on_error:
raise YunohostError('hook_exec_not_terminated', path=path)
else:
logger.error(m18n.n('hook_exec_not_terminated', path=path))
return 1, {}
elif raise_on_error and returncode != 0:
raise YunohostError('hook_exec_failed', path=path)
raw_content = None
try:
with open(stdreturn, 'r') as f:
@ -427,6 +441,25 @@ def hook_exec(path, args=None, raise_on_error=False, no_trace=False,
return returncode, returncontent
def _hook_exec_python(path, args, env, loggers):
dir_ = os.path.dirname(path)
name = os.path.splitext(os.path.basename(path))[0]
if not dir_ in sys.path:
sys.path = [dir_] + sys.path
module = import_module(name)
ret = module.main(args, env, loggers)
# # Assert that the return is a (int, dict) tuple
assert isinstance(ret, tuple) \
and len(ret) == 2 \
and isinstance(ret[0],int) \
and isinstance(ret[1],dict), \
"Module %s did not return a (int, dict) tuple !" % module
return ret
def _extract_filename_parts(filename):
"""Extract hook parts from filename"""
if '-' in filename:
@ -434,6 +467,9 @@ def _extract_filename_parts(filename):
else:
priority = '50'
action = filename
# Remove extension if there's one
action = os.path.splitext(action)[0]
return priority, action