Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/botocore/session.py: 53%
430 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:51 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:51 +0000
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"""
19import copy
20import logging
21import os
22import platform
23import socket
24import warnings
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 HAS_CRT, MutableMapping
42from botocore.configprovider import (
43 BOTOCORE_DEFAUT_SESSION_VARIABLES,
44 ConfigChainFactory,
45 ConfiguredEndpointProvider,
46 ConfigValueStore,
47 DefaultConfigResolver,
48 SmartDefaultsConfigStoreFactory,
49 create_botocore_default_config_mapping,
50)
51from botocore.errorfactory import ClientExceptionsFactory
52from botocore.exceptions import (
53 ConfigNotFound,
54 InvalidDefaultsMode,
55 PartialCredentialsError,
56 ProfileNotFound,
57 UnknownServiceError,
58)
59from botocore.hooks import (
60 EventAliaser,
61 HierarchicalEmitter,
62 first_non_none_response,
63)
64from botocore.loaders import create_loader
65from botocore.model import ServiceModel
66from botocore.parsers import ResponseParserFactory
67from botocore.regions import EndpointResolver
68from botocore.useragent import UserAgentString
69from botocore.utils import (
70 EVENT_ALIASES,
71 IMDSRegionProvider,
72 validate_region_name,
73)
75from botocore.compat import HAS_CRT # noqa
78logger = logging.getLogger(__name__)
81class Session:
82 """
83 The Session object collects together useful functionality
84 from `botocore` as well as important data such as configuration
85 information and credentials into a single, easy-to-use object.
87 :ivar available_profiles: A list of profiles defined in the config
88 file associated with this session.
89 :ivar profile: The current profile.
90 """
92 SESSION_VARIABLES = copy.copy(BOTOCORE_DEFAUT_SESSION_VARIABLES)
94 #: The default format string to use when configuring the botocore logger.
95 LOG_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
97 def __init__(
98 self,
99 session_vars=None,
100 event_hooks=None,
101 include_builtin_handlers=True,
102 profile=None,
103 ):
104 """
105 Create a new Session object.
107 :type session_vars: dict
108 :param session_vars: A dictionary that is used to override some or all
109 of the environment variables associated with this session. The
110 key/value pairs defined in this dictionary will override the
111 corresponding variables defined in ``SESSION_VARIABLES``.
113 :type event_hooks: BaseEventHooks
114 :param event_hooks: The event hooks object to use. If one is not
115 provided, an event hooks object will be automatically created
116 for you.
118 :type include_builtin_handlers: bool
119 :param include_builtin_handlers: Indicates whether or not to
120 automatically register builtin handlers.
122 :type profile: str
123 :param profile: The name of the profile to use for this
124 session. Note that the profile can only be set when
125 the session is created.
127 """
128 if event_hooks is None:
129 self._original_handler = HierarchicalEmitter()
130 else:
131 self._original_handler = event_hooks
132 self._events = EventAliaser(self._original_handler)
133 if include_builtin_handlers:
134 self._register_builtin_handlers(self._events)
135 self.user_agent_name = 'Botocore'
136 self.user_agent_version = __version__
137 self.user_agent_extra = ''
138 # The _profile attribute is just used to cache the value
139 # of the current profile to avoid going through the normal
140 # config lookup process each access time.
141 self._profile = None
142 self._config = None
143 self._credentials = None
144 self._auth_token = None
145 self._profile_map = None
146 # This is a dict that stores per session specific config variable
147 # overrides via set_config_variable().
148 self._session_instance_vars = {}
149 if profile is not None:
150 self._session_instance_vars['profile'] = profile
151 self._client_config = None
152 self._last_client_region_used = None
153 self._components = ComponentLocator()
154 self._internal_components = ComponentLocator()
155 self._register_components()
156 self.session_var_map = SessionVarDict(self, self.SESSION_VARIABLES)
157 if session_vars is not None:
158 self.session_var_map.update(session_vars)
159 invoke_initializers(self)
161 def _register_components(self):
162 self._register_credential_provider()
163 self._register_token_provider()
164 self._register_data_loader()
165 self._register_endpoint_resolver()
166 self._register_event_emitter()
167 self._register_response_parser_factory()
168 self._register_exceptions_factory()
169 self._register_config_store()
170 self._register_monitor()
171 self._register_default_config_resolver()
172 self._register_smart_defaults_factory()
173 self._register_user_agent_creator()
175 def _register_event_emitter(self):
176 self._components.register_component('event_emitter', self._events)
178 def _register_token_provider(self):
179 self._components.lazy_register_component(
180 'token_provider', self._create_token_resolver
181 )
183 def _create_token_resolver(self):
184 return botocore.tokens.create_token_resolver(self)
186 def _register_credential_provider(self):
187 self._components.lazy_register_component(
188 'credential_provider', self._create_credential_resolver
189 )
191 def _create_credential_resolver(self):
192 return botocore.credentials.create_credential_resolver(
193 self, region_name=self._last_client_region_used
194 )
196 def _register_data_loader(self):
197 self._components.lazy_register_component(
198 'data_loader',
199 lambda: create_loader(self.get_config_variable('data_path')),
200 )
202 def _register_endpoint_resolver(self):
203 def create_default_resolver():
204 loader = self.get_component('data_loader')
205 endpoints, path = loader.load_data_with_path('endpoints')
206 uses_builtin = loader.is_builtin_path(path)
207 return EndpointResolver(endpoints, uses_builtin_data=uses_builtin)
209 self._internal_components.lazy_register_component(
210 'endpoint_resolver', create_default_resolver
211 )
213 def _register_default_config_resolver(self):
214 def create_default_config_resolver():
215 loader = self.get_component('data_loader')
216 defaults = loader.load_data('sdk-default-configuration')
217 return DefaultConfigResolver(defaults)
219 self._internal_components.lazy_register_component(
220 'default_config_resolver', create_default_config_resolver
221 )
223 def _register_smart_defaults_factory(self):
224 def create_smart_defaults_factory():
225 default_config_resolver = self._get_internal_component(
226 'default_config_resolver'
227 )
228 imds_region_provider = IMDSRegionProvider(session=self)
229 return SmartDefaultsConfigStoreFactory(
230 default_config_resolver, imds_region_provider
231 )
233 self._internal_components.lazy_register_component(
234 'smart_defaults_factory', create_smart_defaults_factory
235 )
237 def _register_response_parser_factory(self):
238 self._components.register_component(
239 'response_parser_factory', ResponseParserFactory()
240 )
242 def _register_exceptions_factory(self):
243 self._internal_components.register_component(
244 'exceptions_factory', ClientExceptionsFactory()
245 )
247 def _register_builtin_handlers(self, events):
248 for spec in handlers.BUILTIN_HANDLERS:
249 if len(spec) == 2:
250 event_name, handler = spec
251 self.register(event_name, handler)
252 else:
253 event_name, handler, register_type = spec
254 if register_type is handlers.REGISTER_FIRST:
255 self._events.register_first(event_name, handler)
256 elif register_type is handlers.REGISTER_LAST:
257 self._events.register_last(event_name, handler)
259 def _register_config_store(self):
260 config_store_component = ConfigValueStore(
261 mapping=create_botocore_default_config_mapping(self)
262 )
263 self._components.register_component(
264 'config_store', config_store_component
265 )
267 def _register_monitor(self):
268 self._internal_components.lazy_register_component(
269 'monitor', self._create_csm_monitor
270 )
272 def _register_user_agent_creator(self):
273 uas = UserAgentString.from_environment()
274 self._components.register_component('user_agent_creator', uas)
276 def _create_csm_monitor(self):
277 if self.get_config_variable('csm_enabled'):
278 client_id = self.get_config_variable('csm_client_id')
279 host = self.get_config_variable('csm_host')
280 port = self.get_config_variable('csm_port')
281 handler = monitoring.Monitor(
282 adapter=monitoring.MonitorEventAdapter(),
283 publisher=monitoring.SocketPublisher(
284 socket=socket.socket(socket.AF_INET, socket.SOCK_DGRAM),
285 host=host,
286 port=port,
287 serializer=monitoring.CSMSerializer(
288 csm_client_id=client_id
289 ),
290 ),
291 )
292 return handler
293 return None
295 def _get_crt_version(self):
296 user_agent_creator = self.get_component('user_agent_creator')
297 return user_agent_creator._crt_version or 'Unknown'
299 @property
300 def available_profiles(self):
301 return list(self._build_profile_map().keys())
303 def _build_profile_map(self):
304 # This will build the profile map if it has not been created,
305 # otherwise it will return the cached value. The profile map
306 # is a list of profile names, to the config values for the profile.
307 if self._profile_map is None:
308 self._profile_map = self.full_config['profiles']
309 return self._profile_map
311 @property
312 def profile(self):
313 if self._profile is None:
314 profile = self.get_config_variable('profile')
315 self._profile = profile
316 return self._profile
318 def get_config_variable(self, logical_name, methods=None):
319 if methods is not None:
320 return self._get_config_variable_with_custom_methods(
321 logical_name, methods
322 )
323 return self.get_component('config_store').get_config_variable(
324 logical_name
325 )
327 def _get_config_variable_with_custom_methods(self, logical_name, methods):
328 # If a custom list of methods was supplied we need to perserve the
329 # behavior with the new system. To do so a new chain that is a copy of
330 # the old one will be constructed, but only with the supplied methods
331 # being added to the chain. This chain will be consulted for a value
332 # and then thrown out. This is not efficient, nor is the methods arg
333 # used in botocore, this is just for backwards compatibility.
334 chain_builder = SubsetChainConfigFactory(session=self, methods=methods)
335 mapping = create_botocore_default_config_mapping(self)
336 for name, config_options in self.session_var_map.items():
337 config_name, env_vars, default, typecast = config_options
338 build_chain_config_args = {
339 'conversion_func': typecast,
340 'default': default,
341 }
342 if 'instance' in methods:
343 build_chain_config_args['instance_name'] = name
344 if 'env' in methods:
345 build_chain_config_args['env_var_names'] = env_vars
346 if 'config' in methods:
347 build_chain_config_args['config_property_name'] = config_name
348 mapping[name] = chain_builder.create_config_chain(
349 **build_chain_config_args
350 )
351 config_store_component = ConfigValueStore(mapping=mapping)
352 value = config_store_component.get_config_variable(logical_name)
353 return value
355 def set_config_variable(self, logical_name, value):
356 """Set a configuration variable to a specific value.
358 By using this method, you can override the normal lookup
359 process used in ``get_config_variable`` by explicitly setting
360 a value. Subsequent calls to ``get_config_variable`` will
361 use the ``value``. This gives you per-session specific
362 configuration values.
364 ::
365 >>> # Assume logical name 'foo' maps to env var 'FOO'
366 >>> os.environ['FOO'] = 'myvalue'
367 >>> s.get_config_variable('foo')
368 'myvalue'
369 >>> s.set_config_variable('foo', 'othervalue')
370 >>> s.get_config_variable('foo')
371 'othervalue'
373 :type logical_name: str
374 :param logical_name: The logical name of the session variable
375 you want to set. These are the keys in ``SESSION_VARIABLES``.
376 :param value: The value to associate with the config variable.
378 """
379 logger.debug(
380 "Setting config variable for %s to %r",
381 logical_name,
382 value,
383 )
384 self._session_instance_vars[logical_name] = value
386 def instance_variables(self):
387 return copy.copy(self._session_instance_vars)
389 def get_scoped_config(self):
390 """
391 Returns the config values from the config file scoped to the current
392 profile.
394 The configuration data is loaded **only** from the config file.
395 It does not resolve variables based on different locations
396 (e.g. first from the session instance, then from environment
397 variables, then from the config file). If you want this lookup
398 behavior, use the ``get_config_variable`` method instead.
400 Note that this configuration is specific to a single profile (the
401 ``profile`` session variable).
403 If the ``profile`` session variable is set and the profile does
404 not exist in the config file, a ``ProfileNotFound`` exception
405 will be raised.
407 :raises: ConfigNotFound, ConfigParseError, ProfileNotFound
408 :rtype: dict
410 """
411 profile_name = self.get_config_variable('profile')
412 profile_map = self._build_profile_map()
413 # If a profile is not explicitly set return the default
414 # profile config or an empty config dict if we don't have
415 # a default profile.
416 if profile_name is None:
417 return profile_map.get('default', {})
418 elif profile_name not in profile_map:
419 # Otherwise if they specified a profile, it has to
420 # exist (even if it's the default profile) otherwise
421 # we complain.
422 raise ProfileNotFound(profile=profile_name)
423 else:
424 return profile_map[profile_name]
426 @property
427 def full_config(self):
428 """Return the parsed config file.
430 The ``get_config`` method returns the config associated with the
431 specified profile. This property returns the contents of the
432 **entire** config file.
434 :rtype: dict
435 """
436 if self._config is None:
437 try:
438 config_file = self.get_config_variable('config_file')
439 self._config = botocore.configloader.load_config(config_file)
440 except ConfigNotFound:
441 self._config = {'profiles': {}}
442 try:
443 # Now we need to inject the profiles from the
444 # credentials file. We don't actually need the values
445 # in the creds file, only the profile names so that we
446 # can validate the user is not referring to a nonexistent
447 # profile.
448 cred_file = self.get_config_variable('credentials_file')
449 cred_profiles = botocore.configloader.raw_config_parse(
450 cred_file
451 )
452 for profile in cred_profiles:
453 cred_vars = cred_profiles[profile]
454 if profile not in self._config['profiles']:
455 self._config['profiles'][profile] = cred_vars
456 else:
457 self._config['profiles'][profile].update(cred_vars)
458 except ConfigNotFound:
459 pass
460 return self._config
462 def get_default_client_config(self):
463 """Retrieves the default config for creating clients
465 :rtype: botocore.client.Config
466 :returns: The default client config object when creating clients. If
467 the value is ``None`` then there is no default config object
468 attached to the session.
469 """
470 return self._client_config
472 def set_default_client_config(self, client_config):
473 """Sets the default config for creating clients
475 :type client_config: botocore.client.Config
476 :param client_config: The default client config object when creating
477 clients. If the value is ``None`` then there is no default config
478 object attached to the session.
479 """
480 self._client_config = client_config
482 def set_credentials(self, access_key, secret_key, token=None):
483 """
484 Manually create credentials for this session. If you would
485 prefer to use botocore without a config file, environment variables,
486 or IAM roles, you can pass explicit credentials into this
487 method to establish credentials for this session.
489 :type access_key: str
490 :param access_key: The access key part of the credentials.
492 :type secret_key: str
493 :param secret_key: The secret key part of the credentials.
495 :type token: str
496 :param token: An option session token used by STS session
497 credentials.
498 """
499 self._credentials = botocore.credentials.Credentials(
500 access_key, secret_key, token
501 )
503 def get_credentials(self):
504 """
505 Return the :class:`botocore.credential.Credential` object
506 associated with this session. If the credentials have not
507 yet been loaded, this will attempt to load them. If they
508 have already been loaded, this will return the cached
509 credentials.
511 """
512 if self._credentials is None:
513 self._credentials = self._components.get_component(
514 'credential_provider'
515 ).load_credentials()
516 return self._credentials
518 def get_auth_token(self):
519 """
520 Return the :class:`botocore.tokens.AuthToken` object associated with
521 this session. If the authorization token has not yet been loaded, this
522 will attempt to load it. If it has already been loaded, this will
523 return the cached authorization token.
525 """
526 if self._auth_token is None:
527 provider = self._components.get_component('token_provider')
528 self._auth_token = provider.load_token()
529 return self._auth_token
531 def user_agent(self):
532 """
533 Return a string suitable for use as a User-Agent header.
534 The string will be of the form:
536 <agent_name>/<agent_version> Python/<py_ver> <plat_name>/<plat_ver> <exec_env>
538 Where:
540 - agent_name is the value of the `user_agent_name` attribute
541 of the session object (`Botocore` by default).
542 - agent_version is the value of the `user_agent_version`
543 attribute of the session object (the botocore version by default).
544 by default.
545 - py_ver is the version of the Python interpreter beng used.
546 - plat_name is the name of the platform (e.g. Darwin)
547 - plat_ver is the version of the platform
548 - exec_env is exec-env/$AWS_EXECUTION_ENV
550 If ``user_agent_extra`` is not empty, then this value will be
551 appended to the end of the user agent string.
553 """
554 base = (
555 f'{self.user_agent_name}/{self.user_agent_version} '
556 f'Python/{platform.python_version()} '
557 f'{platform.system()}/{platform.release()}'
558 )
559 if HAS_CRT:
560 base += ' awscrt/%s' % self._get_crt_version()
561 if os.environ.get('AWS_EXECUTION_ENV') is not None:
562 base += ' exec-env/%s' % os.environ.get('AWS_EXECUTION_ENV')
563 if self.user_agent_extra:
564 base += ' %s' % self.user_agent_extra
566 return base
568 def get_data(self, data_path):
569 """
570 Retrieve the data associated with `data_path`.
572 :type data_path: str
573 :param data_path: The path to the data you wish to retrieve.
574 """
575 return self.get_component('data_loader').load_data(data_path)
577 def get_service_model(self, service_name, api_version=None):
578 """Get the service model object.
580 :type service_name: string
581 :param service_name: The service name
583 :type api_version: string
584 :param api_version: The API version of the service. If none is
585 provided, then the latest API version will be used.
587 :rtype: L{botocore.model.ServiceModel}
588 :return: The botocore service model for the service.
590 """
591 service_description = self.get_service_data(service_name, api_version)
592 return ServiceModel(service_description, service_name=service_name)
594 def get_waiter_model(self, service_name, api_version=None):
595 loader = self.get_component('data_loader')
596 waiter_config = loader.load_service_model(
597 service_name, 'waiters-2', api_version
598 )
599 return waiter.WaiterModel(waiter_config)
601 def get_paginator_model(self, service_name, api_version=None):
602 loader = self.get_component('data_loader')
603 paginator_config = loader.load_service_model(
604 service_name, 'paginators-1', api_version
605 )
606 return paginate.PaginatorModel(paginator_config)
608 def get_service_data(self, service_name, api_version=None):
609 """
610 Retrieve the fully merged data associated with a service.
611 """
612 data_path = service_name
613 service_data = self.get_component('data_loader').load_service_model(
614 data_path, type_name='service-2', api_version=api_version
615 )
616 service_id = EVENT_ALIASES.get(service_name, service_name)
617 self._events.emit(
618 'service-data-loaded.%s' % service_id,
619 service_data=service_data,
620 service_name=service_name,
621 session=self,
622 )
623 return service_data
625 def get_available_services(self):
626 """
627 Return a list of names of available services.
628 """
629 return self.get_component('data_loader').list_available_services(
630 type_name='service-2'
631 )
633 def set_debug_logger(self, logger_name='botocore'):
634 """
635 Convenience function to quickly configure full debug output
636 to go to the console.
637 """
638 self.set_stream_logger(logger_name, logging.DEBUG)
640 def set_stream_logger(
641 self, logger_name, log_level, stream=None, format_string=None
642 ):
643 """
644 Convenience method to configure a stream logger.
646 :type logger_name: str
647 :param logger_name: The name of the logger to configure
649 :type log_level: str
650 :param log_level: The log level to set for the logger. This
651 is any param supported by the ``.setLevel()`` method of
652 a ``Log`` object.
654 :type stream: file
655 :param stream: A file like object to log to. If none is provided
656 then sys.stderr will be used.
658 :type format_string: str
659 :param format_string: The format string to use for the log
660 formatter. If none is provided this will default to
661 ``self.LOG_FORMAT``.
663 """
664 log = logging.getLogger(logger_name)
665 log.setLevel(logging.DEBUG)
667 ch = logging.StreamHandler(stream)
668 ch.setLevel(log_level)
670 # create formatter
671 if format_string is None:
672 format_string = self.LOG_FORMAT
673 formatter = logging.Formatter(format_string)
675 # add formatter to ch
676 ch.setFormatter(formatter)
678 # add ch to logger
679 log.addHandler(ch)
681 def set_file_logger(self, log_level, path, logger_name='botocore'):
682 """
683 Convenience function to quickly configure any level of logging
684 to a file.
686 :type log_level: int
687 :param log_level: A log level as specified in the `logging` module
689 :type path: string
690 :param path: Path to the log file. The file will be created
691 if it doesn't already exist.
692 """
693 log = logging.getLogger(logger_name)
694 log.setLevel(logging.DEBUG)
696 # create console handler and set level to debug
697 ch = logging.FileHandler(path)
698 ch.setLevel(log_level)
700 # create formatter
701 formatter = logging.Formatter(self.LOG_FORMAT)
703 # add formatter to ch
704 ch.setFormatter(formatter)
706 # add ch to logger
707 log.addHandler(ch)
709 def register(
710 self, event_name, handler, unique_id=None, unique_id_uses_count=False
711 ):
712 """Register a handler with an event.
714 :type event_name: str
715 :param event_name: The name of the event.
717 :type handler: callable
718 :param handler: The callback to invoke when the event
719 is emitted. This object must be callable, and must
720 accept ``**kwargs``. If either of these preconditions are
721 not met, a ``ValueError`` will be raised.
723 :type unique_id: str
724 :param unique_id: An optional identifier to associate with the
725 registration. A unique_id can only be used once for
726 the entire session registration (unless it is unregistered).
727 This can be used to prevent an event handler from being
728 registered twice.
730 :param unique_id_uses_count: boolean
731 :param unique_id_uses_count: Specifies if the event should maintain
732 a count when a ``unique_id`` is registered and unregisted. The
733 event can only be completely unregistered once every register call
734 using the unique id has been matched by an ``unregister`` call.
735 If ``unique_id`` is specified, subsequent ``register``
736 calls must use the same value for ``unique_id_uses_count``
737 as the ``register`` call that first registered the event.
739 :raises ValueError: If the call to ``register`` uses ``unique_id``
740 but the value for ``unique_id_uses_count`` differs from the
741 ``unique_id_uses_count`` value declared by the very first
742 ``register`` call for that ``unique_id``.
743 """
744 self._events.register(
745 event_name,
746 handler,
747 unique_id,
748 unique_id_uses_count=unique_id_uses_count,
749 )
751 def unregister(
752 self,
753 event_name,
754 handler=None,
755 unique_id=None,
756 unique_id_uses_count=False,
757 ):
758 """Unregister a handler with an event.
760 :type event_name: str
761 :param event_name: The name of the event.
763 :type handler: callable
764 :param handler: The callback to unregister.
766 :type unique_id: str
767 :param unique_id: A unique identifier identifying the callback
768 to unregister. You can provide either the handler or the
769 unique_id, you do not have to provide both.
771 :param unique_id_uses_count: boolean
772 :param unique_id_uses_count: Specifies if the event should maintain
773 a count when a ``unique_id`` is registered and unregisted. The
774 event can only be completely unregistered once every ``register``
775 call using the ``unique_id`` has been matched by an ``unregister``
776 call. If the ``unique_id`` is specified, subsequent
777 ``unregister`` calls must use the same value for
778 ``unique_id_uses_count`` as the ``register`` call that first
779 registered the event.
781 :raises ValueError: If the call to ``unregister`` uses ``unique_id``
782 but the value for ``unique_id_uses_count`` differs from the
783 ``unique_id_uses_count`` value declared by the very first
784 ``register`` call for that ``unique_id``.
785 """
786 self._events.unregister(
787 event_name,
788 handler=handler,
789 unique_id=unique_id,
790 unique_id_uses_count=unique_id_uses_count,
791 )
793 def emit(self, event_name, **kwargs):
794 return self._events.emit(event_name, **kwargs)
796 def emit_first_non_none_response(self, event_name, **kwargs):
797 responses = self._events.emit(event_name, **kwargs)
798 return first_non_none_response(responses)
800 def get_component(self, name):
801 try:
802 return self._components.get_component(name)
803 except ValueError:
804 if name in ['endpoint_resolver', 'exceptions_factory']:
805 warnings.warn(
806 'Fetching the %s component with the get_component() '
807 'method is deprecated as the component has always been '
808 'considered an internal interface of botocore' % name,
809 DeprecationWarning,
810 )
811 return self._internal_components.get_component(name)
812 raise
814 def _get_internal_component(self, name):
815 # While this method may be called by botocore classes outside of the
816 # Session, this method should **never** be used by a class that lives
817 # outside of botocore.
818 return self._internal_components.get_component(name)
820 def _register_internal_component(self, name, component):
821 # While this method may be called by botocore classes outside of the
822 # Session, this method should **never** be used by a class that lives
823 # outside of botocore.
824 return self._internal_components.register_component(name, component)
826 def register_component(self, name, component):
827 self._components.register_component(name, component)
829 def lazy_register_component(self, name, component):
830 self._components.lazy_register_component(name, component)
832 def create_client(
833 self,
834 service_name,
835 region_name=None,
836 api_version=None,
837 use_ssl=True,
838 verify=None,
839 endpoint_url=None,
840 aws_access_key_id=None,
841 aws_secret_access_key=None,
842 aws_session_token=None,
843 config=None,
844 ):
845 """Create a botocore client.
847 :type service_name: string
848 :param service_name: The name of the service for which a client will
849 be created. You can use the ``Session.get_available_services()``
850 method to get a list of all available service names.
852 :type region_name: string
853 :param region_name: The name of the region associated with the client.
854 A client is associated with a single region.
856 :type api_version: string
857 :param api_version: The API version to use. By default, botocore will
858 use the latest API version when creating a client. You only need
859 to specify this parameter if you want to use a previous API version
860 of the client.
862 :type use_ssl: boolean
863 :param use_ssl: Whether or not to use SSL. By default, SSL is used.
864 Note that not all services support non-ssl connections.
866 :type verify: boolean/string
867 :param verify: Whether or not to verify SSL certificates.
868 By default SSL certificates are verified. You can provide the
869 following values:
871 * False - do not validate SSL certificates. SSL will still be
872 used (unless use_ssl is False), but SSL certificates
873 will not be verified.
874 * path/to/cert/bundle.pem - A filename of the CA cert bundle to
875 uses. You can specify this argument if you want to use a
876 different CA cert bundle than the one used by botocore.
878 :type endpoint_url: string
879 :param endpoint_url: The complete URL to use for the constructed
880 client. Normally, botocore will automatically construct the
881 appropriate URL to use when communicating with a service. You can
882 specify a complete URL (including the "http/https" scheme) to
883 override this behavior. If this value is provided, then
884 ``use_ssl`` is ignored.
886 :type aws_access_key_id: string
887 :param aws_access_key_id: The access key to use when creating
888 the client. This is entirely optional, and if not provided,
889 the credentials configured for the session will automatically
890 be used. You only need to provide this argument if you want
891 to override the credentials used for this specific client.
893 :type aws_secret_access_key: string
894 :param aws_secret_access_key: The secret key to use when creating
895 the client. Same semantics as aws_access_key_id above.
897 :type aws_session_token: string
898 :param aws_session_token: The session token to use when creating
899 the client. Same semantics as aws_access_key_id above.
901 :type config: botocore.client.Config
902 :param config: Advanced client configuration options. If a value
903 is specified in the client config, its value will take precedence
904 over environment variables and configuration values, but not over
905 a value passed explicitly to the method. If a default config
906 object is set on the session, the config object used when creating
907 the client will be the result of calling ``merge()`` on the
908 default config with the config provided to this call.
910 :rtype: botocore.client.BaseClient
911 :return: A botocore client instance
913 """
914 default_client_config = self.get_default_client_config()
915 # If a config is provided and a default config is set, then
916 # use the config resulting from merging the two.
917 if config is not None and default_client_config is not None:
918 config = default_client_config.merge(config)
919 # If a config was not provided then use the default
920 # client config from the session
921 elif default_client_config is not None:
922 config = default_client_config
924 region_name = self._resolve_region_name(region_name, config)
926 # Figure out the verify value base on the various
927 # configuration options.
928 if verify is None:
929 verify = self.get_config_variable('ca_bundle')
931 if api_version is None:
932 api_version = self.get_config_variable('api_versions').get(
933 service_name, None
934 )
936 loader = self.get_component('data_loader')
937 event_emitter = self.get_component('event_emitter')
938 response_parser_factory = self.get_component('response_parser_factory')
939 if config is not None and config.signature_version is UNSIGNED:
940 credentials = None
941 elif (
942 aws_access_key_id is not None and aws_secret_access_key is not None
943 ):
944 credentials = botocore.credentials.Credentials(
945 access_key=aws_access_key_id,
946 secret_key=aws_secret_access_key,
947 token=aws_session_token,
948 )
949 elif self._missing_cred_vars(aws_access_key_id, aws_secret_access_key):
950 raise PartialCredentialsError(
951 provider='explicit',
952 cred_var=self._missing_cred_vars(
953 aws_access_key_id, aws_secret_access_key
954 ),
955 )
956 else:
957 credentials = self.get_credentials()
958 auth_token = self.get_auth_token()
959 endpoint_resolver = self._get_internal_component('endpoint_resolver')
960 exceptions_factory = self._get_internal_component('exceptions_factory')
961 config_store = copy.copy(self.get_component('config_store'))
962 user_agent_creator = self.get_component('user_agent_creator')
963 # Session configuration values for the user agent string are applied
964 # just before each client creation because they may have been modified
965 # at any time between session creation and client creation.
966 user_agent_creator.set_session_config(
967 session_user_agent_name=self.user_agent_name,
968 session_user_agent_version=self.user_agent_version,
969 session_user_agent_extra=self.user_agent_extra,
970 )
971 defaults_mode = self._resolve_defaults_mode(config, config_store)
972 if defaults_mode != 'legacy':
973 smart_defaults_factory = self._get_internal_component(
974 'smart_defaults_factory'
975 )
976 smart_defaults_factory.merge_smart_defaults(
977 config_store, defaults_mode, region_name
978 )
980 self._add_configured_endpoint_provider(
981 client_name=service_name,
982 config_store=config_store,
983 )
985 client_creator = botocore.client.ClientCreator(
986 loader,
987 endpoint_resolver,
988 self.user_agent(),
989 event_emitter,
990 retryhandler,
991 translate,
992 response_parser_factory,
993 exceptions_factory,
994 config_store,
995 user_agent_creator=user_agent_creator,
996 )
997 client = client_creator.create_client(
998 service_name=service_name,
999 region_name=region_name,
1000 is_secure=use_ssl,
1001 endpoint_url=endpoint_url,
1002 verify=verify,
1003 credentials=credentials,
1004 scoped_config=self.get_scoped_config(),
1005 client_config=config,
1006 api_version=api_version,
1007 auth_token=auth_token,
1008 )
1009 monitor = self._get_internal_component('monitor')
1010 if monitor is not None:
1011 monitor.register(client.meta.events)
1012 return client
1014 def _resolve_region_name(self, region_name, config):
1015 # Figure out the user-provided region based on the various
1016 # configuration options.
1017 if region_name is None:
1018 if config and config.region_name is not None:
1019 region_name = config.region_name
1020 else:
1021 region_name = self.get_config_variable('region')
1023 validate_region_name(region_name)
1024 # For any client that we create in retrieving credentials
1025 # we want to create it using the same region as specified in
1026 # creating this client. It is important to note though that the
1027 # credentials client is only created once per session. So if a new
1028 # client is created with a different region, its credential resolver
1029 # will use the region of the first client. However, that is not an
1030 # issue as of now because the credential resolver uses only STS and
1031 # the credentials returned at regional endpoints are valid across
1032 # all regions in the partition.
1033 self._last_client_region_used = region_name
1034 return region_name
1036 def _resolve_defaults_mode(self, client_config, config_store):
1037 mode = config_store.get_config_variable('defaults_mode')
1039 if client_config and client_config.defaults_mode:
1040 mode = client_config.defaults_mode
1042 default_config_resolver = self._get_internal_component(
1043 'default_config_resolver'
1044 )
1045 default_modes = default_config_resolver.get_default_modes()
1046 lmode = mode.lower()
1047 if lmode not in default_modes:
1048 raise InvalidDefaultsMode(
1049 mode=mode, valid_modes=', '.join(default_modes)
1050 )
1052 return lmode
1054 def _add_configured_endpoint_provider(self, client_name, config_store):
1055 chain = ConfiguredEndpointProvider(
1056 full_config=self.full_config,
1057 scoped_config=self.get_scoped_config(),
1058 client_name=client_name,
1059 )
1060 config_store.set_config_provider(
1061 logical_name='endpoint_url',
1062 provider=chain,
1063 )
1065 def _missing_cred_vars(self, access_key, secret_key):
1066 if access_key is not None and secret_key is None:
1067 return 'aws_secret_access_key'
1068 if secret_key is not None and access_key is None:
1069 return 'aws_access_key_id'
1070 return None
1072 def get_available_partitions(self):
1073 """Lists the available partitions found on disk
1075 :rtype: list
1076 :return: Returns a list of partition names (e.g., ["aws", "aws-cn"])
1077 """
1078 resolver = self._get_internal_component('endpoint_resolver')
1079 return resolver.get_available_partitions()
1081 def get_partition_for_region(self, region_name):
1082 """Lists the partition name of a particular region.
1084 :type region_name: string
1085 :param region_name: Name of the region to list partition for (e.g.,
1086 us-east-1).
1088 :rtype: string
1089 :return: Returns the respective partition name (e.g., aws).
1090 """
1091 resolver = self._get_internal_component('endpoint_resolver')
1092 return resolver.get_partition_for_region(region_name)
1094 def get_available_regions(
1095 self, service_name, partition_name='aws', allow_non_regional=False
1096 ):
1097 """Lists the region and endpoint names of a particular partition.
1099 :type service_name: string
1100 :param service_name: Name of a service to list endpoint for (e.g., s3).
1101 This parameter accepts a service name (e.g., "elb") or endpoint
1102 prefix (e.g., "elasticloadbalancing").
1104 :type partition_name: string
1105 :param partition_name: Name of the partition to limit endpoints to.
1106 (e.g., aws for the public AWS endpoints, aws-cn for AWS China
1107 endpoints, aws-us-gov for AWS GovCloud (US) Endpoints, etc.
1109 :type allow_non_regional: bool
1110 :param allow_non_regional: Set to True to include endpoints that are
1111 not regional endpoints (e.g., s3-external-1,
1112 fips-us-gov-west-1, etc).
1113 :return: Returns a list of endpoint names (e.g., ["us-east-1"]).
1114 """
1115 resolver = self._get_internal_component('endpoint_resolver')
1116 results = []
1117 try:
1118 service_data = self.get_service_data(service_name)
1119 endpoint_prefix = service_data['metadata'].get(
1120 'endpointPrefix', service_name
1121 )
1122 results = resolver.get_available_endpoints(
1123 endpoint_prefix, partition_name, allow_non_regional
1124 )
1125 except UnknownServiceError:
1126 pass
1127 return results
1130class ComponentLocator:
1131 """Service locator for session components."""
1133 def __init__(self):
1134 self._components = {}
1135 self._deferred = {}
1137 def get_component(self, name):
1138 if name in self._deferred:
1139 factory = self._deferred[name]
1140 self._components[name] = factory()
1141 # Only delete the component from the deferred dict after
1142 # successfully creating the object from the factory as well as
1143 # injecting the instantiated value into the _components dict.
1144 try:
1145 del self._deferred[name]
1146 except KeyError:
1147 # If we get here, it's likely that get_component was called
1148 # concurrently from multiple threads, and another thread
1149 # already deleted the entry. This means the factory was
1150 # probably called twice, but cleaning up the deferred entry
1151 # should not crash outright.
1152 pass
1153 try:
1154 return self._components[name]
1155 except KeyError:
1156 raise ValueError("Unknown component: %s" % name)
1158 def register_component(self, name, component):
1159 self._components[name] = component
1160 try:
1161 del self._deferred[name]
1162 except KeyError:
1163 pass
1165 def lazy_register_component(self, name, no_arg_factory):
1166 self._deferred[name] = no_arg_factory
1167 try:
1168 del self._components[name]
1169 except KeyError:
1170 pass
1173class SessionVarDict(MutableMapping):
1174 def __init__(self, session, session_vars):
1175 self._session = session
1176 self._store = copy.copy(session_vars)
1178 def __getitem__(self, key):
1179 return self._store[key]
1181 def __setitem__(self, key, value):
1182 self._store[key] = value
1183 self._update_config_store_from_session_vars(key, value)
1185 def __delitem__(self, key):
1186 del self._store[key]
1188 def __iter__(self):
1189 return iter(self._store)
1191 def __len__(self):
1192 return len(self._store)
1194 def _update_config_store_from_session_vars(
1195 self, logical_name, config_options
1196 ):
1197 # This is for backwards compatibility. The new preferred way to
1198 # modify configuration logic is to use the component system to get
1199 # the config_store component from the session, and then update
1200 # a key with a custom config provider(s).
1201 # This backwards compatibility method takes the old session_vars
1202 # list of tuples and and transforms that into a set of updates to
1203 # the config_store component.
1204 config_chain_builder = ConfigChainFactory(session=self._session)
1205 config_name, env_vars, default, typecast = config_options
1206 config_store = self._session.get_component('config_store')
1207 config_store.set_config_provider(
1208 logical_name,
1209 config_chain_builder.create_config_chain(
1210 instance_name=logical_name,
1211 env_var_names=env_vars,
1212 config_property_names=config_name,
1213 default=default,
1214 conversion_func=typecast,
1215 ),
1216 )
1219class SubsetChainConfigFactory:
1220 """A class for creating backwards compatible configuration chains.
1222 This class can be used instead of
1223 :class:`botocore.configprovider.ConfigChainFactory` to make it honor the
1224 methods argument to get_config_variable. This class can be used to filter
1225 out providers that are not in the methods tuple when creating a new config
1226 chain.
1227 """
1229 def __init__(self, session, methods, environ=None):
1230 self._factory = ConfigChainFactory(session, environ)
1231 self._supported_methods = methods
1233 def create_config_chain(
1234 self,
1235 instance_name=None,
1236 env_var_names=None,
1237 config_property_name=None,
1238 default=None,
1239 conversion_func=None,
1240 ):
1241 """Build a config chain following the standard botocore pattern.
1243 This config chain factory will omit any providers not in the methods
1244 tuple provided at initialization. For example if given the tuple
1245 ('instance', 'config',) it will not inject the environment provider
1246 into the standard config chain. This lets the botocore session support
1247 the custom ``methods`` argument for all the default botocore config
1248 variables when calling ``get_config_variable``.
1249 """
1250 if 'instance' not in self._supported_methods:
1251 instance_name = None
1252 if 'env' not in self._supported_methods:
1253 env_var_names = None
1254 if 'config' not in self._supported_methods:
1255 config_property_name = None
1256 return self._factory.create_config_chain(
1257 instance_name=instance_name,
1258 env_var_names=env_var_names,
1259 config_property_names=config_property_name,
1260 default=default,
1261 conversion_func=conversion_func,
1262 )
1265def get_session(env_vars=None):
1266 """
1267 Return a new session object.
1268 """
1269 return Session(env_vars)