Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for struct with list param shorthand #882

Merged
merged 1 commit into from
Aug 19, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 56 additions & 37 deletions awscli/argprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ class ParamShorthand(object):
SHORTHAND_SHAPES = {
'structure(scalars)': '_key_value_parse',
'structure(scalar)': '_special_key_value_parse',
'structure(list-scalar, scalar)': '_struct_scalar_list_parse',
'map-scalar': '_key_value_parse',
'list-structure(scalar)': '_list_scalar_parse',
'list-structure(scalars)': '_list_key_value_parse',
Expand Down Expand Up @@ -225,42 +226,51 @@ def add_example_fn(self, arg_name, help_command, **kwargs):
def _list_scalar_list_parse(self, param, value):
# Think something like ec2.DescribeInstances.Filters.
# We're looking for key=val1,val2,val3,key2=val1,val2.
parsed = []

for v in value:
struct = self._struct_scalar_list_parse(param.members, v)
parsed.append(struct)

return parsed

def _struct_scalar_list_parse(self, param, value):
# Create a mapping of argument name -> argument object
args = {}
for arg in param.members.members:
for arg in param.members:
# Arg name -> arg object lookup
args[arg.name] = arg
parsed = []
for v in value:
parts = self._split_on_commas(v)
current_parsed = {}
current_key = None
for part in parts:
current = part.split('=', 1)
if len(current) == 2:
# This is a key/value pair.
current_key = current[0].strip()
if current_key not in args:
raise ParamUnknownKeyError(param, current_key,
args.keys())
current_value = unpack_scalar_cli_arg(args[current_key],
current[1].strip())
if args[current_key].type == 'list':
current_parsed[current_key] = current_value.split(',')
else:
current_parsed[current_key] = current_value
elif current_key is not None:
# This is a value which we associate with the current_key,
# so key1=val1,val2
# ^
# |
# val2 is associated with key1.
current_value = unpack_scalar_cli_arg(args[current_key],
current[0])
current_parsed[current_key].append(current_value)

parts = self._split_on_commas(value)
current_parsed = {}
current_key = None
for part in parts:
current = part.split('=', 1)
if len(current) == 2:
# This is a key/value pair.
current_key = current[0].strip()
if current_key not in args:
raise ParamUnknownKeyError(param, current_key,
args.keys())
current_value = unpack_scalar_cli_arg(args[current_key],
current[1].strip())
if args[current_key].type == 'list':
current_parsed[current_key] = current_value.split(',')
else:
raise ParamSyntaxError(part)
parsed.append(current_parsed)
return parsed
current_parsed[current_key] = current_value
elif current_key is not None:
# This is a value which we associate with the current_key,
# so key1=val1,val2
# ^
# |
# val2 is associated with key1.
current_value = unpack_scalar_cli_arg(args[current_key],
current[0])
current_parsed[current_key].append(current_value)
else:
raise ParamSyntaxError(part)

return current_parsed

def _list_scalar_parse(self, param, value):
single_param = param.members.members[0]
Expand Down Expand Up @@ -325,11 +335,7 @@ def _create_name_to_params(self, param):
elif param.type == 'map' and hasattr(param.keys, 'enum'):
return dict([(v, None) for v in param.keys.enum])

def _docs_list_scalar_list_parse(self, param):
s = ('Key value pairs, where values are separated by commas, '
'and multiple pairs are separated by spaces.\n')
s += '%s ' % param.cli_name
inner_params = param.members.members
def _struct_list_scalar_doc_helper(self, param, inner_params):
scalar_params = [p for p in inner_params if p.type in SCALAR_TYPES]
list_params = [p for p in inner_params if p.type == 'list']
pair = ''
Expand All @@ -341,10 +347,23 @@ def _docs_list_scalar_list_parse(self, param):
last_param = list_params[-1]
param_type = last_param.members.type
pair += '%s=%s1,%s2' % (last_param.name, param_type, param_type)
return pair

def _docs_list_scalar_list_parse(self, param):
s = ('Key value pairs, where values are separated by commas, '
'and multiple pairs are separated by spaces.\n')
s += '%s ' % param.cli_name
pair = self._struct_list_scalar_doc_helper(param, param.members.members)
pair += ' %s' % pair
s += pair
return s

def _docs_struct_scalar_list_parse(self, param):
s = ('Key value pairs, where values are separated by commas.\n')
s += '%s ' % param.cli_name
s += self._struct_list_scalar_doc_helper(param, param.members)
return s

def _docs_list_scalar_parse(self, param):
name = param.members.members[0].name
return '%s %s1 %s2 %s3' % (param.cli_name, name, name, name)
Expand Down
75 changes: 75 additions & 0 deletions tests/unit/test_argprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ def assert_shape_type(self, spec, expected_type):
actual_structure = detect_shape_structure(p)
self.assertEqual(actual_structure, expected_type)

def assert_custom_shape_type(self, schema, expected_type):
argument = CustomArgument('test', schema=schema)
argument.create_argument_object()
actual_structure = detect_shape_structure(argument.argument_object)
self.assertEqual(actual_structure, expected_type)

def test_detect_scalar(self):
self.assert_shape_type('iam.AddRoleToInstanceProfile.RoleName',
'scalar')
Expand Down Expand Up @@ -115,6 +121,22 @@ def test_map_scalar(self):
self.assert_shape_type(
'sqs.SetQueueAttributes.Attributes', 'map-scalar')

def test_struct_list_scalar(self):
self.assert_custom_shape_type({
"type": "object",
"properties": {
"Consistent": {
"type": "boolean",
},
"Args": {
"type": "array",
"items": {
"type": "string"
}
}
}
}, 'structure(list-scalar, scalar)')


class TestParamShorthand(BaseArgProcessTest):
def setUp(self):
Expand Down Expand Up @@ -279,6 +301,30 @@ def test_list_structure_scalars_2(self):
])
self.assertEqual(simplified, expected)

def test_struct_list_scalars(self):
schema = {
"type": "object",
"properties": {
"Consistent": {
"type": "boolean",
},
"Args": {
"type": "array",
"items": {
"type": "string"
}
}
}
}

argument = CustomArgument('test', schema=schema)
argument.create_argument_object()
p = argument.argument_object

returned = self.simplify(p, 'Consistent=true,Args=foo1,foo2')
self.assertEqual(returned, {'Consistent': True,
'Args': ['foo1', 'foo2']})

def test_keyval_with_long_values(self):
p = self.get_param_object(
'dynamodb.UpdateTable.ProvisionedThroughput')
Expand Down Expand Up @@ -440,6 +486,35 @@ def test_gen_list_structure_list_scalar_scalar_docs(self):
self.assertIn('Name=string1,Values=string1,string2 '
'Name=string1,Values=string1,string2', doc_string)

def test_gen_structure_list_scalar_docs(self):
schema = {
"type": "object",
"properties": {
"Consistent": {
"type": "boolean",
},
"Args": {
"type": "array",
"items": {
"type": "string"
}
}
}
}

argument = CustomArgument('test', schema=schema)
argument.create_argument_object()

p = argument.argument_object
help_command = OperationHelpCommand(
self.session, p.operation, None, {p.cli_name: argument},
name='foo', event_class='bar')
help_command.param_shorthand.add_example_fn(p.cli_name, help_command)

doc_string = p.example_fn(p)

self.assertIn('Key value pairs', doc_string)
self.assertIn('Consistent=boolean1,Args=string1,string2', doc_string)

class TestUnpackJSONParams(BaseArgProcessTest):
def setUp(self):
Expand Down