Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/botocore/args.py: 17%

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

358 statements  

1# Copyright 2016 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. 

13"""Internal module to help with normalizing botocore client args. 

14 

15This module (and all function/classes within this module) should be 

16considered internal, and *not* a public API. 

17 

18""" 

19 

20import copy 

21import logging 

22import socket 

23 

24import botocore.exceptions 

25import botocore.parsers 

26import botocore.serialize 

27from botocore.config import Config 

28from botocore.endpoint import EndpointCreator 

29from botocore.regions import EndpointResolverBuiltins as EPRBuiltins 

30from botocore.regions import EndpointRulesetResolver 

31from botocore.signers import RequestSigner 

32from botocore.useragent import UserAgentString, register_feature_id 

33from botocore.utils import ( 

34 PRIORITY_ORDERED_SUPPORTED_PROTOCOLS, # noqa: F401 

35 ensure_boolean, 

36 is_s3_accelerate_url, 

37) 

38 

39logger = logging.getLogger(__name__) 

40 

41 

42VALID_REGIONAL_ENDPOINTS_CONFIG = [ 

43 'legacy', 

44 'regional', 

45] 

46LEGACY_GLOBAL_STS_REGIONS = [ 

47 'ap-northeast-1', 

48 'ap-south-1', 

49 'ap-southeast-1', 

50 'ap-southeast-2', 

51 'aws-global', 

52 'ca-central-1', 

53 'eu-central-1', 

54 'eu-north-1', 

55 'eu-west-1', 

56 'eu-west-2', 

57 'eu-west-3', 

58 'sa-east-1', 

59 'us-east-1', 

60 'us-east-2', 

61 'us-west-1', 

62 'us-west-2', 

63] 

64# Maximum allowed length of the ``user_agent_appid`` config field. Longer 

65# values result in a warning-level log message. 

66USERAGENT_APPID_MAXLEN = 50 

67 

68VALID_REQUEST_CHECKSUM_CALCULATION_CONFIG = ( 

69 "when_supported", 

70 "when_required", 

71) 

72VALID_RESPONSE_CHECKSUM_VALIDATION_CONFIG = ( 

73 "when_supported", 

74 "when_required", 

75) 

76 

77 

78VALID_ACCOUNT_ID_ENDPOINT_MODE_CONFIG = ( 

79 'preferred', 

80 'disabled', 

81 'required', 

82) 

83 

84 

85class ClientArgsCreator: 

86 def __init__( 

87 self, 

88 event_emitter, 

89 user_agent, 

90 response_parser_factory, 

91 loader, 

92 exceptions_factory, 

93 config_store, 

94 user_agent_creator=None, 

95 ): 

96 self._event_emitter = event_emitter 

97 self._response_parser_factory = response_parser_factory 

98 self._loader = loader 

99 self._exceptions_factory = exceptions_factory 

100 self._config_store = config_store 

101 if user_agent_creator is None: 

102 self._session_ua_creator = UserAgentString.from_environment() 

103 else: 

104 self._session_ua_creator = user_agent_creator 

105 

106 def get_client_args( 

107 self, 

108 service_model, 

109 region_name, 

110 is_secure, 

111 endpoint_url, 

112 verify, 

113 credentials, 

114 scoped_config, 

115 client_config, 

116 endpoint_bridge, 

117 auth_token=None, 

118 endpoints_ruleset_data=None, 

119 partition_data=None, 

120 ): 

121 final_args = self.compute_client_args( 

122 service_model, 

123 client_config, 

124 endpoint_bridge, 

125 region_name, 

126 endpoint_url, 

127 is_secure, 

128 scoped_config, 

129 ) 

130 

131 service_name = final_args['service_name'] # noqa 

132 parameter_validation = final_args['parameter_validation'] 

133 endpoint_config = final_args['endpoint_config'] 

134 protocol = final_args['protocol'] 

135 config_kwargs = final_args['config_kwargs'] 

136 s3_config = final_args['s3_config'] 

137 partition = endpoint_config['metadata'].get('partition', None) 

138 socket_options = final_args['socket_options'] 

139 configured_endpoint_url = final_args['configured_endpoint_url'] 

140 signing_region = endpoint_config['signing_region'] 

141 endpoint_region_name = endpoint_config['region_name'] 

142 account_id_endpoint_mode = config_kwargs['account_id_endpoint_mode'] 

143 

144 event_emitter = copy.copy(self._event_emitter) 

145 signer = RequestSigner( 

146 service_model.service_id, 

147 signing_region, 

148 endpoint_config['signing_name'], 

149 endpoint_config['signature_version'], 

150 credentials, 

151 event_emitter, 

152 auth_token, 

153 ) 

154 

155 config_kwargs['s3'] = s3_config 

156 new_config = Config(**config_kwargs) 

157 endpoint_creator = EndpointCreator(event_emitter) 

158 

