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

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

447 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.regions import EndpointResolver 

72from botocore.useragent import UserAgentString 

73from botocore.utils import ( 

74 EVENT_ALIASES, 

75 IMDSRegionProvider, 

76 validate_region_name, 

77) 

78 

79logger = logging.getLogger(__name__) 

80 

81 

82class Session: 

83 """ 

84 The Session object collects together useful functionality 

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

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

87 

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

89 file associated with this session. 

90 :ivar profile: The current profile. 

91 """ 

92 

93 SESSION_VARIABLES = copy.copy(BOTOCORE_DEFAUT_SESSION_VARIABLES) 

94 

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

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

97 

98 def __init__( 

99 self, 

100 session_vars=None, 

101 event_hooks=None, 

102 include_builtin_handlers=True, 

103 profile=None, 

104 ): 

105 """ 

106 Create a new Session object. 

107 

108 :type session_vars: dict 

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

110 of the environment variables associated with this session. The 

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

112 corresponding variables defined in ``SESSION_VARIABLES``. 

113 

114 :type event_hooks: BaseEventHooks 

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

116 provided, an event hooks object will be automatically created 

117 for you. 

118 

119 :type include_builtin_handlers: bool 

120 :param include_builtin_handlers: Indicates whether or not to 

121 automatically register builtin handlers. 

122 

123 :type profile: str 

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

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

126 the session is created. 

127 

128 """ 

129 if event_hooks is None: 

130 self._original_handler = HierarchicalEmitter() 

131 else: 

132 self._original_handler = event_hooks 

133 self._events = EventAliaser(self._original_handler) 

134 if include_builtin_handlers: 

135 self._register_builtin_handlers(self._events) 

136 self.user_agent_name = 'Botocore' 

137 self.user_agent_version = __version__ 

138 self.user_agent_extra = '' 

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

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

141 # config lookup process each access time. 

142 self._profile = None 

143 self._config = None 

144 self._credentials = None 

145 self._auth_token = None 

146 self._profile_map = None 

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

148 # overrides via set_config_variable(). 

149 self._session_instance_vars = {} 

150 if profile is not None: 

151 self._session_instance_vars['profile'] = profile 

152 self._client_config = None 

153 self._last_client_region_used = None 

154 self._components = ComponentLocator() 

155 self._internal_components = ComponentLocator() 

156 self._register_components() 

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

158 if session_vars is not None: 

159 self.session_var_map.update(session_vars) 

160 invoke_initializers(self) 

161 

162 def _register_components(self): 

163 self._register_credential_provider() 

164 self._register_token_provider() 

165 self._register_data_loader() 

166 self._register_endpoint_resolver() 

167 self._register_event_emitter() 

168 self._register_response_parser_factory() 

169 self._register_exceptions_factory() 

170 self._register_config_store() 

171 self._register_monitor() 

172 self._register_default_config_resolver() 

173 self._register_smart_defaults_factory() 

174 self._register_user_agent_creator() 

175 

176 def _register_event_emitter(self): 

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

178 

179 def _register_token_provider(self): 

180 self._components.lazy_register_component( 

181 'token_provider', self._create_token_resolver 

182 ) 

183 

184 def _create_token_resolver(self): 

185 return botocore.tokens.create_token_resolver(self) 

186 

187 def _register_credential_provider(self): 

188 self._components.lazy_register_component( 

189 'credential_provider', self._create_credential_resolver 

190 ) 

191 

192 def _create_credential_resolver(self): 

193 return botocore.credentials.create_credential_resolver( 

194 self, region_name=self._last_client_region_used 

195 ) 

196 

197 def _register_data_loader(self): 

198 self._components.lazy_register_component( 

199 'data_loader', 

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

201 ) 

202 

203 def _register_endpoint_resolver(self): 

204 def create_default_resolver(): 

205 loader = self.get_component('data_loader') 

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

207 uses_builtin = loader.is_builtin_path(path) 

