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

355 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 serializer = botocore.serialize.create_serializer( 

174 protocol, parameter_validation 

175 ) 

176 response_parser = botocore.parsers.create_parser(protocol) 

177 

178 ruleset_resolver = self._build_endpoint_resolver( 

179 endpoints_ruleset_data, 

180 partition_data, 

181 client_config, 

182 service_model, 

183 endpoint_region_name, 

184 region_name, 

185 configured_endpoint_url, 

186 endpoint, 

187 is_secure, 

188 endpoint_bridge, 

189 event_emitter, 

190 credentials, 

191 account_id_endpoint_mode, 

192 ) 

193 

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

195 client_ua_creator = self._session_ua_creator.with_client_config( 

196 new_config 

197 ) 

198 supplied_ua = client_config.user_agent if client_config else None 

199 new_config._supplied_user_agent = supplied_ua 

200 

201 return { 

202 'serializer': serializer, 

203 'endpoint': endpoint, 

204 'response_parser': response_parser, 

205 'event_emitter': event_emitter, 

206 'request_signer': signer, 

207 'service_model': service_model, 

208 'loader': self._loader, 

209 'client_config': new_config, 

210 'partition': partition, 

211 'exceptions_factory': self._exceptions_factory, 

212 'endpoint_ruleset_resolver': ruleset_resolver, 

213 'user_agent_creator': client_ua_creator, 

214 } 

215 

216 def compute_client_args( 

217 self, 

218 service_model, 

219 client_config, 

220 endpoint_bridge, 

221 region_name, 

222 endpoint_url, 

223 is_secure, 

224 scoped_config, 

225 ): 

226 service_name = service_model.endpoint_prefix 

227 protocol = service_model.resolved_protocol 

228 parameter_validation = True 

229 if client_config and not client_config.parameter_validation: 

230 parameter_validation = False 

231 elif scoped_config: 

232 raw_value = scoped_config.get('parameter_validation') 

233 if raw_value is not None: 

234 parameter_validation = ensure_boolean(raw_value) 

235 

236 s3_config = self.compute_s3_config(client_config) 

237 

238 configured_endpoint_url = self._compute_configured_endpoint_url( 

239 client_config=client_config, 

240 endpoint_url=endpoint_url, 

241 ) 

242 if configured_endpoint_url is not None: 

243 register_feature_id('ENDPOINT_OVERRIDE') 

244 

245 endpoint_config = self._compute_endpoint_config( 

246 service_name=service_name, 

247 region_name=region_name, 

248 endpoint_url=configured_endpoint_url, 

249 is_secure=is_secure, 

250 endpoint_bridge=endpoint_bridge, 

251 s3_config=s3_config, 

252 ) 

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

254 

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

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

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

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

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

260 # property but not used by Botocore itself. 

261 preliminary_ua_string = self._session_ua_creator.with_client_config( 

262 client_config 

263 ).to_string() 

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

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

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

267 config_kwargs = dict( 

268 region_name=endpoint_config['region_name'], 

269 signature_version=endpoint_config['signature_version'], 

270 user_agent=preliminary_ua_string, 

271 ) 

272 if 'dualstack' in endpoint_variant_tags: 

273 config_kwargs.update(use_dualstack_endpoint=True) 

274 if 'fips' in endpoint_variant_tags: 

275 config_kwargs.update(use_fips_endpoint=True) 

276 if client_config is not None: 