159 endpoint = endpoint_creator.create_endpoint( 

160 service_model, 

161 region_name=endpoint_region_name, 

162 endpoint_url=endpoint_config['endpoint_url'], 

163 verify=verify, 

164 response_parser_factory=self._response_parser_factory, 

165 max_pool_connections=new_config.max_pool_connections, 

166 proxies=new_config.proxies, 

167 timeout=(new_config.connect_timeout, new_config.read_timeout), 

168 socket_options=socket_options, 

169 client_cert=new_config.client_cert, 

170 proxies_config=new_config.proxies_config, 

171 ) 

172 

173 # Emit event to allow service-specific or customer customization of serializer kwargs 

174 event_name = f'creating-serializer.{service_name}' 

175 serializer_kwargs = { 

176 'timestamp_precision': botocore.serialize.TIMESTAMP_PRECISION_DEFAULT 

177 } 

178 event_emitter.emit( 

179 event_name, 

180 protocol_name=protocol, 

181 service_model=service_model, 

182 serializer_kwargs=serializer_kwargs, 

183 ) 

184 

185 serializer = botocore.serialize.create_serializer( 

186 protocol, 

187 parameter_validation, 

188 timestamp_precision=serializer_kwargs['timestamp_precision'], 

189 ) 

190 response_parser = botocore.parsers.create_parser(protocol) 

191 

192 ruleset_resolver = self._build_endpoint_resolver( 

193 endpoints_ruleset_data, 

194 partition_data, 

195 client_config, 

196 service_model, 

197 endpoint_region_name, 

198 region_name, 

199 configured_endpoint_url, 

200 endpoint, 

201 is_secure, 

202 endpoint_bridge, 

203 event_emitter, 

204 credentials, 

205 account_id_endpoint_mode, 

206 ) 

207 

208 # Copy the session's user agent factory and adds client configuration. 

209 client_ua_creator = self._session_ua_creator.with_client_config( 

210 new_config 

211 ) 

212 supplied_ua = client_config.user_agent if client_config else None 

213 new_config._supplied_user_agent = supplied_ua 

214 

215 return { 

216 'serializer': serializer, 

217 'endpoint': endpoint, 

218 'response_parser': response_parser, 

219 'event_emitter': event_emitter, 

220 'request_signer': signer, 

221 'service_model': service_model, 

222 'loader': self._loader, 

223 'client_config': new_config, 

224 'partition': partition, 

225 'exceptions_factory': self._exceptions_factory, 

226 'endpoint_ruleset_resolver': ruleset_resolver, 

227 'user_agent_creator': client_ua_creator, 

228 } 

229 

230 def compute_client_args( 

231 self, 

232 service_model, 

233 client_config, 

234 endpoint_bridge, 

235 region_name, 

236 endpoint_url, 

237 is_secure, 

238 scoped_config, 

239 ): 

240 service_name = service_model.endpoint_prefix 

241 protocol = service_model.resolved_protocol 

242 parameter_validation = True 

243 if client_config and not client_config.parameter_validation: 

244 parameter_validation = False 

245 elif scoped_config: 

246 raw_value = scoped_config.get('parameter_validation') 

247 if raw_value is not None: 

248 parameter_validation = ensure_boolean(raw_value) 

249 

250 s3_config = self.compute_s3_config(client_config) 

251 

252 configured_endpoint_url = self._compute_configured_endpoint_url( 

253 client_config=client_config, 

254 endpoint_url=endpoint_url, 

255 ) 

256 if configured_endpoint_url is not None: 

257 register_feature_id('ENDPOINT_OVERRIDE') 

258 

259 endpoint_config = self._compute_endpoint_config( 

260 service_name=service_name, 

261 region_name=region_name, 

262 endpoint_url=configured_endpoint_url, 

263 is_secure=is_secure, 

264 endpoint_bridge=endpoint_bridge, 

265 s3_config=s3_config, 

266 ) 

267 endpoint_variant_tags = endpoint_config['metadata'].get('tags', []) 

268 

269 # Some third-party libraries expect the final user-agent string in 

270 # ``client.meta.config.user_agent``. To maintain backwards 

271 # compatibility, the preliminary user-agent string (before any Config 

272 # object modifications and without request-specific user-agent 

273 # components) is stored in the new Config object's ``user_agent`` 

274 # property but not used by Botocore itself. 

275 preliminary_ua_string = self._session_ua_creator.with_client_config( 

276 client_config 

277 ).to_string() 

278 # Create a new client config to be passed to the client based 

279 # on the final values. We do not want the user to be able 

280 # to try to modify an existing client with a client config. 

281 config_kwargs = dict( 

282 region_name=endpoint_config['region_name'], 

283 signature_version=endpoint_config['signature_version'], 

284 user_agent=preliminary_ua_string, 

285 ) 

286 if 'dualstack' in endpoint_variant_tags: 

287 config_kwargs.update(use_dualstack_endpoint=True) 

288 if 'fips' in endpoint_variant_tags: 

