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

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

462 statements  

1# Copyright (c) 2012-2013 Mitch Garnaat http://garnaat.org/ 

2# Copyright 2012-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. 

3# 

4# Licensed under the Apache License, Version 2.0 (the "License"). You 

5# may not use this file except in compliance with the License. A copy of 

6# the License is located at 

7# 

8# http://aws.amazon.com/apache2.0/ 

9# 

10# or in the "license" file accompanying this file. This file is 

11# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 

12# ANY KIND, either express or implied. See the License for the specific 

13# language governing permissions and limitations under the License. 

14""" 

15This module contains the main interface to the botocore package, the 

16Session object. 

17""" 

18 

19import copy 

20import logging 

21import os 

22import platform 

23import socket 

24import warnings 

25 

26import botocore.client 

27import botocore.configloader 

28import botocore.credentials 

29import botocore.tokens 

30from botocore import ( 

31 UNSIGNED, 

32 __version__, 

33 handlers, 

34 invoke_initializers, 

35 monitoring, 

36 paginate, 

37 retryhandler, 

38 translate, 

39 waiter, 

40) 

41from botocore.compat import ( 

42 HAS_CRT, # noqa: F401 

43 MutableMapping, 

44) 

45from botocore.configprovider import ( 

46 BOTOCORE_DEFAUT_SESSION_VARIABLES, 

47 ConfigChainFactory, 

48 ConfiguredEndpointProvider, 

49 ConfigValueStore, 

50 DefaultConfigResolver, 

51 SmartDefaultsConfigStoreFactory, 

52 create_botocore_default_config_mapping, 

53) 

54from botocore.context import get_context, with_current_context 

55from botocore.errorfactory import ClientExceptionsFactory 

56from botocore.exceptions import ( 

57 ConfigNotFound, 

58 InvalidDefaultsMode, 

59 PartialCredentialsError, 

60 ProfileNotFound, 

61 UnknownServiceError, 

62) 

63from botocore.hooks import ( 

64 EventAliaser, 

65 HierarchicalEmitter, 

66 first_non_none_response, 

67) 

68from botocore.loaders import create_loader 

69from botocore.model import ServiceModel 

70from botocore.parsers import ResponseParserFactory 

71from botocore.plugin import get_botocore_plugins, load_client_plugins 

72from botocore.regions import EndpointResolver 

73from botocore.useragent import UserAgentString 

74from botocore.utils import ( 

75 EVENT_ALIASES, 

76 IMDSRegionProvider, 

77 validate_region_name, 

78) 

79 

80logger = logging.getLogger(__name__) 

81 

82 

83class Session: 

84 """ 

85 The Session object collects together useful functionality 

86 from `botocore` as well as important data such as configuration 

87 information and credentials into a single, easy-to-use object. 

88 

89 :ivar available_profiles: A list of profiles defined in the config 

90 file associated with this session. 

91 :ivar profile: The current profile. 

92 """ 

93 

94 SESSION_VARIABLES = copy.copy(BOTOCORE_DEFAUT_SESSION_VARIABLES) 

95 

96 #: The default format string to use when configuring the botocore logger. 

97 LOG_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s' 

98 

99 def __init__( 

100 self, 

101 session_vars=None, 

102 event_hooks=None, 

103 include_builtin_handlers=True, 

104 profile=None, 

105 ): 

106 """ 

107 Create a new Session object. 

108 

109 :type session_vars: dict 

110 :param session_vars: A dictionary that is used to override some or all 

111 of the environment variables associated with this session. The 

112 key/value pairs defined in this dictionary will override the 

113 corresponding variables defined in ``SESSION_VARIABLES``. 

114 

115 :type event_hooks: BaseEventHooks 

116 :param event_hooks: The event hooks object to use. If one is not 

117 provided, an event hooks object will be automatically created 

118 for you. 

119 

120 :type include_builtin_handlers: bool 

121 :param include_builtin_handlers: Indicates whether or not to 

122 automatically register builtin handlers. 

123 

124 :type profile: str 

125 :param profile: The name of the profile to use for this 

126 session. Note that the profile can only be set when 

127 the session is created. 

128 

129 """ 

130 if event_hooks is None: 

131 self._original_handler = HierarchicalEmitter() 

132 else: 

133 self._original_handler = event_hooks 

134 self._events = EventAliaser(self._original_handler) 

135 if include_builtin_handlers: 

136 self._register_builtin_handlers(self._events) 

137 self.user_agent_name = 'Botocore' 

138 self.user_agent_version = __version__ 

139 self.user_agent_extra = '' 

140 # The _profile attribute is just used to cache the value 

141 # of the current profile to avoid going through the normal 

142 # config lookup process each access time. 

143 self._profile = None 

144 self._config = None 

145 self._credentials = None 

146 self._auth_token = None 

147 self._profile_map = None 

148 # This is a dict that stores per session specific config variable 

149 # overrides via set_config_variable(). 

150 self._session_instance_vars = {} 

151 if profile is not None: 

152 self._session_instance_vars['profile'] = profile 

153 self._client_config = None 

154 self._last_client_region_used = None 

155 self._components = ComponentLocator() 

156 self._internal_components = ComponentLocator() 

157 self._register_components() 

158 self.session_var_map = SessionVarDict(self, self.SESSION_VARIABLES) 

159 if session_vars is not None: 

160 self.session_var_map.update(session_vars) 

161 invoke_initializers(self) 

162 

163 def _register_components(self): 

164 self._register_credential_provider() 

165 self._register_token_provider() 

166 self._register_data_loader() 

167 self._register_endpoint_resolver() 

168 self._register_event_emitter() 

169 self._register_response_parser_factory() 

170 self._register_exceptions_factory() 

171 self._register_config_store() 

172 self._register_monitor() 

173 self._register_default_config_resolver() 

174 self._register_smart_defaults_factory() 

175 self._register_user_agent_creator() 

176 

177 def _register_event_emitter(self): 

178 self._components.register_component('event_emitter', self._events) 

