Standardize arguments in the actions map

* Move general_arguments to _global.arguments in the actions map
* Introduce global configuration in the actions map (need implementation!)
* Standardize arguments addition during the parser construction
* Fix action name with '-'
This commit is contained in:
Jerome Lebleu 2014-03-12 00:50:09 +01:00
parent 66f60381e4
commit cdcfa24180
2 changed files with 75 additions and 73 deletions

View file

@ -29,12 +29,14 @@
# #
########################################################################## ##########################################################################
# TODO: Add patern for all this
############################# #############################
# General args # # Global parameters #
############################# #############################
general_arguments: _global:
configuration:
auth:
- api
arguments:
-v: -v:
full: --version full: --version
help: Display moulinette version help: Display moulinette version

View file

@ -19,26 +19,20 @@ class _AMapParser(object):
Each interfaces must implement a parser class derived from this Each interfaces must implement a parser class derived from this
class. It is used to parse the main parts of the actions map (i.e. class. It is used to parse the main parts of the actions map (i.e.
general arguments, categories and actions). global arguments, categories and actions).
""" """
## Optional variables
# Each parser classes can overwrite these variables.
"""Either it will parse general arguments, or not"""
parse_general_arguments = True
## Virtual methods ## Virtual methods
# Each parser classes can implement these methods. # Each parser classes can implement these methods.
@staticmethod @staticmethod
def format_arg_name(name, full): def format_arg_names(name, full):
"""Format argument name """Format argument name
Format agument name depending on its 'full' parameters and return Format agument name depending on its 'full' parameter and return
a list to use it as option string for the argument parser. a list of strings which will be used as name or option strings
for the argument parser.
Keyword arguments: Keyword arguments:
- name -- The argument name - name -- The argument name
@ -51,21 +45,17 @@ class _AMapParser(object):
raise NotImplementedError("derived class '%s' must override this method" % \ raise NotImplementedError("derived class '%s' must override this method" % \
self.__class__.__name__) self.__class__.__name__)
def add_general_parser(self, **kwargs): def add_global_parser(self, **kwargs):
"""Add a parser for general arguments """Add a parser for global arguments
Create and return an argument parser for general arguments. Create and return an argument parser for global arguments.
Returns: Returns:
An ArgumentParser based object An ArgumentParser based object
""" """
if not self.parse_general_arguments: raise NotImplementedError("derived class '%s' must override this method" % \
msg = "doesn't parse general arguments" self.__class__.__name__)
else:
msg = "must override this method"
raise NotImplementedError("derived class '%s' %s" % \
(self.__class__.__name__, msg))
def add_category_parser(self, name, **kwargs): def add_category_parser(self, name, **kwargs):
"""Add a parser for a category """Add a parser for a category
@ -124,12 +114,12 @@ class CLIAMapParser(_AMapParser):
self._subparsers = self._parser.add_subparsers() self._subparsers = self._parser.add_subparsers()
@staticmethod @staticmethod
def format_arg_name(name, full): def format_arg_names(name, full):
if name[0] == '-' and full: if name[0] == '-' and full:
return [name, full] return [name, full]
return [name] return [name]
def add_general_parser(self, **kwargs): def add_global_parser(self, **kwargs):
return self._parser return self._parser
def add_category_parser(self, name, category_help=None, **kwargs): def add_category_parser(self, name, category_help=None, **kwargs):
@ -145,7 +135,7 @@ class CLIAMapParser(_AMapParser):
parser = self._subparsers.add_parser(name, help=category_help) parser = self._subparsers.add_parser(name, help=category_help)
return self.__class__(parser) return self.__class__(parser)
def add_action_parser(self, name, action_help, **kwargs): def add_action_parser(self, name, action_help=None, **kwargs):
"""Add a parser for an action """Add a parser for an action
Keyword arguments: Keyword arguments:
@ -234,7 +224,6 @@ class APIAMapParser(_AMapParser):
"""Actions map's API Parser """Actions map's API Parser
""" """
parse_general_arguments = False
def __init__(self): def __init__(self):
self._parsers = {} # dict({(method, path): _HTTPArgumentParser}) self._parsers = {} # dict({(method, path): _HTTPArgumentParser})
@ -248,7 +237,7 @@ class APIAMapParser(_AMapParser):
## Implement virtual methods ## Implement virtual methods
@staticmethod @staticmethod
def format_arg_name(name, full): def format_arg_names(name, full):
if name[0] != '-': if name[0] != '-':
return [name] return [name]
if full: if full:
@ -257,6 +246,9 @@ class APIAMapParser(_AMapParser):
return [name.replace('--', '@', 1)] return [name.replace('--', '@', 1)]
return [name.replace('-', '@', 1)] return [name.replace('-', '@', 1)]
def add_global_parser(self, **kwargs):
raise AttributeError("global arguments are not managed")
def add_category_parser(self, name, **kwargs): def add_category_parser(self, name, **kwargs):
return self return self
@ -271,17 +263,17 @@ class APIAMapParser(_AMapParser):
""" """
if not api: if not api:
return None raise AttributeError("the action '%s' doesn't provide api access" % name)
# Validate action route # Validate action route
m = re.match('(GET|POST|PUT|DELETE) (/\S+)', api) m = re.match('(GET|POST|PUT|DELETE) (/\S+)', api)
if not m: if not m:
return None raise ValueError("the action '%s' doesn't provide api access" % name)
# Check if a parser already exists for the route # Check if a parser already exists for the route
key = (m.group(1), m.group(2)) key = (m.group(1), m.group(2))
if key in self.routes: if key in self.routes:
raise ValueError("A parser for '%s' already exists" % key) raise AttributeError("a parser for '%s' already exists" % key)
# Create and append parser # Create and append parser
parser = _HTTPArgumentParser() parser = _HTTPArgumentParser()
@ -612,8 +604,8 @@ class ActionsMap(object):
arguments[an] = self.extraparser.parse(an, arguments[an], parameters) arguments[an] = self.extraparser.parse(an, arguments[an], parameters)
# Retrieve action information # Retrieve action information
namespace, category, action = arguments.pop('_info') namespace, category, action = arguments.pop('_id')
func_name = '%s_%s' % (category, action) func_name = '%s_%s' % (category, action.replace('-', '_'))
# Lock the moulinette for the namespace # Lock the moulinette for the namespace
with MoulinetteLock(namespace, timeout): with MoulinetteLock(namespace, timeout):
@ -690,67 +682,75 @@ class ActionsMap(object):
An interface relevant's parser object An interface relevant's parser object
""" """
# Define setter for extra parameters ## Get extra parameters
if not self.use_cache: if not self.use_cache:
_set_extra = lambda an, e: self.extraparser.validate(an, e) _get_extra = lambda an, e: self.extraparser.validate(an, e)
else: else:
_set_extra = lambda an, e: e _get_extra = lambda an, e: e
## Add arguments to the parser
def _add_arguments(parser, arguments):
extras = {}
for argn, argp in arguments.items():
names = top_parser.format_arg_names(argn,
argp.pop('full', None))
extra = argp.pop('extra', None)
arg = parser.add_argument(*names, **argp)
if extra:
extras[arg.dest] = _get_extra(arg.dest, extra)
parser.set_defaults(_extra=extras)
# Instantiate parser # Instantiate parser
top_parser = self._parser_class() top_parser = self._parser_class()
# Iterate over actions map namespaces # Iterate over actions map namespaces
for n, actionsmap in actionsmaps.items(): for n, actionsmap in actionsmaps.items():
if 'general_arguments' in actionsmap: # Retrieve global parameters
# Parse general arguments _global = actionsmap.pop('_global', {})
if top_parser.parse_general_arguments:
parser = top_parser.add_general_parser()
for an, ap in actionsmap['general_arguments'].items():
# Replace version number
version = ap.get('version', None)
if version:
ap['version'] = version.replace('%version%',
__version__)
argname = top_parser.format_arg_name(an, ap.pop('full', None))
parser.add_argument(*argname, **ap)
del actionsmap['general_arguments']
# Parse categories # -- Parse global configuration
# TODO
# -- 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(parser, _global['arguments'])
# -- Parse categories
for cn, cp in actionsmap.items(): for cn, cp in actionsmap.items():
try: try:
actions = cp.pop('actions') actions = cp.pop('actions')
except KeyError: except KeyError:
# Invalid category without actions
continue continue
# Add category parser # Get category parser
cat_parser = top_parser.add_category_parser(cn, **cp) cat_parser = top_parser.add_category_parser(cn, **cp)
# Parse actions # -- Parse actions
for an, ap in actions.items(): for an, ap in actions.items():
arguments = ap.pop('arguments', {}) arguments = ap.pop('arguments', {})
# Add action parser
parser = cat_parser.add_action_parser(an, **ap)
try: try:
# Store action information # Get action parser
parser.set_defaults(_info=(n, cn, an)) parser = cat_parser.add_action_parser(an, **ap)
except AttributeError: except AttributeError:
# No parser for the action # No parser for the action
break continue
except ValueError:
# Add action arguments # TODO: Log error
for argn, argp in arguments.items(): continue
name = top_parser.format_arg_name(argn, argp.pop('full', None)) else:
extra = argp.pop('extra', None) # Store action identification and add arguments
parser.set_defaults(_id=(n, cn, an))
arg = parser.add_argument(*name, **argp) _add_arguments(parser, arguments)
if not extra:
continue
# Store extra parameters
extras = parser.get_default('_extra') or {}
extras[arg.dest] = _set_extra(arg.dest, extra)
parser.set_defaults(_extra=extras)
return top_parser return top_parser