277 config_kwargs.update( 

278 connect_timeout=client_config.connect_timeout, 

279 read_timeout=client_config.read_timeout, 

280 max_pool_connections=client_config.max_pool_connections, 

281 proxies=client_config.proxies, 

282 proxies_config=client_config.proxies_config, 

283 retries=client_config.retries, 

284 client_cert=client_config.client_cert, 

285 inject_host_prefix=client_config.inject_host_prefix, 

286 tcp_keepalive=client_config.tcp_keepalive, 

287 user_agent_extra=client_config.user_agent_extra, 

288 user_agent_appid=client_config.user_agent_appid, 

289 request_min_compression_size_bytes=( 

290 client_config.request_min_compression_size_bytes 

291 ), 

292 disable_request_compression=( 

293 client_config.disable_request_compression 

294 ), 

295 client_context_params=client_config.client_context_params, 

296 sigv4a_signing_region_set=( 

297 client_config.sigv4a_signing_region_set 

298 ), 

299 request_checksum_calculation=( 

300 client_config.request_checksum_calculation 

301 ), 

302 response_checksum_validation=( 

303 client_config.response_checksum_validation 

304 ), 

305 account_id_endpoint_mode=client_config.account_id_endpoint_mode, 

306 auth_scheme_preference=client_config.auth_scheme_preference, 

307 ) 

308 self._compute_retry_config(config_kwargs) 

309 self._compute_connect_timeout(config_kwargs) 

310 self._compute_user_agent_appid_config(config_kwargs) 

311 self._compute_request_compression_config(config_kwargs) 

312 self._compute_sigv4a_signing_region_set_config(config_kwargs) 

313 self._compute_checksum_config(config_kwargs) 

314 self._compute_account_id_endpoint_mode_config(config_kwargs) 

315 self._compute_inject_host_prefix(client_config, config_kwargs) 

316 self._compute_auth_scheme_preference_config( 

317 client_config, config_kwargs 

318 ) 

319 self._compute_signature_version_config(client_config, config_kwargs) 

320 s3_config = self.compute_s3_config(client_config) 

321 

322 is_s3_service = self._is_s3_service(service_name) 

323 

324 if is_s3_service and 'dualstack' in endpoint_variant_tags: 

325 if s3_config is None: 

326 s3_config = {} 

327 s3_config['use_dualstack_endpoint'] = True 

328 

329 return { 

330 'service_name': service_name, 

331 'parameter_validation': parameter_validation, 

332 'configured_endpoint_url': configured_endpoint_url, 

333 'endpoint_config': endpoint_config, 

334 'protocol': protocol, 

335 'config_kwargs': config_kwargs, 

336 's3_config': s3_config, 

337 'socket_options': self._compute_socket_options( 

338 scoped_config, client_config 

339 ), 

340 } 

341 

342 def _compute_inject_host_prefix(self, client_config, config_kwargs): 

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

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

345 if ( 

346 client_config is None 

347 or client_config._inject_host_prefix == 'UNSET' 

348 ): 

349 configured_disable_host_prefix_injection = ( 

350 self._config_store.get_config_variable( 

351 'disable_host_prefix_injection' 

352 ) 

353 ) 

354 if configured_disable_host_prefix_injection is not None: 

355 config_kwargs[ 

356 'inject_host_prefix' 

357 ] = not configured_disable_host_prefix_injection 

358 else: 

359 config_kwargs['inject_host_prefix'] = True 

360 

361 def _compute_configured_endpoint_url(self, client_config, endpoint_url): 

362 if endpoint_url is not None: 

363 return endpoint_url 

364 

365 if self._ignore_configured_endpoint_urls(client_config): 

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

367 return endpoint_url 

368 

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

370 

371 def _ignore_configured_endpoint_urls(self, client_config): 

372 if ( 

373 client_config 

374 and client_config.ignore_configured_endpoint_urls is not None 

375 ): 

376 return client_config.ignore_configured_endpoint_urls 

377 

378 return self._config_store.get_config_variable( 

379 'ignore_configured_endpoint_urls' 

380 ) 

381 

382 def compute_s3_config(self, client_config): 

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

384 

385 # Next specific client config values takes precedence over 

386 # specific values in the scoped config. 

387 if client_config is not None: 

388 if client_config.s3 is not None: 

389 if s3_configuration is None: 

390 s3_configuration = client_config.s3 

391 else: 

392 # The current s3_configuration dictionary may be 

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

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

395 # before it actually gets updated. 

396 s3_configuration = s3_configuration.copy() 

397 s3_configuration.update(client_config.s3) 