179 

180 def _register_token_provider(self): 

181 self._components.lazy_register_component( 

182 'token_provider', self._create_token_resolver 

183 ) 

184 

185 def _create_token_resolver(self): 

186 return botocore.tokens.create_token_resolver(self) 

187 

188 def _register_credential_provider(self): 

189 self._components.lazy_register_component( 

190 'credential_provider', self._create_credential_resolver 

191 ) 

192 

193 def _create_credential_resolver(self): 

194 return botocore.credentials.create_credential_resolver( 

195 self, region_name=self._last_client_region_used 

196 ) 

197 

198 def _register_data_loader(self): 

199 self._components.lazy_register_component( 

200 'data_loader', 

201 lambda: create_loader(self.get_config_variable('data_path')), 

202 ) 

203 

204 def _register_endpoint_resolver(self): 

205 def create_default_resolver(): 

206 loader = self.get_component('data_loader') 

207 endpoints, path = loader.load_data_with_path('endpoints') 

208 uses_builtin = loader.is_builtin_path(path) 

209 return EndpointResolver(endpoints, uses_builtin_data=uses_builtin) 

210 

211 self._internal_components.lazy_register_component( 

212 'endpoint_resolver', create_default_resolver 

213 ) 

214 

215 def _register_default_config_resolver(self): 

216 def create_default_config_resolver(): 

217 loader = self.get_component('data_loader') 

218 defaults = loader.load_data('sdk-default-configuration') 

219 return DefaultConfigResolver(defaults) 

220 

221 self._internal_components.lazy_register_component( 

222 'default_config_resolver', create_default_config_resolver 

223 ) 

224 

225 def _register_smart_defaults_factory(self): 

226 def create_smart_defaults_factory(): 

227 default_config_resolver = self._get_internal_component( 

228 'default_config_resolver' 

229 ) 

230 imds_region_provider = IMDSRegionProvider(session=self) 

231 return SmartDefaultsConfigStoreFactory( 

232 default_config_resolver, imds_region_provider 

233 ) 

234 

235 self._internal_components.lazy_register_component( 

236 'smart_defaults_factory', create_smart_defaults_factory 

237 ) 

238 

239 def _register_response_parser_factory(self): 

240 self._components.register_component( 

241 'response_parser_factory', ResponseParserFactory() 

242 ) 

243 

244 def _register_exceptions_factory(self): 

245 self._internal_components.register_component( 

246 'exceptions_factory', ClientExceptionsFactory() 

247 ) 

248 

249 def _register_builtin_handlers(self, events): 

250 for spec in handlers.BUILTIN_HANDLERS: 

251 if len(spec) == 2: 

252 event_name, handler = spec 

253 self.register(event_name, handler) 

254 else: 

255 event_name, handler, register_type = spec 

256 if register_type is handlers.REGISTER_FIRST: 

257 self._events.register_first(event_name, handler) 

258 elif register_type is handlers.REGISTER_LAST: 

259 self._events.register_last(event_name, handler) 

260 

261 def _register_config_store(self): 

262 config_store_component = ConfigValueStore( 

263 mapping=create_botocore_default_config_mapping(self) 

264 ) 

265 self._components.register_component( 

266 'config_store', config_store_component 

267 ) 

268 

269 def _register_monitor(self): 

270 self._internal_components.lazy_register_component( 

271 'monitor', self._create_csm_monitor 

272 ) 

273 

274 def _register_user_agent_creator(self): 

275 uas = UserAgentString.from_environment() 

276 self._components.register_component('user_agent_creator', uas) 

277 

278 def _create_csm_monitor(self): 

279 if self.get_config_variable('csm_enabled'): 

280 client_id = self.get_config_variable('csm_client_id') 

281 host = self.get_config_variable('csm_host') 

282 port = self.get_config_variable('csm_port') 

283 handler = monitoring.Monitor( 

284 adapter=monitoring.MonitorEventAdapter(), 

285 publisher=monitoring.SocketPublisher( 

286 socket=socket.socket(socket.AF_INET, socket.SOCK_DGRAM), 

287 host=host, 

288 port=port, 

289 serializer=monitoring.CSMSerializer( 

290 csm_client_id=client_id 

291 ), 

292 ), 

293 ) 

294 return handler 

295 return None 

296 

297 def _get_crt_version(self): 

298 user_agent_creator = self.get_component('user_agent_creator') 

299 return user_agent_creator._crt_version or 'Unknown' 

300 

301 @property 

302 def available_profiles(self): 

303 return list(self._build_profile_map().keys()) 

304 

305 def _build_profile_map(self): 

306 # This will build the profile map if it has not been created, 

307 # otherwise it will return the cached value. The profile map 

308 # is a list of profile names, to the config values for the profile. 

309 if self._profile_map is None: 

310 self._profile_map = self.full_config['profiles'] 

311 return self._profile_map 

312 

313 @property 

314 def profile(self): 

315 if self._profile is None: 

316 profile = self.get_config_variable('profile') 

317 self._profile = profile 

318 return self._profile 

319 

320 def get_config_variable(self, logical_name, methods=None): 

321 if methods is not None: 

322 return self._get_config_variable_with_custom_methods( 

323 logical_name, methods 

324 ) 

325 return self.get_component('config_store').get_config_variable( 

326 logical_name 

327 ) 

328 

329 def _get_config_variable_with_custom_methods(self, logical_name, methods): 

330 # If a custom list of methods was supplied we need to perserve the 

331 # behavior with the new system. To do so a new chain that is a copy of 

332 # the old one will be constructed, but only with the supplied methods 

333 # being added to the chain. This chain will be consulted for a value 

334 # and then thrown out. This is not efficient, nor is the methods arg 

335 # used in botocore, this is just for backwards compatibility. 

336 chain_builder = SubsetChainConfigFactory(session=self, methods=methods) 

337 mapping = create_botocore_default_config_mapping(self) 

338 for name, config_options in self.session_var_map.items(): 

