From 4764dfbef33695d655debfc0413ebb6ef75b7906 Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sat, 22 Jul 2017 12:02:52 +0200 Subject: [PATCH 01/34] [mod] autopep8 --- moulinette/utils/filesystem.py | 2 ++ moulinette/utils/process.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/moulinette/utils/filesystem.py b/moulinette/utils/filesystem.py index 737bdd18..bbea872c 100644 --- a/moulinette/utils/filesystem.py +++ b/moulinette/utils/filesystem.py @@ -9,6 +9,7 @@ from moulinette.core import MoulinetteError # Files & directories -------------------------------------------------- + def read_file(file_path): """ Read a regular text file @@ -94,6 +95,7 @@ def write_to_file(file_path, data, file_mode="w"): m18n.g('error_writing_file', file=file_path, error=str(e))) + def append_to_file(file_path, data): """ Append a single string or a list of string to a text file. diff --git a/moulinette/utils/process.py b/moulinette/utils/process.py index 6475f056..e48f27a9 100644 --- a/moulinette/utils/process.py +++ b/moulinette/utils/process.py @@ -106,7 +106,7 @@ def call_async_output(args, callback, **kwargs): # Call multiple commands ----------------------------------------------- def run_commands(cmds, callback=None, separate_stderr=False, shell=True, - **kwargs): + **kwargs): """Run multiple commands with error management Run a list of commands and allow to manage how to treat errors either From 173fa202ad37c33c2e1d84bf9e5bee45db0e9d9f Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 23 Jul 2017 02:54:52 +0200 Subject: [PATCH 02/34] [mod] remove useless try/except --- moulinette/actionsmap.py | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index 3fe8cd03..dfcb68eb 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -601,27 +601,20 @@ class ActionsMap(object): # -- Parse global arguments if 'arguments' in _global: - try: - # Get global arguments parser - parser = top_parser.add_global_parser() - except AttributeError: - # No parser for global arguments - pass - else: - # Add arguments - _add_arguments(GLOBAL_SECTION, parser, + if hasattr(top_parser, "add_global_parser"): + _add_arguments(GLOBAL_SECTION, top_parser.add_global_parser(), _global['arguments']) # -- Parse categories for cn, cp in actionsmap.items(): - try: - actions = cp.pop('actions') - except KeyError: + if "actions" not in cp: # Invalid category without actions logger.warning("no actions found in category '%s' in " "namespace '%s'", cn, n) continue + actions = cp.pop('actions') + # Get category parser cat_parser = top_parser.add_category_parser(cn, **cp) @@ -629,10 +622,12 @@ class ActionsMap(object): for an, ap in actions.items(): args = ap.pop('arguments', {}) tid = (n, cn, an) - try: + + if 'configuration' in ap: conf = ap.pop('configuration') _set_conf = lambda p: p.set_conf(tid, conf) - except KeyError: + + else: # No action configuration _set_conf = lambda p: False @@ -646,10 +641,10 @@ class ActionsMap(object): logger.warning("cannot add action (%s, %s, %s): %s", n, cn, an, e) continue - else: - # Store action identifier and add arguments - a_parser.set_defaults(_tid=tid) - _add_arguments(tid, a_parser, args) - _set_conf(cat_parser) + + # Store action identifier and add arguments + a_parser.set_defaults(_tid=tid) + _add_arguments(tid, a_parser, args) + _set_conf(cat_parser) return top_parser From 4fd3b962aa2181b10617e6394dfe31dd9fda4253 Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 23 Jul 2017 03:08:24 +0200 Subject: [PATCH 03/34] [mod] renaming + comment --- moulinette/actionsmap.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index dfcb68eb..12dcb38a 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -589,8 +589,13 @@ class ActionsMap(object): # Instantiate parser top_parser = self.parser_class(**kwargs) - # Iterate over actions map namespaces - for n, actionsmap in actionsmaps.items(): + # namespace, actionsmap is a tuple where: + # + # * namespace define the top "name", for us it will always be + # "yunohost" and there well be only this one + # * actionsmap is the actual actionsmap that we care about + for namespace, actionsmap in actionsmaps.items(): + print "n>>>", namespace # Retrieve global parameters _global = actionsmap.pop('_global', {}) @@ -610,7 +615,7 @@ class ActionsMap(object): if "actions" not in cp: # Invalid category without actions logger.warning("no actions found in category '%s' in " - "namespace '%s'", cn, n) + "namespace '%s'", cn, namespace) continue actions = cp.pop('actions') @@ -621,7 +626,7 @@ class ActionsMap(object): # -- Parse actions for an, ap in actions.items(): args = ap.pop('arguments', {}) - tid = (n, cn, an) + tid = (namespace, cn, an) if 'configuration' in ap: conf = ap.pop('configuration') @@ -639,7 +644,7 @@ class ActionsMap(object): continue except ValueError as e: logger.warning("cannot add action (%s, %s, %s): %s", - n, cn, an, e) + namespace, cn, an, e) continue # Store action identifier and add arguments From e851c76d6e0d66eb40c671cc176ff46833bcf5ad Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 23 Jul 2017 03:08:38 +0200 Subject: [PATCH 04/34] [mod] remove debug print --- moulinette/actionsmap.py | 1 - 1 file changed, 1 deletion(-) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index 12dcb38a..f171d4c7 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -595,7 +595,6 @@ class ActionsMap(object): # "yunohost" and there well be only this one # * actionsmap is the actual actionsmap that we care about for namespace, actionsmap in actionsmaps.items(): - print "n>>>", namespace # Retrieve global parameters _global = actionsmap.pop('_global', {}) From 1c8f387bae674b612196bf2424bef466c3e03286 Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 23 Jul 2017 03:15:12 +0200 Subject: [PATCH 05/34] [mod] more renaming --- moulinette/actionsmap.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index f171d4c7..714a839c 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -610,22 +610,22 @@ class ActionsMap(object): _global['arguments']) # -- Parse categories - for cn, cp in actionsmap.items(): - if "actions" not in cp: + for category_name, category_values in actionsmap.items(): + if "actions" not in category_values: # Invalid category without actions logger.warning("no actions found in category '%s' in " - "namespace '%s'", cn, namespace) + "namespace '%s'", category_name, namespace) continue - actions = cp.pop('actions') + actions = category_values.pop('actions') # Get category parser - cat_parser = top_parser.add_category_parser(cn, **cp) + cat_parser = top_parser.add_category_parser(category_name, **category_values) # -- Parse actions for an, ap in actions.items(): args = ap.pop('arguments', {}) - tid = (namespace, cn, an) + tid = (namespace, category_name, an) if 'configuration' in ap: conf = ap.pop('configuration') @@ -643,7 +643,7 @@ class ActionsMap(object): continue except ValueError as e: logger.warning("cannot add action (%s, %s, %s): %s", - namespace, cn, an, e) + namespace, category_name, an, e) continue # Store action identifier and add arguments From be9b9c9b4302d5bf2860a65b848eff6ba658ee0b Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 23 Jul 2017 03:16:57 +0200 Subject: [PATCH 06/34] [doc] explicit comment --- moulinette/actionsmap.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index 714a839c..7840c1fd 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -609,7 +609,8 @@ class ActionsMap(object): _add_arguments(GLOBAL_SECTION, top_parser.add_global_parser(), _global['arguments']) - # -- Parse categories + # category_name is stuff like "user", "domain", "hooks"... + # category_values is the values of this category (like actions) for category_name, category_values in actionsmap.items(): if "actions" not in category_values: # Invalid category without actions From 0f7d5d341da185d56b6b92452b7708aed6df163a Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 23 Jul 2017 03:17:01 +0200 Subject: [PATCH 07/34] [mod] more renaming --- moulinette/actionsmap.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index 7840c1fd..927ebaab 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -621,7 +621,7 @@ class ActionsMap(object): actions = category_values.pop('actions') # Get category parser - cat_parser = top_parser.add_category_parser(category_name, **category_values) + category_parser = top_parser.add_category_parser(category_name, **category_values) # -- Parse actions for an, ap in actions.items(): @@ -638,7 +638,7 @@ class ActionsMap(object): try: # Get action parser - a_parser = cat_parser.add_action_parser(an, tid, **ap) + a_parser = category_parser.add_action_parser(an, tid, **ap) except AttributeError: # No parser for the action continue @@ -650,6 +650,6 @@ class ActionsMap(object): # Store action identifier and add arguments a_parser.set_defaults(_tid=tid) _add_arguments(tid, a_parser, args) - _set_conf(cat_parser) + _set_conf(category_parser) return top_parser From e138c3e377c9ccfebd3ec62963d93cdfeda41a4a Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 23 Jul 2017 03:19:13 +0200 Subject: [PATCH 08/34] [mod] more renaming and comments --- moulinette/actionsmap.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index 927ebaab..bcb92938 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -623,13 +623,14 @@ class ActionsMap(object): # Get category parser category_parser = top_parser.add_category_parser(category_name, **category_values) - # -- Parse actions - for an, ap in actions.items(): - args = ap.pop('arguments', {}) - tid = (namespace, category_name, an) + # action_name is like "list" of "domain list" + # action_options are the values + for action_name, action_options in actions.items(): + args = action_options.pop('arguments', {}) + tid = (namespace, category_name, action_name) - if 'configuration' in ap: - conf = ap.pop('configuration') + if 'configuration' in action_options: + conf = action_options.pop('configuration') _set_conf = lambda p: p.set_conf(tid, conf) else: @@ -638,13 +639,13 @@ class ActionsMap(object): try: # Get action parser - a_parser = category_parser.add_action_parser(an, tid, **ap) + a_parser = category_parser.add_action_parser(action_name, tid, **action_options) except AttributeError: # No parser for the action continue except ValueError as e: logger.warning("cannot add action (%s, %s, %s): %s", - namespace, category_name, an, e) + namespace, category_name, action_name, e) continue # Store action identifier and add arguments From 179958797ae110704584df66dfa7e31735cfa549 Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 23 Jul 2017 03:28:25 +0200 Subject: [PATCH 09/34] [mod] more renaming --- moulinette/actionsmap.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index bcb92938..dcef320f 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -626,12 +626,12 @@ class ActionsMap(object): # action_name is like "list" of "domain list" # action_options are the values for action_name, action_options in actions.items(): - args = action_options.pop('arguments', {}) + arguments = action_options.pop('arguments', {}) tid = (namespace, category_name, action_name) if 'configuration' in action_options: - conf = action_options.pop('configuration') - _set_conf = lambda p: p.set_conf(tid, conf) + configuration = action_options.pop('configuration') + _set_conf = lambda p: p.set_conf(tid, configuration) else: # No action configuration @@ -650,7 +650,7 @@ class ActionsMap(object): # Store action identifier and add arguments a_parser.set_defaults(_tid=tid) - _add_arguments(tid, a_parser, args) + _add_arguments(tid, a_parser, arguments) _set_conf(category_parser) return top_parser From 61eaec0d536526b4c270a62bc6ece4cee41ce3e7 Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 23 Jul 2017 03:35:51 +0200 Subject: [PATCH 10/34] [doc] usefull comment --- moulinette/actionsmap.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index dcef320f..9c1809ec 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -587,6 +587,10 @@ class ActionsMap(object): parser.add_argument(*names, **argp) # Instantiate parser + # + # this either returns: + # * moulinette.interfaces.cli.ActionsMapParser + # * moulinette.interfaces.api.ActionsMapParser top_parser = self.parser_class(**kwargs) # namespace, actionsmap is a tuple where: From 681bba9ee1b4c4a41425ab1d81c5e39e8a0bb51b Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 23 Jul 2017 03:35:57 +0200 Subject: [PATCH 11/34] [mod] more renaming --- moulinette/actionsmap.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index 9c1809ec..e2e05bd5 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -643,7 +643,7 @@ class ActionsMap(object): try: # Get action parser - a_parser = category_parser.add_action_parser(action_name, tid, **action_options) + action_parser = category_parser.add_action_parser(action_name, tid, **action_options) except AttributeError: # No parser for the action continue @@ -653,8 +653,8 @@ class ActionsMap(object): continue # Store action identifier and add arguments - a_parser.set_defaults(_tid=tid) - _add_arguments(tid, a_parser, arguments) + action_parser.set_defaults(_tid=tid) + _add_arguments(tid, action_parser, arguments) _set_conf(category_parser) return top_parser From 390b1ec17ad636bea429fe0b3d1a9d74e1feaf84 Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 23 Jul 2017 03:40:37 +0200 Subject: [PATCH 12/34] [mod] this is always true --- moulinette/actionsmap.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index e2e05bd5..dafb48ab 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -609,9 +609,8 @@ class ActionsMap(object): # -- Parse global arguments if 'arguments' in _global: - if hasattr(top_parser, "add_global_parser"): - _add_arguments(GLOBAL_SECTION, top_parser.add_global_parser(), - _global['arguments']) + _add_arguments(GLOBAL_SECTION, top_parser.add_global_parser(), + _global['arguments']) # category_name is stuff like "user", "domain", "hooks"... # category_values is the values of this category (like actions) From 70fdbd01880540ab0f785e5c1b37ec4f5832ddbc Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 23 Jul 2017 03:41:45 +0200 Subject: [PATCH 13/34] [mod] remove conditions that are always True --- moulinette/actionsmap.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index dafb48ab..41f654e1 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -602,14 +602,9 @@ class ActionsMap(object): # Retrieve global parameters _global = actionsmap.pop('_global', {}) - # -- Parse global configuration - if 'configuration' in _global: - # Set global configuration - top_parser.set_global_conf(_global['configuration']) + top_parser.set_global_conf(_global['configuration']) - # -- Parse global arguments - if 'arguments' in _global: - _add_arguments(GLOBAL_SECTION, top_parser.add_global_parser(), + _add_arguments(GLOBAL_SECTION, top_parser.add_global_parser(), _global['arguments']) # category_name is stuff like "user", "domain", "hooks"... From 477d3721d161f6049eae2b8db49faf1d76dfccbf Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 23 Jul 2017 03:46:41 +0200 Subject: [PATCH 14/34] [mod] more renaming --- moulinette/actionsmap.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index 41f654e1..b0bb6f47 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -569,22 +569,23 @@ class ActionsMap(object): # Add arguments to the parser def _add_arguments(tid, parser, arguments): - for argn, argp in arguments.items(): - names = top_parser.format_arg_names(str(argn), - argp.pop('full', None)) + for argument_name, argument_options in arguments.items(): + print argument_options + names = top_parser.format_arg_names(str(argument_name), + argument_options.pop('full', None)) try: - argp['type'] = eval(argp['type']) + argument_options['type'] = eval(argument_options['type']) except: pass try: - extra = argp.pop('extra') - arg_dest = (parser.add_argument(*names, **argp)).dest + extra = argument_options.pop('extra') + arg_dest = (parser.add_argument(*names, **argument_options)).dest self.extraparser.add_argument(tid, arg_dest, extra, validate_extra) except KeyError: # No extra parameters - parser.add_argument(*names, **argp) + parser.add_argument(*names, **argument_options) # Instantiate parser # From a1395bdec6f62e9e2f6e42ead10beb323ebe01ca Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 23 Jul 2017 03:46:56 +0200 Subject: [PATCH 15/34] [doc] add a comment from the docstring --- moulinette/actionsmap.py | 1 + 1 file changed, 1 insertion(+) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index b0bb6f47..9090e862 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -603,6 +603,7 @@ class ActionsMap(object): # Retrieve global parameters _global = actionsmap.pop('_global', {}) + # Set the global configuration to use for the parser. top_parser.set_global_conf(_global['configuration']) _add_arguments(GLOBAL_SECTION, top_parser.add_global_parser(), From eed5cde8d20f71abb3c91454e283b7c6229c5edb Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 23 Jul 2017 03:47:52 +0200 Subject: [PATCH 16/34] [mod] remove useless exception --- moulinette/actionsmap.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index 9090e862..abdf361b 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -570,7 +570,6 @@ class ActionsMap(object): # Add arguments to the parser def _add_arguments(tid, parser, arguments): for argument_name, argument_options in arguments.items(): - print argument_options names = top_parser.format_arg_names(str(argument_name), argument_options.pop('full', None)) try: @@ -578,14 +577,14 @@ class ActionsMap(object): except: pass - try: - extra = argument_options.pop('extra') - arg_dest = (parser.add_argument(*names, **argument_options)).dest - self.extraparser.add_argument(tid, arg_dest, extra, - validate_extra) - except KeyError: - # No extra parameters + if "extra" not in argument_options: parser.add_argument(*names, **argument_options) + continue + + extra = argument_options.pop('extra') + arg_dest = (parser.add_argument(*names, **argument_options)).dest + self.extraparser.add_argument(tid, arg_dest, extra, + validate_extra) # Instantiate parser # From 0ded786883c6caa474a5894f947632bc49917077 Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 23 Jul 2017 03:48:01 +0200 Subject: [PATCH 17/34] [mod] remove useless () --- moulinette/actionsmap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index abdf361b..eaa53112 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -582,7 +582,7 @@ class ActionsMap(object): continue extra = argument_options.pop('extra') - arg_dest = (parser.add_argument(*names, **argument_options)).dest + arg_dest = parser.add_argument(*names, **argument_options).dest self.extraparser.add_argument(tid, arg_dest, extra, validate_extra) From 4fde7dd2a40be623288eab0a896a85795b49d6da Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 23 Jul 2017 03:48:16 +0200 Subject: [PATCH 18/34] [mod] style --- moulinette/actionsmap.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index eaa53112..036d565e 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -583,8 +583,7 @@ class ActionsMap(object): extra = argument_options.pop('extra') arg_dest = parser.add_argument(*names, **argument_options).dest - self.extraparser.add_argument(tid, arg_dest, extra, - validate_extra) + self.extraparser.add_argument(tid, arg_dest, extra, validate_extra) # Instantiate parser # From 7853dda9fcad63d15c93b49c67bff69e85ad237f Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 23 Jul 2017 03:50:39 +0200 Subject: [PATCH 19/34] [mod] it's better to fail than to silencly ignore a typo --- moulinette/actionsmap.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index 036d565e..66edf84c 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -572,10 +572,9 @@ class ActionsMap(object): for argument_name, argument_options in arguments.items(): names = top_parser.format_arg_names(str(argument_name), argument_options.pop('full', None)) - try: + + if "type" in argument_options: argument_options['type'] = eval(argument_options['type']) - except: - pass if "extra" not in argument_options: parser.add_argument(*names, **argument_options) From 5f34437704f9019de5720a962415da84f07a954d Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 23 Jul 2017 03:51:18 +0200 Subject: [PATCH 20/34] [mod] more renaming --- moulinette/actionsmap.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index 66edf84c..60313bc0 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -581,8 +581,8 @@ class ActionsMap(object): continue extra = argument_options.pop('extra') - arg_dest = parser.add_argument(*names, **argument_options).dest - self.extraparser.add_argument(tid, arg_dest, extra, validate_extra) + argument_dest = parser.add_argument(*names, **argument_options).dest + self.extraparser.add_argument(tid, argument_dest, extra, validate_extra) # Instantiate parser # From 79bb5b427f7f81011484bb79e0fcb407c9be14c1 Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 23 Jul 2017 03:54:56 +0200 Subject: [PATCH 21/34] [doc] add a comment to avoid looking at yet-another-file --- moulinette/actionsmap.py | 1 + 1 file changed, 1 insertion(+) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index 60313bc0..6b677640 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -603,6 +603,7 @@ class ActionsMap(object): # Set the global configuration to use for the parser. top_parser.set_global_conf(_global['configuration']) + # GLOBAL_SECTION = '_global' _add_arguments(GLOBAL_SECTION, top_parser.add_global_parser(), _global['arguments']) From 1caa172072f36ef965bf1005f3b93e53f15a8150 Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 23 Jul 2017 04:16:12 +0200 Subject: [PATCH 22/34] [mod] renaming, make method name match reality and had a method for code readability --- moulinette/actionsmap.py | 7 ++++--- moulinette/interfaces/__init__.py | 3 +++ moulinette/interfaces/api.py | 2 +- moulinette/interfaces/cli.py | 7 +++++-- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index 6b677640..9f8da1f5 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -603,9 +603,10 @@ class ActionsMap(object): # Set the global configuration to use for the parser. top_parser.set_global_conf(_global['configuration']) - # GLOBAL_SECTION = '_global' - _add_arguments(GLOBAL_SECTION, top_parser.add_global_parser(), - _global['arguments']) + if top_parser.has_global_parser(): + # GLOBAL_SECTION = '_global' + _add_arguments(GLOBAL_SECTION, top_parser.get_global_parser(), + _global['arguments']) # category_name is stuff like "user", "domain", "hooks"... # category_values is the values of this category (like actions) diff --git a/moulinette/interfaces/__init__.py b/moulinette/interfaces/__init__.py index 3b2e69bd..6d5d332b 100644 --- a/moulinette/interfaces/__init__.py +++ b/moulinette/interfaces/__init__.py @@ -72,6 +72,9 @@ class BaseActionsMapParser(object): raise NotImplementedError("derived class '%s' must override this method" % self.__class__.__name__) + def has_global_parser(self): + return False + def add_global_parser(self, **kwargs): """Add a parser for global arguments diff --git a/moulinette/interfaces/api.py b/moulinette/interfaces/api.py index 5d96af98..345624b1 100644 --- a/moulinette/interfaces/api.py +++ b/moulinette/interfaces/api.py @@ -548,7 +548,7 @@ class ActionsMapParser(BaseActionsMapParser): return [name.replace('--', '@', 1)] return [name.replace('-', '@', 1)] - def add_global_parser(self, **kwargs): + def get_global_parser(self, **kwargs): raise AttributeError("global arguments are not managed") def add_category_parser(self, name, **kwargs): diff --git a/moulinette/interfaces/cli.py b/moulinette/interfaces/cli.py index ada6697d..6a85f7a5 100644 --- a/moulinette/interfaces/cli.py +++ b/moulinette/interfaces/cli.py @@ -235,7 +235,7 @@ class ActionsMapParser(BaseActionsMapParser): if top_parser: # Append each top parser action to the global group - glob = self.add_global_parser() + glob = self.get_global_parser() for action in top_parser._actions: action.dest = SUPPRESS glob._add_action(action) @@ -252,7 +252,10 @@ class ActionsMapParser(BaseActionsMapParser): return [name, full] return [name] - def add_global_parser(self, **kwargs): + def has_global_parser(self): + return True + + def get_global_parser(self, **kwargs): if not self._global_parser: self._global_parser = self._parser.add_argument_group( "global arguments") From 2c368596e9a84130cf79f779a83592966fff36b4 Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 23 Jul 2017 04:44:13 +0200 Subject: [PATCH 23/34] [mod] remove one indirection level --- moulinette/actionsmap.py | 2 +- moulinette/interfaces/api.py | 3 --- moulinette/interfaces/cli.py | 13 ++++--------- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index 9f8da1f5..5c468a42 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -605,7 +605,7 @@ class ActionsMap(object): if top_parser.has_global_parser(): # GLOBAL_SECTION = '_global' - _add_arguments(GLOBAL_SECTION, top_parser.get_global_parser(), + _add_arguments(GLOBAL_SECTION, top_parser.global_parser, _global['arguments']) # category_name is stuff like "user", "domain", "hooks"... diff --git a/moulinette/interfaces/api.py b/moulinette/interfaces/api.py index 345624b1..3147dcf2 100644 --- a/moulinette/interfaces/api.py +++ b/moulinette/interfaces/api.py @@ -548,9 +548,6 @@ class ActionsMapParser(BaseActionsMapParser): return [name.replace('--', '@', 1)] return [name.replace('-', '@', 1)] - def get_global_parser(self, **kwargs): - raise AttributeError("global arguments are not managed") - def add_category_parser(self, name, **kwargs): return self diff --git a/moulinette/interfaces/cli.py b/moulinette/interfaces/cli.py index 6a85f7a5..33468f00 100644 --- a/moulinette/interfaces/cli.py +++ b/moulinette/interfaces/cli.py @@ -231,14 +231,15 @@ class ActionsMapParser(BaseActionsMapParser): self._parser = parser or ExtendedArgumentParser() self._subparsers = self._parser.add_subparsers(**subparser_kwargs) - self._global_parser = parent._global_parser if parent else None + self.global_parser = parent.global_parser if parent else None if top_parser: + self.global_parser = self._parser.add_argument_group("global arguments") + # Append each top parser action to the global group - glob = self.get_global_parser() for action in top_parser._actions: action.dest = SUPPRESS - glob._add_action(action) + self.global_parser._add_action(action) # Implement virtual properties @@ -255,12 +256,6 @@ class ActionsMapParser(BaseActionsMapParser): def has_global_parser(self): return True - def get_global_parser(self, **kwargs): - if not self._global_parser: - self._global_parser = self._parser.add_argument_group( - "global arguments") - return self._global_parser - def add_category_parser(self, name, category_help=None, **kwargs): """Add a parser for a category From e65e6f59906b41815402174febd5051df8721336 Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 23 Jul 2017 04:46:01 +0200 Subject: [PATCH 24/34] [mod] we always have actions --- moulinette/actionsmap.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index 5c468a42..6b36d32c 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -611,12 +611,6 @@ class ActionsMap(object): # category_name is stuff like "user", "domain", "hooks"... # category_values is the values of this category (like actions) for category_name, category_values in actionsmap.items(): - if "actions" not in category_values: - # Invalid category without actions - logger.warning("no actions found in category '%s' in " - "namespace '%s'", category_name, namespace) - continue - actions = category_values.pop('actions') # Get category parser From 5194eb75c4b78b09842d70af72017147e15e059a Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 23 Jul 2017 05:13:53 +0200 Subject: [PATCH 25/34] [doc] add more comments --- moulinette/actionsmap.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index 6b36d32c..d0f0f601 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -569,7 +569,11 @@ class ActionsMap(object): # Add arguments to the parser def _add_arguments(tid, parser, arguments): + # parser is argparse._ArgumentGroup for top_parser + # or ExtendedArgumentParser for other cases + # or maybe something else? for argument_name, argument_options in arguments.items(): + # will adapt arguments name for cli or api context names = top_parser.format_arg_names(str(argument_name), argument_options.pop('full', None)) From 8d94e1aa434c8d42923fc8e17ebcd6f20a81447d Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 23 Jul 2017 05:19:47 +0200 Subject: [PATCH 26/34] [mod] simplify code again --- moulinette/actionsmap.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index d0f0f601..2faa6993 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -626,14 +626,6 @@ class ActionsMap(object): arguments = action_options.pop('arguments', {}) tid = (namespace, category_name, action_name) - if 'configuration' in action_options: - configuration = action_options.pop('configuration') - _set_conf = lambda p: p.set_conf(tid, configuration) - - else: - # No action configuration - _set_conf = lambda p: False - try: # Get action parser action_parser = category_parser.add_action_parser(action_name, tid, **action_options) @@ -648,6 +640,9 @@ class ActionsMap(object): # Store action identifier and add arguments action_parser.set_defaults(_tid=tid) _add_arguments(tid, action_parser, arguments) - _set_conf(category_parser) + + if 'configuration' in action_options: + configuration = action_options.pop('configuration') + category_parser.set_conf(tid, configuration) return top_parser From 66111c9b8c77ae0015839b97e7c1361537c8e4f2 Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 23 Jul 2017 05:21:30 +0200 Subject: [PATCH 27/34] [mod] case never happen --- moulinette/actionsmap.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index 2faa6993..016566d6 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -632,10 +632,6 @@ class ActionsMap(object): except AttributeError: # No parser for the action continue - except ValueError as e: - logger.warning("cannot add action (%s, %s, %s): %s", - namespace, category_name, action_name, e) - continue # Store action identifier and add arguments action_parser.set_defaults(_tid=tid) From f595c37a4a1af7039118d3f9631ad51815f620c1 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 23 Jul 2017 09:13:53 -0400 Subject: [PATCH 28/34] Adding basic subcategory loop --- moulinette/actionsmap.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index 016566d6..35be7ac6 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -615,7 +615,16 @@ class ActionsMap(object): # category_name is stuff like "user", "domain", "hooks"... # category_values is the values of this category (like actions) for category_name, category_values in actionsmap.items(): - actions = category_values.pop('actions') + + if "actions" in category_values: + actions = category_values.pop('actions') + else: + actions = {} + + if "subcategories" in category_values: + subcategories = category_values.pop('subcategories') + else: + subcategories = {} # Get category parser category_parser = top_parser.add_category_parser(category_name, **category_values) @@ -641,4 +650,12 @@ class ActionsMap(object): configuration = action_options.pop('configuration') category_parser.set_conf(tid, configuration) + # subcategory_name is like "cert" in "domain cert status" + # subcategory_values is the values of this subcategory (like actions) + for subcategory_name, subcategory_values in subcategories.items(): + + print subcategory_name + print subcategory_values + + return top_parser From c01cef3147ebf4e6b5c2f48ea652c8ad5aa11763 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 23 Jul 2017 09:17:27 -0400 Subject: [PATCH 29/34] Adding actions to subcategory --- moulinette/actionsmap.py | 27 +++++++++++++++++++++++++++ moulinette/interfaces/cli.py | 15 +++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index 35be7ac6..2a02cedd 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -657,5 +657,32 @@ class ActionsMap(object): print subcategory_name print subcategory_values + actions = subcategory_values.pop('actions') + + # Get subcategory parser + subcategory_parser = category_parser.add_subcategory_parser(subcategory_name, **subcategory_values) + + # action_name is like "status" of "domain cert status" + # action_options are the values + for action_name, action_options in actions.items(): + arguments = action_options.pop('arguments', {}) + tid = (namespace, category_name, subcategory_name, action_name) + + try: + # Get action parser + action_parser = subcategory_parser.add_action_parser(action_name, tid, **action_options) + except AttributeError: + # No parser for the action + continue + + # Store action identifier and add arguments + action_parser.set_defaults(_tid=tid) + _add_arguments(tid, action_parser, arguments) + + if 'configuration' in action_options: + configuration = action_options.pop('configuration') + subcategory_parser.set_conf(tid, configuration) + + return top_parser diff --git a/moulinette/interfaces/cli.py b/moulinette/interfaces/cli.py index 33468f00..433da026 100644 --- a/moulinette/interfaces/cli.py +++ b/moulinette/interfaces/cli.py @@ -271,6 +271,21 @@ class ActionsMapParser(BaseActionsMapParser): 'title': "actions", 'required': True }) + def add_subcategory_parser(self, name, subcategory_help=None, **kwargs): + """Add a parser for a subcategory + + Keyword arguments: + - subcategory_help -- A brief description for the category + + Returns: + A new ActionsMapParser object for the category + + """ + parser = self._subparsers.add_parser(name, help=subcategory_help, **kwargs) + return self.__class__(self, parser, { + 'title': "actions", 'required': True + }) + def add_action_parser(self, name, tid, action_help=None, deprecated=False, deprecated_alias=[], **kwargs): """Add a parser for an action From 30d6b3056920a6ce59754efda26aba4fb8e34f1f Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 23 Jul 2017 09:25:33 -0400 Subject: [PATCH 30/34] Removing debug prints --- moulinette/actionsmap.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index 2a02cedd..6390910f 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -654,9 +654,6 @@ class ActionsMap(object): # subcategory_values is the values of this subcategory (like actions) for subcategory_name, subcategory_values in subcategories.items(): - print subcategory_name - print subcategory_values - actions = subcategory_values.pop('actions') # Get subcategory parser From 4b27bf6e83a4f166ad7cbe3246dd8d9f8c4ee4b5 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 23 Jul 2017 09:34:32 -0400 Subject: [PATCH 31/34] Correctly call actions inside subcategories --- moulinette/actionsmap.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index 6390910f..798b6a08 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -453,8 +453,16 @@ class ActionsMap(object): return arguments.get(TO_RETURN_PROP) # Retrieve action information - namespace, category, action = tid - func_name = '%s_%s' % (category, action.replace('-', '_')) + if len(tid) == 4: + namespace, category, subcategory, action = tid + func_name = '%s_%s_%s' % (category, subcategory, action.replace('-', '_')) + full_action_name = "%s.%s.%s.%s" % (namespace, category, subcategory, action) + else: + assert len(tid) == 3 + namespace, category, action = tid + subcategory = None + func_name = '%s_%s' % (category, action.replace('-', '_')) + full_action_name = "%s.%s.%s" % (namespace, category, action) # Lock the moulinette for the namespace with MoulinetteLock(namespace, timeout): @@ -465,17 +473,17 @@ class ActionsMap(object): func = getattr(mod, func_name) except (AttributeError, ImportError): logger.exception("unable to load function %s.%s.%s", - namespace, category, func_name) + namespace, func_name) raise MoulinetteError(errno.EIO, m18n.g('error_see_log')) else: log_id = start_action_logging() if logger.isEnabledFor(logging.DEBUG): # Log arguments in debug mode only for safety reasons - logger.info('processing action [%s]: %s.%s.%s with args=%s', - log_id, namespace, category, action, arguments) + logger.info('processing action [%s]: %s with args=%s', + log_id, full_action_name, arguments) else: - logger.info('processing action [%s]: %s.%s.%s', - log_id, namespace, category, action) + logger.info('processing action [%s]: %s', + log_id, full_action_name) # Load translation and process the action m18n.load_namespace(namespace) From 6a6fb89fd0aba3c9d4cd694478de418a0ba0068a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 23 Jul 2017 15:47:20 -0400 Subject: [PATCH 32/34] Dirty hack to be able to display 'actions' and 'subcategories' in different section in --help --- moulinette/interfaces/__init__.py | 64 ++++++++++++++++++++++++++++++- moulinette/interfaces/cli.py | 11 ++++-- 2 files changed, 70 insertions(+), 5 deletions(-) diff --git a/moulinette/interfaces/__init__.py b/moulinette/interfaces/__init__.py index 6d5d332b..31b83a7c 100644 --- a/moulinette/interfaces/__init__.py +++ b/moulinette/interfaces/__init__.py @@ -5,7 +5,8 @@ import os import errno import logging import argparse -from collections import deque +import copy +from collections import deque, OrderedDict from moulinette.core import (init_authenticator, MoulinetteError) @@ -460,7 +461,7 @@ class _ExtendedSubParsersAction(argparse._SubParsersAction): self.required = required self._deprecated_command_map = {} - def add_parser(self, name, **kwargs): + def add_parser(self, name, type_=None, **kwargs): deprecated = kwargs.pop('deprecated', False) deprecated_alias = kwargs.pop('deprecated_alias', []) @@ -477,6 +478,8 @@ class _ExtendedSubParsersAction(argparse._SubParsersAction): self._deprecated_command_map[command] = name self._name_parser_map[command] = parser + parser.type = type_ + return parser def __call__(self, parser, namespace, values, option_string=None): @@ -557,6 +560,63 @@ class ExtendedArgumentParser(argparse.ArgumentParser): action, arg_strings) return value + # Adapted from : + # https://github.com/python/cpython/blob/af26c15110b76195e62a06d17e39176d42c0511c/Lib/argparse.py#L2293-L2314 + def format_help(self): + formatter = self._get_formatter() + + # usage + formatter.add_usage(self.usage, self._actions, + self._mutually_exclusive_groups) + + # description + formatter.add_text(self.description) + + # positionals, optionals and user-defined groups + for action_group in self._action_groups: + + # Dirty hack to separate 'subcommands' + # into 'actions' and 'subcategories' + if action_group.title == "subcommands": + + # Make a copy of the "action group actions"... + choice_actions = action_group._group_actions[0]._choices_actions + actions_subparser = copy.copy(action_group._group_actions[0]) + subcategories_subparser = copy.copy(action_group._group_actions[0]) + + # Filter "action"-type and "subcategory"-type commands + actions_subparser.choices = OrderedDict([(k,v) for k,v in actions_subparser.choices.items() if v.type == "action"]) + subcategories_subparser.choices = OrderedDict([(k,v) for k,v in subcategories_subparser.choices.items() if v.type == "subcategory"]) + + actions_choices = actions_subparser.choices.keys() + subcategories_choices = subcategories_subparser.choices.keys() + + actions_subparser._choices_actions = [ c for c in choice_actions if c.dest in actions_choices ] + subcategories_subparser._choices_actions = [ c for c in choice_actions if c.dest in subcategories_choices ] + + # Display each section (actions and subcategories) + if actions_choices != []: + formatter.start_section("actions") + formatter.add_arguments([actions_subparser]) + formatter.end_section() + + if subcategories_choices != []: + formatter.start_section("subcategories") + formatter.add_arguments([subcategories_subparser]) + formatter.end_section() + + else: + formatter.start_section(action_group.title) + formatter.add_text(action_group.description) + formatter.add_arguments(action_group._group_actions) + formatter.end_section() + + # epilog + formatter.add_text(self.epilog) + + # determine help from format above + return formatter.format_help() + # This is copy-pasta from the original argparse.HelpFormatter : # https://github.com/python/cpython/blob/1e73dbbc29c96d0739ffef92db36f63aa1aa30da/Lib/argparse.py#L293-L383 diff --git a/moulinette/interfaces/cli.py b/moulinette/interfaces/cli.py index 433da026..e6c23b7d 100644 --- a/moulinette/interfaces/cli.py +++ b/moulinette/interfaces/cli.py @@ -268,7 +268,7 @@ class ActionsMapParser(BaseActionsMapParser): """ parser = self._subparsers.add_parser(name, help=category_help, **kwargs) return self.__class__(self, parser, { - 'title': "actions", 'required': True + 'title': "subcommands", 'required': True }) def add_subcategory_parser(self, name, subcategory_help=None, **kwargs): @@ -281,7 +281,10 @@ class ActionsMapParser(BaseActionsMapParser): A new ActionsMapParser object for the category """ - parser = self._subparsers.add_parser(name, help=subcategory_help, **kwargs) + parser = self._subparsers.add_parser(name, + type_="subcategory", + help=subcategory_help, + **kwargs) return self.__class__(self, parser, { 'title': "actions", 'required': True }) @@ -299,7 +302,9 @@ class ActionsMapParser(BaseActionsMapParser): A new ExtendedArgumentParser object for the action """ - return self._subparsers.add_parser(name, help=action_help, + return self._subparsers.add_parser(name, + type_="action", + help=action_help, deprecated=deprecated, deprecated_alias=deprecated_alias) From 74281816b479274a3cae24af221d9e53a3511790 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 23 Jul 2017 16:16:10 -0400 Subject: [PATCH 33/34] Add subcategory stuff for the API to work --- moulinette/interfaces/api.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/moulinette/interfaces/api.py b/moulinette/interfaces/api.py index 3147dcf2..392a0d20 100644 --- a/moulinette/interfaces/api.py +++ b/moulinette/interfaces/api.py @@ -551,6 +551,9 @@ class ActionsMapParser(BaseActionsMapParser): def add_category_parser(self, name, **kwargs): return self + def add_subcategory_parser(self, name, **kwargs): + return self + def add_action_parser(self, name, tid, api=None, **kwargs): """Add a parser for an action From bb3f685c903cfd2c8cfc6d8fd92b914d6b440b80 Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Thu, 3 Aug 2017 15:28:12 +0200 Subject: [PATCH 34/34] [fix] too many %s in string formatting --- moulinette/actionsmap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index a4e00d60..8edacc7e 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -472,7 +472,7 @@ class ActionsMap(object): fromlist=[func_name]) func = getattr(mod, func_name) except (AttributeError, ImportError): - logger.exception("unable to load function %s.%s.%s", + logger.exception("unable to load function %s.%s", namespace, func_name) raise MoulinetteError(errno.EIO, m18n.g('error_see_log')) else: