def test_include_output(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_output=include_params
     )
     self.assert_contains_lines_in_order([
         '.. py:method:: foo(**kwargs)',
         '  This describes the foo method.',
         '  **Request Syntax**',
         '  ::',
         '    response = client.foo(',
         '        Bar=\'string\'',
         '    )',
         '  :type Bar: string',
         '  :param Bar:',
         '  :rtype: dict',
         '  :returns:',
         '    **Response Syntax**',
         '    ::',
         '      {',
         '          \'Bar\': \'string\'',
         '          \'Biz\': \'string\'',
         '      }',
         '    **Response Structure**',
         '    - *(dict) --*',
         '      - **Bar** *(string) --*',
         '      - **Biz** *(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'
     ])
Beispiel #3
0
 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_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)
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 ibm_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_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)
Beispiel #8
0
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,
        )
Beispiel #9
0
class ClientExceptionsDocumenter(object):
    _USER_GUIDE_LINK = (
        'https://ibm_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],
        )