mirror of
https://github.com/YunoHost/moulinette.git
synced 2024-09-03 20:06:31 +02:00
Merge pull request #143 from YunoHost/support-subcategories
[enh] Support subcategories
This commit is contained in:
commit
5bf2f46f47
4 changed files with 144 additions and 12 deletions
|
@ -456,8 +456,16 @@ class ActionsMap(object):
|
||||||
return arguments.get(TO_RETURN_PROP)
|
return arguments.get(TO_RETURN_PROP)
|
||||||
|
|
||||||
# Retrieve action information
|
# Retrieve action information
|
||||||
namespace, category, action = tid
|
if len(tid) == 4:
|
||||||
func_name = '%s_%s' % (category, action.replace('-', '_'))
|
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
|
# Lock the moulinette for the namespace
|
||||||
with MoulinetteLock(namespace, timeout):
|
with MoulinetteLock(namespace, timeout):
|
||||||
|
@ -467,18 +475,18 @@ class ActionsMap(object):
|
||||||
fromlist=[func_name])
|
fromlist=[func_name])
|
||||||
func = getattr(mod, func_name)
|
func = getattr(mod, func_name)
|
||||||
except (AttributeError, ImportError):
|
except (AttributeError, ImportError):
|
||||||
logger.exception("unable to load function %s.%s.%s",
|
logger.exception("unable to load function %s.%s",
|
||||||
namespace, category, func_name)
|
namespace, func_name)
|
||||||
raise MoulinetteError(errno.EIO, m18n.g('error_see_log'))
|
raise MoulinetteError(errno.EIO, m18n.g('error_see_log'))
|
||||||
else:
|
else:
|
||||||
log_id = start_action_logging()
|
log_id = start_action_logging()
|
||||||
if logger.isEnabledFor(logging.DEBUG):
|
if logger.isEnabledFor(logging.DEBUG):
|
||||||
# Log arguments in debug mode only for safety reasons
|
# Log arguments in debug mode only for safety reasons
|
||||||
logger.info('processing action [%s]: %s.%s.%s with args=%s',
|
logger.info('processing action [%s]: %s with args=%s',
|
||||||
log_id, namespace, category, action, arguments)
|
log_id, full_action_name, arguments)
|
||||||
else:
|
else:
|
||||||
logger.info('processing action [%s]: %s.%s.%s',
|
logger.info('processing action [%s]: %s',
|
||||||
log_id, namespace, category, action)
|
log_id, full_action_name)
|
||||||
|
|
||||||
# Load translation and process the action
|
# Load translation and process the action
|
||||||
m18n.load_namespace(namespace)
|
m18n.load_namespace(namespace)
|
||||||
|
@ -595,7 +603,16 @@ class ActionsMap(object):
|
||||||
# category_name is stuff like "user", "domain", "hooks"...
|
# category_name is stuff like "user", "domain", "hooks"...
|
||||||
# category_values is the values of this category (like actions)
|
# category_values is the values of this category (like actions)
|
||||||
for category_name, category_values in actionsmap.items():
|
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
|
# Get category parser
|
||||||
category_parser = top_parser.add_category_parser(category_name,
|
category_parser = top_parser.add_category_parser(category_name,
|
||||||
|
@ -625,4 +642,36 @@ class ActionsMap(object):
|
||||||
if 'configuration' in action_options:
|
if 'configuration' in action_options:
|
||||||
category_parser.set_conf(tid, action_options['configuration'])
|
category_parser.set_conf(tid, action_options['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():
|
||||||
|
|
||||||
|
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)
|
||||||
|
action_parser.add_arguments(arguments,
|
||||||
|
extraparser=self.extraparser,
|
||||||
|
format_arg_names=top_parser.format_arg_names,
|
||||||
|
validate_extra=validate_extra)
|
||||||
|
|
||||||
|
if 'configuration' in action_options:
|
||||||
|
category_parser.set_conf(tid, action_options['configuration'])
|
||||||
|
|
||||||
return top_parser
|
return top_parser
|
||||||
|
|
|
@ -5,7 +5,8 @@ import os
|
||||||
import errno
|
import errno
|
||||||
import logging
|
import logging
|
||||||
import argparse
|
import argparse
|
||||||
from collections import deque
|
import copy
|
||||||
|
from collections import deque, OrderedDict
|
||||||
|
|
||||||
from moulinette import msignals, msettings, m18n
|
from moulinette import msignals, msettings, m18n
|
||||||
from moulinette.core import (init_authenticator, MoulinetteError)
|
from moulinette.core import (init_authenticator, MoulinetteError)
|
||||||
|
@ -461,7 +462,7 @@ class _ExtendedSubParsersAction(argparse._SubParsersAction):
|
||||||
self.required = required
|
self.required = required
|
||||||
self._deprecated_command_map = {}
|
self._deprecated_command_map = {}
|
||||||
|
|
||||||
def add_parser(self, name, **kwargs):
|
def add_parser(self, name, type_=None, **kwargs):
|
||||||
deprecated = kwargs.pop('deprecated', False)
|
deprecated = kwargs.pop('deprecated', False)
|
||||||
deprecated_alias = kwargs.pop('deprecated_alias', [])
|
deprecated_alias = kwargs.pop('deprecated_alias', [])
|
||||||
|
|
||||||
|
@ -478,6 +479,8 @@ class _ExtendedSubParsersAction(argparse._SubParsersAction):
|
||||||
self._deprecated_command_map[command] = name
|
self._deprecated_command_map[command] = name
|
||||||
self._name_parser_map[command] = parser
|
self._name_parser_map[command] = parser
|
||||||
|
|
||||||
|
parser.type = type_
|
||||||
|
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def __call__(self, parser, namespace, values, option_string=None):
|
def __call__(self, parser, namespace, values, option_string=None):
|
||||||
|
@ -576,6 +579,63 @@ class ExtendedArgumentParser(argparse.ArgumentParser):
|
||||||
action, arg_strings)
|
action, arg_strings)
|
||||||
return value
|
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 :
|
# This is copy-pasta from the original argparse.HelpFormatter :
|
||||||
# https://github.com/python/cpython/blob/1e73dbbc29c96d0739ffef92db36f63aa1aa30da/Lib/argparse.py#L293-L383
|
# https://github.com/python/cpython/blob/1e73dbbc29c96d0739ffef92db36f63aa1aa30da/Lib/argparse.py#L293-L383
|
||||||
|
|
|
@ -579,6 +579,9 @@ class ActionsMapParser(BaseActionsMapParser):
|
||||||
def add_category_parser(self, name, **kwargs):
|
def add_category_parser(self, name, **kwargs):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def add_subcategory_parser(self, name, **kwargs):
|
||||||
|
return self
|
||||||
|
|
||||||
def add_action_parser(self, name, tid, api=None, **kwargs):
|
def add_action_parser(self, name, tid, api=None, **kwargs):
|
||||||
"""Add a parser for an action
|
"""Add a parser for an action
|
||||||
|
|
||||||
|
|
|
@ -268,6 +268,24 @@ class ActionsMapParser(BaseActionsMapParser):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
parser = self._subparsers.add_parser(name, help=category_help, **kwargs)
|
parser = self._subparsers.add_parser(name, help=category_help, **kwargs)
|
||||||
|
return self.__class__(self, parser, {
|
||||||
|
'title': "subcommands", '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,
|
||||||
|
type_="subcategory",
|
||||||
|
help=subcategory_help,
|
||||||
|
**kwargs)
|
||||||
return self.__class__(self, parser, {
|
return self.__class__(self, parser, {
|
||||||
'title': "actions", 'required': True
|
'title': "actions", 'required': True
|
||||||
})
|
})
|
||||||
|
@ -285,7 +303,9 @@ class ActionsMapParser(BaseActionsMapParser):
|
||||||
A new ExtendedArgumentParser object for the action
|
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=deprecated,
|
||||||
deprecated_alias=deprecated_alias)
|
deprecated_alias=deprecated_alias)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue