Skip to content

Instantly share code, notes, and snippets.

@xZise
Last active August 29, 2015 14:11
Show Gist options
  • Save xZise/75c482a5871f0b1cb55b to your computer and use it in GitHub Desktop.
Save xZise/75c482a5871f0b1cb55b to your computer and use it in GitHub Desktop.
Parser for advanced argparse values
#!/usr/bin/python
# -*- coding: utf-8 -*-
from collections import OrderedDict
def parse_value(value, parameters, allow_unknown=False):
"""
Parse the value of an argument into subarguments.
The value is basically a comma-separated string. Each comma-separated value
can be named which is separated by an equals sign. It is possible to escape
both commas and equal signs. Any equal signs after a name must not be
escaped. The value may be unnamed but only if there aren't any named
parameters before it.
It must contain predefined parameters with a name and a default value if
the parameter is optional.
>>> params = OrderedDict((('title', (False, None)), ('type', (True, 'default'))))
>>> parse_value('foo,bar', params)
{'title': 'foo', 'type': 'bar'}
>>> parse_value('foo,type=bar', params)
{'title': 'foo', 'type': 'bar'}
>>> parse_value('title=foo,type=bar', params)
{'title': 'foo', 'type': 'bar'}
>>> parse_value('foo', params)
{'title': 'foo', 'type': 'default'}
>>> parse_value('foo,', params)
{'title': 'foo', 'type': ''}
# TODO: Those are invalid: Check their exceptions
>>> parse_value('title=foo,bar', params)
>>> parse_value('title=foo,title=bar', params)
>>> parse_value('foo,title=bar', params)
>>> parse_value('foo,=bar', params)
>>> parse_value('foo,unknown=bar', params)
@param value: The value which should be parsed
@type value: str
@param parameters: The predefined parameters mapping a name to a tuple
containing if it's optional and a default value when it's optional.
@type parameters: OrderedDict
@param allow_unknown: Allow values inside of value which are not defined
in parameters.
@type allow_unknown: bool
"""
name = None
escaped = False
buffer_value = ''
result = {}
parameter_number = 0
value += ','
for i in range(len(value)):
if not escaped:
if value[i] == '\\':
escaped = True
continue
elif value[i] == '=' and name is None:
name = buffer_value
buffer_value = ''
if not name:
# TODO: More specific exception
raise Exception('No parameter name was given')
elif name not in parameters and not allow_unknown:
raise Exception('The parameter {0} is not known.'.format(name))
continue
elif value[i] == ',':
if name is None:
if parameter_number is False:
# TODO: Emulate closer argparse
raise Exception('Positional parameter after keyword')
name = parameters[parameter_number][0]
parameter_number += 1
else:
parameter_number = False
if name in result:
raise Exception('The value for {0} is already defined.'.format(name))
result[name] = buffer_value
buffer_value = ''
name = None
continue
buffer_value += value[i]
escaped = False
undefined = [n for n, p in parameters.items() if not p[0] and n not in result]
if undefined:
raise Exception('The required parameter(s) "{0}" are not defined.'.format('", "'.join(undefined)))
for default_name, default_value in parameters.items():
if default_value[0] and default_name not in result:
result[default_name] = default_value[1]
return result
import sys
import traceback
parameters_exmpl = OrderedDict((('title', (False, None)), ('type', (True, 'default'))))
for arg in sys.argv[1:]:
print('Parsing "{}":'.format(arg))
for allow in [False, True]:
print('Allow unknown: {}'.format(allow))
try:
for result_line in sorted(parse_value(arg, parameters_exmpl, allow).items()):
print(' {}: {}'.format(*result_line))
except Exception as e:
print('!! Exception occured')
traceback.print_exc()
print('')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment