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.utils import get_official_service_name 16from botocore.docs.method import document_custom_method 17from botocore.docs.method import document_model_driven_method 18from botocore.docs.method import get_instance_public_methods 19from botocore.docs.sharedexample import document_shared_examples 20from botocore.docs.example import ResponseExampleDocumenter 21from botocore.docs.params import ResponseParamsDocumenter 22from botocore.docs.utils import DocumentedShape 23from botocore.compat import OrderedDict 24 25 26class ClientDocumenter(object): 27 def __init__(self, client, shared_examples=None): 28 self._client = client 29 self._shared_examples = shared_examples 30 if self._shared_examples is None: 31 self._shared_examples = {} 32 self._service_name = self._client.meta.service_model.service_name 33 34 def document_client(self, section): 35 """Documents a client and its methods 36 37 :param section: The section to write to. 38 """ 39 self._add_title(section) 40 self._add_class_signature(section) 41 client_methods = get_instance_public_methods(self._client) 42 self._add_client_intro(section, client_methods) 43 self._add_client_methods(section, client_methods) 44 45 def _add_title(self, section): 46 section.style.h2('Client') 47 48 def _add_client_intro(self, section, client_methods): 49 section = section.add_new_section('intro') 50 # Write out the top level description for the client. 51 official_service_name = get_official_service_name( 52 self._client.meta.service_model) 53 section.write( 54 'A low-level client representing %s' % official_service_name) 55 56 # Write out the client example instantiation. 57 self._add_client_creation_example(section) 58 59 # List out all of the possible client methods. 60 section.style.new_line() 61 section.write('These are the available methods:') 62 section.style.new_line() 63 class_name = self._client.__class__.__name__ 64 for method_name in sorted(client_methods): 65 section.style.li(':py:meth:`~%s.Client.%s`' % ( 66 class_name, method_name)) 67 68 def _add_class_signature(self, section): 69 section.style.start_sphinx_py_class( 70 class_name='%s.Client' % self._client.__class__.__name__) 71 72 def _add_client_creation_example(self, section): 73 section.style.start_codeblock() 74 section.style.new_line() 75 section.write( 76 'client = session.create_client(\'{service}\')'.format( 77 service=self._service_name) 78 ) 79 section.style.end_codeblock() 80 81 def _add_client_methods(self, section, client_methods): 82 section = section.add_new_section('methods') 83 for method_name in sorted(client_methods): 84 self._add_client_method( 85 section, method_name, client_methods[method_name]) 86 87 def _add_client_method(self, section, method_name, method): 88 section = section.add_new_section(method_name) 89 if self._is_custom_method(method_name): 90 self._add_custom_method(section, method_name, method) 91 else: 92 self._add_model_driven_method(section, method_name) 93 94 def _is_custom_method(self, method_name): 95 return method_name not in self._client.meta.method_to_api_mapping 96 97 def _add_custom_method(self, section, method_name, method): 98 document_custom_method(section, method_name, method) 99 100 def _add_method_exceptions_list(self, section, operation_model): 101 error_section = section.add_new_section('exceptions') 102 error_section.style.new_line() 103 error_section.style.bold('Exceptions') 104 error_section.style.new_line() 105 client_name = self._client.__class__.__name__ 106 for error in operation_model.error_shapes: 107 class_name = '%s.Client.exceptions.%s' % (client_name, error.name) 108 error_section.style.li(':py:class:`%s`' % class_name) 109 110 def _add_model_driven_method(self, section, method_name): 111 service_model = self._client.meta.service_model 112 operation_name = self._client.meta.method_to_api_mapping[method_name] 113 operation_model = service_model.operation_model(operation_name) 114 115 example_prefix = 'response = client.%s' % method_name 116 document_model_driven_method( 117 section, method_name, operation_model, 118 event_emitter=self._client.meta.events, 119 method_description=operation_model.documentation, 120 example_prefix=example_prefix, 121 ) 122 123 # Add any modeled exceptions 124 if operation_model.error_shapes: 125 self._add_method_exceptions_list(section, operation_model) 126 127 # Add the shared examples 128 shared_examples = self._shared_examples.get(operation_name) 129 if shared_examples: 130 document_shared_examples( 131 section, operation_model, example_prefix, shared_examples) 132 133 134class ClientExceptionsDocumenter(object): 135 _USER_GUIDE_LINK = ( 136 'https://boto3.amazonaws.com/' 137 'v1/documentation/api/latest/guide/error-handling.html' 138 ) 139 _GENERIC_ERROR_SHAPE = DocumentedShape( 140 name='Error', 141 type_name='structure', 142 documentation=( 143 'Normalized access to common exception attributes.' 144 ), 145 members=OrderedDict([ 146 ('Code', DocumentedShape( 147 name='Code', 148 type_name='string', 149 documentation=( 150 'An identifier specifying the exception type.' 151 ), 152 )), 153 ('Message', DocumentedShape( 154 name='Message', 155 type_name='string', 156 documentation=( 157 'A descriptive message explaining why the exception ' 158 'occured.' 159 ), 160 )), 161 ]), 162 ) 163 164 def __init__(self, client): 165 self._client = client 166 self._service_name = self._client.meta.service_model.service_name 167 168 def document_exceptions(self, section): 169 self._add_title(section) 170 self._add_overview(section) 171 self._add_exceptions_list(section) 172 self._add_exception_classes(section) 173 174 def _add_title(self, section): 175 section.style.h2('Client Exceptions') 176 177 def _add_overview(self, section): 178 section.style.new_line() 179 section.write( 180 'Client exceptions are available on a client instance ' 181 'via the ``exceptions`` property. For more detailed instructions ' 182 'and examples on the exact usage of client exceptions, see the ' 183 'error handling ' 184 ) 185 section.style.external_link( 186 title='user guide', 187 link=self._USER_GUIDE_LINK, 188 ) 189 section.write('.') 190 section.style.new_line() 191 192 def _exception_class_name(self, shape): 193 cls_name = self._client.__class__.__name__ 194 return '%s.Client.exceptions.%s' % (cls_name, shape.name) 195 196 def _add_exceptions_list(self, section): 197 error_shapes = self._client.meta.service_model.error_shapes 198 if not error_shapes: 199 section.style.new_line() 200 section.write('This client has no modeled exception classes.') 201 section.style.new_line() 202 return 203 section.style.new_line() 204 section.write('The available client exceptions are:') 205 section.style.new_line() 206 for shape in error_shapes: 207 class_name = self._exception_class_name(shape) 208 section.style.li(':py:class:`%s`' % class_name) 209 210 def _add_exception_classes(self, section): 211 for shape in self._client.meta.service_model.error_shapes: 212 self._add_exception_class(section, shape) 213 214 def _add_exception_class(self, section, shape): 215 class_section = section.add_new_section(shape.name) 216 class_name = self._exception_class_name(shape) 217 class_section.style.start_sphinx_py_class(class_name=class_name) 218 self._add_top_level_documentation(class_section, shape) 219 self._add_exception_catch_example(class_section, shape) 220 self._add_response_attr(class_section, shape) 221 class_section.style.end_sphinx_py_class() 222 223 def _add_top_level_documentation(self, section, shape): 224 if shape.documentation: 225 section.style.new_line() 226 section.include_doc_string(shape.documentation) 227 section.style.new_line() 228 229 def _add_exception_catch_example(self, section, shape): 230 section.style.new_line() 231 section.style.bold('Example') 232 section.style.start_codeblock() 233 section.write('try:') 234 section.style.indent() 235 section.style.new_line() 236 section.write('...') 237 section.style.dedent() 238 section.style.new_line() 239 section.write('except client.exceptions.%s as e:' % shape.name) 240 section.style.indent() 241 section.style.new_line() 242 section.write('print(e.response)') 243 section.style.dedent() 244 section.style.end_codeblock() 245 246 def _add_response_attr(self, section, shape): 247 response_section = section.add_new_section('response') 248 response_section.style.start_sphinx_py_attr('response') 249 self._add_response_attr_description(response_section) 250 self._add_response_example(response_section, shape) 251 self._add_response_params(response_section, shape) 252 response_section.style.end_sphinx_py_attr() 253 254 def _add_response_attr_description(self, section): 255 section.style.new_line() 256 section.include_doc_string( 257 'The parsed error response. All exceptions have a top level ' 258 '``Error`` key that provides normalized access to common ' 259 'exception atrributes. All other keys are specific to this ' 260 'service or exception class.' 261 ) 262 section.style.new_line() 263 264 def _add_response_example(self, section, shape): 265 example_section = section.add_new_section('syntax') 266 example_section.style.new_line() 267 example_section.style.bold('Syntax') 268 example_section.style.new_paragraph() 269 documenter = ResponseExampleDocumenter( 270 service_name=self._service_name, 271 operation_name=None, 272 event_emitter=self._client.meta.events, 273 ) 274 documenter.document_example( 275 example_section, shape, include=[self._GENERIC_ERROR_SHAPE], 276 ) 277 278 def _add_response_params(self, section, shape): 279 params_section = section.add_new_section('Structure') 280 params_section.style.new_line() 281 params_section.style.bold('Structure') 282 params_section.style.new_paragraph() 283 documenter = ResponseParamsDocumenter( 284 service_name=self._service_name, 285 operation_name=None, 286 event_emitter=self._client.meta.events, 287 ) 288 documenter.document_params( 289 params_section, shape, include=[self._GENERIC_ERROR_SHAPE], 290 ) 291