208 return EndpointResolver(endpoints, uses_builtin_data=uses_builtin) 

209 

210 self._internal_components.lazy_register_component( 

211 'endpoint_resolver', create_default_resolver 

212 ) 

213 

214 def _register_default_config_resolver(self): 

215 def create_default_config_resolver(): 

216 loader = self.get_component('data_loader') 

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

218 return DefaultConfigResolver(defaults) 

219 

220 self._internal_components.lazy_register_component( 

221 'default_config_resolver', create_default_config_resolver 

222 ) 

223 

224 def _register_smart_defaults_factory(self): 

225 def create_smart_defaults_factory(): 

226 default_config_resolver = self._get_internal_component( 

227 'default_config_resolver' 

228 ) 

229 imds_region_provider = IMDSRegionProvider(session=self) 

230 return SmartDefaultsConfigStoreFactory( 

231 default_config_resolver, imds_region_provider 

232 ) 

233 

234 self._internal_components.lazy_register_component( 

235 'smart_defaults_factory', create_smart_defaults_factory 

236 ) 

237 

238 def _register_response_parser_factory(self): 

239 self._components.register_component( 

240 'response_parser_factory', ResponseParserFactory() 

241 ) 

242 

243 def _register_exceptions_factory(self): 

244 self._internal_components.register_component( 

245 'exceptions_factory', ClientExceptionsFactory() 

246 ) 

247 

248 def _register_builtin_handlers(self, events): 

249 for spec in handlers.BUILTIN_HANDLERS: 

250 if len(spec) == 2: 

251 event_name, handler = spec 

252 self.register(event_name, handler) 

253 else: 

254 event_name, handler, register_type = spec 

255 if register_type is handlers.REGISTER_FIRST: 

256 self._events.register_first(event_name, handler) 

257 elif register_type is handlers.REGISTER_LAST: 

258 self._events.register_last(event_name, handler) 

259 

260 def _register_config_store(self): 

261 config_store_component = ConfigValueStore( 

262 mapping=create_botocore_default_config_mapping(self) 

263 ) 

264 self._components.register_component( 

265 'config_store', config_store_component 

266 ) 

267 

268 def _register_monitor(self): 

269 self._internal_components.lazy_register_component( 

270 'monitor', self._create_csm_monitor 

271 ) 

272 

273 def _register_user_agent_creator(self): 

274 uas = UserAgentString.from_environment() 

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

276 

277 def _create_csm_monitor(self): 

278 if self.get_config_variable('csm_enabled'): 

279 client_id = self.get_config_variable('csm_client_id') 

280 host = self.get_config_variable('csm_host') 

281 port = self.get_config_variable('csm_port') 

282 handler = monitoring.Monitor( 

283 adapter=monitoring.MonitorEventAdapter(), 

284 publisher=monitoring.SocketPublisher( 

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

286 host=host, 

287 port=port, 

288 serializer=monitoring.CSMSerializer( 

289 csm_client_id=client_id 

290 ), 

291 ), 

292 ) 

293 return handler 

294 return None 

295 

296 def _get_crt_version(self): 

297 user_agent_creator = self.get_component('user_agent_creator') 

298 return user_agent_creator._crt_version or 'Unknown' 

299 

300 @property 

301 def available_profiles(self): 

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

303 

304 def _build_profile_map(self): 

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

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

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

308 if self._profile_map is None: 

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

310 return self._profile_map 

311 

312 @property 

313 def profile(self): 

314 if self._profile is None: 

315 profile = self.get_config_variable('profile') 

316 self._profile = profile 

317 return self._profile 

318 

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

320 if methods is not None: 

321 return self._get_config_variable_with_custom_methods( 

322 logical_name, methods 

323 ) 

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

325 logical_name 

326 ) 

327 

328 def _get_config_variable_with_custom_methods(self, logical_name, methods): 

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

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

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

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

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

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

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

336 mapping = create_botocore_default_config_mapping(self) 

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

338 config_name, env_vars, default, typecast = config_options 