339 config_name, env_vars, default, typecast = config_options 

340 build_chain_config_args = { 

341 'conversion_func': typecast, 

342 'default': default, 

343 } 

344 if 'instance' in methods: 

345 build_chain_config_args['instance_name'] = name 

346 if 'env' in methods: 

347 build_chain_config_args['env_var_names'] = env_vars 

348 if 'config' in methods: 

349 build_chain_config_args['config_property_name'] = config_name 

350 mapping[name] = chain_builder.create_config_chain( 

351 **build_chain_config_args 

352 ) 

353 config_store_component = ConfigValueStore(mapping=mapping) 

354 value = config_store_component.get_config_variable(logical_name) 

355 return value 

356 

357 def set_config_variable(self, logical_name, value): 

358 """Set a configuration variable to a specific value. 

359 

360 By using this method, you can override the normal lookup 

361 process used in ``get_config_variable`` by explicitly setting 

362 a value. Subsequent calls to ``get_config_variable`` will 

363 use the ``value``. This gives you per-session specific 

364 configuration values. 

365 

366 :: 

367 >>> # Assume logical name 'foo' maps to env var 'FOO' 

368 >>> os.environ['FOO'] = 'myvalue' 

369 >>> s.get_config_variable('foo') 

370 'myvalue' 

371 >>> s.set_config_variable('foo', 'othervalue') 

372 >>> s.get_config_variable('foo') 

373 'othervalue' 

374 

375 :type logical_name: str 

376 :param logical_name: The logical name of the session variable 

377 you want to set. These are the keys in ``SESSION_VARIABLES``. 

378 :param value: The value to associate with the config variable. 

379 

380 """ 

381 logger.debug( 

382 "Setting config variable for %s to %r", 

383 logical_name, 

384 value, 

385 ) 

386 self._session_instance_vars[logical_name] = value 

387 

388 def instance_variables(self): 

389 return copy.copy(self._session_instance_vars) 

390 

391 def get_scoped_config(self): 

392 """ 

393 Returns the config values from the config file scoped to the current 

394 profile. 

395 

396 The configuration data is loaded **only** from the config file. 

397 It does not resolve variables based on different locations 

398 (e.g. first from the session instance, then from environment 

399 variables, then from the config file). If you want this lookup 

400 behavior, use the ``get_config_variable`` method instead. 

401 

402 Note that this configuration is specific to a single profile (the 

403 ``profile`` session variable). 

404 

405 If the ``profile`` session variable is set and the profile does 

406 not exist in the config file, a ``ProfileNotFound`` exception 

407 will be raised. 

408 

409 :raises: ConfigNotFound, ConfigParseError, ProfileNotFound 

410 :rtype: dict 

411 

412 """ 

413 profile_name = self.get_config_variable('profile') 

414 profile_map = self._build_profile_map() 

415 # If a profile is not explicitly set return the default 

416 # profile config or an empty config dict if we don't have 

417 # a default profile. 

418 if profile_name is None: 

419 return profile_map.get('default', {}) 

420 elif profile_name not in profile_map: 

421 # Otherwise if they specified a profile, it has to 

422 # exist (even if it's the default profile) otherwise 

423 # we complain. 

424 raise ProfileNotFound(profile=profile_name) 

425 else: 

426 return profile_map[profile_name] 

427 

428 @property 

429 def full_config(self): 

430 """Return the parsed config file. 

431 

432 The ``get_config`` method returns the config associated with the 

433 specified profile. This property returns the contents of the 

434 **entire** config file. 

435 

436 :rtype: dict 

437 """ 

438 if self._config is None: 

439 try: 

440 config_file = self.get_config_variable('config_file') 

441 self._config = botocore.configloader.load_config(config_file) 

442 except ConfigNotFound: 

443 self._config = {'profiles': {}} 

444 try: 

445 # Now we need to inject the profiles from the 

446 # credentials file. We don't actually need the values 

447 # in the creds file, only the profile names so that we 

448 # can validate the user is not referring to a nonexistent 

449 # profile. 

450 cred_file = self.get_config_variable('credentials_file') 

451 cred_profiles = botocore.configloader.raw_config_parse( 

452 cred_file 

453 ) 

454 for profile in cred_profiles: 

455 cred_vars = cred_profiles[profile] 

456 if profile not in self._config['profiles']: 

457 self._config['profiles'][profile] = cred_vars 

458 else: 

459 self._config['profiles'][profile].update(cred_vars) 

460 except ConfigNotFound: 

461 pass 

462 return self._config 

463 

464 def get_default_client_config(self): 

465 """Retrieves the default config for creating clients 

466 

467 :rtype: botocore.client.Config 

468 :returns: The default client config object when creating clients. If 

469 the value is ``None`` then there is no default config object 

470 attached to the session. 

471 """ 

472 return self._client_config 

473 

474 def set_default_client_config(self, client_config): 

475 """Sets the default config for creating clients 

476 

477 :type client_config: botocore.client.Config 

478 :param client_config: The default client config object when creating 

479 clients. If the value is ``None`` then there is no default config 

480 object attached to the session. 

481 """ 

482 self._client_config = client_config 

483 

484 def set_credentials( 

485 self, access_key, secret_key, token=None, account_id=None 

486 ): 

487 """ 

488 Manually create credentials for this session. If you would 

489 prefer to use botocore without a config file, environment variables, 

490 or IAM roles, you can pass explicit credentials into this 

491 method to establish credentials for this session. 

492 

493 :type access_key: str 

494 :param access_key: The access key part of the credentials. 

495 

496 :type secret_key: str 

497 :param secret_key: The secret key part of the credentials. 

498 

499 :type token: str 

500 :param token: An option session token used by STS session 

501 credentials. 

502 

503 :type account_id: str 

504 :param account_id: An optional account ID part of the credentials. 

505 """ 

506 self._credentials = botocore.credentials.Credentials( 

507 access_key, secret_key, token, account_id=account_id 

508 ) 

509 

510 def get_credentials(self): 

