def doc_option_example(self, arg_name, help_command, **kwargs): doc = help_command.doc cli_argument = help_command.arg_table[arg_name] if cli_argument.group_name in self._arg_groups: if cli_argument.group_name in self._documented_arg_groups: # Args with group_names (boolean args) don't # need to generate example syntax. return argument_model = cli_argument.argument_model docgen = ParamShorthandDocGen() if docgen.supports_shorthand(cli_argument): # TODO: bcdoc should not know about shorthand syntax. This # should be pulled out into a separate handler in the # awscli.customizations package. example_shorthand_syntax = docgen.generate_shorthand_example(cli_argument) if example_shorthand_syntax is None: # If the shorthand syntax returns a value of None, # this indicates to us that there is no example # needed for this param so we can immediately # return. return doc.style.new_paragraph() doc.write("Shorthand Syntax") doc.style.start_codeblock() for example_line in example_shorthand_syntax.splitlines(): doc.writeln(example_line) doc.style.end_codeblock() if ( argument_model is not None and argument_model.type_name == "list" and argument_model.member.type_name in SCALAR_TYPES ): # A list of scalars is special. While you *can* use # JSON ( ["foo", "bar", "baz"] ), you can also just # use the argparse behavior of space separated lists. # "foo" "bar" "baz". In fact we don't even want to # document the JSON syntax in this case. member = argument_model.member doc.style.new_paragraph() doc.write("Syntax") doc.style.start_codeblock() example_type = self._json_example_value_name(member, include_enum_values=False) doc.write("%s %s ..." % (example_type, example_type)) if isinstance(member, StringShape) and member.enum: # If we have enum values, we can tell the user # exactly what valid values they can provide. self._write_valid_enums(doc, member.enum) doc.style.end_codeblock() doc.style.new_paragraph() elif cli_argument.cli_type_name not in SCALAR_TYPES: doc.style.new_paragraph() doc.write("JSON Syntax") doc.style.start_codeblock() self._json_example(doc, argument_model, stack=[]) doc.style.end_codeblock() doc.style.new_paragraph()
def doc_option_example(self, arg_name, help_command, event_name, **kwargs): service_id, operation_name = \ find_service_and_method_in_event_name(event_name) doc = help_command.doc cli_argument = help_command.arg_table[arg_name] if cli_argument.group_name in self._arg_groups: if cli_argument.group_name in self._documented_arg_groups: # Args with group_names (boolean args) don't # need to generate example syntax. return argument_model = cli_argument.argument_model docgen = ParamShorthandDocGen() if docgen.supports_shorthand(cli_argument.argument_model): example_shorthand_syntax = docgen.generate_shorthand_example( cli_argument, service_id, operation_name) if example_shorthand_syntax is None: # If the shorthand syntax returns a value of None, # this indicates to us that there is no example # needed for this param so we can immediately # return. return if example_shorthand_syntax: doc.style.new_paragraph() doc.write('Shorthand Syntax') doc.style.start_codeblock() for example_line in example_shorthand_syntax.splitlines(): doc.writeln(example_line) doc.style.end_codeblock() if argument_model is not None and argument_model.type_name == 'list' and \ argument_model.member.type_name in SCALAR_TYPES: # A list of scalars is special. While you *can* use # JSON ( ["foo", "bar", "baz"] ), you can also just # use the argparse behavior of space separated lists. # "foo" "bar" "baz". In fact we don't even want to # document the JSON syntax in this case. member = argument_model.member doc.style.new_paragraph() doc.write('Syntax') doc.style.start_codeblock() example_type = self._json_example_value_name( member, include_enum_values=False) doc.write('%s %s ...' % (example_type, example_type)) if isinstance(member, StringShape) and member.enum: # If we have enum values, we can tell the user # exactly what valid values they can provide. self._write_valid_enums(doc, member.enum) doc.style.end_codeblock() doc.style.new_paragraph() elif cli_argument.cli_type_name not in SCALAR_TYPES: doc.style.new_paragraph() doc.write('JSON Syntax') doc.style.start_codeblock() self._json_example(doc, argument_model, stack=[]) doc.style.end_codeblock() doc.style.new_paragraph()
def doc_option_example(self, arg_name, help_command, **kwargs): doc = help_command.doc cli_argument = help_command.arg_table[arg_name] if cli_argument.group_name in self._arg_groups: if cli_argument.group_name in self._documented_arg_groups: # Args with group_names (boolean args) don't # need to generate example syntax. return argument_model = cli_argument.argument_model docgen = ParamShorthandDocGen() if docgen.supports_shorthand(cli_argument): # TODO: bcdoc should not know about shorthand syntax. This # should be pulled out into a separate handler in the # awscli.customizations package. example_shorthand_syntax = docgen.generate_shorthand_example( cli_argument) if example_shorthand_syntax is None: # If the shorthand syntax returns a value of None, # this indicates to us that there is no example # needed for this param so we can immediately # return. return doc.style.new_paragraph() doc.write('Shorthand Syntax') doc.style.start_codeblock() for example_line in example_shorthand_syntax.splitlines(): doc.writeln(example_line) doc.style.end_codeblock() if argument_model is not None and argument_model.type_name == 'list' and \ argument_model.member.type_name in SCALAR_TYPES: # A list of scalars is special. While you *can* use # JSON ( ["foo", "bar", "baz"] ), you can also just # use the argparse behavior of space separated lists. # "foo" "bar" "baz". In fact we don't even want to # document the JSON syntax in this case. doc.style.new_paragraph() doc.write('Syntax') doc.style.start_codeblock() example_type = self._json_example_value_name( argument_model.member, include_enum_values=False) doc.write('%s %s ...' % (example_type, example_type)) if 'enum' in argument_model.member.metadata: # If we have enum values, we can tell the user # exactly what valid values they can provide. enum = argument_model.member.metadata['enum'] self._write_valid_enums(doc, enum) doc.style.end_codeblock() doc.style.new_paragraph() elif cli_argument.cli_type_name not in SCALAR_TYPES: doc.style.new_paragraph() doc.write('JSON Syntax') doc.style.start_codeblock() self._json_example(doc, argument_model, stack=[]) doc.style.end_codeblock() doc.style.new_paragraph()
def setUp(self): super(TestDocGen, self).setUp() self.simplify = ParamShorthand() self.shorthand_documenter = ParamShorthandDocGen()
class TestDocGen(BaseArgProcessTest): # These aren't very extensive doc tests, as we want to stay somewhat # flexible and allow the docs to slightly change without breaking these # tests. def setUp(self): super(TestDocGen, self).setUp() self.simplify = ParamShorthand() self.shorthand_documenter = ParamShorthandDocGen() def get_generated_example_for(self, argument): # Returns a string containing the generated documentation. return self.shorthand_documenter.generate_shorthand_example(argument) def assert_generated_example_is(self, argument, expected_docs): generated_docs = self.get_generated_example_for(argument) self.assertEqual(generated_docs, expected_docs) def assert_generated_example_contains(self, argument, expected_to_contain): generated_docs = self.get_generated_example_for(argument) self.assertIn(expected_to_contain, generated_docs) def test_gen_map_type_docs(self): argument = self.get_param_model('sqs.SetQueueAttributes.Attributes') expected_example_str = ( "--attributes key_name=string,key_name2=string\n" "Where valid key names are:\n" " Policy") self.assert_generated_example_contains(argument, expected_example_str) def test_gen_list_scalar_docs(self): argument = self.get_param_model( 'elb.RegisterInstancesWithLoadBalancer.Instances') doc_string = '--instances InstanceId1 InstanceId2 InstanceId3' self.assert_generated_example_is(argument, doc_string) def test_gen_list_structure_of_scalars_docs(self): argument = self.get_param_model('elb.CreateLoadBalancer.Listeners') generated_example = self.get_generated_example_for(argument) self.assertIn( 'Key value pairs, with multiple values separated by a space.', generated_example) self.assertIn('Protocol=string', generated_example) self.assertIn('LoadBalancerPort=integer', generated_example) self.assertIn('InstanceProtocol=string', generated_example) self.assertIn('InstancePort=integer', generated_example) self.assertIn('SSLCertificateId=string', generated_example) def test_gen_list_structure_multiple_scalar_docs(self): argument = self.get_param_model( 'emr.ModifyInstanceGroups.InstanceGroups') expected = ('Key value pairs, where values are separated by commas, ' 'and multiple pairs are separated by spaces.\n' '--instance-groups InstanceGroupId=string1,' 'InstanceCount=integer1,EC2InstanceIdsToTerminate=string1,' 'string2 InstanceGroupId=string1,InstanceCount=integer1,' 'EC2InstanceIdsToTerminate=string1,string2') self.assert_generated_example_is(argument, expected) def test_gen_list_structure_list_scalar_scalar_docs(self): # Verify that we have *two* top level list items displayed, # so we make it clear that multiple values are separated by spaces. argument = self.get_param_model('ec2.DescribeInstances.Filters') generated_example = self.get_generated_example_for(argument) self.assertIn('multiple pairs are separated by spaces', generated_example) self.assertIn( 'Name=string1,Values=string1,string2 ' 'Name=string1,Values=string1,string2', generated_example) def test_gen_structure_list_scalar_docs(self): schema = { "type": "object", "properties": { "Consistent": { "type": "boolean", }, "Args": { "type": "array", "items": { "type": "string" } } } } argument_model = create_argument_model_from_schema(schema) cli_argument = CustomArgument('test', argument_model=argument_model) generated_example = self.get_generated_example_for(cli_argument) self.assertIn('Key value pairs', generated_example) self.assertIn('Consistent=boolean1,Args=string1,string2', generated_example)
def setUp(self): super(TestDocGen, self).setUp() self.shorthand_documenter = ParamShorthandDocGen()
class TestDocGen(BaseArgProcessTest): # These aren't very extensive doc tests, as we want to stay somewhat # flexible and allow the docs to slightly change without breaking these # tests. def setUp(self): super(TestDocGen, self).setUp() self.shorthand_documenter = ParamShorthandDocGen() def get_generated_example_for(self, argument): # Returns a string containing the generated documentation. return self.shorthand_documenter.generate_shorthand_example( argument.cli_name, argument.argument_model) def assert_generated_example_is(self, argument, expected_docs): generated_docs = self.get_generated_example_for(argument) self.assertEqual(generated_docs, expected_docs) def assert_generated_example_contains(self, argument, expected_to_contain): generated_docs = self.get_generated_example_for(argument) self.assertIn(expected_to_contain, generated_docs) def test_gen_map_type_docs(self): argument = self.get_param_model('sqs.SetQueueAttributes.Attributes') expected_example_str = ( "KeyName1=string,KeyName2=string\n\n" "Where valid key names are:\n" " Policy" ) self.assert_generated_example_contains(argument, expected_example_str) def test_gen_list_scalar_docs(self): argument = self.get_param_model( 'elb.RegisterInstancesWithLoadBalancer.Instances') doc_string = '--instances InstanceId1 InstanceId2 InstanceId3' self.assert_generated_example_is(argument, doc_string) def test_gen_list_structure_of_scalars_docs(self): argument = self.get_param_model('elb.CreateLoadBalancer.Listeners') generated_example = self.get_generated_example_for(argument) self.assertIn('Protocol=string', generated_example) self.assertIn('LoadBalancerPort=integer', generated_example) self.assertIn('InstanceProtocol=string', generated_example) self.assertIn('InstancePort=integer', generated_example) self.assertIn('SSLCertificateId=string', generated_example) def test_gen_list_structure_multiple_scalar_docs(self): expected = ( 'Scalar1=string,' 'Scalar2=string,' 'List1=string,string ...' ) arg = model.DenormalizedStructureBuilder().with_members(OrderedDict([ ('List', {'type': 'list', 'member': { 'type': 'structure', 'members': OrderedDict([ ('Scalar1', {'type': 'string'}), ('Scalar2', {'type': 'string'}), ('List1', { 'type': 'list', 'member': {'type': 'string'}, }), ]), }}), ])).build_model().members['List'] rendered = self.shorthand_documenter.generate_shorthand_example( '--foo', arg) self.assertIn(expected, rendered) def test_gen_list_structure_list_scalar_scalar_docs(self): # Verify that we have *two* top level list items displayed, # so we make it clear that multiple values are separated by spaces. argument = self.get_param_model('ec2.DescribeInstances.Filters') generated_example = self.get_generated_example_for(argument) self.assertIn('Name=string,Values=string,string', generated_example) def test_gen_structure_list_scalar_docs(self): schema = { "type": "object", "properties": { "Consistent": { "type": "boolean", }, "Args": { "type": "array", "items": { "type": "string" } } } } argument_model = create_argument_model_from_schema(schema) m = model.DenormalizedStructureBuilder().with_members(OrderedDict([ ('Consistent', {'type': 'boolean'}), ('Args', { 'type': 'list', 'member': { 'type': 'string', }}), ])).build_model() generated_example = self.shorthand_documenter.generate_shorthand_example( '--foo', m) self.assertIn('Consistent=boolean,Args=string,string', generated_example) def test_can_gen_recursive_structure(self): argument = self.get_param_model('dynamodb.PutItem.Item') generated_example = self.get_generated_example_for(argument) def test_can_document_nested_structs(self): argument = self.get_param_model('ec2.RunInstances.BlockDeviceMappings') generated_example = self.get_generated_example_for(argument) self.assertIn('Ebs={SnapshotId=string', generated_example) def test_can_document_nested_lists(self): m = model.DenormalizedStructureBuilder().with_members({ 'A': { 'type': 'list', 'member': { 'type': 'list', 'member': {'type': 'string'}, }, }, }).build_model() generated_example = self.shorthand_documenter.generate_shorthand_example( '--foo', m) self.assertIn('A=[[string,string],[string,string]]', generated_example) def test_can_generated_nested_maps(self): m = model.DenormalizedStructureBuilder().with_members({ 'A': { 'type': 'map', 'key': {'type': 'string'}, 'value': {'type': 'string'} }, }).build_model() generated_example = self.shorthand_documenter.generate_shorthand_example( '--foo', m) self.assertIn('A={KeyName1=string,KeyName2=string}', generated_example) def test_list_of_structures_with_triple_dots(self): list_shape = { 'type': 'list', 'member': {'shape': 'StructShape'}, } shapes = { 'Top': list_shape, 'String': {'type': 'string'}, 'StructShape': { 'type': 'structure', 'members': OrderedDict([ ('A', {'shape': 'String'}), ('B', {'shape': 'String'}), ]) } } m = model.ListShape( shape_name='Top', shape_model=list_shape, shape_resolver=model.ShapeResolver(shapes)) generated_example = self.shorthand_documenter.generate_shorthand_example( '--foo', m) self.assertIn('A=string,B=string ...', generated_example) def test_handle_special_case_value_struct_not_documented(self): m = model.DenormalizedStructureBuilder().with_members({ 'Value': {'type': 'string'} }).build_model() generated_example = self.shorthand_documenter.generate_shorthand_example( '--foo', m) # This is one of the special cases, we shouldn't generate any # shorthand example for this shape. self.assertIsNone(generated_example) def test_can_document_recursive_struct(self): # It's a little more work to set up a recursive # shape because DenormalizedStructureBuilder cannot handle # recursion. struct_shape = { 'type': 'structure', 'members': OrderedDict([ ('Recurse', {'shape': 'SubShape'}), ('Scalar', {'shape': 'String'}), ]), } shapes = { 'Top': struct_shape, 'String': {'type': 'string'}, 'SubShape': { 'type': 'structure', 'members': OrderedDict([ ('SubRecurse', {'shape': 'Top'}), ('Scalar', {'shape': 'String'}), ]), } } m = model.StructureShape( shape_name='Top', shape_model=struct_shape, shape_resolver=model.ShapeResolver(shapes)) generated_example = self.shorthand_documenter.generate_shorthand_example( '--foo', m) self.assertIn( 'Recurse={SubRecurse={( ... recursive ... ),Scalar=string},' 'Scalar=string},Scalar=string', generated_example) def test_skip_deeply_nested_shorthand(self): # The eventual goal is to have a better way to document # deeply nested shorthand params, but for now, we'll # only document shorthand params up to a certain stack level. m = model.DenormalizedStructureBuilder().with_members({ 'A': { 'type': 'structure', 'members': { 'B': { 'type': 'structure', 'members': { 'C': { 'type': 'structure', 'members': { 'D': {'type': 'string'}, } } } } } }, }).build_model() generated_example = self.shorthand_documenter.generate_shorthand_example( '--foo', m) self.assertEqual(generated_example, '')
class TestDocGen(BaseArgProcessTest): # These aren't very extensive doc tests, as we want to stay somewhat # flexible and allow the docs to slightly change without breaking these # tests. def setUp(self): super(TestDocGen, self).setUp() self.shorthand_documenter = ParamShorthandDocGen() def get_generated_example_for(self, argument): # Returns a string containing the generated documentation. return self.shorthand_documenter.generate_shorthand_example( argument.cli_name, argument.argument_model) def assert_generated_example_is(self, argument, expected_docs): generated_docs = self.get_generated_example_for(argument) self.assertEqual(generated_docs, expected_docs) def assert_generated_example_contains(self, argument, expected_to_contain): generated_docs = self.get_generated_example_for(argument) self.assertIn(expected_to_contain, generated_docs) def test_gen_map_type_docs(self): argument = self.get_param_model('sqs.SetQueueAttributes.Attributes') expected_example_str = ("KeyName1=string,KeyName2=string\n\n" "Where valid key names are:\n" " Policy") self.assert_generated_example_contains(argument, expected_example_str) def test_gen_list_scalar_docs(self): argument = self.get_param_model( 'elb.RegisterInstancesWithLoadBalancer.Instances') doc_string = '--instances InstanceId1 InstanceId2 InstanceId3' self.assert_generated_example_is(argument, doc_string) def test_gen_list_structure_of_scalars_docs(self): argument = self.get_param_model('elb.CreateLoadBalancer.Listeners') generated_example = self.get_generated_example_for(argument) self.assertIn('Protocol=string', generated_example) self.assertIn('LoadBalancerPort=integer', generated_example) self.assertIn('InstanceProtocol=string', generated_example) self.assertIn('InstancePort=integer', generated_example) self.assertIn('SSLCertificateId=string', generated_example) def test_gen_list_structure_multiple_scalar_docs(self): argument = self.get_param_model( 'emr.ModifyInstanceGroups.InstanceGroups') expected = ('InstanceGroupId=string,' 'InstanceCount=integer,EC2InstanceIdsToTerminate=string,' 'string ...') self.assert_generated_example_is(argument, expected) def test_gen_list_structure_list_scalar_scalar_docs(self): # Verify that we have *two* top level list items displayed, # so we make it clear that multiple values are separated by spaces. argument = self.get_param_model('ec2.DescribeInstances.Filters') generated_example = self.get_generated_example_for(argument) self.assertIn('Name=string,Values=string,string', generated_example) def test_gen_structure_list_scalar_docs(self): schema = { "type": "object", "properties": { "Consistent": { "type": "boolean", }, "Args": { "type": "array", "items": { "type": "string" } } } } argument_model = create_argument_model_from_schema(schema) m = model.DenormalizedStructureBuilder().with_members( OrderedDict([ ('Consistent', { 'type': 'boolean' }), ('Args', { 'type': 'list', 'member': { 'type': 'string', } }), ])).build_model() generated_example = self.shorthand_documenter.generate_shorthand_example( '--foo', m) self.assertIn('Consistent=boolean,Args=string,string', generated_example) def test_can_gen_recursive_structure(self): argument = self.get_param_model('dynamodb.PutItem.Item') generated_example = self.get_generated_example_for(argument) def test_can_document_nested_structs(self): argument = self.get_param_model('ec2.RunInstances.BlockDeviceMappings') generated_example = self.get_generated_example_for(argument) self.assertIn('Ebs={SnapshotId=string', generated_example) def test_can_document_nested_lists(self): m = model.DenormalizedStructureBuilder().with_members({ 'A': { 'type': 'list', 'member': { 'type': 'list', 'member': { 'type': 'string' }, }, }, }).build_model() generated_example = self.shorthand_documenter.generate_shorthand_example( '--foo', m) self.assertIn('A=[[string,string],[string,string]]', generated_example) def test_can_generated_nested_maps(self): m = model.DenormalizedStructureBuilder().with_members({ 'A': { 'type': 'map', 'key': { 'type': 'string' }, 'value': { 'type': 'string' } }, }).build_model() generated_example = self.shorthand_documenter.generate_shorthand_example( '--foo', m) self.assertIn('A={KeyName1=string,KeyName2=string}', generated_example) def test_list_of_structures_with_triple_dots(self): list_shape = { 'type': 'list', 'member': { 'shape': 'StructShape' }, } shapes = { 'Top': list_shape, 'String': { 'type': 'string' }, 'StructShape': { 'type': 'structure', 'members': OrderedDict([ ('A', { 'shape': 'String' }), ('B', { 'shape': 'String' }), ]) } } m = model.ListShape(shape_name='Top', shape_model=list_shape, shape_resolver=model.ShapeResolver(shapes)) generated_example = self.shorthand_documenter.generate_shorthand_example( '--foo', m) self.assertIn('A=string,B=string ...', generated_example) def test_handle_special_case_value_struct_not_documented(self): m = model.DenormalizedStructureBuilder().with_members({ 'Value': { 'type': 'string' } }).build_model() generated_example = self.shorthand_documenter.generate_shorthand_example( '--foo', m) # This is one of the special cases, we shouldn't generate any # shorthand example for this shape. self.assertIsNone(generated_example) def test_can_document_recursive_struct(self): # It's a little more work to set up a recursive # shape because DenormalizedStructureBuilder cannot handle # recursion. struct_shape = { 'type': 'structure', 'members': OrderedDict([ ('Recurse', { 'shape': 'SubShape' }), ('Scalar', { 'shape': 'String' }), ]), } shapes = { 'Top': struct_shape, 'String': { 'type': 'string' }, 'SubShape': { 'type': 'structure', 'members': OrderedDict([ ('SubRecurse', { 'shape': 'Top' }), ('Scalar', { 'shape': 'String' }), ]), } } m = model.StructureShape(shape_name='Top', shape_model=struct_shape, shape_resolver=model.ShapeResolver(shapes)) generated_example = self.shorthand_documenter.generate_shorthand_example( '--foo', m) self.assertIn( 'Recurse={SubRecurse={( ... recursive ... ),Scalar=string},' 'Scalar=string},Scalar=string', generated_example) def test_skip_deeply_nested_shorthand(self): # The eventual goal is to have a better way to document # deeply nested shorthand params, but for now, we'll # only document shorthand params up to a certain stack level. m = model.DenormalizedStructureBuilder().with_members({ 'A': { 'type': 'structure', 'members': { 'B': { 'type': 'structure', 'members': { 'C': { 'type': 'structure', 'members': { 'D': { 'type': 'string' }, } } } } } }, }).build_model() generated_example = self.shorthand_documenter.generate_shorthand_example( '--foo', m) self.assertEqual(generated_example, '')
from six import BytesIO from docutils.core import publish_string import awscli.clidriver from awscli.argprocess import ParamShorthandDocGen try: from botocore.docs.bcdoc import textwriter except ImportError: from awscli.bcdoc import textwriter from awsshell import determine_doc_index_filename from awsshell.utils import remove_html from awsshell import docs SHORTHAND_DOC = ParamShorthandDocGen() def new_index(): return {'arguments': [], 'argument_metadata': {}, 'commands': [], 'children': {}} def index_command(index_dict, help_command): arg_table = help_command.arg_table for arg in arg_table: arg_obj = arg_table[arg] metadata = { 'required': arg_obj.required, 'type_name': arg_obj.cli_type_name, 'minidoc': '',
def setUp(self): super(TestDocGen, self).setUp() self.shorthand_documenter = ParamShorthandDocGen() self.service_name = 'foo' self.operation_name = 'bar'
class TestDocGen(BaseArgProcessTest): # These aren't very extensive doc tests, as we want to stay somewhat # flexible and allow the docs to slightly change without breaking these # tests. def setUp(self): super(TestDocGen, self).setUp() self.shorthand_documenter = ParamShorthandDocGen() self.service_name = 'foo' self.operation_name = 'bar' def get_generated_example_for(self, argument): # Returns a string containing the generated documentation. return self.shorthand_documenter.generate_shorthand_example( argument, self.service_name, self.operation_name) def assert_generated_example_is(self, argument, expected_docs): generated_docs = self.get_generated_example_for(argument) self.assertEqual(generated_docs, expected_docs) def assert_generated_example_contains(self, argument, expected_to_contain): generated_docs = self.get_generated_example_for(argument) self.assertIn(expected_to_contain, generated_docs) def test_gen_map_type_docs(self): argument = self.get_param_model('sqs.SetQueueAttributes.Attributes') expected_example_str = ("KeyName1=string,KeyName2=string\n\n" "Where valid key names are:\n") self.assert_generated_example_contains(argument, expected_example_str) def test_gen_list_scalar_docs(self): self.service_name = 'elb' self.operation_name = 'register-instances-with-load-balancer' argument = self.get_param_model( 'elb.RegisterInstancesWithLoadBalancer.Instances') doc_string = '--instances InstanceId1 InstanceId2 InstanceId3' self.assert_generated_example_is(argument, doc_string) def test_flattens_marked_single_member_structure_list(self): argument = self.create_argument( { 'Arg': { 'type': 'list', 'member': { 'type': 'structure', 'members': { 'Bar': { 'type': 'string' } } } } }, 'arg') argument.argument_model = argument.argument_model.members['Arg'] uses_old_list = 'awscli.argprocess.ParamShorthand._uses_old_list_case' with mock.patch(uses_old_list, mock.Mock(return_value=True)): self.assert_generated_example_is(argument, '--arg Bar1 Bar2 Bar3') def test_does_not_flatten_unmarked_single_member_structure_list(self): argument = self.create_argument( { 'Arg': { 'type': 'list', 'member': { 'type': 'structure', 'members': { 'Bar': { 'type': 'string' } } } } }, 'arg') argument.argument_model = argument.argument_model.members['Arg'] uses_old_list = 'awscli.argprocess.ParamShorthand._uses_old_list_case' with mock.patch(uses_old_list, mock.Mock(return_value=False)): self.assert_generated_example_is(argument, 'Bar=string ...') def test_gen_list_structure_of_scalars_docs(self): argument = self.get_param_model('elb.CreateLoadBalancer.Listeners') generated_example = self.get_generated_example_for(argument) self.assertIn('Protocol=string', generated_example) self.assertIn('LoadBalancerPort=integer', generated_example) self.assertIn('InstanceProtocol=string', generated_example) self.assertIn('InstancePort=integer', generated_example) self.assertIn('SSLCertificateId=string', generated_example) def test_gen_list_structure_multiple_scalar_docs(self): expected = ('Scalar1=string,' 'Scalar2=string,' 'List1=string,string ...') m = model.DenormalizedStructureBuilder().with_members( OrderedDict([ ('List', { 'type': 'list', 'member': { 'type': 'structure', 'members': OrderedDict([ ('Scalar1', { 'type': 'string' }), ('Scalar2', { 'type': 'string' }), ('List1', { 'type': 'list', 'member': { 'type': 'string' }, }), ]), } }), ])).build_model().members['List'] argument = mock.Mock() argument.argument_model = m argument.name = 'foo' argument.cli_name = '--foo' generated_example = self.get_generated_example_for(argument) self.assertIn(expected, generated_example) def test_gen_list_structure_list_scalar_scalar_docs(self): # Verify that we have *two* top level list items displayed, # so we make it clear that multiple values are separated by spaces. argument = self.get_param_model('ec2.DescribeInstances.Filters') generated_example = self.get_generated_example_for(argument) self.assertIn('Name=string,Values=string,string', generated_example) def test_gen_structure_list_scalar_docs(self): argument = self.create_argument( OrderedDict([ ('Consistent', { 'type': 'boolean' }), ('Args', { 'type': 'list', 'member': { 'type': 'string' } }), ]), 'foo') generated_example = self.get_generated_example_for(argument) self.assertIn('Consistent=boolean,Args=string,string', generated_example) def test_can_gen_recursive_structure(self): argument = self.get_param_model('dynamodb.PutItem.Item') generated_example = self.get_generated_example_for(argument) def test_can_document_nested_structs(self): argument = self.get_param_model('ec2.RunInstances.BlockDeviceMappings') generated_example = self.get_generated_example_for(argument) self.assertRegexpMatches(generated_example, 'Ebs={\w+=\w+') def test_can_document_nested_lists(self): argument = self.create_argument({ 'A': { 'type': 'list', 'member': { 'type': 'list', 'member': { 'type': 'string' }, }, }, }) generated_example = self.get_generated_example_for(argument) self.assertIn('A=[[string,string],[string,string]]', generated_example) def test_can_generated_nested_maps(self): argument = self.create_argument({ 'A': { 'type': 'map', 'key': { 'type': 'string' }, 'value': { 'type': 'string' } }, }) generated_example = self.get_generated_example_for(argument) self.assertIn('A={KeyName1=string,KeyName2=string}', generated_example) def test_list_of_structures_with_triple_dots(self): list_shape = { 'type': 'list', 'member': { 'shape': 'StructShape' }, } shapes = { 'Top': list_shape, 'String': { 'type': 'string' }, 'StructShape': { 'type': 'structure', 'members': OrderedDict([ ('A', { 'shape': 'String' }), ('B', { 'shape': 'String' }), ]) } } m = model.ListShape(shape_name='Top', shape_model=list_shape, shape_resolver=model.ShapeResolver(shapes)) argument = mock.Mock() argument.argument_model = m argument.name = 'foo' argument.cli_name = '--foo' generated_example = self.get_generated_example_for(argument) self.assertIn('A=string,B=string ...', generated_example) def test_handle_special_case_value_struct_not_documented(self): argument = self.create_argument({'Value': {'type': 'string'}}) generated_example = self.get_generated_example_for(argument) # This is one of the special cases, we shouldn't generate any # shorthand example for this shape. self.assertIsNone(generated_example) def test_can_document_recursive_struct(self): # It's a little more work to set up a recursive # shape because DenormalizedStructureBuilder cannot handle # recursion. struct_shape = { 'type': 'structure', 'members': OrderedDict([ ('Recurse', { 'shape': 'SubShape' }), ('Scalar', { 'shape': 'String' }), ]), } shapes = { 'Top': struct_shape, 'String': { 'type': 'string' }, 'SubShape': { 'type': 'structure', 'members': OrderedDict([ ('SubRecurse', { 'shape': 'Top' }), ('Scalar', { 'shape': 'String' }), ]), } } m = model.StructureShape(shape_name='Top', shape_model=struct_shape, shape_resolver=model.ShapeResolver(shapes)) argument = mock.Mock() argument.argument_model = m argument.name = 'foo' argument.cli_name = '--foo' generated_example = self.get_generated_example_for(argument) self.assertIn( 'Recurse={SubRecurse={( ... recursive ... ),Scalar=string},' 'Scalar=string},Scalar=string', generated_example) def test_skip_deeply_nested_shorthand(self): # The eventual goal is to have a better way to document # deeply nested shorthand params, but for now, we'll # only document shorthand params up to a certain stack level. argument = self.create_argument({ 'A': { 'type': 'structure', 'members': { 'B': { 'type': 'structure', 'members': { 'C': { 'type': 'structure', 'members': { 'D': { 'type': 'string' }, } } } } } }, }) generated_example = self.get_generated_example_for(argument) self.assertEqual(generated_example, '')
class TestDocGen(BaseArgProcessTest): # These aren't very extensive doc tests, as we want to stay somewhat # flexible and allow the docs to slightly change without breaking these # tests. def setUp(self): super(TestDocGen, self).setUp() self.simplify = ParamShorthand() self.shorthand_documenter = ParamShorthandDocGen() def get_generated_example_for(self, argument): # Returns a string containing the generated documentation. return self.shorthand_documenter.generate_shorthand_example(argument) def assert_generated_example_is(self, argument, expected_docs): generated_docs = self.get_generated_example_for(argument) self.assertEqual(generated_docs, expected_docs) def assert_generated_example_contains(self, argument, expected_to_contain): generated_docs = self.get_generated_example_for(argument) self.assertIn(expected_to_contain, generated_docs) def test_gen_map_type_docs(self): argument = self.get_param_model('sqs.SetQueueAttributes.Attributes') expected_example_str = ( "--attributes key_name=string,key_name2=string\n" "Where valid key names are:\n" " Policy" ) self.assert_generated_example_contains(argument, expected_example_str) def test_gen_list_scalar_docs(self): argument = self.get_param_model( 'elb.RegisterInstancesWithLoadBalancer.Instances') doc_string = '--instances InstanceId1 InstanceId2 InstanceId3' self.assert_generated_example_is(argument, doc_string) def test_gen_list_structure_of_scalars_docs(self): argument = self.get_param_model('elb.CreateLoadBalancer.Listeners') generated_example = self.get_generated_example_for(argument) self.assertIn( 'Key value pairs, with multiple values separated by a space.', generated_example) self.assertIn('Protocol=string', generated_example) self.assertIn('LoadBalancerPort=integer', generated_example) self.assertIn('InstanceProtocol=string', generated_example) self.assertIn('InstancePort=integer', generated_example) self.assertIn('SSLCertificateId=string', generated_example) def test_gen_list_structure_multiple_scalar_docs(self): argument = self.get_param_model( 'emr.ModifyInstanceGroups.InstanceGroups') expected = ( 'Key value pairs, where values are separated by commas, ' 'and multiple pairs are separated by spaces.\n' '--instance-groups InstanceGroupId=string1,' 'InstanceCount=integer1,EC2InstanceIdsToTerminate=string1,' 'string2 InstanceGroupId=string1,InstanceCount=integer1,' 'EC2InstanceIdsToTerminate=string1,string2') self.assert_generated_example_is(argument, expected) def test_gen_list_structure_list_scalar_scalar_docs(self): # Verify that we have *two* top level list items displayed, # so we make it clear that multiple values are separated by spaces. argument = self.get_param_model('ec2.DescribeInstances.Filters') generated_example = self.get_generated_example_for(argument) self.assertIn('multiple pairs are separated by spaces', generated_example) self.assertIn('Name=string1,Values=string1,string2 ' 'Name=string1,Values=string1,string2', generated_example) def test_gen_structure_list_scalar_docs(self): schema = { "type": "object", "properties": { "Consistent": { "type": "boolean", }, "Args": { "type": "array", "items": { "type": "string" } } } } argument_model = create_argument_model_from_schema(schema) cli_argument = CustomArgument('test', argument_model=argument_model) generated_example = self.get_generated_example_for(cli_argument) self.assertIn('Key value pairs', generated_example) self.assertIn('Consistent=boolean1,Args=string1,string2', generated_example)