339 build_chain_config_args = { 

340 'conversion_func': typecast, 

341 'default': default, 

342 } 

343 if 'instance' in methods: 

344 build_chain_config_args['instance_name'] = name 

345 if 'env' in methods: 

346 build_chain_config_args['env_var_names'] = env_vars 

347 if 'config' in methods: 

348 build_chain_config_args['config_property_name'] = config_name 

349 mapping[name] = chain_builder.create_config_chain( 

350 **build_chain_config_args 

351 ) 

352 config_store_component = ConfigValueStore(mapping=mapping) 

353 value = config_store_component.get_config_variable(logical_name) 

354 return value 

355 

356 def set_config_variable(self, logical_name, value): 

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

358 

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

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

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

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

363 configuration values. 

364 

365 :: 

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

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

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

369 'myvalue' 

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

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

372 'othervalue' 

373 

374 :type logical_name: str 

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

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

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

378 

379 """ 

380 logger.debug( 

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

382 logical_name, 

383 value, 

384 ) 

385 self._session_instance_vars[logical_name] = value 

386 

387 def instance_variables(self): 

388 return copy.copy(self._session_instance_vars) 

389 

390 def get_scoped_config(self): 

391 """ 

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

393 profile. 

394 

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

396 It does not resolve variables based on different locations 

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

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

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

400 

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

402 ``profile`` session variable). 

403 

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

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

406 will be raised. 

407 

408 :raises: ConfigNotFound, ConfigParseError, ProfileNotFound 

409 :rtype: dict 

410 

411 """ 

412 profile_name = self.get_config_variable('profile') 

413 profile_map = self._build_profile_map() 

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

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

416 # a default profile. 

417 if profile_name is None: 

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

419 elif profile_name not in profile_map: 

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

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

422 # we complain. 

423 raise ProfileNotFound(profile=profile_name) 

424 else: 

425 return profile_map[profile_name] 

426 

427 @property 

428 def full_config(self): 

429 """Return the parsed config file. 

430 

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

432 specified profile. This property returns the contents of the 

433 **entire** config file. 

434 

435 :rtype: dict 

436 """ 

437 if self._config is None: 

438 try: 

439 config_file = self.get_config_variable('config_file') 

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

441 except ConfigNotFound: 

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

443 try: 

444 # Now we need to inject the profiles from the 

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

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

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

448 # profile. 

449 cred_file = self.get_config_variable('credentials_file') 

450 cred_profiles = botocore.configloader.raw_config_parse( 

451 cred_file 

452 ) 

453 for profile in cred_profiles: 

454 cred_vars = cred_profiles[profile] 

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

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

457 else: 

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

459 except ConfigNotFound: 

460 pass 

461 return self._config 

462 

463 def get_default_client_config(self): 

464 """Retrieves the default config for creating clients 

465 

466 :rtype: botocore.client.Config 

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

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

469 attached to the session. 

470 """ 

471 return self._client_config 

472 

473 def set_default_client_config(self, client_config): 

474 """Sets the default config for creating clients 

475 

476 :type client_config: botocore.client.Config 

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

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

479 object attached to the session. 

480 """ 

481 self._client_config = client_config 

482 

483 def set_credentials( 

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

485 ): 

486 """ 

487 Manually create credentials for this session. If you would 

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

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

490 method to establish credentials for this session. 

491 

492 :type access_key: str 

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

494 

495 :type secret_key: str 

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

497 

498 :type token: str 

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

500 credentials. 

501 

502 :type account_id: str 

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

504 """ 

505 self._credentials = botocore.credentials.Credentials( 

506 access_key, secret_key, token, account_id=account_id 

507 ) 

508 

509 def get_credentials(self): 

510 """ 

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

512 associated with this session. If the credentials have not 

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

514 have already been loaded, this will return the cached 

515 credentials. 

516 

517 """ 

518 if self._credentials is None: 

519 self._credentials = self._components.get_component( 

520 'credential_provider' 

521 ).load_credentials() 

522 return self._credentials 

523 

524 def get_auth_token(self, **kwargs): 

525 """ 

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

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

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

