Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/botocore/docs/client.py: 22%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

232 statements  

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 os 

14 

15from botocore import xform_name 

16from botocore.compat import OrderedDict 

17from botocore.docs.bcdoc.restdoc import DocumentStructure 

18from botocore.docs.example import ResponseExampleDocumenter 

19from botocore.docs.method import ( 

20 document_custom_method, 

21 document_model_driven_method, 

22 get_instance_public_methods, 

23) 

24from botocore.docs.params import ResponseParamsDocumenter 

25from botocore.docs.sharedexample import document_shared_examples 

26from botocore.docs.utils import DocumentedShape, get_official_service_name 

27 

28 

29def _allowlist_generate_presigned_url(method_name, service_name, **kwargs): 

30 if method_name != 'generate_presigned_url': 

31 return None 

32 return service_name in ['s3'] 

33 

34 

35class ClientDocumenter: 

36 _CLIENT_METHODS_FILTERS = [ 

37 _allowlist_generate_presigned_url, 

38 ] 

39 

40 def __init__(self, client, root_docs_path, shared_examples=None): 

41 self._client = client 

42 self._client_class_name = self._client.__class__.__name__ 

43 self._root_docs_path = root_docs_path 

44 self._shared_examples = shared_examples 

45 if self._shared_examples is None: 

46 self._shared_examples = {} 

47 self._service_name = self._client.meta.service_model.service_name 

48 

49 def document_client(self, section): 

50 """Documents a client and its methods 

51 

52 :param section: The section to write to. 

53 """ 

54 self._add_title(section) 

55 self._add_class_signature(section) 

56 client_methods = self._get_client_methods() 

57 self._add_client_intro(section, client_methods) 

58 self._add_client_methods(client_methods) 

59 

60 def _get_client_methods(self): 

61 client_methods = get_instance_public_methods(self._client) 

62 return self._filter_client_methods(client_methods) 

63 

64 def _filter_client_methods(self, client_methods): 

65 filtered_methods = {} 

66 for method_name, method in client_methods.items(): 

67 include = self._filter_client_method( 

68 method=method, 

69 method_name=method_name, 

70 service_name=self._service_name, 

71 ) 

72 if include: 

73 filtered_methods[method_name] = method 

74 return filtered_methods 

75 

76 def _filter_client_method(self, **kwargs): 

77 # Apply each filter to the method 

78 for filter in self._CLIENT_METHODS_FILTERS: 

79 filter_include = filter(**kwargs) 

80 # Use the first non-None value returned by any of the filters 

81 if filter_include is not None: 

82 return filter_include 

83 # Otherwise default to including it 

84 return True 

85 

86 def _add_title(self, section): 

87 section.style.h2('Client') 

88 

89 def _add_client_intro(self, section, client_methods): 

90 section = section.add_new_section('intro') 

91 # Write out the top level description for the client. 

92 official_service_name = get_official_service_name( 

93 self._client.meta.service_model 

94 ) 

95 section.write( 

96 f"A low-level client representing {official_service_name}" 

97 ) 

98 section.style.new_line() 

99 section.include_doc_string( 

100 self._client.meta.service_model.documentation 

101 ) 

102 

103 # Write out the client example instantiation. 

104 self._add_client_creation_example(section) 

105 

106 # List out all of the possible client methods. 

107 section.style.dedent() 

108 section.style.new_paragraph() 

109 section.writeln('These are the available methods:') 

110 section.style.toctree() 

111 for method_name in sorted(client_methods): 

112 section.style.tocitem(f'{self._service_name}/client/{method_name}') 

113 

114 def _add_class_signature(self, section): 

115 section.style.start_sphinx_py_class( 

116 class_name=f'{self._client_class_name}.Client' 

117 ) 

118 

119 def _add_client_creation_example(self, section): 

120 section.style.start_codeblock() 

121 section.style.new_line() 

122 section.write( 

123 f'client = session.create_client(\'{self._service_name}\')' 

124 ) 