511 """ 

512 Return the :class:`botocore.credential.Credential` object 

513 associated with this session. If the credentials have not 

514 yet been loaded, this will attempt to load them. If they 

515 have already been loaded, this will return the cached 

516 credentials. 

517 

518 """ 

519 if self._credentials is None: 

520 self._credentials = self._components.get_component( 

521 'credential_provider' 

522 ).load_credentials() 

523 return self._credentials 

524 

525 def get_auth_token(self, **kwargs): 

526 """ 

527 Return the :class:`botocore.tokens.AuthToken` object associated with 

528 this session. If the authorization token has not yet been loaded, this 

529 will attempt to load it. If it has already been loaded, this will 

530 return the cached authorization token. 

531 

532 """ 

533 provider = self._components.get_component('token_provider') 

534 

535 signing_name = kwargs.get('signing_name') 

536 if signing_name is not None: 

537 auth_token = provider.load_token(signing_name=signing_name) 

538 if auth_token is not None: 

539 return auth_token 

540 

541 if self._auth_token is None: 

542 self._auth_token = provider.load_token() 

543 return self._auth_token 

544 

545 def user_agent(self): 

546 """ 

547 Return a string suitable for use as a User-Agent header. 

548 The string will be of the form: 

549 

550 <agent_name>/<agent_version> Python/<py_ver> <plat_name>/<plat_ver> <exec_env> 

551 

552 Where: 

553 

554 - agent_name is the value of the `user_agent_name` attribute 

555 of the session object (`Botocore` by default). 

556 - agent_version is the value of the `user_agent_version` 

557 attribute of the session object (the botocore version by default). 

558 by default. 

559 - py_ver is the version of the Python interpreter beng used. 

560 - plat_name is the name of the platform (e.g. Darwin) 

561 - plat_ver is the version of the platform 

562 - exec_env is exec-env/$AWS_EXECUTION_ENV 

563 

564 If ``user_agent_extra`` is not empty, then this value will be 

565 appended to the end of the user agent string. 

566 

567 """ 

568 base = ( 

569 f'{self.user_agent_name}/{self.user_agent_version} ' 

570 f'Python/{platform.python_version()} ' 

571 f'{platform.system()}/{platform.release()}' 

572 ) 

573 if HAS_CRT: 

574 base += f' awscrt/{self._get_crt_version()}' 

575 if os.environ.get('AWS_EXECUTION_ENV') is not None: 

576 base += ' exec-env/{}'.format(os.environ.get('AWS_EXECUTION_ENV')) 

577 if self.user_agent_extra: 

578 base += f' {self.user_agent_extra}' 

579 

580 return base 

581 

582 def get_data(self, data_path): 

583 """ 

584 Retrieve the data associated with `data_path`. 

585 

586 :type data_path: str 

587 :param data_path: The path to the data you wish to retrieve. 

588 """ 

589 return self.get_component('data_loader').load_data(data_path) 

590 

591 def get_service_model(self, service_name, api_version=None): 

592 """Get the service model object. 

593 

594 :type service_name: string 

595 :param service_name: The service name 

596 

597 :type api_version: string 

598 :param api_version: The API version of the service. If none is 

599 provided, then the latest API version will be used. 

600 

601 :rtype: L{botocore.model.ServiceModel} 

602 :return: The botocore service model for the service. 

603 

604 """ 

605 service_description = self.get_service_data(service_name, api_version) 

606 return ServiceModel(service_description, service_name=service_name) 

607 

608 def get_waiter_model(self, service_name, api_version=None): 

609 loader = self.get_component('data_loader') 

610 waiter_config = loader.load_service_model( 

611 service_name, 'waiters-2', api_version 

612 ) 

613 return waiter.WaiterModel(waiter_config) 

614 

615 def get_paginator_model(self, service_name, api_version=None): 

616 loader = self.get_component('data_loader') 

617 paginator_config = loader.load_service_model( 

618 service_name, 'paginators-1', api_version 

619 ) 

620 return paginate.PaginatorModel(paginator_config) 

621 

622 def get_service_data(self, service_name, api_version=None): 

623 """ 

624 Retrieve the fully merged data associated with a service. 

625 """ 

626 data_path = service_name 

627 service_data = self.get_component('data_loader').load_service_model( 

628 data_path, type_name='service-2', api_version=api_version 

629 ) 

630 service_id = EVENT_ALIASES.get(service_name, service_name) 

631 self._events.emit( 

632 f'service-data-loaded.{service_id}', 

633 service_data=service_data, 

634 service_name=service_name, 

635 session=self, 

636 ) 

637 return service_data 

638 

639 def get_available_services(self): 

640 """ 

641 Return a list of names of available services. 

642 """ 

643 return self.get_component('data_loader').list_available_services( 

644 type_name='service-2' 

645 ) 

646 

647 def set_debug_logger(self, logger_name='botocore'): 

648 """ 

649 Convenience function to quickly configure full debug output 

650 to go to the console. 

651 """ 

652 self.set_stream_logger(logger_name, logging.DEBUG) 

653 

654 def set_stream_logger( 

655 self, logger_name, log_level, stream=None, format_string=None 

656 ): 

657 """ 

658 Convenience method to configure a stream logger. 

659 

660 :type logger_name: str 

661 :param logger_name: The name of the logger to configure 

662 

663 :type log_level: str 

664 :param log_level: The log level to set for the logger. This 

665 is any param supported by the ``.setLevel()`` method of 

666 a ``Log`` object. 

667 

668 :type stream: file 

669 :param stream: A file like object to log to. If none is provided 

670 then sys.stderr will be used. 

671 

672 :type format_string: str 

673 :param format_string: The format string to use for the log 

674 formatter. If none is provided this will default to 

675 ``self.LOG_FORMAT``. 

676 

677 """ 

678 log = logging.getLogger(logger_name) 

679 log.setLevel(logging.DEBUG) 

680 

681 ch = logging.StreamHandler(stream) 

682 ch.setLevel(log_level) 