529 return the cached authorization token. 

530 

531 """ 

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

533 

534 signing_name = kwargs.get('signing_name') 

535 if signing_name is not None: 

536 auth_token = provider.load_token(signing_name=signing_name) 

537 if auth_token is not None: 

538 return auth_token 

539 

540 if self._auth_token is None: 

541 self._auth_token = provider.load_token() 

542 return self._auth_token 

543 

544 def user_agent(self): 

545 """ 

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

547 The string will be of the form: 

548 

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

550 

551 Where: 

552 

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

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

555 - agent_version is the value of the `user_agent_version` 

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

557 by default. 

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

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

560 - plat_ver is the version of the platform 

561 - exec_env is exec-env/$AWS_EXECUTION_ENV 

562 

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

564 appended to the end of the user agent string. 

565 

566 """ 

567 base = ( 

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

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

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

571 ) 

572 if HAS_CRT: 

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

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

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

576 if self.user_agent_extra: 

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

578 

579 return base 

580 

581 def get_data(self, data_path): 

582 """ 

583 Retrieve the data associated with `data_path`. 

584 

585 :type data_path: str 

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

587 """ 

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

589 

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

591 """Get the service model object. 

592 

593 :type service_name: string 

594 :param service_name: The service name 

595 

596 :type api_version: string 

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

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

599 

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

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

602 

603 """ 

604 service_description = self.get_service_data(service_name, api_version) 

605 return ServiceModel(service_description, service_name=service_name) 

606 

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

608 loader = self.get_component('data_loader') 

609 waiter_config = loader.load_service_model( 

610 service_name, 'waiters-2', api_version 

611 ) 

612 return waiter.WaiterModel(waiter_config) 

613 

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

615 loader = self.get_component('data_loader') 

616 paginator_config = loader.load_service_model( 

617 service_name, 'paginators-1', api_version 

618 ) 

619 return paginate.PaginatorModel(paginator_config) 

620 

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

622 """ 

623 Retrieve the fully merged data associated with a service. 

624 """ 

625 data_path = service_name 

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

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

628 ) 

629 service_id = EVENT_ALIASES.get(service_name, service_name) 

630 self._events.emit( 

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

632 service_data=service_data, 

633 service_name=service_name, 

634 session=self, 

635 ) 

636 return service_data 

637 

638 def get_available_services(self): 

639 """ 

640 Return a list of names of available services. 

641 """ 

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

643 type_name='service-2' 

644 ) 

645 

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

647 """ 

648 Convenience function to quickly configure full debug output 

649 to go to the console. 

650 """ 

651 self.set_stream_logger(logger_name, logging.DEBUG) 

652 

653 def set_stream_logger( 

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

655 ): 

656 """ 

657 Convenience method to configure a stream logger. 

658 

659 :type logger_name: str 

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

661 

662 :type log_level: str 

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

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

665 a ``Log`` object. 

666 

667 :type stream: file 

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

669 then sys.stderr will be used. 

670 

671 :type format_string: str 

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

673 formatter. If none is provided this will default to 

674 ``self.LOG_FORMAT``. 

675 

676 """ 

677 log = logging.getLogger(logger_name) 

678 log.setLevel(logging.DEBUG) 

679 

680 ch = logging.StreamHandler(stream) 

681 ch.setLevel(log_level) 

682 

683 # create formatter 

684 if format_string is None: 

685 format_string = self.LOG_FORMAT 

686 formatter = logging.Formatter(format_string) 

687 

688 # add formatter to ch 

689 ch.setFormatter(formatter) 

690 

691 # add ch to logger 

692 log.addHandler(ch) 

693 

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

695 """ 

696 Convenience function to quickly configure any level of logging 

697 to a file. 

698 

699 :type log_level: int 

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

701 

702 :type path: string 

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

704 if it doesn't already exist. 