289 config_kwargs.update(use_fips_endpoint=True) 

290 if client_config is not None: 

291 config_kwargs.update( 

292 connect_timeout=client_config.connect_timeout, 

293 read_timeout=client_config.read_timeout, 

294 max_pool_connections=client_config.max_pool_connections, 

295 proxies=client_config.proxies, 

296 proxies_config=client_config.proxies_config, 

297 retries=client_config.retries, 

298 client_cert=client_config.client_cert, 

299 inject_host_prefix=client_config.inject_host_prefix, 

300 tcp_keepalive=client_config.tcp_keepalive, 

301 user_agent_extra=client_config.user_agent_extra, 

302 user_agent_appid=client_config.user_agent_appid, 

303 request_min_compression_size_bytes=( 

304 client_config.request_min_compression_size_bytes 

305 ), 

306 disable_request_compression=( 

307 client_config.disable_request_compression 

308 ), 

309 client_context_params=client_config.client_context_params, 

310 sigv4a_signing_region_set=( 

311 client_config.sigv4a_signing_region_set 

312 ), 

313 request_checksum_calculation=( 

314 client_config.request_checksum_calculation 

315 ), 

316 response_checksum_validation=( 

317 client_config.response_checksum_validation 

318 ), 

319 account_id_endpoint_mode=client_config.account_id_endpoint_mode, 

320 auth_scheme_preference=client_config.auth_scheme_preference, 

321 ) 

322 self._compute_retry_config(config_kwargs) 

323 self._compute_connect_timeout(config_kwargs) 

324 self._compute_user_agent_appid_config(config_kwargs) 

325 self._compute_request_compression_config(config_kwargs) 

326 self._compute_sigv4a_signing_region_set_config(config_kwargs) 

327 self._compute_checksum_config(config_kwargs) 

328 self._compute_account_id_endpoint_mode_config(config_kwargs) 

329 self._compute_inject_host_prefix(client_config, config_kwargs) 

330 self._compute_auth_scheme_preference_config( 

331 client_config, config_kwargs 

332 ) 

333 self._compute_signature_version_config(client_config, config_kwargs) 

334 s3_config = self.compute_s3_config(client_config) 

335 

336 is_s3_service = self._is_s3_service(service_name) 

337 

338 if is_s3_service and 'dualstack' in endpoint_variant_tags: 

339 if s3_config is None: 

340 s3_config = {} 

341 s3_config['use_dualstack_endpoint'] = True 

342 

343 return { 

344 'service_name': service_name, 

345 'parameter_validation': parameter_validation, 

346 'configured_endpoint_url': configured_endpoint_url, 

347 'endpoint_config': endpoint_config, 

348 'protocol': protocol, 

349 'config_kwargs': config_kwargs, 

350 's3_config': s3_config, 

351 'socket_options': self._compute_socket_options( 

352 scoped_config, client_config 

353 ), 

354 } 

355 

356 def _compute_inject_host_prefix(self, client_config, config_kwargs): 

357 # In the cases that a Config object was not provided, or the private value 

358 # remained UNSET, we should resolve the value from the config store. 

359 if ( 

360 client_config is None 

361 or client_config._inject_host_prefix == 'UNSET' 

362 ): 

363 configured_disable_host_prefix_injection = ( 

364 self._config_store.get_config_variable( 

365 'disable_host_prefix_injection' 

366 ) 

367 ) 

368 if configured_disable_host_prefix_injection is not None: 

369 config_kwargs[ 

370 'inject_host_prefix' 

371 ] = not configured_disable_host_prefix_injection 

372 else: 

373 config_kwargs['inject_host_prefix'] = True 

374 

375 def _compute_configured_endpoint_url(self, client_config, endpoint_url): 

376 if endpoint_url is not None: 

377 return endpoint_url 

378 

379 if self._ignore_configured_endpoint_urls(client_config): 

380 logger.debug("Ignoring configured endpoint URLs.") 

381 return endpoint_url 

382 

383 return self._config_store.get_config_variable('endpoint_url') 

384 

385 def _ignore_configured_endpoint_urls(self, client_config): 

386 if ( 

387 client_config 

388 and client_config.ignore_configured_endpoint_urls is not None 

389 ): 

390 return client_config.ignore_configured_endpoint_urls 

391 

392 return self._config_store.get_config_variable( 

393 'ignore_configured_endpoint_urls' 

394 ) 

395 

396 def compute_s3_config(self, client_config): 

397 s3_configuration = self._config_store.get_config_variable('s3') 

398 

399 # Next specific client config values takes precedence over 

400 # specific values in the scoped config. 

401 if client_config is not None: 

402 if client_config.s3 is not None: 

403 if s3_configuration is None: 

404 s3_configuration = client_config.s3 

405 else: 

406 # The current s3_configuration dictionary may be 

407 # from a source that only should be read from so 

408 # we want to be safe and just make a copy of it to modify 