683 

684 # create formatter 

685 if format_string is None: 

686 format_string = self.LOG_FORMAT 

687 formatter = logging.Formatter(format_string) 

688 

689 # add formatter to ch 

690 ch.setFormatter(formatter) 

691 

692 # add ch to logger 

693 log.addHandler(ch) 

694 

695 def set_file_logger(self, log_level, path, logger_name='botocore'): 

696 """ 

697 Convenience function to quickly configure any level of logging 

698 to a file. 

699 

700 :type log_level: int 

701 :param log_level: A log level as specified in the `logging` module 

702 

703 :type path: string 

704 :param path: Path to the log file. The file will be created 

705 if it doesn't already exist. 

706 """ 

707 log = logging.getLogger(logger_name) 

708 log.setLevel(logging.DEBUG) 

709 

710 # create console handler and set level to debug 

711 ch = logging.FileHandler(path) 

712 ch.setLevel(log_level) 

713 

714 # create formatter 

715 formatter = logging.Formatter(self.LOG_FORMAT) 

716 

717 # add formatter to ch 

718 ch.setFormatter(formatter) 

719 

720 # add ch to logger 

721 log.addHandler(ch) 

722 

723 def register( 

724 self, event_name, handler, unique_id=None, unique_id_uses_count=False 

725 ): 

726 """Register a handler with an event. 

727 

728 :type event_name: str 

729 :param event_name: The name of the event. 

730 

731 :type handler: callable 

732 :param handler: The callback to invoke when the event 

733 is emitted. This object must be callable, and must 

734 accept ``**kwargs``. If either of these preconditions are 

735 not met, a ``ValueError`` will be raised. 

736 

737 :type unique_id: str 

738 :param unique_id: An optional identifier to associate with the 

739 registration. A unique_id can only be used once for 

740 the entire session registration (unless it is unregistered). 

741 This can be used to prevent an event handler from being 

742 registered twice. 

743 

744 :param unique_id_uses_count: boolean 

745 :param unique_id_uses_count: Specifies if the event should maintain 

746 a count when a ``unique_id`` is registered and unregisted. The 

747 event can only be completely unregistered once every register call 

748 using the unique id has been matched by an ``unregister`` call. 

749 If ``unique_id`` is specified, subsequent ``register`` 

750 calls must use the same value for ``unique_id_uses_count`` 

751 as the ``register`` call that first registered the event. 

752 

753 :raises ValueError: If the call to ``register`` uses ``unique_id`` 

754 but the value for ``unique_id_uses_count`` differs from the 

755 ``unique_id_uses_count`` value declared by the very first 

756 ``register`` call for that ``unique_id``. 

757 """ 

758 self._events.register( 

759 event_name, 

760 handler, 

761 unique_id, 

762 unique_id_uses_count=unique_id_uses_count, 

763 ) 

764 

765 def unregister( 

766 self, 

767 event_name, 

768 handler=None, 

769 unique_id=None, 

770 unique_id_uses_count=False, 

771 ): 

772 """Unregister a handler with an event. 

773 

774 :type event_name: str 

775 :param event_name: The name of the event. 

776 

777 :type handler: callable 

778 :param handler: The callback to unregister. 

779 

780 :type unique_id: str 

781 :param unique_id: A unique identifier identifying the callback 

782 to unregister. You can provide either the handler or the 

783 unique_id, you do not have to provide both. 

784 

785 :param unique_id_uses_count: boolean 

786 :param unique_id_uses_count: Specifies if the event should maintain 

787 a count when a ``unique_id`` is registered and unregisted. The 

788 event can only be completely unregistered once every ``register`` 

789 call using the ``unique_id`` has been matched by an ``unregister`` 

790 call. If the ``unique_id`` is specified, subsequent 

791 ``unregister`` calls must use the same value for 

792 ``unique_id_uses_count`` as the ``register`` call that first 

793 registered the event. 

794 

795 :raises ValueError: If the call to ``unregister`` uses ``unique_id`` 

796 but the value for ``unique_id_uses_count`` differs from the 

797 ``unique_id_uses_count`` value declared by the very first 

798 ``register`` call for that ``unique_id``. 

799 """ 

800 self._events.unregister( 

801 event_name, 

802 handler=handler, 

803 unique_id=unique_id, 

804 unique_id_uses_count=unique_id_uses_count, 

805 ) 

806 

807 def emit(self, event_name, **kwargs): 

808 return self._events.emit(event_name, **kwargs) 

809 

810 def emit_first_non_none_response(self, event_name, **kwargs): 

811 responses = self._events.emit(event_name, **kwargs) 

812 return first_non_none_response(responses) 

813 

814 def get_component(self, name): 

815 try: 

816 return self._components.get_component(name) 

817 except ValueError: 

818 if name in ['endpoint_resolver', 'exceptions_factory']: 

819 warnings.warn( 

820 f'Fetching the {name} component with the get_component() ' 

821 'method is deprecated as the component has always been ' 

822 'considered an internal interface of botocore', 

823 DeprecationWarning, 

824 ) 

825 return self._internal_components.get_component(name) 

826 raise 

827 

828 def _get_internal_component(self, name): 

829 # While this method may be called by botocore classes outside of the 

830 # Session, this method should **never** be used by a class that lives 

831 # outside of botocore. 

832 return self._internal_components.get_component(name) 

833 

834 def _register_internal_component(self, name, component): 

835 # While this method may be called by botocore classes outside of the 

836 # Session, this method should **never** be used by a class that lives 

837 # outside of botocore. 

838 return self._internal_components.register_component(name, component) 

839 

840 def register_component(self, name, component): 

841 self._components.register_component(name, component) 

842 

843 def lazy_register_component(self, name, component): 

844 self._components.lazy_register_component(name, component) 

845 

846 @with_current_context() 