705 """ 

706 log = logging.getLogger(logger_name) 

707 log.setLevel(logging.DEBUG) 

708 

709 # create console handler and set level to debug 

710 ch = logging.FileHandler(path) 

711 ch.setLevel(log_level) 

712 

713 # create formatter 

714 formatter = logging.Formatter(self.LOG_FORMAT) 

715 

716 # add formatter to ch 

717 ch.setFormatter(formatter) 

718 

719 # add ch to logger 

720 log.addHandler(ch) 

721 

722 def register( 

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

724 ): 

725 """Register a handler with an event. 

726 

727 :type event_name: str 

728 :param event_name: The name of the event. 

729 

730 :type handler: callable 

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

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

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

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

735 

736 :type unique_id: str 

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

738 registration. A unique_id can only be used once for 

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

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

741 registered twice. 

742 

743 :param unique_id_uses_count: boolean 

744 :param unique_id_uses_count: Specifies if the event should maintain 

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

746 event can only be completely unregistered once every register call 

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

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

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

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

751 

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

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

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

755 ``register`` call for that ``unique_id``. 

756 """ 

757 self._events.register( 

758 event_name, 

759 handler, 

760 unique_id, 

761 unique_id_uses_count=unique_id_uses_count, 

762 ) 

763 

764 def unregister( 

765 self, 

766 event_name, 

767 handler=None, 

768 unique_id=None, 

769 unique_id_uses_count=False, 

770 ): 

771 """Unregister a handler with an event. 

772 

773 :type event_name: str 

774 :param event_name: The name of the event. 

775 

776 :type handler: callable 

777 :param handler: The callback to unregister. 

778 

779 :type unique_id: str 

780 :param unique_id: A unique identifier identifying the callback 

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

782 unique_id, you do not have to provide both. 

783 

784 :param unique_id_uses_count: boolean 

785 :param unique_id_uses_count: Specifies if the event should maintain 

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

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

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

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

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

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

792 registered the event. 

793 

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

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

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

797 ``register`` call for that ``unique_id``. 

798 """ 

799 self._events.unregister( 

800 event_name, 

801 handler=handler, 

802 unique_id=unique_id, 

803 unique_id_uses_count=unique_id_uses_count, 

804 ) 

805 

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

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

808 

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

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

811 return first_non_none_response(responses) 

812 

813 def get_component(self, name): 

814 try: 

815 return self._components.get_component(name) 

816 except ValueError: 

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

818 warnings.warn( 

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

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

821 'considered an internal interface of botocore', 

822 DeprecationWarning, 

823 ) 

824 return self._internal_components.get_component(name) 

825 raise 

826 

827 def _get_internal_component(self, name): 

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

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

830 # outside of botocore. 

831 return self._internal_components.get_component(name) 

832 

833 def _register_internal_component(self, name, component): 

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

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

836 # outside of botocore. 

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

838 

839 def register_component(self, name, component): 

840 self._components.register_component(name, component) 

841 

842 def lazy_register_component(self, name, component): 

843 self._components.lazy_register_component(name, component) 

844 

845 @with_current_context() 

846 def create_client( 

847 self, 

848 service_name, 

849 region_name=None, 

850 api_version=None, 

851 use_ssl=True, 

852 verify=None, 

853 endpoint_url=None, 

854 aws_access_key_id=None, 

855 aws_secret_access_key=None, 

856 aws_session_token=None, 

857 config=None, 

858 aws_account_id=None, 

859 ): 

860 """Create a botocore client. 

861 

862 :type service_name: string 

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

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

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

866 

867 :type region_name: string 

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

869 A client is associated with a single region. 

870 

871 :type api_version: string 

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

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

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

875 of the client. 

876 

877 :type use_ssl: boolean 

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

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

880 

881 :type verify: boolean/string 

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

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

884 following values: 

885 

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

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

888 will not be verified. 

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

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

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

892 

893 :type endpoint_url: string 

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

895 client. Normally, botocore will automatically construct the 

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

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

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

