def test_include_input(self): include_params = [ DocumentedShape(name='Biz', type_name='string', documentation='biz docs') ] document_model_driven_method( self.doc_structure, 'foo', self.operation_model, event_emitter=self.event_emitter, method_description='This describes the foo method.', example_prefix='response = client.foo', include_input=include_params) self.assert_contains_lines_in_order([ '.. py:method:: foo(**kwargs)', ' This describes the foo method.', ' **Request Syntax**', ' ::', ' response = client.foo(', ' Bar=\'string\',', ' Biz=\'string\'', ' )', ' :type Bar: string', ' :param Bar:', ' :type Biz: string', ' :param Biz: biz docs', ' :rtype: dict', ' :returns:', ' **Response Syntax**', ' ::', ' {', ' \'Bar\': \'string\'', ' }', ' **Response Structure**', ' - *(dict) --*', ' - **Bar** *(string) --*' ])
def test_include_output(self): include_params = [ DocumentedShape(name='Biz', type_name='string', documentation='biz docs') ] document_model_driven_resource_method( self.doc_structure, 'foo', self.operation_model, event_emitter=self.event_emitter, method_description='This describes the foo method.', example_prefix='response = myservice.foo', include_output=include_params, resource_action_model=self.service_resource_model.actions[0]) self.assert_contains_lines_in_order([ '.. py:method:: foo(**kwargs)', ' This describes the foo method.', ' **Request Syntax** ', ' ::', ' response = myservice.foo(', " Foo='string',", " Bar='string'", ' )', ' :type Foo: string', ' :param Foo: Documents Foo', ' :type Bar: string', ' :param Bar: Documents Bar', ' :rtype: dict', ' :returns: ', ' **Response Syntax** ', ' ::', ' {', " 'Foo': 'string',", " 'Bar': 'string',", " 'Biz': 'string'", ' }', ' **Response Structure** ', ' - *(dict) --* ', ' - **Foo** *(string) --* Documents Foo', ' - **Bar** *(string) --* Documents Bar', ' - **Biz** *(string) --* biz docs' ])
def setUp(self): super(TestDocumentInclude, self).setUp() self.add_shape_to_params('Foo', 'String', 'This describes foo.') self.include_params = [ DocumentedShape(name='Baz', type_name='integer', documentation='This describes baz.') ]
def test_document_signature_exclude_and_include(self): exclude_params = ['Foo', 'Bar', 'Baz'] include_params = [ DocumentedShape( name='Biz', type_name='integer', documentation='biz docs') ] document_model_driven_signature( self.doc_structure, 'my_method', self.operation_model, include=include_params, exclude=exclude_params) self.assert_contains_line( '.. py:method:: my_method(**kwargs)')
def document_paginate_method(section, paginator_name, event_emitter, service_model, paginator_config, include_signature=True): """Documents the paginate method of a paginator :param section: The section to write to :param paginator_name: The name of the paginator. It is snake cased. :param event_emitter: The event emitter to use to emit events :param service_model: The service model :param paginator_config: The paginator config associated to a particular paginator. :param include_signature: Whether or not to include the signature. It is useful for generating docstrings. """ # Retrieve the operation model of the underlying operation. operation_model = service_model.operation_model(paginator_name) # Add representations of the request and response parameters # we want to include in the description of the paginate method. # These are parameters we expose via the botocore interface. pagination_config_members = OrderedDict() pagination_config_members['MaxItems'] = DocumentedShape( name='MaxItems', type_name='integer', documentation=('<p>The total number of items to return. If the total ' 'number of items available is more than the value ' 'specified in max-items then a <code>NextToken</code> ' 'will be provided in the output that you can use to ' 'resume pagination.</p>')) pagination_config_members['PageSize'] = DocumentedShape( name='PageSize', type_name='integer', documentation='<p>The size of each page.<p>') pagination_config_members['StartingToken'] = DocumentedShape( name='StartingToken', type_name='string', documentation=('<p>A token to specify where to start paginating. ' 'This is the <code>NextToken</code> from a previous ' 'response.</p>')) botocore_pagination_params = [ DocumentedShape( name='PaginationConfig', type_name='structure', documentation=( '<p>A dictionary that provides parameters to control ' 'pagination.</p>'), members=pagination_config_members) ] botocore_pagination_response_params = [ DocumentedShape(name='NextToken', type_name='string', documentation=('<p>A token to resume pagination.</p>')) ] service_pagination_params = [] # Add the normal input token of the method to a list # of input paramters that we wish to hide since we expose our own. if isinstance(paginator_config['input_token'], list): service_pagination_params += paginator_config['input_token'] else: service_pagination_params.append(paginator_config['input_token']) # Hide the limit key in the documentation. if paginator_config.get('limit_key', None): service_pagination_params.append(paginator_config['limit_key']) # Hide the output tokens in the documentation. service_pagination_response_params = [] if isinstance(paginator_config['output_token'], list): service_pagination_response_params += paginator_config['output_token'] else: service_pagination_response_params.append( paginator_config['output_token']) paginate_description = ( 'Creates an iterator that will paginate through responses ' 'from :py:meth:`{0}.Client.{1}`.'.format( get_service_module_name(service_model), xform_name(paginator_name))) document_model_driven_method( section, 'paginate', operation_model, event_emitter=event_emitter, method_description=paginate_description, example_prefix='response_iterator = paginator.paginate', include_input=botocore_pagination_params, include_output=botocore_pagination_response_params, exclude_input=service_pagination_params, exclude_output=service_pagination_response_params, include_signature=include_signature)
def document_collection_method( section, resource_name, action_name, event_emitter, collection_model, service_model, include_signature=True, ): """Documents a collection method :param section: The section to write to :param resource_name: The name of the resource :param action_name: The name of collection action. Currently only can be all, filter, limit, or page_size :param event_emitter: The event emitter to use to emit events :param collection_model: The model of the collection :param service_model: The model of the service :param include_signature: Whether or not to include the signature. It is useful for generating docstrings. """ operation_model = service_model.operation_model( collection_model.request.operation) underlying_operation_members = [] if operation_model.input_shape: underlying_operation_members = operation_model.input_shape.members example_resource_name = xform_name(resource_name) if service_model.service_name == resource_name: example_resource_name = resource_name custom_action_info_dict = { 'all': { 'method_description': (f'Creates an iterable of all {collection_model.resource.type} ' f'resources in the collection.'), 'example_prefix': '{}_iterator = {}.{}.all'.format( xform_name(collection_model.resource.type), example_resource_name, collection_model.name, ), 'exclude_input': underlying_operation_members, }, 'filter': { 'method_description': (f'Creates an iterable of all {collection_model.resource.type} ' f'resources in the collection filtered by kwargs passed to ' f'method. A {collection_model.resource.type} collection will ' f'include all resources by default if no filters are provided, ' f'and extreme caution should be taken when performing actions ' f'on all resources.'), 'example_prefix': '{}_iterator = {}.{}.filter'.format( xform_name(collection_model.resource.type), example_resource_name, collection_model.name, ), 'exclude_input': get_resource_ignore_params(collection_model.request.params), }, 'limit': { 'method_description': (f'Creates an iterable up to a specified amount of ' f'{collection_model.resource.type} resources in the collection.'), 'example_prefix': '{}_iterator = {}.{}.limit'.format( xform_name(collection_model.resource.type), example_resource_name, collection_model.name, ), 'include_input': [ DocumentedShape( name='count', type_name='integer', documentation=('The limit to the number of resources ' 'in the iterable.'), ) ], 'exclude_input': underlying_operation_members, }, 'page_size': { 'method_description': (f'Creates an iterable of all {collection_model.resource.type} ' f'resources in the collection, but limits the number of ' f'items returned by each service call by the specified amount.'), 'example_prefix': '{}_iterator = {}.{}.page_size'.format( xform_name(collection_model.resource.type), example_resource_name, collection_model.name, ), 'include_input': [ DocumentedShape( name='count', type_name='integer', documentation=('The number of items returned by each ' 'service call'), ) ], 'exclude_input': underlying_operation_members, }, } if action_name in custom_action_info_dict: action_info = custom_action_info_dict[action_name] document_model_driven_resource_method( section=section, method_name=action_name, operation_model=operation_model, event_emitter=event_emitter, resource_action_model=collection_model, include_signature=include_signature, **action_info, )
def document_wait_method(section, waiter_name, event_emitter, service_model, service_waiter_model, include_signature=True): """Documents a the wait method of a waiter :param section: The section to write to :param waiter_name: The name of the waiter :param event_emitter: The event emitter to use to emit events :param service_model: The service model :param service_waiter_model: The waiter model associated to the service :param include_signature: Whether or not to include the signature. It is useful for generating docstrings. """ waiter_model = service_waiter_model.get_waiter(waiter_name) operation_model = service_model.operation_model(waiter_model.operation) waiter_config_members = OrderedDict() waiter_config_members['Delay'] = DocumentedShape( name='Delay', type_name='integer', documentation=('<p>The amount of time in seconds to wait between ' 'attempts. Default: {0}</p>'.format( waiter_model.delay))) waiter_config_members['MaxAttempts'] = DocumentedShape( name='MaxAttempts', type_name='integer', documentation=('<p>The maximum number of attempts to be made. ' 'Default: {0}</p>'.format(waiter_model.max_attempts))) botocore_waiter_params = [ DocumentedShape( name='WaiterConfig', type_name='structure', documentation=( '<p>A dictionary that provides parameters to control ' 'waiting behavior.</p>'), members=waiter_config_members) ] wait_description = ( 'Polls :py:meth:`{0}.Client.{1}` every {2} ' 'seconds until a successful state is reached. An error is ' 'returned after {3} failed checks.'.format( get_service_module_name(service_model), xform_name(waiter_model.operation), waiter_model.delay, waiter_model.max_attempts)) document_model_driven_method(section, 'wait', operation_model, event_emitter=event_emitter, method_description=wait_description, example_prefix='waiter.wait', include_input=botocore_waiter_params, document_output=False, include_signature=include_signature)
def document_collection_method(section, resource_name, action_name, event_emitter, collection_model, service_model, include_signature=True): """Documents a collection method :param section: The section to write to :param resource_name: The name of the resource :param action_name: The name of collection action. Currently only can be all, filter, limit, or page_size :param event_emitter: The event emitter to use to emit events :param collection_model: The model of the collection :param service_model: The model of the service :param include_signature: Whether or not to include the signature. It is useful for generating docstrings. """ operation_model = service_model.operation_model( collection_model.request.operation) underlying_operation_members = [] if operation_model.input_shape: underlying_operation_members = operation_model.input_shape.members example_resource_name = xform_name(resource_name) if service_model.service_name == resource_name: example_resource_name = resource_name custom_action_info_dict = { 'all': { 'method_description': ('Creates an iterable of all %s resources ' 'in the collection.' % collection_model.resource.type), 'example_prefix': '%s_iterator = %s.%s.all' % (xform_name(collection_model.resource.type), example_resource_name, collection_model.name), 'exclude_input': underlying_operation_members }, 'filter': { 'method_description': ('Creates an iterable of all %s resources ' 'in the collection filtered by kwargs passed to ' 'method.' % collection_model.resource.type), 'example_prefix': '%s_iterator = %s.%s.filter' % (xform_name(collection_model.resource.type), example_resource_name, collection_model.name), 'exclude_input': get_resource_ignore_params(collection_model.request.params) }, 'limit': { 'method_description': ('Creates an iterable up to a specified amount of ' '%s resources in the collection.' % collection_model.resource.type), 'example_prefix': '%s_iterator = %s.%s.limit' % (xform_name(collection_model.resource.type), example_resource_name, collection_model.name), 'include_input': [ DocumentedShape( name='count', type_name='integer', documentation=('The limit to the number of resources ' 'in the iterable.')) ], 'exclude_input': underlying_operation_members }, 'page_size': { 'method_description': ('Creates an iterable of all %s resources ' 'in the collection, but limits the number of ' 'items returned by each service call by the specified ' 'amount.' % collection_model.resource.type), 'example_prefix': '%s_iterator = %s.%s.page_size' % (xform_name(collection_model.resource.type), example_resource_name, collection_model.name), 'include_input': [ DocumentedShape( name='count', type_name='integer', documentation=('The number of items returned by each ' 'service call')) ], 'exclude_input': underlying_operation_members } } if action_name in custom_action_info_dict: action_info = custom_action_info_dict[action_name] document_model_driven_resource_method( section=section, method_name=action_name, operation_model=operation_model, event_emitter=event_emitter, resource_action_model=collection_model, include_signature=include_signature, **action_info)
class ClientExceptionsDocumenter(object): _USER_GUIDE_LINK = ( 'https://boto3.amazonaws.com/' 'v1/documentation/api/latest/guide/error-handling.html') _GENERIC_ERROR_SHAPE = DocumentedShape( name='Error', type_name='structure', documentation=('Normalized access to common exception attributes.'), members=OrderedDict([ ('Code', DocumentedShape( name='Code', type_name='string', documentation=( 'An identifier specifying the exception type.'), )), ('Message', DocumentedShape( name='Message', type_name='string', documentation=( 'A descriptive message explaining why the exception ' 'occured.'), )), ]), ) def __init__(self, client): self._client = client self._service_name = self._client.meta.service_model.service_name def document_exceptions(self, section): self._add_title(section) self._add_overview(section) self._add_exceptions_list(section) self._add_exception_classes(section) def _add_title(self, section): section.style.h2('Client Exceptions') def _add_overview(self, section): section.style.new_line() section.write( 'Client exceptions are available on a client instance ' 'via the ``exceptions`` property. For more detailed instructions ' 'and examples on the exact usage of client exceptions, see the ' 'error handling ') section.style.external_link( title='user guide', link=self._USER_GUIDE_LINK, ) section.write('.') section.style.new_line() def _exception_class_name(self, shape): cls_name = self._client.__class__.__name__ return '%s.Client.exceptions.%s' % (cls_name, shape.name) def _add_exceptions_list(self, section): error_shapes = self._client.meta.service_model.error_shapes if not error_shapes: section.style.new_line() section.write('This client has no modeled exception classes.') section.style.new_line() return section.style.new_line() section.write('The available client exceptions are:') section.style.new_line() for shape in error_shapes: class_name = self._exception_class_name(shape) section.style.li(':py:class:`%s`' % class_name) def _add_exception_classes(self, section): for shape in self._client.meta.service_model.error_shapes: self._add_exception_class(section, shape) def _add_exception_class(self, section, shape): class_section = section.add_new_section(shape.name) class_name = self._exception_class_name(shape) class_section.style.start_sphinx_py_class(class_name=class_name) self._add_top_level_documentation(class_section, shape) self._add_exception_catch_example(class_section, shape) self._add_response_attr(class_section, shape) class_section.style.end_sphinx_py_class() def _add_top_level_documentation(self, section, shape): if shape.documentation: section.style.new_line() section.include_doc_string(shape.documentation) section.style.new_line() def _add_exception_catch_example(self, section, shape): section.style.new_line() section.style.bold('Example') section.style.start_codeblock() section.write('try:') section.style.indent() section.style.new_line() section.write('...') section.style.dedent() section.style.new_line() section.write('except client.exceptions.%s as e:' % shape.name) section.style.indent() section.style.new_line() section.write('print(e.response)') section.style.dedent() section.style.end_codeblock() def _add_response_attr(self, section, shape): response_section = section.add_new_section('response') response_section.style.start_sphinx_py_attr('response') self._add_response_attr_description(response_section) self._add_response_example(response_section, shape) self._add_response_params(response_section, shape) response_section.style.end_sphinx_py_attr() def _add_response_attr_description(self, section): section.style.new_line() section.include_doc_string( 'The parsed error response. All exceptions have a top level ' '``Error`` key that provides normalized access to common ' 'exception atrributes. All other keys are specific to this ' 'service or exception class.') section.style.new_line() def _add_response_example(self, section, shape): example_section = section.add_new_section('syntax') example_section.style.new_line() example_section.style.bold('Syntax') example_section.style.new_paragraph() documenter = ResponseExampleDocumenter( service_name=self._service_name, operation_name=None, event_emitter=self._client.meta.events, ) documenter.document_example( example_section, shape, include=[self._GENERIC_ERROR_SHAPE], ) def _add_response_params(self, section, shape): params_section = section.add_new_section('Structure') params_section.style.new_line() params_section.style.bold('Structure') params_section.style.new_paragraph() documenter = ResponseParamsDocumenter( service_name=self._service_name, operation_name=None, event_emitter=self._client.meta.events, ) documenter.document_params( params_section, shape, include=[self._GENERIC_ERROR_SHAPE], )
def _document_custom_action(self, section, action_name, collection): operation_model = self._service_model.operation_model( collection.request.operation) underlying_operation_members = [] if operation_model.input_shape: underlying_operation_members = operation_model.input_shape.members resource_name = xform_name(self._resource_name) if self.represents_service_resource: resource_name = self._resource_name custom_action_info_dict = { 'all': { 'method_description': ('Creates an iterable of all %s resources ' 'in the collection.' % collection.resource.type), 'example_prefix': '%s_iterator = %s.%s.all' % (xform_name( collection.resource.type), resource_name, collection.name), 'exclude_input': underlying_operation_members }, 'filter': { 'method_description': ('Creates an iterable of all %s resources ' 'in the collection filtered by kwargs passed to ' 'method.' % collection.resource.type), 'example_prefix': '%s_iterator = %s.%s.filter' % (xform_name( collection.resource.type), resource_name, collection.name), 'exclude_input': get_resource_ignore_params(collection.request.params) }, 'limit': { 'method_description': ('Creates an iterable up to a specified amount of ' '%s resources in the collection.' % collection.resource.type), 'example_prefix': '%s_iterator = %s.%s.limit' % (xform_name( collection.resource.type), resource_name, collection.name), 'include_input': [ DocumentedShape( name='count', type_name='integer', documentation=('The limit to the number of resources ' 'in the iterable.')) ], 'exclude_input': underlying_operation_members }, 'page_size': { 'method_description': ('Creates an iterable of all %s resources ' 'in the collection, but limits the number of ' 'items returned by each service call by the specified ' 'amount.' % collection.resource.type), 'example_prefix': '%s_iterator = %s.%s.page_size' % (xform_name( collection.resource.type), resource_name, collection.name), 'include_input': [ DocumentedShape( name='count', type_name='integer', documentation=('The number of items returned by each ' 'service call')) ], 'exclude_input': underlying_operation_members } } if action_name in custom_action_info_dict: action_info = custom_action_info_dict[action_name] document_model_driven_resource_method( section=section, method_name=action_name, operation_model=operation_model, event_emitter=self._resource.meta.client.meta.events, resource_action_model=collection, **action_info)