1# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"). You
4# may not use this file except in compliance with the License. A copy of
5# the License is located at
6#
7# http://aws.amazon.com/apache2.0/
8#
9# or in the "license" file accompanying this file. This file is
10# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11# ANY KIND, either express or implied. See the License for the specific
12# language governing permissions and limitations under the License.
13import inspect
14
15from botocore.docs.params import RequestParamsDocumenter
16from botocore.docs.params import ResponseParamsDocumenter
17from botocore.docs.example import ResponseExampleDocumenter
18from botocore.docs.example import RequestExampleDocumenter
19
20
21AWS_DOC_BASE = 'https://docs.aws.amazon.com/goto/WebAPI'
22
23
24def get_instance_public_methods(instance):
25    """Retrieves an objects public methods
26
27    :param instance: The instance of the class to inspect
28    :rtype: dict
29    :returns: A dictionary that represents an instance's methods where
30        the keys are the name of the methods and the
31        values are the handler to the method.
32    """
33    instance_members = inspect.getmembers(instance)
34    instance_methods = {}
35    for name, member in instance_members:
36        if not name.startswith('_'):
37            if inspect.ismethod(member):
38                instance_methods[name] = member
39    return instance_methods
40
41
42def document_model_driven_signature(section, name, operation_model,
43                                    include=None, exclude=None):
44    """Documents the signature of a model-driven method
45
46    :param section: The section to write the documentation to.
47
48    :param name: The name of the method
49
50    :param operation_model: The operation model for the method
51
52    :type include: Dictionary where keys are parameter names and
53        values are the shapes of the parameter names.
54    :param include: The parameter shapes to include in the documentation.
55
56    :type exclude: List of the names of the parameters to exclude.
57    :param exclude: The names of the parameters to exclude from
58        documentation.
59    """
60    params = {}
61    if operation_model.input_shape:
62        params = operation_model.input_shape.members
63
64    parameter_names = list(params.keys())
65
66    if include is not None:
67        for member in include:
68            parameter_names.append(member.name)
69
70    if exclude is not None:
71        for member in exclude:
72            if member in parameter_names:
73                parameter_names.remove(member)
74
75    signature_params = ''
76    if parameter_names:
77        signature_params = '**kwargs'
78    section.style.start_sphinx_py_method(name, signature_params)
79
80
81def document_custom_signature(section, name, method,
82                              include=None, exclude=None):
83    """Documents the signature of a custom method
84
85    :param section: The section to write the documentation to.
86
87    :param name: The name of the method
88
89    :param method: The handle to the method being documented
90
91    :type include: Dictionary where keys are parameter names and
92        values are the shapes of the parameter names.
93    :param include: The parameter shapes to include in the documentation.
94
95    :type exclude: List of the names of the parameters to exclude.
96    :param exclude: The names of the parameters to exclude from
97        documentation.
98    """
99    args, varargs, keywords, defaults = inspect.getargspec(method)
100    args = args[1:]
101    signature_params = inspect.formatargspec(
102        args, varargs, keywords, defaults)
103    signature_params = signature_params.lstrip('(')
104    signature_params = signature_params.rstrip(')')
105    section.style.start_sphinx_py_method(name, signature_params)
106
107
108def document_custom_method(section, method_name, method):
109    """Documents a non-data driven method
110
111    :param section: The section to write the documentation to.
112
113    :param method_name: The name of the method
114
115    :param method: The handle to the method being documented
116    """
117    document_custom_signature(
118        section, method_name, method)
119    method_intro_section = section.add_new_section('method-intro')
120    method_intro_section.writeln('')
121    doc_string = inspect.getdoc(method)
122    if doc_string is not None:
123        method_intro_section.style.write_py_doc_string(doc_string)
124
125
126def document_model_driven_method(section, method_name, operation_model,
127                                 event_emitter, method_description=None,
128                                 example_prefix=None, include_input=None,
129                                 include_output=None, exclude_input=None,
130                                 exclude_output=None, document_output=True,
131                                 include_signature=True):
132    """Documents an individual method
133
134    :param section: The section to write to
135
136    :param method_name: The name of the method
137
138    :param operation_model: The model of the operation
139
140    :param event_emitter: The event emitter to use to emit events
141
142    :param example_prefix: The prefix to use in the method example.
143
144    :type include_input: Dictionary where keys are parameter names and
145        values are the shapes of the parameter names.
146    :param include_input: The parameter shapes to include in the
147        input documentation.
148
149    :type include_output: Dictionary where keys are parameter names and
150        values are the shapes of the parameter names.
151    :param include_input: The parameter shapes to include in the
152        output documentation.
153
154    :type exclude_input: List of the names of the parameters to exclude.
155    :param exclude_input: The names of the parameters to exclude from
156        input documentation.
157
158    :type exclude_output: List of the names of the parameters to exclude.
159    :param exclude_input: The names of the parameters to exclude from
160        output documentation.
161
162    :param document_output: A boolean flag to indicate whether to
163        document the output.
164
165    :param include_signature: Whether or not to include the signature.
166        It is useful for generating docstrings.
167    """
168    # Add the signature if specified.
169    if include_signature:
170        document_model_driven_signature(
171            section, method_name, operation_model, include=include_input,
172            exclude=exclude_input)
173
174    # Add the description for the method.
175    method_intro_section = section.add_new_section('method-intro')
176    method_intro_section.include_doc_string(method_description)
177    if operation_model.deprecated:
178        method_intro_section.style.start_danger()
179        method_intro_section.writeln(
180                'This operation is deprecated and may not function as '
181                'expected. This operation should not be used going forward '
182                'and is only kept for the purpose of backwards compatiblity.')
183        method_intro_section.style.end_danger()
184    service_uid = operation_model.service_model.metadata.get('uid')
185    if service_uid is not None:
186        method_intro_section.style.new_paragraph()
187        method_intro_section.write("See also: ")
188        link = '%s/%s/%s' % (AWS_DOC_BASE, service_uid,
189                             operation_model.name)
190        method_intro_section.style.external_link(title="AWS API Documentation",
191                                                 link=link)
192        method_intro_section.writeln('')
193
194    # Add the example section.
195    example_section = section.add_new_section('example')
196    example_section.style.new_paragraph()
197    example_section.style.bold('Request Syntax')
198
199    context = {
200        'special_shape_types': {
201            'streaming_input_shape': operation_model.get_streaming_input(),
202            'streaming_output_shape': operation_model.get_streaming_output(),
203            'eventstream_output_shape': operation_model.get_event_stream_output(),
204        },
205    }
206
207    if operation_model.input_shape:
208        RequestExampleDocumenter(
209            service_name=operation_model.service_model.service_name,
210            operation_name=operation_model.name,
211            event_emitter=event_emitter, context=context).document_example(
212                example_section, operation_model.input_shape,
213                prefix=example_prefix, include=include_input,
214                exclude=exclude_input)
215    else:
216        example_section.style.new_paragraph()
217        example_section.style.start_codeblock()
218        example_section.write(example_prefix + '()')
219
220    # Add the request parameter documentation.
221    request_params_section = section.add_new_section('request-params')
222    if operation_model.input_shape:
223        RequestParamsDocumenter(
224            service_name=operation_model.service_model.service_name,
225            operation_name=operation_model.name,
226            event_emitter=event_emitter, context=context).document_params(
227                request_params_section, operation_model.input_shape,
228                include=include_input, exclude=exclude_input)
229
230    # Add the return value documentation
231    return_section = section.add_new_section('return')
232    return_section.style.new_line()
233    if operation_model.output_shape is not None and document_output:
234        return_section.write(':rtype: dict')
235        return_section.style.new_line()
236        return_section.write(':returns: ')
237        return_section.style.indent()
238        return_section.style.new_line()
239
240        # If the operation is an event stream, describe the tagged union
241        event_stream_output = operation_model.get_event_stream_output()
242        if event_stream_output:
243            event_section = return_section.add_new_section('event-stream')
244            event_section.style.new_paragraph()
245            event_section.write(
246                'The response of this operation contains an '
247                ':class:`.EventStream` member. When iterated the '
248                ':class:`.EventStream` will yield events based on the '
249                'structure below, where only one of the top level keys '
250                'will be present for any given event.'
251            )
252            event_section.style.new_line()
253
254        # Add an example return value
255        return_example_section = return_section.add_new_section('example')
256        return_example_section.style.new_line()
257        return_example_section.style.bold('Response Syntax')
258        return_example_section.style.new_paragraph()
259        ResponseExampleDocumenter(
260            service_name=operation_model.service_model.service_name,
261            operation_name=operation_model.name,
262            event_emitter=event_emitter,
263            context=context).document_example(
264                return_example_section, operation_model.output_shape,
265                include=include_output, exclude=exclude_output)
266
267        # Add a description for the return value
268        return_description_section = return_section.add_new_section(
269            'description')
270        return_description_section.style.new_line()
271        return_description_section.style.bold('Response Structure')
272        return_description_section.style.new_paragraph()
273        ResponseParamsDocumenter(
274            service_name=operation_model.service_model.service_name,
275            operation_name=operation_model.name,
276            event_emitter=event_emitter,
277            context=context).document_params(
278                return_description_section, operation_model.output_shape,
279                include=include_output, exclude=exclude_output)
280    else:
281        return_section.write(':returns: None')
282