409 # before it actually gets updated. 

410 s3_configuration = s3_configuration.copy() 

411 s3_configuration.update(client_config.s3) 

412 

413 return s3_configuration 

414 

415 def _is_s3_service(self, service_name): 

416 """Whether the service is S3 or S3 Control. 

417 

418 Note that throughout this class, service_name refers to the endpoint 

419 prefix, not the folder name of the service in botocore/data. For 

420 S3 Control, the folder name is 's3control' but the endpoint prefix is 

421 's3-control'. 

422 """ 

423 return service_name in ['s3', 's3-control'] 

424 

425 def _compute_endpoint_config( 

426 self, 

427 service_name, 

428 region_name, 

429 endpoint_url, 

430 is_secure, 

431 endpoint_bridge, 

432 s3_config, 

433 ): 

434 resolve_endpoint_kwargs = { 

435 'service_name': service_name, 

436 'region_name': region_name, 

437 'endpoint_url': endpoint_url, 

438 'is_secure': is_secure, 

439 'endpoint_bridge': endpoint_bridge, 

440 } 

441 if service_name == 's3': 

442 return self._compute_s3_endpoint_config( 

443 s3_config=s3_config, **resolve_endpoint_kwargs 

444 ) 

445 if service_name == 'sts': 

446 return self._compute_sts_endpoint_config(**resolve_endpoint_kwargs) 

447 return self._resolve_endpoint(**resolve_endpoint_kwargs) 

448 

449 def _compute_s3_endpoint_config( 

450 self, s3_config, **resolve_endpoint_kwargs 

451 ): 

452 force_s3_global = self._should_force_s3_global( 

453 resolve_endpoint_kwargs['region_name'], s3_config 

454 ) 

455 if force_s3_global: 

456 resolve_endpoint_kwargs['region_name'] = None 

457 endpoint_config = self._resolve_endpoint(**resolve_endpoint_kwargs) 

458 self._set_region_if_custom_s3_endpoint( 

459 endpoint_config, resolve_endpoint_kwargs['endpoint_bridge'] 

460 ) 

461 # For backwards compatibility reasons, we want to make sure the 

462 # client.meta.region_name will remain us-east-1 if we forced the 

463 # endpoint to be the global region. Specifically, if this value 

464 # changes to aws-global, it breaks logic where a user is checking 

465 # for us-east-1 as the global endpoint such as in creating buckets. 

466 if force_s3_global and endpoint_config['region_name'] == 'aws-global': 

467 endpoint_config['region_name'] = 'us-east-1' 

468 return endpoint_config 

469 

470 def _should_force_s3_global(self, region_name, s3_config): 

471 s3_regional_config = 'legacy' 

472 if s3_config and 'us_east_1_regional_endpoint' in s3_config: 

473 s3_regional_config = s3_config['us_east_1_regional_endpoint'] 

474 self._validate_s3_regional_config(s3_regional_config) 

475 

476 is_global_region = region_name in ('us-east-1', None) 

477 return s3_regional_config == 'legacy' and is_global_region 

478 

479 def _validate_s3_regional_config(self, config_val): 

480 if config_val not in VALID_REGIONAL_ENDPOINTS_CONFIG: 

481 raise botocore.exceptions.InvalidS3UsEast1RegionalEndpointConfigError( 

482 s3_us_east_1_regional_endpoint_config=config_val 

483 ) 

484 

485 def _set_region_if_custom_s3_endpoint( 

486 self, endpoint_config, endpoint_bridge 

487 ): 

488 # If a user is providing a custom URL, the endpoint resolver will 

489 # refuse to infer a signing region. If we want to default to s3v4, 

490 # we have to account for this. 

491 if ( 

492 endpoint_config['signing_region'] is None 

493 and endpoint_config['region_name'] is None 

494 ): 

495 endpoint = endpoint_bridge.resolve('s3') 

496 endpoint_config['signing_region'] = endpoint['signing_region'] 

497 endpoint_config['region_name'] = endpoint['region_name'] 

498 

499 def _compute_sts_endpoint_config(self, **resolve_endpoint_kwargs): 

500 endpoint_config = self._resolve_endpoint(**resolve_endpoint_kwargs) 

501 if self._should_set_global_sts_endpoint( 

502 resolve_endpoint_kwargs['region_name'], 

503 resolve_endpoint_kwargs['endpoint_url'], 

504 endpoint_config, 

505 ): 

506 self._set_global_sts_endpoint( 

507 endpoint_config, resolve_endpoint_kwargs['is_secure'] 

508 ) 

509 return endpoint_config 

510 

511 def _should_set_global_sts_endpoint( 

512 self, region_name, endpoint_url, endpoint_config 

513 ): 

514 has_variant_tags = endpoint_config and endpoint_config.get( 

515 'metadata', {} 

516 ).get('tags') 

517 if endpoint_url or has_variant_tags: 

518 return False 