899 ``use_ssl`` is ignored. 

900 

901 :type aws_access_key_id: string 

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

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

904 the credentials configured for the session will automatically 

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

906 to override the credentials used for this specific client. 

907 

908 :type aws_secret_access_key: string 

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

910 the client. Same semantics as aws_access_key_id above. 

911 

912 :type aws_session_token: string 

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

914 the client. Same semantics as aws_access_key_id above. 

915 

916 :type config: botocore.client.Config 

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

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

919 over environment variables and configuration values, but not over 

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

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

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

923 default config with the config provided to this call. 

924 

925 :type aws_account_id: string 

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

927 the client. Same semantics as aws_access_key_id above. 

928 

929 :rtype: botocore.client.BaseClient 

930 :return: A botocore client instance 

931 

932 """ 

933 default_client_config = self.get_default_client_config() 

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

935 # use the config resulting from merging the two. 

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

937 config = default_client_config.merge(config) 

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

939 # client config from the session 

940 elif default_client_config is not None: 

941 config = default_client_config 

942 

943 region_name = self._resolve_region_name(region_name, config) 

944 

945 # Figure out the verify value base on the various 

946 # configuration options. 

947 if verify is None: 

948 verify = self.get_config_variable('ca_bundle') 

949 

950 if api_version is None: 

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

952 service_name, None 

953 ) 

954 

955 loader = self.get_component('data_loader') 

956 event_emitter = self.get_component('event_emitter') 

957 response_parser_factory = self.get_component('response_parser_factory') 

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

959 credentials = None 

960 elif ( 

961 aws_access_key_id is not None and aws_secret_access_key is not None 

962 ): 

963 credentials = botocore.credentials.Credentials( 

964 access_key=aws_access_key_id, 

965 secret_key=aws_secret_access_key, 

966 token=aws_session_token, 

967 account_id=aws_account_id, 

968 ) 

969 elif self._missing_cred_vars(aws_access_key_id, aws_secret_access_key): 

970 raise PartialCredentialsError( 

971 provider='explicit', 

972 cred_var=self._missing_cred_vars( 

973 aws_access_key_id, aws_secret_access_key 

974 ), 

975 ) 

976 else: 

977 if ignored_credentials := self._get_ignored_credentials( 

978 aws_session_token, aws_account_id 

979 ): 

980 logger.debug( 

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

982 f"an access key id and secret key on the session or client: {ignored_credentials}" 

983 ) 

984 credentials = self.get_credentials() 

985 auth_token = self.get_auth_token() 

986 endpoint_resolver = self._get_internal_component('endpoint_resolver') 

987 exceptions_factory = self._get_internal_component('exceptions_factory') 

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

989 user_agent_creator = self.get_component('user_agent_creator') 

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

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

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

993 user_agent_creator.set_session_config( 

994 session_user_agent_name=self.user_agent_name, 

995 session_user_agent_version=self.user_agent_version, 

996 session_user_agent_extra=self.user_agent_extra, 

997 ) 

998 defaults_mode = self._resolve_defaults_mode(config, config_store) 

999 if defaults_mode != 'legacy': 

1000 smart_defaults_factory = self._get_internal_component( 

1001 'smart_defaults_factory' 

1002 ) 

1003 smart_defaults_factory.merge_smart_defaults( 

1004 config_store, defaults_mode, region_name 

1005 ) 

1006 

1007 self._add_configured_endpoint_provider( 

1008 client_name=service_name, 

1009 config_store=config_store, 

1010 ) 

1011 

1012 user_agent_creator.set_client_features(get_context().features) 

1013 

1014 client_creator = botocore.client.ClientCreator( 

1015 loader, 

1016 endpoint_resolver, 

1017 self.user_agent(), 

1018 event_emitter, 

1019 retryhandler, 

1020 translate, 

1021 response_parser_factory, 

1022 exceptions_factory, 

1023 config_store, 

1024 user_agent_creator=user_agent_creator, 

1025 auth_token_resolver=self.get_auth_token, 

1026 ) 

1027 client = client_creator.create_client( 

1028 service_name=service_name, 

1029 region_name=region_name, 

1030 is_secure=use_ssl, 

1031 endpoint_url=endpoint_url, 

1032 verify=verify, 

1033 credentials=credentials, 

1034 scoped_config=self.get_scoped_config(), 

1035 client_config=config, 

1036 api_version=api_version, 

1037 auth_token=auth_token, 

1038 ) 

1039 monitor = self._get_internal_component('monitor') 

1040 if monitor is not None: 

1041 monitor.register(client.meta.events) 

1042 return client 

1043 

1044 def _resolve_region_name(self, region_name, config): 

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

1046 # configuration options. 

1047 if region_name is None: 

1048 if config and config.region_name is not None: 

1049 region_name = config.region_name 

1050 else: 

1051 region_name = self.get_config_variable('region') 

1052 

1053 validate_region_name(region_name) 

1054 # For any client that we create in retrieving credentials 

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

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

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

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

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

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

1061 # the credentials returned at regional endpoints are valid across 

1062 # all regions in the partition. 

1063 self._last_client_region_used = region_name 

1064 return region_name 

1065 

1066 def _resolve_defaults_mode(self, client_config, config_store): 

1067 mode = config_store.get_config_variable('defaults_mode') 

1068 

1069 if client_config and client_config.defaults_mode: 

1070 mode = client_config.defaults_mode 

1071 

1072 default_config_resolver = self._get_internal_component( 

1073 'default_config_resolver' 

1074 ) 

1075 default_modes = default_config_resolver.get_default_modes() 

1076 lmode = mode.lower() 

1077 if lmode not in default_modes: 

1078 raise InvalidDefaultsMode( 

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

1080 ) 

1081 

1082 return lmode 

1083 

1084 def _add_configured_endpoint_provider(self, client_name, config_store): 

1085 chain = ConfiguredEndpointProvider( 

1086 full_config=self.full_config, 

1087 scoped_config=self.get_scoped_config(), 

1088 client_name=client_name, 

1089 ) 

1090 config_store.set_config_provider( 

1091 logical_name='endpoint_url', 

1092 provider=chain, 

1093 ) 

1094 

1095 def _missing_cred_vars(self, access_key, secret_key): 

1096 if access_key is not None and secret_key is None: 

1097 return 'aws_secret_access_key' 

1098 if secret_key is not None and access_key is None: 

1099 return 'aws_access_key_id' 

1100 return None 

1101 

1102 def get_available_partitions(self): 

1103 """Lists the available partitions found on disk 