847 def create_client( 

848 self, 

849 service_name, 

850 region_name=None, 

851 api_version=None, 

852 use_ssl=True, 

853 verify=None, 

854 endpoint_url=None, 

855 aws_access_key_id=None, 

856 aws_secret_access_key=None, 

857 aws_session_token=None, 

858 config=None, 

859 aws_account_id=None, 

860 ): 

861 """Create a botocore client. 

862 

863 :type service_name: string 

864 :param service_name: The name of the service for which a client will 

865 be created. You can use the ``Session.get_available_services()`` 

866 method to get a list of all available service names. 

867 

868 :type region_name: string 

869 :param region_name: The name of the region associated with the client. 

870 A client is associated with a single region. 

871 

872 :type api_version: string 

873 :param api_version: The API version to use. By default, botocore will 

874 use the latest API version when creating a client. You only need 

875 to specify this parameter if you want to use a previous API version 

876 of the client. 

877 

878 :type use_ssl: boolean 

879 :param use_ssl: Whether or not to use SSL. By default, SSL is used. 

880 Note that not all services support non-ssl connections. 

881 

882 :type verify: boolean/string 

883 :param verify: Whether or not to verify SSL certificates. 

884 By default SSL certificates are verified. You can provide the 

885 following values: 

886 

887 * False - do not validate SSL certificates. SSL will still be 

888 used (unless use_ssl is False), but SSL certificates 

889 will not be verified. 

890 * path/to/cert/bundle.pem - A filename of the CA cert bundle to 

891 uses. You can specify this argument if you want to use a 

892 different CA cert bundle than the one used by botocore. 

893 

894 :type endpoint_url: string 

895 :param endpoint_url: The complete URL to use for the constructed 

896 client. Normally, botocore will automatically construct the 

897 appropriate URL to use when communicating with a service. You can 

898 specify a complete URL (including the "http/https" scheme) to 

899 override this behavior. If this value is provided, then 

900 ``use_ssl`` is ignored. 

901 

902 :type aws_access_key_id: string 

903 :param aws_access_key_id: The access key to use when creating 

904 the client. This is entirely optional, and if not provided, 

905 the credentials configured for the session will automatically 

906 be used. You only need to provide this argument if you want 

907 to override the credentials used for this specific client. 

908 

909 :type aws_secret_access_key: string 

910 :param aws_secret_access_key: The secret key to use when creating 

911 the client. Same semantics as aws_access_key_id above. 

912 

913 :type aws_session_token: string 

914 :param aws_session_token: The session token to use when creating 

915 the client. Same semantics as aws_access_key_id above. 

916 

917 :type config: botocore.client.Config 

918 :param config: Advanced client configuration options. If a value 

919 is specified in the client config, its value will take precedence 

920 over environment variables and configuration values, but not over 

921 a value passed explicitly to the method. If a default config 

922 object is set on the session, the config object used when creating 

923 the client will be the result of calling ``merge()`` on the 

924 default config with the config provided to this call. 

925 

926 :type aws_account_id: string 

927 :param aws_account_id: The account id to use when creating 

928 the client. Same semantics as aws_access_key_id above. 

929 

930 :rtype: botocore.client.BaseClient 

931 :return: A botocore client instance 

932 

933 """ 

934 default_client_config = self.get_default_client_config() 

935 # If a config is provided and a default config is set, then 

936 # use the config resulting from merging the two. 

937 if config is not None and default_client_config is not None: 

938 config = default_client_config.merge(config) 

939 # If a config was not provided then use the default 

940 # client config from the session 

941 elif default_client_config is not None: 

942 config = default_client_config 

943 

944 region_name = self._resolve_region_name(region_name, config) 

945 

946 # Figure out the verify value base on the various 

947 # configuration options. 

948 if verify is None: 

949 verify = self.get_config_variable('ca_bundle') 

950 

951 if api_version is None: 

952 api_version = self.get_config_variable('api_versions').get( 

953 service_name, None 

954 ) 

955 

956 loader = self.get_component('data_loader') 

957 event_emitter = self.get_component('event_emitter') 

958 response_parser_factory = self.get_component('response_parser_factory') 

959 if config is not None and config.signature_version is UNSIGNED: 

960 credentials = None 

961 elif ( 

962 aws_access_key_id is not None and aws_secret_access_key is not None 

963 ): 

964 credentials = botocore.credentials.Credentials( 

965 access_key=aws_access_key_id, 

966 secret_key=aws_secret_access_key, 

967 token=aws_session_token, 

968 account_id=aws_account_id, 

969 ) 

970 elif self._missing_cred_vars(aws_access_key_id, aws_secret_access_key): 

971 raise PartialCredentialsError( 

972 provider='explicit', 

973 cred_var=self._missing_cred_vars( 

974 aws_access_key_id, aws_secret_access_key 

975 ), 

976 ) 

977 else: 

978 if ignored_credentials := self._get_ignored_credentials( 

979 aws_session_token, aws_account_id 

980 ): 

981 logger.debug( 

982 "Ignoring the following credential-related values which were set without " 

983 "an access key id and secret key on the session or client: %s", 

984 ignored_credentials, 

985 ) 

986 credentials = self.get_credentials() 

987 auth_token = self.get_auth_token() 

988 endpoint_resolver = self._get_internal_component('endpoint_resolver') 

989 exceptions_factory = self._get_internal_component('exceptions_factory') 

990 config_store = copy.copy(self.get_component('config_store')) 

991 user_agent_creator = self.get_component('user_agent_creator') 

992 # Session configuration values for the user agent string are applied 

993 # just before each client creation because they may have been modified 

994 # at any time between session creation and client creation. 

995 user_agent_creator.set_session_config( 

996 session_user_agent_name=self.user_agent_name, 

997 session_user_agent_version=self.user_agent_version, 

998 session_user_agent_extra=self.user_agent_extra, 

999 ) 

1000 defaults_mode = self._resolve_defaults_mode(config, config_store) 

1001 if defaults_mode != 'legacy': 

1002 smart_defaults_factory = self._get_internal_component( 

1003 'smart_defaults_factory' 

1004 ) 