519 return ( 

520 self._get_sts_regional_endpoints_config() == 'legacy' 

521 and region_name in LEGACY_GLOBAL_STS_REGIONS 

522 ) 

523 

524 def _get_sts_regional_endpoints_config(self): 

525 sts_regional_endpoints_config = self._config_store.get_config_variable( 

526 'sts_regional_endpoints' 

527 ) 

528 if not sts_regional_endpoints_config: 

529 sts_regional_endpoints_config = 'regional' 

530 if ( 

531 sts_regional_endpoints_config 

532 not in VALID_REGIONAL_ENDPOINTS_CONFIG 

533 ): 

534 raise botocore.exceptions.InvalidSTSRegionalEndpointsConfigError( 

535 sts_regional_endpoints_config=sts_regional_endpoints_config 

536 ) 

537 return sts_regional_endpoints_config 

538 

539 def _set_global_sts_endpoint(self, endpoint_config, is_secure): 

540 scheme = 'https' if is_secure else 'http' 

541 endpoint_config['endpoint_url'] = f'{scheme}://sts.amazonaws.com' 

542 endpoint_config['signing_region'] = 'us-east-1' 

543 

544 def _resolve_endpoint( 

545 self, 

546 service_name, 

547 region_name, 

548 endpoint_url, 

549 is_secure, 

550 endpoint_bridge, 

551 ): 

552 return endpoint_bridge.resolve( 

553 service_name, region_name, endpoint_url, is_secure 

554 ) 

555 

556 def _compute_socket_options(self, scoped_config, client_config=None): 

557 # This disables Nagle's algorithm and is the default socket options 

558 # in urllib3. 

559 socket_options = [(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)] 

560 client_keepalive = client_config and client_config.tcp_keepalive 

561 scoped_keepalive = scoped_config and self._ensure_boolean( 

562 scoped_config.get("tcp_keepalive", False) 

563 ) 

564 # Enables TCP Keepalive if specified in client config object or shared config file. 

565 if client_keepalive or scoped_keepalive: 

566 socket_options.append((socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)) 

567 return socket_options 

568 

569 def _compute_retry_config(self, config_kwargs): 

570 self._compute_retry_max_attempts(config_kwargs) 

571 self._compute_retry_mode(config_kwargs) 

572 

573 def _compute_retry_max_attempts(self, config_kwargs): 

574 # There's a pre-existing max_attempts client config value that actually 

575 # means max *retry* attempts. There's also a `max_attempts` we pull 

576 # from the config store that means *total attempts*, which includes the 

577 # intitial request. We can't change what `max_attempts` means in 

578 # client config so we try to normalize everything to a new 

579 # "total_max_attempts" variable. We ensure that after this, the only 

580 # configuration for "max attempts" is the 'total_max_attempts' key. 

581 # An explicitly provided max_attempts in the client config 

582 # overrides everything. 

583 retries = config_kwargs.get('retries') 

584 if retries is not None: 

585 if 'total_max_attempts' in retries: 

586 retries.pop('max_attempts', None) 

587 return 

588 if 'max_attempts' in retries: 

589 value = retries.pop('max_attempts') 

590 # client config max_attempts means total retries so we 

591 # have to add one for 'total_max_attempts' to account 

592 # for the initial request. 

593 retries['total_max_attempts'] = value + 1 

594 return 

595 # Otherwise we'll check the config store which checks env vars, 

596 # config files, etc. There is no default value for max_attempts 

597 # so if this returns None and we don't set a default value here. 

598 max_attempts = self._config_store.get_config_variable('max_attempts') 

599 if max_attempts is not None: 

600 if retries is None: 

601 retries = {} 

602 config_kwargs['retries'] = retries 

603 retries['total_max_attempts'] = max_attempts 

604 

605 def _compute_retry_mode(self, config_kwargs): 

606 retries = config_kwargs.get('retries') 

607 if retries is None: 

608 retries = {} 

609 config_kwargs['retries'] = retries 

610 elif 'mode' in retries: 

611 # If there's a retry mode explicitly set in the client config 

612 # that overrides everything. 

613 return 

614 retry_mode = self._config_store.get_config_variable('retry_mode') 

615 if retry_mode is None: 

616 retry_mode = 'legacy' 

617 retries['mode'] = retry_mode 

618 

619 def _compute_connect_timeout(self, config_kwargs): 

620 # Checking if connect_timeout is set on the client config. 

621 # If it is not, we check the config_store in case a 

622 # non legacy default mode has been configured. 

623 connect_timeout = config_kwargs.get('connect_timeout') 

624 if connect_timeout is not None: 

625 return 

626 connect_timeout = self._config_store.get_config_variable( 

627 'connect_timeout' 

628 ) 

629 if connect_timeout: 

630 config_kwargs['connect_timeout'] = connect_timeout 

631 

632 def _compute_request_compression_config(self, config_kwargs): 

633 min_size = config_kwargs.get('request_min_compression_size_bytes') 