1104 

1105 :rtype: list 

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

1107 """ 

1108 resolver = self._get_internal_component('endpoint_resolver') 

1109 return resolver.get_available_partitions() 

1110 

1111 def get_partition_for_region(self, region_name): 

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

1113 

1114 :type region_name: string 

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

1116 us-east-1). 

1117 

1118 :rtype: string 

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

1120 """ 

1121 resolver = self._get_internal_component('endpoint_resolver') 

1122 return resolver.get_partition_for_region(region_name) 

1123 

1124 def get_available_regions( 

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

1126 ): 

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

1128 

1129 :type service_name: string 

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

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

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

1133 

1134 :type partition_name: string 

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

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

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

1138 

1139 :type allow_non_regional: bool 

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

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

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

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

1144 """ 

1145 resolver = self._get_internal_component('endpoint_resolver') 

1146 results = [] 

1147 try: 

1148 service_data = self.get_service_data(service_name) 

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

1150 'endpointPrefix', service_name 

1151 ) 

1152 results = resolver.get_available_endpoints( 

1153 endpoint_prefix, partition_name, allow_non_regional 

1154 ) 

1155 except UnknownServiceError: 

1156 pass 

1157 return results 

1158 

1159 def _get_ignored_credentials(self, aws_session_token, aws_account_id): 

1160 credential_inputs = [] 

1161 if aws_session_token: 

1162 credential_inputs.append('aws_session_token') 

1163 if aws_account_id: 

1164 credential_inputs.append('aws_account_id') 

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

1166 

1167 

1168class ComponentLocator: 

1169 """Service locator for session components.""" 

1170 

1171 def __init__(self): 

1172 self._components = {} 

1173 self._deferred = {} 

1174 

1175 def get_component(self, name): 

1176 if name in self._deferred: 

1177 factory = self._deferred[name] 

1178 self._components[name] = factory() 

1179 # Only delete the component from the deferred dict after 

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

1181 # injecting the instantiated value into the _components dict. 

1182 try: 

1183 del self._deferred[name] 

1184 except KeyError: 

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

1186 # concurrently from multiple threads, and another thread 

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

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

1189 # should not crash outright. 

1190 pass 

1191 try: 

1192 return self._components[name] 

1193 except KeyError: 

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

1195 

1196 def register_component(self, name, component): 

1197 self._components[name] = component 

1198 try: 

1199 del self._deferred[name] 

1200 except KeyError: 

1201 pass 

1202 

1203 def lazy_register_component(self, name, no_arg_factory): 

1204 self._deferred[name] = no_arg_factory 

1205 try: 

1206 del self._components[name] 

1207 except KeyError: 

1208 pass 

1209 

1210 

1211class SessionVarDict(MutableMapping): 

1212 def __init__(self, session, session_vars): 

1213 self._session = session 

1214 self._store = copy.copy(session_vars) 

1215 

1216 def __getitem__(self, key): 

1217 return self._store[key] 

1218 

1219 def __setitem__(self, key, value): 

1220 self._store[key] = value 

1221 self._update_config_store_from_session_vars(key, value) 

1222 

1223 def __delitem__(self, key): 

1224 del self._store[key] 

1225 

1226 def __iter__(self): 

1227 return iter(self._store) 

1228 

1229 def __len__(self): 

1230 return len(self._store) 

1231 

1232 def _update_config_store_from_session_vars( 

1233 self, logical_name, config_options 

1234 ): 

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

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

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

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

1239 # This backwards compatibility method takes the old session_vars 

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

1241 # the config_store component. 

1242 config_chain_builder = ConfigChainFactory(session=self._session) 

1243 config_name, env_vars, default, typecast = config_options 

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

1245 config_store.set_config_provider( 

1246 logical_name, 

1247 config_chain_builder.create_config_chain( 

1248 instance_name=logical_name, 

1249 env_var_names=env_vars, 

1250 config_property_names=config_name, 

1251 default=default, 

1252 conversion_func=typecast, 

1253 ), 

1254 ) 

1255 

1256 

1257class SubsetChainConfigFactory: 

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

1259 

1260 This class can be used instead of 

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

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

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

1264 chain. 

1265 """ 

1266 

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

1268 self._factory = ConfigChainFactory(session, environ) 

1269 self._supported_methods = methods 

1270 

1271 def create_config_chain( 

1272 self, 

1273 instance_name=None, 

1274 env_var_names=None, 

1275 config_property_name=None, 

1276 default=None, 

1277 conversion_func=None, 

1278 ): 

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

1280 

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

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

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

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

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

1286 variables when calling ``get_config_variable``. 

1287 """ 

1288 if 'instance' not in self._supported_methods: 

1289 instance_name = None 

1290 if 'env' not in self._supported_methods: 

1291 env_var_names = None 

1292 if 'config' not in self._supported_methods: 

1293 config_property_name = None 

1294 return self._factory.create_config_chain( 

1295 instance_name=instance_name, 

1296 env_var_names=env_var_names, 

1297 config_property_names=config_property_name, 

1298 default=default, 

1299 conversion_func=conversion_func, 

1300 ) 

1301 

1302 

1303def get_session(env_vars=None): 

1304 """ 

1305 Return a new session object. 

1306 """ 

1307 return Session(env_vars)