1005 smart_defaults_factory.merge_smart_defaults( 

1006 config_store, defaults_mode, region_name 

1007 ) 

1008 

1009 self._add_configured_endpoint_provider( 

1010 client_name=service_name, 

1011 config_store=config_store, 

1012 ) 

1013 

1014 user_agent_creator.set_client_features(get_context().features) 

1015 

1016 client_creator = botocore.client.ClientCreator( 

1017 loader, 

1018 endpoint_resolver, 

1019 self.user_agent(), 

1020 event_emitter, 

1021 retryhandler, 

1022 translate, 

1023 response_parser_factory, 

1024 exceptions_factory, 

1025 config_store, 

1026 user_agent_creator=user_agent_creator, 

1027 auth_token_resolver=self.get_auth_token, 

1028 ) 

1029 client = client_creator.create_client( 

1030 service_name=service_name, 

1031 region_name=region_name, 

1032 is_secure=use_ssl, 

1033 endpoint_url=endpoint_url, 

1034 verify=verify, 

1035 credentials=credentials, 

1036 scoped_config=self.get_scoped_config(), 

1037 client_config=config, 

1038 api_version=api_version, 

1039 auth_token=auth_token, 

1040 ) 

1041 monitor = self._get_internal_component('monitor') 

1042 if monitor is not None: 

1043 monitor.register(client.meta.events) 

1044 self._register_client_plugins(client) 

1045 return client 

1046 

1047 def _resolve_region_name(self, region_name, config): 

1048 # Figure out the user-provided region based on the various 

1049 # configuration options. 

1050 if region_name is None: 

1051 if config and config.region_name is not None: 

1052 region_name = config.region_name 

1053 else: 

1054 region_name = self.get_config_variable('region') 

1055 

1056 validate_region_name(region_name) 

1057 # For any client that we create in retrieving credentials 

1058 # we want to create it using the same region as specified in 

1059 # creating this client. It is important to note though that the 

1060 # credentials client is only created once per session. So if a new 

1061 # client is created with a different region, its credential resolver 

1062 # will use the region of the first client. However, that is not an 

1063 # issue as of now because the credential resolver uses only STS and 

1064 # the credentials returned at regional endpoints are valid across 

1065 # all regions in the partition. 

1066 self._last_client_region_used = region_name 

1067 return region_name 

1068 

1069 def _resolve_defaults_mode(self, client_config, config_store): 

1070 mode = config_store.get_config_variable('defaults_mode') 

1071 

1072 if client_config and client_config.defaults_mode: 

1073 mode = client_config.defaults_mode 

1074 

1075 default_config_resolver = self._get_internal_component( 

1076 'default_config_resolver' 

1077 ) 

1078 default_modes = default_config_resolver.get_default_modes() 

1079 lmode = mode.lower() 

1080 if lmode not in default_modes: 

1081 raise InvalidDefaultsMode( 

1082 mode=mode, valid_modes=', '.join(default_modes) 

1083 ) 

1084 

1085 return lmode 

1086 

1087 def _add_configured_endpoint_provider(self, client_name, config_store): 

1088 chain = ConfiguredEndpointProvider( 

1089 full_config=self.full_config, 

1090 scoped_config=self.get_scoped_config(), 

1091 client_name=client_name, 

1092 ) 

1093 config_store.set_config_provider( 

1094 logical_name='endpoint_url', 

1095 provider=chain, 

1096 ) 

1097 

1098 def _missing_cred_vars(self, access_key, secret_key): 

1099 if access_key is not None and secret_key is None: 

1100 return 'aws_secret_access_key' 

1101 if secret_key is not None and access_key is None: 

1102 return 'aws_access_key_id' 

1103 return None 

1104 

1105 def get_available_partitions(self): 

1106 """Lists the available partitions found on disk 

1107 

1108 :rtype: list 

1109 :return: Returns a list of partition names (e.g., ["aws", "aws-cn"]) 

1110 """ 

1111 resolver = self._get_internal_component('endpoint_resolver') 

1112 return resolver.get_available_partitions() 

1113 

1114 def get_partition_for_region(self, region_name): 

1115 """Lists the partition name of a particular region. 

1116 

1117 :type region_name: string 

1118 :param region_name: Name of the region to list partition for (e.g., 

1119 us-east-1). 

1120 

1121 :rtype: string 

1122 :return: Returns the respective partition name (e.g., aws). 

1123 """ 

1124 resolver = self._get_internal_component('endpoint_resolver') 

1125 return resolver.get_partition_for_region(region_name) 

1126 

1127 def get_available_regions( 

1128 self, service_name, partition_name='aws', allow_non_regional=False 

1129 ): 

1130 """Lists the region and endpoint names of a particular partition. 

1131 

1132 :type service_name: string 

1133 :param service_name: Name of a service to list endpoint for (e.g., s3). 

1134 This parameter accepts a service name (e.g., "elb") or endpoint 

1135 prefix (e.g., "elasticloadbalancing"). 

1136 

1137 :type partition_name: string 

1138 :param partition_name: Name of the partition to limit endpoints to. 

1139 (e.g., aws for the public AWS endpoints, aws-cn for AWS China 

1140 endpoints, aws-us-gov for AWS GovCloud (US) Endpoints, etc. 

1141 

1142 :type allow_non_regional: bool 

1143 :param allow_non_regional: Set to True to include endpoints that are 

1144 not regional endpoints (e.g., s3-external-1, 

1145 fips-us-gov-west-1, etc). 

1146 :return: Returns a list of endpoint names (e.g., ["us-east-1"]). 

1147 """ 

1148 resolver = self._get_internal_component('endpoint_resolver') 

1149 results = [] 

1150 try: 

1151 service_data = self.get_service_data(service_name) 

1152 endpoint_prefix = service_data['metadata'].get( 

1153 'endpointPrefix', service_name 

1154 ) 

1155 results = resolver.get_available_endpoints( 

1156 endpoint_prefix, partition_name, allow_non_regional 

1157 ) 