125 section.style.end_codeblock() 

126 

127 def _add_client_methods(self, client_methods): 

128 for method_name in sorted(client_methods): 

129 # Create a new DocumentStructure for each client method and add contents. 

130 method_doc_structure = DocumentStructure( 

131 method_name, target='html' 

132 ) 

133 self._add_client_method( 

134 method_doc_structure, method_name, client_methods[method_name] 

135 ) 

136 # Write client methods in individual/nested files. 

137 # Path: <root>/reference/services/<service>/client/<method_name>.rst 

138 client_dir_path = os.path.join( 

139 self._root_docs_path, self._service_name, 'client' 

140 ) 

141 method_doc_structure.write_to_file(client_dir_path, method_name) 

142 

143 def _add_client_method(self, section, method_name, method): 

144 breadcrumb_section = section.add_new_section('breadcrumb') 

145 breadcrumb_section.style.ref( 

146 self._client_class_name, f'../../{self._service_name}' 

147 ) 

148 breadcrumb_section.write(f' / Client / {method_name}') 

149 section.add_title_section(method_name) 

150 method_section = section.add_new_section( 

151 method_name, 

152 context={'qualifier': f'{self._client_class_name}.Client.'}, 

153 ) 

154 if self._is_custom_method(method_name): 

155 self._add_custom_method( 

156 method_section, 

157 method_name, 

158 method, 

159 ) 

160 else: 

161 self._add_model_driven_method(method_section, method_name) 

162 

163 def _is_custom_method(self, method_name): 

164 return method_name not in self._client.meta.method_to_api_mapping 

165 

166 def _add_custom_method(self, section, method_name, method): 

167 document_custom_method(section, method_name, method) 

168 

169 def _add_method_exceptions_list(self, section, operation_model): 

170 error_section = section.add_new_section('exceptions') 

171 error_section.style.new_line() 

172 error_section.style.bold('Exceptions') 

173 error_section.style.new_line() 

174 for error in operation_model.error_shapes: 

175 class_name = ( 

176 f'{self._client_class_name}.Client.exceptions.{error.name}' 

177 ) 

178 error_section.style.li(f':py:class:`{class_name}`') 

179 

180 def _add_model_driven_method(self, section, method_name): 

181 service_model = self._client.meta.service_model 

182 operation_name = self._client.meta.method_to_api_mapping[method_name] 

183 operation_model = service_model.operation_model(operation_name) 

184 

185 example_prefix = f'response = client.{method_name}' 

186 full_method_name = ( 

187 f"{section.context.get('qualifier', '')}{method_name}" 

188 ) 

189 document_model_driven_method( 

190 section, 

191 full_method_name, 

192 operation_model, 

193 event_emitter=self._client.meta.events, 

194 method_description=operation_model.documentation, 

195 example_prefix=example_prefix, 

196 ) 

197 

198 # Add any modeled exceptions 

199 if operation_model.error_shapes: 

200 self._add_method_exceptions_list(section, operation_model) 

201 

202 # Add the shared examples 

203 shared_examples = self._shared_examples.get(operation_name) 

204 if shared_examples: 

205 document_shared_examples( 

206 section, operation_model, example_prefix, shared_examples 

207 ) 

208 

209 

210class ClientExceptionsDocumenter: 

211 _USER_GUIDE_LINK = ( 

212 'https://boto3.amazonaws.com/' 

213 'v1/documentation/api/latest/guide/error-handling.html' 

214 ) 

215 _GENERIC_ERROR_SHAPE = DocumentedShape( 

216 name='Error', 

217 type_name='structure', 

218 documentation=('Normalized access to common exception attributes.'), 

219 members=OrderedDict( 

220 [ 

221 ( 

222 'Code', 

223 DocumentedShape( 

224 name='Code', 

225 type_name='string', 

226 documentation=( 

227 'An identifier specifying the exception type.' 

228 ), 

229 ), 

230 ), 

231 ( 

232 'Message', 

233 DocumentedShape( 

234 name='Message', 

235 type_name='string', 

236 documentation=( 

237 'A descriptive message explaining why the exception ' 

238 'occured.' 

239 ), 

240 ), 

241 ), 

242 ] 

243 ), 

244 ) 