634 disabled = config_kwargs.get('disable_request_compression') 

635 if min_size is None: 

636 min_size = self._config_store.get_config_variable( 

637 'request_min_compression_size_bytes' 

638 ) 

639 # conversion func is skipped so input validation must be done here 

640 # regardless if the value is coming from the config store or the 

641 # config object 

642 min_size = self._validate_min_compression_size(min_size) 

643 config_kwargs['request_min_compression_size_bytes'] = min_size 

644 

645 if disabled is None: 

646 disabled = self._config_store.get_config_variable( 

647 'disable_request_compression' 

648 ) 

649 else: 

650 # if the user provided a value we must check if it's a boolean 

651 disabled = ensure_boolean(disabled) 

652 config_kwargs['disable_request_compression'] = disabled 

653 

654 def _validate_min_compression_size(self, min_size): 

655 min_allowed_min_size = 1 

656 max_allowed_min_size = 1048576 

657 error_msg_base = ( 

658 f'Invalid value "{min_size}" for ' 

659 'request_min_compression_size_bytes.' 

660 ) 

661 try: 

662 min_size = int(min_size) 

663 except (ValueError, TypeError): 

664 msg = ( 

665 f'{error_msg_base} Value must be an integer. ' 

666 f'Received {type(min_size)} instead.' 

667 ) 

668 raise botocore.exceptions.InvalidConfigError(error_msg=msg) 

669 if not min_allowed_min_size <= min_size <= max_allowed_min_size: 

670 msg = ( 

671 f'{error_msg_base} Value must be between ' 

672 f'{min_allowed_min_size} and {max_allowed_min_size}.' 

673 ) 

674 raise botocore.exceptions.InvalidConfigError(error_msg=msg) 

675 

676 return min_size 

677 

678 def _ensure_boolean(self, val): 

679 if isinstance(val, bool): 

680 return val 

681 else: 

682 return val.lower() == 'true' 

683 

684 def _build_endpoint_resolver( 

685 self, 

686 endpoints_ruleset_data, 

687 partition_data, 

688 client_config, 

689 service_model, 

690 endpoint_region_name, 

691 region_name, 

692 endpoint_url, 

693 endpoint, 

694 is_secure, 

695 endpoint_bridge, 

696 event_emitter, 

697 credentials, 

698 account_id_endpoint_mode, 

699 ): 

700 if endpoints_ruleset_data is None: 

701 return None 

702 

703 # The legacy EndpointResolver is global to the session, but 

704 # EndpointRulesetResolver is service-specific. Builtins for 

705 # EndpointRulesetResolver must not be derived from the legacy 

706 # endpoint resolver's output, including final_args, s3_config, 

707 # etc. 

708 s3_config_raw = self.compute_s3_config(client_config) or {} 

709 service_name_raw = service_model.endpoint_prefix 

710 # Maintain complex logic for s3 and sts endpoints for backwards 

711 # compatibility. 

712 if service_name_raw in ['s3', 'sts'] or region_name is None: 

713 eprv2_region_name = endpoint_region_name 

714 else: 

715 eprv2_region_name = region_name 

716 resolver_builtins = self.compute_endpoint_resolver_builtin_defaults( 

717 region_name=eprv2_region_name, 

718 service_name=service_name_raw, 

719 s3_config=s3_config_raw, 

720 endpoint_bridge=endpoint_bridge, 

721 client_endpoint_url=endpoint_url, 

722 legacy_endpoint_url=endpoint.host, 

723 credentials=credentials, 

724 account_id_endpoint_mode=account_id_endpoint_mode, 

725 ) 

726 # Client context params for s3 conflict with the available settings 

727 # in the `s3` parameter on the `Config` object. If the same parameter 

728 # is set in both places, the value in the `s3` parameter takes priority. 

729 if client_config is not None: 

730 client_context = client_config.client_context_params or {} 

731 else: 

732 client_context = {} 

733 if self._is_s3_service(service_name_raw): 

734 client_context.update(s3_config_raw) 

735 

736 sig_version = ( 

737 client_config.signature_version 

738 if client_config is not None 

739 else None 

740 ) 

741 return EndpointRulesetResolver( 

742 endpoint_ruleset_data=endpoints_ruleset_data, 

743 partition_data=partition_data, 

744 service_model=service_model, 

745 builtins=resolver_builtins, 

746 client_context=client_context, 

747 event_emitter=event_emitter, 

748 use_ssl=is_secure, 

749 requested_auth_scheme=sig_version, 

750 ) 

751 

752 def compute_endpoint_resolver_builtin_defaults( 

753 self, 

754 region_name, 

755 service_name, 

756 s3_config, 

757 endpoint_bridge, 

758 client_endpoint_url, 

759 legacy_endpoint_url, 

760 credentials, 

761 account_id_endpoint_mode, 

762 ): 

763 # EndpointRulesetResolver rulesets may accept an "SDK::Endpoint" as 