398 

399 return s3_configuration 

400 

401 def _is_s3_service(self, service_name): 

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

403 

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

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

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

407 's3-control'. 

408 """ 

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

410 

411 def _compute_endpoint_config( 

412 self, 

413 service_name, 

414 region_name, 

415 endpoint_url, 

416 is_secure, 

417 endpoint_bridge, 

418 s3_config, 

419 ): 

420 resolve_endpoint_kwargs = { 

421 'service_name': service_name, 

422 'region_name': region_name, 

423 'endpoint_url': endpoint_url, 

424 'is_secure': is_secure, 

425 'endpoint_bridge': endpoint_bridge, 

426 } 

427 if service_name == 's3': 

428 return self._compute_s3_endpoint_config( 

429 s3_config=s3_config, **resolve_endpoint_kwargs 

430 ) 

431 if service_name == 'sts': 

432 return self._compute_sts_endpoint_config(**resolve_endpoint_kwargs) 

433 return self._resolve_endpoint(**resolve_endpoint_kwargs) 

434 

435 def _compute_s3_endpoint_config( 

436 self, s3_config, **resolve_endpoint_kwargs 

437 ): 

438 force_s3_global = self._should_force_s3_global( 

439 resolve_endpoint_kwargs['region_name'], s3_config 

440 ) 

441 if force_s3_global: 

442 resolve_endpoint_kwargs['region_name'] = None 

443 endpoint_config = self._resolve_endpoint(**resolve_endpoint_kwargs) 

444 self._set_region_if_custom_s3_endpoint( 

445 endpoint_config, resolve_endpoint_kwargs['endpoint_bridge'] 

446 ) 

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

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

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

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

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

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

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

454 return endpoint_config 

455 

456 def _should_force_s3_global(self, region_name, s3_config): 

457 s3_regional_config = 'legacy' 

458 if s3_config and 'us_east_1_regional_endpoint' in s3_config: 

459 s3_regional_config = s3_config['us_east_1_regional_endpoint'] 

460 self._validate_s3_regional_config(s3_regional_config) 

461 

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

463 return s3_regional_config == 'legacy' and is_global_region 

464 

465 def _validate_s3_regional_config(self, config_val): 

466 if config_val not in VALID_REGIONAL_ENDPOINTS_CONFIG: 

467 raise botocore.exceptions.InvalidS3UsEast1RegionalEndpointConfigError( 

468 s3_us_east_1_regional_endpoint_config=config_val 

469 ) 

470 

471 def _set_region_if_custom_s3_endpoint( 

472 self, endpoint_config, endpoint_bridge 

473 ): 

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

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

476 # we have to account for this. 

477 if ( 

478 endpoint_config['signing_region'] is None 

479 and endpoint_config['region_name'] is None 

480 ): 

481 endpoint = endpoint_bridge.resolve('s3') 

482 endpoint_config['signing_region'] = endpoint['signing_region'] 

483 endpoint_config['region_name'] = endpoint['region_name'] 

484 

485 def _compute_sts_endpoint_config(self, **resolve_endpoint_kwargs): 

486 endpoint_config = self._resolve_endpoint(**resolve_endpoint_kwargs) 

487 if self._should_set_global_sts_endpoint( 

488 resolve_endpoint_kwargs['region_name'], 

489 resolve_endpoint_kwargs['endpoint_url'], 

490 endpoint_config, 

491 ): 

492 self._set_global_sts_endpoint( 

493 endpoint_config, resolve_endpoint_kwargs['is_secure'] 

494 ) 

495 return endpoint_config 

496 

497 def _should_set_global_sts_endpoint( 

498 self, region_name, endpoint_url, endpoint_config 

499 ): 

500 has_variant_tags = endpoint_config and endpoint_config.get( 

501 'metadata', {} 

502 ).get('tags') 

503 if endpoint_url or has_variant_tags: 

504 return False 

505 return ( 

506 self._get_sts_regional_endpoints_config() == 'legacy' 

507 and region_name in LEGACY_GLOBAL_STS_REGIONS 

508 ) 

509 

510 def _get_sts_regional_endpoints_config(self): 

511 sts_regional_endpoints_config = self._config_store.get_config_variable( 

512 'sts_regional_endpoints' 

513 ) 

514 if not sts_regional_endpoints_config: 

515 sts_regional_endpoints_config = 'legacy' 

516 if ( 

517 sts_regional_endpoints_config 

518 not in VALID_REGIONAL_ENDPOINTS_CONFIG 

519 ): 

520 raise botocore.exceptions.InvalidSTSRegionalEndpointsConfigError( 

521 sts_regional_endpoints_config=sts_regional_endpoints_config 

522 ) 

523 return sts_regional_endpoints_config 

524 

525 def _set_global_sts_endpoint(self, endpoint_config, is_secure): 

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

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

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

529 

530 def _resolve_endpoint( 

531 self, 

532 service_name, 

533 region_name, 

534 endpoint_url, 

535 is_secure, 

536 endpoint_bridge, 

537 ): 

538 return endpoint_bridge.resolve( 

539 service_name, region_name, endpoint_url, is_secure 

540 ) 

541 

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

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

544 # in urllib3. 

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

546 client_keepalive = client_config and client_config.tcp_keepalive 

547 scoped_keepalive = scoped_config and self._ensure_boolean( 

548 scoped_config.get("tcp_keepalive", False) 

549 ) 

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

551 if client_keepalive or scoped_keepalive: 

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

553 return socket_options 

554 

555 def _compute_retry_config(self, config_kwargs): 

556 self._compute_retry_max_attempts(config_kwargs) 

557 self._compute_retry_mode(config_kwargs) 

558 

559 def _compute_retry_max_attempts(self, config_kwargs): 

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

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

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

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

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

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

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

567 # An explicitly provided max_attempts in the client config 

568 # overrides everything. 

569 retries = config_kwargs.get('retries') 

570 if retries is not None: 

571 if 'total_max_attempts' in retries: 

572 retries.pop('max_attempts', None) 

573 return 

574 if 'max_attempts' in retries: 

575 value = retries.pop('max_attempts') 

576 # client config max_attempts means total retries so we 

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

578 # for the initial request. 

579 retries['total_max_attempts'] = value + 1 

580 return 

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

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

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

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

585 if max_attempts is not None: 

586 if retries is None: 

587 retries = {} 

588 config_kwargs['retries'] = retries 

589 retries['total_max_attempts'] = max_attempts 

590 

591 def _compute_retry_mode(self, config_kwargs): 

592 retries = config_kwargs.get('retries') 

593 if retries is None: 

594 retries = {} 

595 config_kwargs['retries'] = retries 

596 elif 'mode' in retries: 

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

598 # that overrides everything. 

599 return 

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

601 if retry_mode is None: 

602 retry_mode = 'legacy' 

603 retries['mode'] = retry_mode 

604 

605 def _compute_connect_timeout(self, config_kwargs): 

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

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

608 # non legacy default mode has been configured. 

609 connect_timeout = config_kwargs.get('connect_timeout') 

610 if connect_timeout is not None: 

611 return 

612 connect_timeout = self._config_store.get_config_variable( 

613 'connect_timeout' 

614 ) 

615 if connect_timeout: 

616 config_kwargs['connect_timeout'] = connect_timeout 

617 

618 def _compute_request_compression_config(self, config_kwargs): 

619 min_size = config_kwargs.get('request_min_compression_size_bytes') 

620 disabled = config_kwargs.get('disable_request_compression') 

621 if min_size is None: 

622 min_size = self._config_store.get_config_variable( 

623 'request_min_compression_size_bytes' 

624 ) 

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

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

627 # config object 

628 min_size = self._validate_min_compression_size(min_size) 

629 config_kwargs['request_min_compression_size_bytes'] = min_size 

630 

631 if disabled is None: 

632 disabled = self._config_store.get_config_variable( 

633 'disable_request_compression' 

634 ) 

635 else: 

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

637 disabled = ensure_boolean(disabled) 

638 config_kwargs['disable_request_compression'] = disabled 

639 

640 def _validate_min_compression_size(self, min_size): 

641 min_allowed_min_size = 1 

642 max_allowed_min_size = 1048576 

643 error_msg_base = ( 

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

645 'request_min_compression_size_bytes.' 

646 ) 

647 try: 

648 min_size = int(min_size) 

649 except (ValueError, TypeError): 

650 msg = ( 

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

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

653 ) 

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

655 if not min_allowed_min_size <= min_size <= max_allowed_min_size: 

656 msg = ( 

657 f'{error_msg_base} Value must be between ' 

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

659 ) 

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

661 

662 return min_size 

663 

664 def _ensure_boolean(self, val): 

665 if isinstance(val, bool): 

666 return val 

667 else: 

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

669 

670 def _build_endpoint_resolver( 

671 self, 

672 endpoints_ruleset_data, 

673 partition_data, 

674 client_config, 

675 service_model, 

676 endpoint_region_name, 

677 region_name, 

678 endpoint_url, 

679 endpoint, 

680 is_secure, 

681 endpoint_bridge, 

682 event_emitter, 

683 credentials, 

684 account_id_endpoint_mode, 

685 ): 

686 if endpoints_ruleset_data is None: 

687 return None 

688 

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

690 # EndpointRulesetResolver is service-specific. Builtins for 

691 # EndpointRulesetResolver must not be derived from the legacy 

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

693 # etc. 

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

695 service_name_raw = service_model.endpoint_prefix 

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

697 # compatibility. 

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

699 eprv2_region_name = endpoint_region_name 

700 else: 

701 eprv2_region_name = region_name 

702 resolver_builtins = self.compute_endpoint_resolver_builtin_defaults( 

703 region_name=eprv2_region_name, 

704 service_name=service_name_raw, 

705 s3_config=s3_config_raw, 

706 endpoint_bridge=endpoint_bridge, 

707 client_endpoint_url=endpoint_url, 

708 legacy_endpoint_url=endpoint.host, 

709 credentials=credentials, 

710 account_id_endpoint_mode=account_id_endpoint_mode, 

711 ) 

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

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

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

715 if client_config is not None: 

716 client_context = client_config.client_context_params or {} 

717 else: 

718 client_context = {} 

719 if self._is_s3_service(service_name_raw): 

720 client_context.update(s3_config_raw) 

721 

722 sig_version = ( 

723 client_config.signature_version 

724 if client_config is not None 

725 else None 

726 ) 

727 return EndpointRulesetResolver( 

728 endpoint_ruleset_data=endpoints_ruleset_data, 

729 partition_data=partition_data, 

730 service_model=service_model, 

731 builtins=resolver_builtins, 

732 client_context=client_context, 

733 event_emitter=event_emitter, 

734 use_ssl=is_secure, 

735 requested_auth_scheme=sig_version, 

736 ) 

737 

738 def compute_endpoint_resolver_builtin_defaults( 

739 self, 

740 region_name, 

741 service_name, 

742 s3_config, 

743 endpoint_bridge, 

744 client_endpoint_url, 

745 legacy_endpoint_url, 

746 credentials, 

747 account_id_endpoint_mode, 

748 ): 

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

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

751 # always takes priority. 

752 if client_endpoint_url: 

753 given_endpoint = client_endpoint_url 

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

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

756 # endpoint resolution is provided to EndpointRulesetResolver. 

757 elif not endpoint_bridge.resolver_uses_builtin_data(): 

758 given_endpoint = legacy_endpoint_url 

759 else: 

760 given_endpoint = None 

761 

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

763 # forcing path style addressing in incompatible situations raises an 

764 # exception or silently ignores the config setting. The 

765 # AWS_S3_FORCE_PATH_STYLE parameter is adjusted both here and for each 

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

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

768 force_path_style = False 

769 elif client_endpoint_url is not None and not is_s3_accelerate_url( 

770 client_endpoint_url 

771 ): 

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

773 else: 

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

775 

776 return { 

777 EPRBuiltins.AWS_REGION: region_name, 

778 EPRBuiltins.AWS_USE_FIPS: ( 

779 # SDK_ENDPOINT cannot be combined with AWS_USE_FIPS 

780 given_endpoint is None 

781 # use legacy resolver's _resolve_endpoint_variant_config_var() 

782 # or default to False if it returns None 

783 and endpoint_bridge._resolve_endpoint_variant_config_var( 

784 'use_fips_endpoint' 

785 ) 

786 or False 

787 ), 

788 EPRBuiltins.AWS_USE_DUALSTACK: ( 

789 # SDK_ENDPOINT cannot be combined with AWS_USE_DUALSTACK 

790 given_endpoint is None 

791 # use legacy resolver's _resolve_use_dualstack_endpoint() and 

792 # or default to False if it returns None 

793 and endpoint_bridge._resolve_use_dualstack_endpoint( 

794 service_name 

795 ) 

796 or False 

797 ), 

798 EPRBuiltins.AWS_STS_USE_GLOBAL_ENDPOINT: ( 

799 self._should_set_global_sts_endpoint( 

800 region_name=region_name, 

801 endpoint_url=None, 

802 endpoint_config=None, 

803 ) 

804 ), 

805 EPRBuiltins.AWS_S3_USE_GLOBAL_ENDPOINT: ( 

806 self._should_force_s3_global(region_name, s3_config) 

807 ), 

808 EPRBuiltins.AWS_S3_ACCELERATE: s3_config.get( 

809 'use_accelerate_endpoint', False 

810 ), 

811 EPRBuiltins.AWS_S3_FORCE_PATH_STYLE: force_path_style, 

812 EPRBuiltins.AWS_S3_USE_ARN_REGION: s3_config.get( 

813 'use_arn_region', True 

814 ), 

815 EPRBuiltins.AWS_S3CONTROL_USE_ARN_REGION: s3_config.get( 

816 'use_arn_region', False 

817 ), 

818 EPRBuiltins.AWS_S3_DISABLE_MRAP: s3_config.get( 

819 's3_disable_multiregion_access_points', False 

820 ), 

821 EPRBuiltins.SDK_ENDPOINT: given_endpoint, 

822 EPRBuiltins.ACCOUNT_ID: credentials.get_deferred_property( 

823 'account_id' 

824 ) 

825 if credentials 

826 else None, 

827 EPRBuiltins.ACCOUNT_ID_ENDPOINT_MODE: account_id_endpoint_mode, 

828 } 

829 

830 def _compute_user_agent_appid_config(self, config_kwargs): 

831 user_agent_appid = config_kwargs.get('user_agent_appid') 

832 if user_agent_appid is None: 

833 user_agent_appid = self._config_store.get_config_variable( 

834 'user_agent_appid' 

835 ) 

836 if ( 

837 user_agent_appid is not None 

838 and len(user_agent_appid) > USERAGENT_APPID_MAXLEN 

839 ): 

840 logger.warning( 

841 'The configured value for user_agent_appid exceeds the ' 

842 f'maximum length of {USERAGENT_APPID_MAXLEN} characters.' 

843 ) 

844 config_kwargs['user_agent_appid'] = user_agent_appid 

845 

846 def _compute_sigv4a_signing_region_set_config(self, config_kwargs): 

847 sigv4a_signing_region_set = config_kwargs.get( 

848 'sigv4a_signing_region_set' 

849 ) 

850 if sigv4a_signing_region_set is None: 

851 sigv4a_signing_region_set = self._config_store.get_config_variable( 

852 'sigv4a_signing_region_set' 

853 ) 

854 config_kwargs['sigv4a_signing_region_set'] = sigv4a_signing_region_set 

855 

856 def _compute_checksum_config(self, config_kwargs): 

857 self._handle_checksum_config( 

858 config_kwargs, 

859 config_key="request_checksum_calculation", 

860 valid_options=VALID_REQUEST_CHECKSUM_CALCULATION_CONFIG, 

861 ) 

862 self._handle_checksum_config( 

863 config_kwargs, 

864 config_key="response_checksum_validation", 

865 valid_options=VALID_RESPONSE_CHECKSUM_VALIDATION_CONFIG, 

866 ) 

867 

868 def _handle_checksum_config( 

869 self, 

870 config_kwargs, 

871 config_key, 

872 valid_options, 

873 ): 

874 value = config_kwargs.get(config_key) 

875 if value is None: 

876 value = self._config_store.get_config_variable(config_key) 

877 

878 if isinstance(value, str): 

879 value = value.lower() 

880 

881 if value not in valid_options: 

882 raise botocore.exceptions.InvalidChecksumConfigError( 

883 config_key=config_key, 

884 config_value=value, 

885 valid_options=valid_options, 

886 ) 

887 self._register_checksum_config_feature_ids(value, config_key) 

888 config_kwargs[config_key] = value 

889 

890 def _register_checksum_config_feature_ids(self, value, config_key): 

891 checksum_config_feature_id = None 

892 if config_key == "request_checksum_calculation": 

893 checksum_config_feature_id = ( 

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

895 ) 

896 elif config_key == "response_checksum_validation": 

897 checksum_config_feature_id = ( 

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

899 ) 

900 if checksum_config_feature_id is not None: 

901 register_feature_id(checksum_config_feature_id) 

902 

903 def _compute_account_id_endpoint_mode_config(self, config_kwargs): 

904 config_key = 'account_id_endpoint_mode' 

905 

906 # Disable account id based endpoint routing for unsigned requests 

907 # since there are no credentials to resolve. 

908 signature_version = config_kwargs.get('signature_version') 

909 if signature_version is botocore.UNSIGNED: 

910 config_kwargs[config_key] = 'disabled' 

911 return 

912 

913 account_id_endpoint_mode = config_kwargs.get(config_key) 

914 if account_id_endpoint_mode is None: 

915 account_id_endpoint_mode = self._config_store.get_config_variable( 

916 config_key 

917 ) 

918 

919 if isinstance(account_id_endpoint_mode, str): 

920 account_id_endpoint_mode = account_id_endpoint_mode.lower() 

921 

922 if ( 

923 account_id_endpoint_mode 

924 not in VALID_ACCOUNT_ID_ENDPOINT_MODE_CONFIG 

925 ): 

926 raise botocore.exceptions.InvalidConfigError( 

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

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

929 ) 

930 

931 config_kwargs[config_key] = account_id_endpoint_mode 

932 

933 def _compute_auth_scheme_preference_config( 

934 self, client_config, config_kwargs 

935 ): 

936 config_key = 'auth_scheme_preference' 

937 set_in_config_object = False 

938 

939 if client_config and client_config.auth_scheme_preference: 

940 value = client_config.auth_scheme_preference 

941 set_in_config_object = True 

942 else: 

943 value = self._config_store.get_config_variable(config_key) 

944 

945 if value is None: 

946 config_kwargs[config_key] = None 

947 return 

948 

949 if not isinstance(value, str): 

950 raise botocore.exceptions.InvalidConfigError( 

951 error_msg=( 

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

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

954 ) 

955 ) 

956 

957 value = ','.join( 

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

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

960 if item.strip() 

961 ) 

962 

963 if set_in_config_object: 

964 value = ClientConfigString(value) 

965 

966 config_kwargs[config_key] = value 

967 

968 def _compute_signature_version_config(self, client_config, config_kwargs): 

969 if client_config and client_config.signature_version: 

970 value = client_config.signature_version 

971 if isinstance(value, str): 

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

973 

974 

975class ConfigObjectWrapper: 

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

977 

978 pass 

979 

980 

981class ClientConfigString(str, ConfigObjectWrapper): 

982 def __new__(cls, value=None): 

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