245 

246 def __init__(self, client, root_docs_path): 

247 self._client = client 

248 self._client_class_name = self._client.__class__.__name__ 

249 self._service_name = self._client.meta.service_model.service_name 

250 self._root_docs_path = root_docs_path 

251 

252 def document_exceptions(self, section): 

253 self._add_title(section) 

254 self._add_overview(section) 

255 self._add_exceptions_list(section) 

256 self._add_exception_classes() 

257 

258 def _add_title(self, section): 

259 section.style.h2('Client Exceptions') 

260 

261 def _add_overview(self, section): 

262 section.style.new_line() 

263 section.write( 

264 'Client exceptions are available on a client instance ' 

265 'via the ``exceptions`` property. For more detailed instructions ' 

266 'and examples on the exact usage of client exceptions, see the ' 

267 'error handling ' 

268 ) 

269 section.style.external_link( 

270 title='user guide', 

271 link=self._USER_GUIDE_LINK, 

272 ) 

273 section.write('.') 

274 section.style.new_line() 

275 

276 def _exception_class_name(self, shape): 

277 return f'{self._client_class_name}.Client.exceptions.{shape.name}' 

278 

279 def _add_exceptions_list(self, section): 

280 error_shapes = self._client.meta.service_model.error_shapes 

281 if not error_shapes: 

282 section.style.new_line() 

283 section.write('This client has no modeled exception classes.') 

284 section.style.new_line() 

285 return 

286 section.style.new_line() 

287 section.writeln('The available client exceptions are:') 

288 section.style.toctree() 

289 for shape in error_shapes: 

290 section.style.tocitem( 

291 f'{self._service_name}/client/exceptions/{shape.name}' 

292 ) 

293 

294 def _add_exception_classes(self): 

295 for shape in self._client.meta.service_model.error_shapes: 

296 # Create a new DocumentStructure for each exception method and add contents. 

297 exception_doc_structure = DocumentStructure( 

298 shape.name, target='html' 

299 ) 

300 self._add_exception_class(exception_doc_structure, shape) 

301 # Write exceptions in individual/nested files. 

302 # Path: <root>/reference/services/<service>/client/exceptions/<exception_name>.rst 

303 exception_dir_path = os.path.join( 

304 self._root_docs_path, 

305 self._service_name, 

306 'client', 

307 'exceptions', 

308 ) 

309 exception_doc_structure.write_to_file( 

310 exception_dir_path, shape.name 

311 ) 

312 

313 def _add_exception_class(self, section, shape): 

314 breadcrumb_section = section.add_new_section('breadcrumb') 

315 breadcrumb_section.style.ref( 

316 self._client_class_name, f'../../../{self._service_name}' 

317 ) 

318 breadcrumb_section.write(f' / Client / exceptions / {shape.name}') 

319 section.add_title_section(shape.name) 

320 class_section = section.add_new_section(shape.name) 

321 class_name = self._exception_class_name(shape) 

322 class_section.style.start_sphinx_py_class(class_name=class_name) 

323 self._add_top_level_documentation(class_section, shape) 

324 self._add_exception_catch_example(class_section, shape) 

325 self._add_response_attr(class_section, shape) 

326 class_section.style.end_sphinx_py_class() 

327 

328 def _add_top_level_documentation(self, section, shape): 

329 if shape.documentation: 

330 section.style.new_line() 

331 section.include_doc_string(shape.documentation) 

332 section.style.new_line() 

333 

334 def _add_exception_catch_example(self, section, shape): 

335 section.style.new_line() 

336 section.style.bold('Example') 

337 section.style.new_paragraph() 

338 section.style.start_codeblock() 

339 section.write('try:') 

340 section.style.indent() 

341 section.style.new_line() 

342 section.write('...') 