764 # input. If the endpoint_url argument of create_client() is set, it 

765 # always takes priority. 

766 if client_endpoint_url: 

767 given_endpoint = client_endpoint_url 

768 # If an endpoints.json data file other than the one bundled within 

769 # the botocore/data directory is used, the output of legacy 

770 # endpoint resolution is provided to EndpointRulesetResolver. 

771 elif not endpoint_bridge.resolver_uses_builtin_data(): 

772 given_endpoint = legacy_endpoint_url 

773 else: 

774 given_endpoint = None 

775 

776 # The endpoint rulesets differ from legacy botocore behavior in whether 

777 # forcing path style addressing in incompatible situations raises an 

778 # exception or silently ignores the config setting. The 

779 # AWS_S3_FORCE_PATH_STYLE parameter is adjusted both here and for each 

780 # operation so that the ruleset behavior is backwards compatible. 

781 if s3_config.get('use_accelerate_endpoint', False): 

782 force_path_style = False 

783 elif client_endpoint_url is not None and not is_s3_accelerate_url( 

784 client_endpoint_url 

785 ): 

786 force_path_style = s3_config.get('addressing_style') != 'virtual' 

787 else: 

788 force_path_style = s3_config.get('addressing_style') == 'path' 

789 

790 return { 

791 EPRBuiltins.AWS_REGION: region_name, 

792 EPRBuiltins.AWS_USE_FIPS: ( 

793 # SDK_ENDPOINT cannot be combined with AWS_USE_FIPS 

794 given_endpoint is None 

795 # use legacy resolver's _resolve_endpoint_variant_config_var() 

796 # or default to False if it returns None 

797 and endpoint_bridge._resolve_endpoint_variant_config_var( 

798 'use_fips_endpoint' 

799 ) 

800 or False 

801 ), 

802 EPRBuiltins.AWS_USE_DUALSTACK: ( 

803 # SDK_ENDPOINT cannot be combined with AWS_USE_DUALSTACK 

804 given_endpoint is None 

805 # use legacy resolver's _resolve_use_dualstack_endpoint() and 

806 # or default to False if it returns None 

807 and endpoint_bridge._resolve_use_dualstack_endpoint( 

808 service_name 

809 ) 

810 or False 

811 ), 

812 EPRBuiltins.AWS_STS_USE_GLOBAL_ENDPOINT: ( 

813 self._should_set_global_sts_endpoint( 

814 region_name=region_name, 

815 endpoint_url=None, 

816 endpoint_config=None, 

817 ) 

818 ), 

819 EPRBuiltins.AWS_S3_USE_GLOBAL_ENDPOINT: ( 

820 self._should_force_s3_global(region_name, s3_config) 

821 ), 

822 EPRBuiltins.AWS_S3_ACCELERATE: s3_config.get( 

823 'use_accelerate_endpoint', False 

824 ), 

825 EPRBuiltins.AWS_S3_FORCE_PATH_STYLE: force_path_style, 

826 EPRBuiltins.AWS_S3_USE_ARN_REGION: s3_config.get( 

827 'use_arn_region', True 

828 ), 

829 EPRBuiltins.AWS_S3CONTROL_USE_ARN_REGION: s3_config.get( 

830 'use_arn_region', False 

831 ), 

832 EPRBuiltins.AWS_S3_DISABLE_MRAP: s3_config.get( 

833 's3_disable_multiregion_access_points', False 

834 ), 

835 EPRBuiltins.SDK_ENDPOINT: given_endpoint, 

836 EPRBuiltins.ACCOUNT_ID: credentials.get_deferred_property( 

837 'account_id' 

838 ) 

839 if credentials 

840 else None, 

841 EPRBuiltins.ACCOUNT_ID_ENDPOINT_MODE: account_id_endpoint_mode, 

842 } 

843 

844 def _compute_user_agent_appid_config(self, config_kwargs): 

845 user_agent_appid = config_kwargs.get('user_agent_appid') 

846 if user_agent_appid is None: 

847 user_agent_appid = self._config_store.get_config_variable( 

848 'user_agent_appid' 

849 ) 

850 if ( 

851 user_agent_appid is not None 

852 and len(user_agent_appid) > USERAGENT_APPID_MAXLEN 

853 ): 

854 logger.warning( 

855 'The configured value for user_agent_appid exceeds the ' 

856 'maximum length of %d characters.', 

857 USERAGENT_APPID_MAXLEN, 

858 ) 

859 config_kwargs['user_agent_appid'] = user_agent_appid 

860 

861 def _compute_sigv4a_signing_region_set_config(self, config_kwargs): 

862 sigv4a_signing_region_set = config_kwargs.get( 

863 'sigv4a_signing_region_set' 

864 ) 

865 if sigv4a_signing_region_set is None: 

866 sigv4a_signing_region_set = self._config_store.get_config_variable( 

867 'sigv4a_signing_region_set' 

868 ) 

