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

232 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-08 06:51 +0000

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 'client = session.create_client(\'{service}\')'.format( 

124 service=self._service_name 

125 ) 

126 ) 

127 section.style.end_codeblock() 

128 

129 def _add_client_methods(self, client_methods): 

130 for method_name in sorted(client_methods): 

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

132 method_doc_structure = DocumentStructure( 

133 method_name, target='html' 

134 ) 

135 self._add_client_method( 

136 method_doc_structure, method_name, client_methods[method_name] 

137 ) 

138 # Write client methods in individual/nested files. 

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

140 client_dir_path = os.path.join( 

141 self._root_docs_path, self._service_name, 'client' 

142 ) 

143 method_doc_structure.write_to_file(client_dir_path, method_name) 

144 

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

146 breadcrumb_section = section.add_new_section('breadcrumb') 

147 breadcrumb_section.style.ref( 

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

149 ) 

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

151 section.add_title_section(method_name) 

152 method_section = section.add_new_section( 

153 method_name, 

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

155 ) 

156 if self._is_custom_method(method_name): 

157 self._add_custom_method( 

158 method_section, 

159 method_name, 

160 method, 

161 ) 

162 else: 

163 self._add_model_driven_method(method_section, method_name) 

164 

165 def _is_custom_method(self, method_name): 

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

167 

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

169 document_custom_method(section, method_name, method) 

170 

171 def _add_method_exceptions_list(self, section, operation_model): 

172 error_section = section.add_new_section('exceptions') 

173 error_section.style.new_line() 

174 error_section.style.bold('Exceptions') 

175 error_section.style.new_line() 

176 for error in operation_model.error_shapes: 

177 class_name = ( 

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

179 ) 

180 error_section.style.li(':py:class:`%s`' % class_name) 

181 

182 def _add_model_driven_method(self, section, method_name): 

183 service_model = self._client.meta.service_model 

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

185 operation_model = service_model.operation_model(operation_name) 

186 

187 example_prefix = 'response = client.%s' % method_name 

188 full_method_name = ( 

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

190 ) 

191 document_model_driven_method( 

192 section, 

193 full_method_name, 

194 operation_model, 

195 event_emitter=self._client.meta.events, 

196 method_description=operation_model.documentation, 

197 example_prefix=example_prefix, 

198 ) 

199 

200 # Add any modeled exceptions 

201 if operation_model.error_shapes: 

202 self._add_method_exceptions_list(section, operation_model) 

203 

204 # Add the shared examples 

205 shared_examples = self._shared_examples.get(operation_name) 

206 if shared_examples: 

207 document_shared_examples( 

208 section, operation_model, example_prefix, shared_examples 

209 ) 

210 

211 

212class ClientExceptionsDocumenter: 

213 _USER_GUIDE_LINK = ( 

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

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

216 ) 

217 _GENERIC_ERROR_SHAPE = DocumentedShape( 

218 name='Error', 

219 type_name='structure', 

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

221 members=OrderedDict( 

222 [ 

223 ( 

224 'Code', 

225 DocumentedShape( 

226 name='Code', 

227 type_name='string', 

228 documentation=( 

229 'An identifier specifying the exception type.' 

230 ), 

231 ), 

232 ), 

233 ( 

234 'Message', 

235 DocumentedShape( 

236 name='Message', 

237 type_name='string', 

238 documentation=( 

239 'A descriptive message explaining why the exception ' 

240 'occured.' 

241 ), 

242 ), 

243 ), 

244 ] 

245 ), 

246 ) 

247 

248 def __init__(self, client, root_docs_path): 

249 self._client = client 

250 self._client_class_name = self._client.__class__.__name__ 

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

252 self._root_docs_path = root_docs_path 

253 

254 def document_exceptions(self, section): 

255 self._add_title(section) 

256 self._add_overview(section) 

257 self._add_exceptions_list(section) 

258 self._add_exception_classes() 

259 

260 def _add_title(self, section): 

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

262 

263 def _add_overview(self, section): 

264 section.style.new_line() 

265 section.write( 

266 'Client exceptions are available on a client instance ' 

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

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

269 'error handling ' 

270 ) 

271 section.style.external_link( 

272 title='user guide', 

273 link=self._USER_GUIDE_LINK, 

274 ) 

275 section.write('.') 

276 section.style.new_line() 

277 

278 def _exception_class_name(self, shape): 

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

280 

281 def _add_exceptions_list(self, section): 

282 error_shapes = self._client.meta.service_model.error_shapes 

283 if not error_shapes: 

284 section.style.new_line() 

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

286 section.style.new_line() 

287 return 

288 section.style.new_line() 

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

290 section.style.toctree() 

291 for shape in error_shapes: 

292 section.style.tocitem( 

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

294 ) 

295 

296 def _add_exception_classes(self): 

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

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