1158 except UnknownServiceError: 

1159 pass 

1160 return results 

1161 

1162 def _get_ignored_credentials(self, aws_session_token, aws_account_id): 

1163 credential_inputs = [] 

1164 if aws_session_token: 

1165 credential_inputs.append('aws_session_token') 

1166 if aws_account_id: 

1167 credential_inputs.append('aws_account_id') 

1168 return ', '.join(credential_inputs) if credential_inputs else None 

1169 

1170 def _register_client_plugins(self, client): 

1171 plugins_list = get_botocore_plugins() 

1172 if plugins_list == "DISABLED" or not plugins_list: 

1173 return 

1174 

1175 client_plugins = {} 

1176 for plugin in plugins_list.split(','): 

1177 try: 

1178 name, module = [part.strip() for part in plugin.split('=')] 

1179 client_plugins[name] = module 

1180 except ValueError: 

1181 logger.warning( 

1182 "Invalid plugin format: %s. Expected 'name=module'", plugin 

1183 ) 

1184 

1185 if client_plugins: 

1186 load_client_plugins(client, client_plugins) 

1187 

1188 

1189class ComponentLocator: 

1190 """Service locator for session components.""" 

1191 

1192 def __init__(self): 

1193 self._components = {} 

1194 self._deferred = {} 

1195 

1196 def get_component(self, name): 

1197 if name in self._deferred: 

1198 factory = self._deferred[name] 

1199 self._components[name] = factory() 

1200 # Only delete the component from the deferred dict after 

1201 # successfully creating the object from the factory as well as 

1202 # injecting the instantiated value into the _components dict. 

1203 try: 

1204 del self._deferred[name] 

1205 except KeyError: 

1206 # If we get here, it's likely that get_component was called 

1207 # concurrently from multiple threads, and another thread 

1208 # already deleted the entry. This means the factory was 

1209 # probably called twice, but cleaning up the deferred entry 

1210 # should not crash outright. 

1211 pass 

1212 try: 

1213 return self._components[name] 

1214 except KeyError: 

1215 raise ValueError(f"Unknown component: {name}") 

1216 

1217 def register_component(self, name, component): 

1218 self._components[name] = component 

1219 try: 

1220 del self._deferred[name] 

1221 except KeyError: 

1222 pass 

1223 

1224 def lazy_register_component(self, name, no_arg_factory): 

1225 self._deferred[name] = no_arg_factory 

1226 try: 

1227 del self._components[name] 

1228 except KeyError: 

1229 pass 

1230 

1231 

1232class SessionVarDict(MutableMapping): 

1233 def __init__(self, session, session_vars): 

1234 self._session = session 

1235 self._store = copy.copy(session_vars) 

1236 

1237 def __getitem__(self, key): 

1238 return self._store[key] 

1239 

1240 def __setitem__(self, key, value): 

1241 self._store[key] = value 

1242 self._update_config_store_from_session_vars(key, value) 

1243 

1244 def __delitem__(self, key): 

1245 del self._store[key] 

1246 

1247 def __iter__(self): 

1248 return iter(self._store) 

1249 

1250 def __len__(self): 

1251 return len(self._store) 

1252 

1253 def _update_config_store_from_session_vars( 

1254 self, logical_name, config_options 

1255 ): 

1256 # This is for backwards compatibility. The new preferred way to 

1257 # modify configuration logic is to use the component system to get 

1258 # the config_store component from the session, and then update 

1259 # a key with a custom config provider(s). 

1260 # This backwards compatibility method takes the old session_vars 

1261 # list of tuples and and transforms that into a set of updates to 

1262 # the config_store component. 

1263 config_chain_builder = ConfigChainFactory(session=self._session) 

1264 config_name, env_vars, default, typecast = config_options 

1265 config_store = self._session.get_component('config_store') 

1266 config_store.set_config_provider( 

1267 logical_name, 

1268 config_chain_builder.create_config_chain( 

1269 instance_name=logical_name, 

1270 env_var_names=env_vars, 

1271 config_property_names=config_name, 

1272 default=default, 

1273 conversion_func=typecast, 

1274 ), 

1275 ) 

1276 

1277 

1278class SubsetChainConfigFactory: 

1279 """A class for creating backwards compatible configuration chains. 

1280 

1281 This class can be used instead of 

1282 :class:`botocore.configprovider.ConfigChainFactory` to make it honor the 

1283 methods argument to get_config_variable. This class can be used to filter 

1284 out providers that are not in the methods tuple when creating a new config 

1285 chain. 

1286 """ 

1287 

1288 def __init__(self, session, methods, environ=None): 

1289 self._factory = ConfigChainFactory(session, environ) 

1290 self._supported_methods = methods 

1291 

1292 def create_config_chain( 

1293 self, 

1294 instance_name=None, 

1295 env_var_names=None, 

1296 config_property_name=None, 

1297 default=None, 

1298 conversion_func=None, 

1299 ): 

1300 """Build a config chain following the standard botocore pattern. 

1301 

1302 This config chain factory will omit any providers not in the methods 

1303 tuple provided at initialization. For example if given the tuple 

1304 ('instance', 'config',) it will not inject the environment provider 

1305 into the standard config chain. This lets the botocore session support 

1306 the custom ``methods`` argument for all the default botocore config 

1307 variables when calling ``get_config_variable``. 

1308 """ 

1309 if 'instance' not in self._supported_methods: 

1310 instance_name = None 

1311 if 'env' not in self._supported_methods: 

1312 env_var_names = None 

1313 if 'config' not in self._supported_methods: 

1314 config_property_name = None 

1315 return self._factory.create_config_chain( 

1316 instance_name=instance_name, 

1317 env_var_names=env_var_names, 

1318 config_property_names=config_property_name, 

1319 default=default, 

1320 conversion_func=conversion_func, 

1321 ) 

1322 

1323 

1324def get_session(env_vars=None): 

1325 """ 

1326 Return a new session object. 

1327 """ 

1328 return Session(env_vars)