869 config_kwargs['sigv4a_signing_region_set'] = sigv4a_signing_region_set 

870 

871 def _compute_checksum_config(self, config_kwargs): 

872 self._handle_checksum_config( 

873 config_kwargs, 

874 config_key="request_checksum_calculation", 

875 valid_options=VALID_REQUEST_CHECKSUM_CALCULATION_CONFIG, 

876 ) 

877 self._handle_checksum_config( 

878 config_kwargs, 

879 config_key="response_checksum_validation", 

880 valid_options=VALID_RESPONSE_CHECKSUM_VALIDATION_CONFIG, 

881 ) 

882 

883 def _handle_checksum_config( 

884 self, 

885 config_kwargs, 

886 config_key, 

887 valid_options, 

888 ): 

889 value = config_kwargs.get(config_key) 

890 if value is None: 

891 value = self._config_store.get_config_variable(config_key) 

892 

893 if isinstance(value, str): 

894 value = value.lower() 

895 

896 if value not in valid_options: 

897 raise botocore.exceptions.InvalidChecksumConfigError( 

898 config_key=config_key, 

899 config_value=value, 

900 valid_options=valid_options, 

901 ) 

902 self._register_checksum_config_feature_ids(value, config_key) 

903 config_kwargs[config_key] = value 

904 

905 def _register_checksum_config_feature_ids(self, value, config_key): 

906 checksum_config_feature_id = None 

907 if config_key == "request_checksum_calculation": 

908 checksum_config_feature_id = ( 

909 f"FLEXIBLE_CHECKSUMS_REQ_{value.upper()}" 

910 ) 

911 elif config_key == "response_checksum_validation": 

912 checksum_config_feature_id = ( 

913 f"FLEXIBLE_CHECKSUMS_RES_{value.upper()}" 

914 ) 

915 if checksum_config_feature_id is not None: 

916 register_feature_id(checksum_config_feature_id) 

917 

918 def _compute_account_id_endpoint_mode_config(self, config_kwargs): 

919 config_key = 'account_id_endpoint_mode' 

920 

921 # Disable account id based endpoint routing for unsigned requests 

922 # since there are no credentials to resolve. 

923 signature_version = config_kwargs.get('signature_version') 

924 if signature_version is botocore.UNSIGNED: 

925 config_kwargs[config_key] = 'disabled' 

926 return 

927 

928 account_id_endpoint_mode = config_kwargs.get(config_key) 

929 if account_id_endpoint_mode is None: 

930 account_id_endpoint_mode = self._config_store.get_config_variable( 

931 config_key 

932 ) 

933 

934 if isinstance(account_id_endpoint_mode, str): 

935 account_id_endpoint_mode = account_id_endpoint_mode.lower() 

936 

937 if ( 

938 account_id_endpoint_mode 

939 not in VALID_ACCOUNT_ID_ENDPOINT_MODE_CONFIG 

940 ): 

941 raise botocore.exceptions.InvalidConfigError( 

942 error_msg=f"The configured value '{account_id_endpoint_mode}' for '{config_key}' is " 

943 f"invalid. Valid values are: {VALID_ACCOUNT_ID_ENDPOINT_MODE_CONFIG}." 

944 ) 

945 

946 config_kwargs[config_key] = account_id_endpoint_mode 

947 

948 def _compute_auth_scheme_preference_config( 

949 self, client_config, config_kwargs 

950 ): 

951 config_key = 'auth_scheme_preference' 

952 set_in_config_object = False 

953 

954 if client_config and client_config.auth_scheme_preference: 

955 value = client_config.auth_scheme_preference 

956 set_in_config_object = True 

957 else: 

958 value = self._config_store.get_config_variable(config_key) 

959 

960 if value is None: 

961 config_kwargs[config_key] = None 

962 return 

963 

964 if not isinstance(value, str): 

965 raise botocore.exceptions.InvalidConfigError( 

966 error_msg=( 

967 f"{config_key} must be a comma-delimited string. " 

968 f"Received {type(value)} instead: {value}." 

969 ) 

970 ) 

971 

972 value = ','.join( 

973 item.replace(' ', '').replace('\t', '') 

974 for item in value.split(',') 

975 if item.strip() 

976 ) 

977 

978 if set_in_config_object: 

979 value = ClientConfigString(value) 

980 

981 config_kwargs[config_key] = value 

982 

983 def _compute_signature_version_config(self, client_config, config_kwargs): 

984 if client_config and client_config.signature_version: 

985 value = client_config.signature_version 

986 if isinstance(value, str): 

987 config_kwargs['signature_version'] = ClientConfigString(value) 

988 

989 

990class ConfigObjectWrapper: 

991 """Base class to mark values set via in-code Config object.""" 

992 

993 pass 

994 

995 

996class ClientConfigString(str, ConfigObjectWrapper): 

997 def __new__(cls, value=None): 

998 return super().__new__(cls, value)