343 section.style.dedent() 

344 section.style.new_line() 

345 section.write(f'except client.exceptions.{shape.name} as e:') 

346 section.style.indent() 

347 section.style.new_line() 

348 section.write('print(e.response)') 

349 section.style.dedent() 

350 section.style.end_codeblock() 

351 

352 def _add_response_attr(self, section, shape): 

353 response_section = section.add_new_section('response') 

354 response_section.style.start_sphinx_py_attr('response') 

355 self._add_response_attr_description(response_section) 

356 self._add_response_example(response_section, shape) 

357 self._add_response_params(response_section, shape) 

358 response_section.style.end_sphinx_py_attr() 

359 

360 def _add_response_attr_description(self, section): 

361 section.style.new_line() 

362 section.include_doc_string( 

363 'The parsed error response. All exceptions have a top level ' 

364 '``Error`` key that provides normalized access to common ' 

365 'exception atrributes. All other keys are specific to this ' 

366 'service or exception class.' 

367 ) 

368 section.style.new_line() 

369 

370 def _add_response_example(self, section, shape): 

371 example_section = section.add_new_section('syntax') 

372 example_section.style.new_line() 

373 example_section.style.bold('Syntax') 

374 example_section.style.new_paragraph() 

375 documenter = ResponseExampleDocumenter( 

376 service_name=self._service_name, 

377 operation_name=None, 

378 event_emitter=self._client.meta.events, 

379 ) 

380 documenter.document_example( 

381 example_section, 

382 shape, 

383 include=[self._GENERIC_ERROR_SHAPE], 

384 ) 

385 

386 def _add_response_params(self, section, shape): 

387 params_section = section.add_new_section('Structure') 

388 params_section.style.new_line() 

389 params_section.style.bold('Structure') 

390 params_section.style.new_paragraph() 

391 documenter = ResponseParamsDocumenter( 

392 service_name=self._service_name, 

393 operation_name=None, 

394 event_emitter=self._client.meta.events, 

395 ) 

396 documenter.document_params( 

397 params_section, 

398 shape, 

399 include=[self._GENERIC_ERROR_SHAPE], 

400 ) 

401 

402 

403class ClientContextParamsDocumenter: 

404 _CONFIG_GUIDE_LINK = ( 

405 'https://boto3.amazonaws.com/' 

406 'v1/documentation/api/latest/guide/configuration.html' 

407 ) 

408 

409 OMITTED_CONTEXT_PARAMS = { 

410 's3': ( 

411 'Accelerate', 

412 'DisableMultiRegionAccessPoints', 

413 'ForcePathStyle', 

414 'UseArnRegion', 

415 ), 

416 's3control': ('UseArnRegion',), 

417 } 

418 

419 def __init__(self, service_name, context_params): 

420 self._service_name = service_name 

421 self._context_params = context_params 

422 

423 def document_context_params(self, section): 

424 self._add_title(section) 

425 self._add_overview(section) 

426 self._add_context_params_list(section) 

427 

428 def _add_title(self, section): 

429 section.style.h2('Client Context Parameters') 

430 

431 def _add_overview(self, section): 

432 section.style.new_line() 

433 section.write( 

434 'Client context parameters are configurable on a client ' 

435 'instance via the ``client_context_params`` parameter in the ' 

436 '``Config`` object. For more detailed instructions and examples ' 

437 'on the exact usage of context params see the ' 

438 ) 

439 section.style.external_link( 

440 title='configuration guide', 

441 link=self._CONFIG_GUIDE_LINK, 

442 ) 

443 section.write('.') 

444 section.style.new_line() 

445 

446 def _add_context_params_list(self, section): 

447 section.style.new_line() 

448 sn = f'``{self._service_name}``' 

449 section.writeln(f'The available {sn} client context params are:') 

450 for param in self._context_params: 

451 section.style.new_line() 

452 name = f'``{xform_name(param.name)}``' 

453 section.write(f'* {name} ({param.type}) - {param.documentation}')