299 exception_doc_structure = DocumentStructure( 

300 shape.name, target='html' 

301 ) 

302 self._add_exception_class(exception_doc_structure, shape) 

303 # Write exceptions in individual/nested files. 

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

305 exception_dir_path = os.path.join( 

306 self._root_docs_path, 

307 self._service_name, 

308 'client', 

309 'exceptions', 

310 ) 

311 exception_doc_structure.write_to_file( 

312 exception_dir_path, shape.name 

313 ) 

314 

315 def _add_exception_class(self, section, shape): 

316 breadcrumb_section = section.add_new_section('breadcrumb') 

317 breadcrumb_section.style.ref( 

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

319 ) 

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

321 section.add_title_section(shape.name) 

322 class_section = section.add_new_section(shape.name) 

323 class_name = self._exception_class_name(shape) 

324 class_section.style.start_sphinx_py_class(class_name=class_name) 

325 self._add_top_level_documentation(class_section, shape) 

326 self._add_exception_catch_example(class_section, shape) 

327 self._add_response_attr(class_section, shape) 

328 class_section.style.end_sphinx_py_class() 

329 

330 def _add_top_level_documentation(self, section, shape): 

331 if shape.documentation: 

332 section.style.new_line() 

333 section.include_doc_string(shape.documentation) 

334 section.style.new_line() 

335 

336 def _add_exception_catch_example(self, section, shape): 

337 section.style.new_line() 

338 section.style.bold('Example') 

339 section.style.new_paragraph() 

340 section.style.start_codeblock() 

341 section.write('try:') 

342 section.style.indent() 

343 section.style.new_line() 

344 section.write('...') 

345 section.style.dedent() 

346 section.style.new_line() 

347 section.write('except client.exceptions.%s as e:' % shape.name) 

348 section.style.indent() 

349 section.style.new_line() 

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

351 section.style.dedent() 

352 section.style.end_codeblock() 

353 

354 def _add_response_attr(self, section, shape): 

355 response_section = section.add_new_section('response') 

356 response_section.style.start_sphinx_py_attr('response') 

357 self._add_response_attr_description(response_section) 

358 self._add_response_example(response_section, shape) 

359 self._add_response_params(response_section, shape) 

360 response_section.style.end_sphinx_py_attr() 

361 

362 def _add_response_attr_description(self, section): 

363 section.style.new_line() 

364 section.include_doc_string( 

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

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

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

368 'service or exception class.' 

369 ) 

370 section.style.new_line() 

371 

372 def _add_response_example(self, section, shape): 

373 example_section = section.add_new_section('syntax') 

374 example_section.style.new_line() 

375 example_section.style.bold('Syntax') 

376 example_section.style.new_paragraph() 

377 documenter = ResponseExampleDocumenter( 

378 service_name=self._service_name, 

379 operation_name=None, 

380 event_emitter=self._client.meta.events, 

381 ) 

382 documenter.document_example( 

383 example_section, 

384 shape, 

385 include=[self._GENERIC_ERROR_SHAPE], 

386 ) 

387 

388 def _add_response_params(self, section, shape): 

389 params_section = section.add_new_section('Structure') 

390 params_section.style.new_line() 

391 params_section.style.bold('Structure') 

392 params_section.style.new_paragraph() 

393 documenter = ResponseParamsDocumenter( 

394 service_name=self._service_name, 

395 operation_name=None, 

396 event_emitter=self._client.meta.events, 

397 ) 

398 documenter.document_params( 

399 params_section, 

400 shape, 

401 include=[self._GENERIC_ERROR_SHAPE], 

402 ) 

403 

404 

405class ClientContextParamsDocumenter: 

406 _CONFIG_GUIDE_LINK = ( 

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

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

409 ) 

410 

411 OMITTED_CONTEXT_PARAMS = { 

412 's3': ( 

413 'Accelerate', 

414 'DisableMultiRegionAccessPoints', 

415 'ForcePathStyle', 

416 'UseArnRegion', 

417 ), 

418 's3control': ('UseArnRegion',), 

419 } 

420 

421 def __init__(self, service_name, context_params): 

422 self._service_name = service_name 

423 self._context_params = context_params 

424 

425 def document_context_params(self, section): 

426 self._add_title(section) 

427 self._add_overview(section) 

428 self._add_context_params_list(section) 

429 

430 def _add_title(self, section): 

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

432 

433 def _add_overview(self, section): 

434 section.style.new_line() 

435 section.write( 

436 'Client context parameters are configurable on a client ' 

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

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

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

440 ) 

441 section.style.external_link( 

442 title='configuration guide', 

443 link=self._CONFIG_GUIDE_LINK, 

444 ) 

445 section.write('.') 

446 section.style.new_line() 

447 

448 def _add_context_params_list(self, section): 

449 section.style.new_line() 

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

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

452 for param in self._context_params: 

453 section.style.new_line() 

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

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